Примеры работы с виртуальными таблицами остатков

Примеры работы с виртуальными таблицами остатков на языке программирования 1С:Предприятие. Примеры позволяют быстро разобраться в вопросе и использовать код в своих разработках

Базовый запрос к виртуальной таблице остатков регистра накопления

// Получение остатков товаров на текущий момент
Процедура ПолучитьОстаткиТоваров()

    Запрос = Новый Запрос;
    Запрос.Текст = "
    |ВЫБРАТЬ
    |   ОстаткиТоваров.Номенклатура,
    |   ОстаткиТоваров.Склад,
    |   ОстаткиТоваров.Количество Остаток,
    |   ОстаткиТоваров.Сумма
    |ИЗ
    |   РегистрНакопления.ТоварыНаСкладах.Остатки КАК ОстаткиТоваров
    |ГДЕ
    |   ОстаткиТоваров.Количество > 0";
    
    Результат = Запрос.Выполнить().Выгрузить();
    
    Для Каждого Строка Из Результат Цикл
        Сообщить("Товар: " + Строка.Номенклатура + 
                 ", Склад: " + Строка.Склад + 
                 ", Остаток: " + Строка.Остаток);
    КонецЦикла;
    
КонецПроцедуры

Остатки на конкретную дату

// Получение остатков на определенную дату с условиями отбора
Процедура ПолучитьОстаткиНаДату(ДатаОстатков)

    Запрос = Новый Запрос;
    Запрос.Текст = "
    |ВЫБРАТЬ
    |   ОстаткиТоваров.Номенклатура,
    |   ОстаткиТоваров.Склад,
    |   ОстаткиТоваров.Количество КАК Остаток,
    |   ОстаткиТоваров.Сумма КАК Сумма
    |ИЗ
    |   РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаОстатков) КАК ОстаткиТоваров
    |ГДЕ
    |   ОстаткиТоваров.Количество > 0";
    
    Запрос.УстановитьПараметр("ДатаОстатков", ДатаОстатков);
    
    Результат = Запрос.Выполнить().Выгрузить();
    
    Сообщить("Остатки на " + Формат(ДатаОстатков, "ДФ=yyyy-MM-dd") + ":");
    Для Каждого Строка Из Результат Цикл
        Сообщить(Строка.Номенклатура + ": " + Строка.Остаток + " шт.");
    КонецЦикла;
    
КонецПроцедуры

Остатки с отбором по измерениям

// Получение остатков с фильтрацией по конкретным значениям измерений
Процедура ПолучитьОстаткиПоСкладуИТовару()

    Запрос = Новый Запрос;
    Запрос.Текст = "
    |ВЫБРАТЬ
    |   ОстаткиТоваров.Номенклатура,
    |   ОстаткиТоваров.Количество КАК Остаток,
    |   ОстаткиТоваров.Сумма КАК Сумма
    |ИЗ
    |   РегистрНакопления.ТоварыНаСкладах.Остатки(,, Склад = &Склад) КАК ОстаткиТоваров
    |ГДЕ
    |   ОстаткиТоваров.Номенклатура = &Номенклатура";
    
    // Устанавливаем параметры отбора
    Запрос.УстановитьПараметр("Склад", Справочники.Склады.НайтиПоНаименованию("Основной склад"));
    Запрос.УстановитьПараметр("Номенклатура", Справочники.Номенклатура.НайтиПоНаименованию("Ноутбук"));
    
    Результат = Запрос.Выполнить().Выгрузить();
    
    Если Результат.Количество() > 0 Тогда
        Строка = Результат[0];
        Сообщить("Остаток ноутбуков на основном складе: " + Строка.Остаток + " шт.");
    Иначе
        Сообщить("Товар отсутствует на складе");
    КонецЕсли;
    
КонецПроцедуры

Остатки по нескольким измерениям с группировкой

// Получение остатков с группировкой по измерениям
Процедура ПолучитьОстаткиСГруппировкой()

    Запрос = Новый Запрос;
    Запрос.Текст = "
    |ВЫБРАТЬ
    |   ОстаткиТоваров.Склад,
    |   СУММА(ОстаткиТоваров.Количество) КАК ВсегоТоваров,
    |   СУММА(ОстаткиТоваров.Сумма) КАК ОбщаяСумма
    |ИЗ
    |   РегистрНакопления.ТоварыНаСкладах.Остатки(,,) КАК ОстаткиТоваров
    |ГДЕ
    |   ОстаткиТоваров.Количество > 0
    |СГРУППИРОВАТЬ ПО
    |   ОстаткиТоваров.Склад";
    
    Результат = Запрос.Выполнить().Выгрузить();
    
    Сообщико("Сводка остатков по складам:");
    Для Каждого Строка Из Результат Цикл
        Сообщить("Склад: " + Строка.Склад + 
                 ", количество товаров: " + Строка.ВсегоТоваров + 
                 ", сумма: " + Строка.ОбщаяСумма);
    КонецЦикла;
    
КонецПроцедуры

Остатки с использованием периода (срез последних)

// Получение остатков на конец каждого месяца
Процедура ПолучитьОстаткиНаКонцыМесяцев()

    Запрос = Новый Запрос;
    Запрос.Текст = "
    |ВЫБРАТЬ
    |   Номенклатура.Ссылка КАК Товар,
    |   Номенклатура.Наименование,
    |   Периоды.Период,
    |   Остатки.КонечныйОстаток КАК Остаток,
    |   Остатки.КонечныйОстатокСумма КАК Сумма
    |ИЗ
    |   Справочник.Номенклатура КАК Номенклатура
    |       ЛЕВОЕ СОЕДИНЕНИЕ (
    |           ВЫБРАТЬ РАЗЛИЧНЫЕ
    |               КОНЕЦПЕРИОДА(&ДатаНачала, МЕСЯЦ) КАК Период
    |           ИЗ
    |               РегистрНакопления.ТоварыНаСкладах.Остатки
    |           ГДЕ
    |               КОНЕЦПЕРИОДА(&ДатаНачала, МЕСЯЦ) МЕЖДУ &ДатаНачала И &ДатаОкончания
    |       ) КАК Периоды
    |       ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(Периоды.Период,)
    |       КАК Остатки
    |       ПО Номенклатура.Ссылка = Остатки.Номенклатура";
    
    Запрос.УстановитьПараметр("ДатаНачала", НачалоГода(ТекущаяДата()));
    Запрос.УстановитьПараметр("ДатаОкончания", ТекущаяДата());
    
    Результат = Запрос.Выполнить().Выгрузить();
    
    // Выводим результат
    Для Каждого Строка Из Результат Цикл
        Сообщить(Строка.Наименование + " на " + Строка.Период + ": " + 
                 Строка.Остаток + " шт. на сумму " + Строка.Сумма);
    КонецЦикла;
    
КонецПроцедуры

Объединение остатков с оборотами

// Получение начальных остатков, оборотов и конечных остатков
Функция ПолучитьОстаткиСОборОтами(ДатаНачала, ДатаОкончания)

    Запрос = Новый Запрос;
    Запрос.Текст = "
    |ВЫБРАТЬ
    |   Движения.Номенклатура,
    |   СУММА(Движения.Количество) КАК ОборотПриход,
    |   СУММА(Движения.Сумма) КАК ОборотСумма
    |ПОМЕСТИТЬ ВТ_Обороты
    |ИЗ
    |   РегистрНакопления.ТоварыНаСкладах.Обороты(&ДатаНачала, &ДатаОкончания, ,) КАК Движения
    |ГДЕ
    |   Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
    |СГРУППИРОВАТЬ ПО
    |   Движения.Номенклатура";
    
    Запрос.УстановитьПараметр("ДатаНачала", ДатаНачала);
    Запрос.УстановитьПараметр("ДатаОкончания", ДатаОкончания);
    
    Результат = Запрос.Выполнить().Выгрузить();
    
    // Получаем начальные остатки
    ЗапросОстатки = Новый Запрос;
    ЗапросОстатки.Текст = "
    |ВЫБРАТЬ
    |   Остатки.Номенклатура,
    |   Остатки.Количество КАК НачальныйОстаток,
    |   Остатки.Сумма КАК НачальнаяСумма
    |ИЗ
    |   РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаНачала) КАК Остатки";
    
    ЗапросОстатки.УстановитьПараметр("ДатаНачала", ДатаНачала);
    НачальныеОстатки = ЗапросОстатки.Выполнить().Выгрузить();
    
    // Формируем итоговую таблицу
    ИтоговаяТаблица = Новый ТаблицаЗначений;
    ИтоговаяТаблица.Колонки.Добавить("Номенклатура");
    ИтоговаяТаблица.Колонки.Добавить("НачальныйОстаток");
    ИтоговаяТаблица.Колонки.Добавить("ОборотПриход");
    ИтоговаяТаблица.Колонки.Добавить("ОборотСумма");
    ИтоговаяТаблица.Колонки.Добавить("КонечныйОстаток");
    
    // Объединяем данные
    Для Каждого НачОст Из НачальныеОстатки Цикл
        НоваяСтрока = ИтоговаяТаблица.Добавить();
        НоваяСтрока.Номенклатура = НачОст.Номенклатура;
        НоваяСтрока.НачальныйОстаток = НачОст.НачальныйОстаток;
        
        // Ищем обороты
        Найденная = Результат.Найти(НачОст.Номенклатура, "Номенклатура");
        Если Найденная <> Неопределено Тогда
            НоваяСтрока.ОборотПриход = Найденная.ОборотПриход;
            НоваяСтрока.ОборотСумма = Найденная.ОборотСумма;
        КонецЕсли;
        
        НоваяСтрока.КонечныйОстаток = НоваяСтрока.НачальныйОстаток + 
                                       ?(ЗначениеЗаполнено(НоваяСтрока.ОборотПриход), 
                                         НоваяСтрока.ОборотПриход, 0);
    КонецЦикла;
    
    Возврат ИтоговаяТаблица;
    
КонецФункции

// Пример использования
Процедура АнализДвиженияТоваров()

    ДатаНач = НачалоМесяца(ТекущаяДата());
    ДатаКон = ТекущаяДата();
    
    Результат = ПолучитьОстаткиСОборОтами(ДатаНач, ДатаКон);
    
    Сообщить("Анализ движения товаров за период:");
    Для Каждого Строка Из Результат Цикл
        Сообщить(Строка.Номенклатура + 
                 ": нач=" + Строка.НачальныйОстаток +
                 ", приход=" + Строка.ОборотПриход +
                 ", кон=" + Строка.КонечныйОстаток);
    КонецЦикла;
    
КонецПроцедуры

Остатки с произвольным условием отбора

// Получение остатков с динамическими условиями отбора
Процедура ПолучитьОстаткиСУсловием(ТоварыПоКоторымНужныОстатки)

    Запрос = Новый Запрос;
    Запрос.Текст = "
    |ВЫБРАТЬ
    |   ОстаткиТоваров.Номенклатура,
    |   ОстаткиТоваров.Склад,
    |   ОстаткиТоваров.Количество КАК Остаток
    |ИЗ
    |   РегистрНакопления.ТоварыНаСкладах.Остатки(,,) КАК ОстаткиТоваров
    |ГДЕ
    |   ОстаткиТоваров.Номенклатура В (&СписокТоваров)
    |   И ОстаткиТоваров.Количество > 0";
    
    // Передаем массив товаров как параметр
    Запрос.УстановитьПараметр("СписокТоваров", ТоварыПоКоторымНужныОстатки);
    
    Результат = Запрос.Выполнить().Выгрузить();
    
    Для Каждого Строка Из Результат Цикл
        Сообщить(Строка.Номенклатура + " на складе " + 
                 Строка.Склад + ": " + Строка.Остаток);
    КонецЦикла;
    
КонецПроцедуры

Остатки с использованием временных таблиц

// Работа с остатками через временные таблицы для сложных расчетов
Процедура ОстаткиЧерезВременныеТаблицы()

    МенеджерВТ = Новый МенеджерВременныхТаблиц;
    
    // Шаг 1: создаем временную таблицу с нужными товарами
    Запрос1 = Новый Запрос;
    Запрос1.МенеджерВременныхТаблиц = МенеджерВТ;
    Запрос1.Текст = "
    |ВЫБРАТЬ
    |   Номенклатура.Ссылка КАК Товар,
    |   Номенклатура.Наименование
    |ПОМЕСТИТЬ ВТ_НужныеТовары
    |ИЗ
    |   Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |   Номенклатура.ЭтоГруппа = ЛОЖЬ
    |   И Номенклатура.Цена > 10000";
    
    Запрос1.Выполнить();
    
    // Шаг 2: получаем остатки для этих товаров
    Запрос2 = Новый Запрос;
    Запрос2.МенеджерВременныхТаблиц = МенеджерВТ;
    Запрос2.Текст = "
    |ВЫБРАТЬ
    |   ВТ_НужныеТовары.Товар,
    |   ВТ_НужныеТовары.Наименование,
    |   СУММА(Остатки.Количество) КАК ОбщийОстаток,
    |   СУММА(Остатки.Сумма) КАК ОбщаяСумма
    |ИЗ
    |   ВТ_НужныеТовары КАК ВТ_НужныеТовары
    |       ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаОтчета,) КАК Остатки
    |       ПО ВТ_НужныеТовары.Товар = Остатки.Номенклатура
    |СГРУППИРОВАТЬ ПО
    |   ВТ_НужныеТовары.Товар,
    |   ВТ_НужныеТовары.Наименование
    |ИМЕЮЩИЕ
    |   СУММА(Остатки.Количество) > 0";
    
    Запрос2.УстановитьПараметр("ДатаОтчета", ТекущаяДата());
    
    Результат = Запрос2.Выполнить().Выгрузить();
    
    Сообщить("Дорогие товары с ненулевыми остатками:");
    Для Каждого Строка Из Результат Цикл
        Сообщить(Строка.Наименование + ": остаток " + 
                 Строка.ОбщийОстаток + " шт. на сумму " + Строка.ОбщаяСумма);
    КонецЦикла;
    
КонецПроцедуры

Остатки регистра бухгалтерии

// Получение остатков по регистру бухгалтерии
Процедура ПолучитьОстаткиБухгалтерии()

    Запрос = Новый Запрос;
    Запрос.Текст = "
    |ВЫБРАТЬ
    |   Остатки.Счет КАК Счет,
    |   Остатки.Субконто1 КАК Субконто,
    |   Остатки.СуммаНачальныйОстаток КАК НачальныйОстаток,
    |   Остатки.СуммаКонечныйОстаток КАК КонечныйОстаток
    |ИЗ
    |   РегистрБухгалтерии.Хозрасчет.Остатки(&ДатаНачала, &ДатаОкончания) КАК Остатки
    |ГДЕ
    |   Остатки.Счет.Вид = ЗНАЧЕНИЕ(ВидСчета.Активный)
    |   И Остатки.СуммаКонечныйОстаток <> 0
    |СГРУППИРОВАТЬ ПО
    |   Остатки.Счет,
    |   Остатки.Субконто1";
    
    Запрос.УстановитьПараметр("ДатаНачала", НачалоМесяца(ТекущаяДата()));
    Запрос.УстановитьПараметр("ДатаОкончания", ТекущаяДата());
    
    Результат = Запрос.Выполнить().Выгрузить();
    
    Сообщико("Остатки по активным счетам:");
    Для Каждого Строка Из Результат Цикл
        Сообщить("Счет: " + Строка.Счет + 
                 ", субконто: " + Строка.Субконто +
                 ", начальный остаток: " + Строка.НачальныйОстаток +
                 ", конечный остаток: " + Строка.КонечныйОстаток);
    КонецЦикла;
    
КонецПроцедуры

Остатки регистра сведений (срез последних)

// Получение актуальных значений из регистра сведений (срез последних)
Процедура ПолучитьАктуальныеКурсыВалют()

    Запрос = Новый Запрос;
    Запрос.Текст = "
    |ВЫБРАТЬ
    |   Валюты.Ссылка КАК Валюта,
    |   Валюты.Наименование,
    |   Курсы.Курс,
    |   Курсы.Кратность,
    |   Курсы.Период
    |ИЗ
    |   Справочник.Валюты КАК Валюты
    |       ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют.СрезПоследних(&ДатаОтчета) КАК Курсы
    |       ПО Валюты.Ссылка = Курсы.Валюта";
    
    Запрос.УстановитьПараметр("ДатаОтчета", ТекущаяДата());
    
    Результат = Запрос.Выполнить().Выгрузить();
    
    Сообщить("Актуальные курсы валют на " + Формат(ТекущаяДата(), "ДФ=yyyy-MM-dd") + ":");
    Для Каждого Строка Из Результат Цикл
        Если ЗначениеЗаполнено(Строка.Курс) Тогда
            Сообщить(Строка.Наименование + ": " + Строка.Курс + 
                     " (кратность " + Строка.Кратность + ")");
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

// Срез последних на определенную дату
Процедура ПолучитьКурсыНаПрошлуюДату()

    ПрошлаяДата = ТекущаяДата() - 7;
    
    Запрос = Новый Запрос;
    Запрос.Текст = "
    |ВЫБРАТЬ
    |   Курсы.Валюта,
    |   Курсы.Курс,
    |   Курсы.Кратность,
    |   Курсы.Период
    |ИЗ
    |   РегистрСведений.КурсыВалют.СрезПоследних(&ДатаСреза) КАК Курсы";
    
    Запрос.УстановитьПараметр("ДатаСреза", ПрошлаяДата);
    
    Результат = Запрос.Выполнить().Выгрузить();
    
    Сообщить("Курсы валют на " + Формат(ПрошлаяДата, "ДФ=yyyy-MM-dd") + ":");
    Для Каждого Строка Из Результат Цикл
        Сообщить(Строка.Валюта + ": " + Строка.Курс);
    КонецЦикла;
    
КонецПроцедуры

Остатки с подзапросом для расчета средневзвешенной цены

// Расчет средневзвешенной цены товара на основе остатков
Процедура РассчитатьСредневзвешеннуюЦену()

    Запрос = Новый Запрос;
    Запрос.Текст = "
    |ВЫБРАТЬ
    |   Остатки.Номенклатура,
    |   СУММА(Остатки.Количество) КАК ВсегоКоличество,
    |   СУММА(Остатки.Сумма) КАК ОбщаяСумма,
    |   ВЫРАЗИТЬ(СУММА(Остатки.Сумма) / СУММА(Остатки.Количество) КАК ЧИСЛО(15, 2)) КАК СредняяЦена
    |ИЗ
    |   РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаОстатков,,) КАК Остатки
    |ГДЕ
    |   Остатки.Количество > 0
    |СГРУППИРОВАТЬ ПО
    |   Остатки.Номенклатура";
    
    Запрос.УстановитьПараметр("ДатаОстатков", ТекущаяДата());
    
    Результат = Запрос.Выполнить().Выгрузить();
    
    Сообщико("Средневзвешенные цены:");
    Для Каждого Строка Из Результат Цикл
        Если Строка.ВсегоКоличество > 0 Тогда
            Сообщить(Строка.Номенклатура + 
                     ": кол-во=" + Строка.ВсегоКоличество +
                     ", сумма=" + Строка.ОбщаяСумма +
                     ", ср.цена=" + Строка.СредняяЦена);
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Остатки с использованием периода (срез первых)

// Получение начальных остатков на начало периода (срез первых)
Процедура ПолучитьНачальныеОстатки()

    ДатаНачалаПериода = НачалоМесяца(ТекущаяДата());
    
    Запрос = Новый Запрос;
    Запрос.Текст = "
    |ВЫБРАТЬ
    |   Номенклатура.Ссылка КАК Товар,
    |   Номенклатура.Наименование,
    |   Остатки.Количество КАК НачальныйОстаток,
    |   Остатки.Сумма КАК НачальнаяСумма
    |ИЗ
    |   Справочник.Номенклатура КАК Номенклатура
    |       ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаНачала) КАК Остатки
    |       ПО Номенклатура.Ссылка = Остатки.Номенклатура
    |ГДЕ
    |   Номенклатура.ЭтоГруппа = ЛОЖЬ
    |   И Остатки.Количество > 0";
    
    Запрос.УстановитьПараметр("ДатаНачала", ДатаНачалаПериода);
    
    Результат = Запрос.Выполнить().Выгрузить();
    
    Сообщить("Начальные остатки на " + Формат(ДатаНачалаПериода, "ДФ=yyyy-MM-dd") + ":");
    Для Каждого Строка Из Результат Цикл
        Если ЗначениеЗаполнено(Строка.НачальныйОстаток) Тогда
            Сообщить(Строка.Наименование + ": " + Строка.НачальныйОстаток + 
                     " шт. на сумму " + Строка.НачальнаяСумма);
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Примечания

// Важные особенности работы с виртуальными таблицами остатков:
// 1. Виртуальная таблица Остатки рассчитывается на указанную дату
// 2. Синтаксис: РегистрНакопления.ИмяРегистра.Остатки(<Дата>, <ОтборПоИзмерениям>)
// 3. Параметр <ОтборПоИзмерениям> позволяет установить отборы (например, Товар = &Товар)
// 4. Для регистров бухгалтерии используется синтаксис с периодом: Остатки(<ДатаНач>, <ДатаКон>)
// 5. СрезПоследних и СрезПервых доступны для регистров сведений
// 6. Виртуальные таблицы остатков оптимальны по производительности
// 7. Для получения остатков на несколько дат используйте соединение с таблицей периодов
// 8. Виртуальная таблица Остатки не поддерживает группировку по измерениям (нужно группировать в запросе)
// 9. При работе с большими данными рекомендуется использовать условия в параметрах отбора, а не в WHERE
// 10. Для регистров накопления остатки рассчитываются как итог по движениям с учетом активных границ итогов
// 11. Использование виртуальной таблицы Остатки без параметров возвращает остатки на текущий момент
// 12. При массовых расчетах остатков на разные даты эффективнее использовать одну виртуальную таблицу с параметрами

Поделиться с друзьями
Smirnov code
Добавить комментарий