Как получить остатки товаров в разрезе партий в запросе 1С

Программист 1С v8.3 (Управляемые формы) Управленческий учет Торговля и дистрибуция
← На главную

При работе с регистрами накопления в 1С программисты часто сталкиваются с необходимостью детализации данных. Одной из типичных задач является получение остатков не только по номенклатуре, но и в разрезе конкретных партий поступления. В этой статье мы разберем, как правильно составить запрос к виртуальной таблице остатков, почему в большинстве случаев не требуется ручная группировка и как корректно обработать пустые значения партий.

Анализ исходной проблемы

Рассмотрим ситуацию: у нас есть регистр накопления ЗапасыНаСкладах, который имеет измерения Организация, Номенклатура, Характеристика, Партия, СтруктурнаяЕдиница и Ячейка. Автор исходного запроса столкнулся с тем, что данные выводятся общей суммой по номенклатуре, игнорируя разбиение по партиям.

Первоначальный код запроса выглядел следующим образом:


ВЫБРАТЬ
    ОстаткиНаСкладе.Номенклатура КАК Номенклатура,
    ОстаткиНаСкладе.КоличествоОстаток КАК Остаток
ИЗ
    РегистрНакопления.ЗапасыНаСкладах.Остатки(&Дата, 
        Организация = &Организация 
        И Номенклатура = &Номенклатура 
        И СтруктурнаяЕдиница = &СтруктурнаяЕдиница) КАК ОстаткиНаСкладе

Проанализируем этот код. В секции ВЫБРАТЬ указана только Номенклатура. Несмотря на то что в самом регистре данные хранятся с детализацией до партии, виртуальная таблица Остатки работает по принципу автоматической свертки итогов по тем полям, которые выбрал разработчик.

Решение проблемы: добавление измерения в выборку

Для того чтобы система "развернула" остатки по партиям, нам необходимо просто добавить поле Партия в список выбираемых полей. Рассмотрим исправленный вариант запроса:


ВЫБРАТЬ
    ОстаткиНаСкладе.Номенклатура КАК Номенклатура,
    ОстаткиНаСкладе.Партия КАК Партия,
    ОстаткиНаСкладе.КоличествоОстаток КАК Остаток
ИЗ
    РегистрНакопления.ЗапасыНаСкладах.Остатки(&Дата, 
        Организация = &Организация 
        И Номенклатура = &Номенклатура 
        И СтруктурнаяЕдиница = &СтруктурнаяЕдиница) КАК ОстаткиНаСкладе

Как только мы добавляем ОстаткиНаСкладе.Партия, платформа 1С понимает, что итоги нужно рассчитать в разрезе партий. Если для одной номенклатуры на складе числится три разные партии, мы получим три строки в результате запроса.

Механика работы виртуальной таблицы и группировка

Выясним причину, по которой нам не нужно использовать конструкцию СГРУППИРОВАТЬ ПО. Виртуальные таблицы 1С (Остатки, Обороты, ОстаткиИОбороты) — это не физические таблицы в базе данных SQL, а сложные подзапросы, которые генерирует платформа.

Проанализируем ситуацию подробнее: когда мы обращаемся к РегистрНакопления.ИмяРегистра.Остатки, 1С обращается к таблицам итогов. Платформа анализирует список полей в секции ВЫБРАТЬ и автоматически добавляет их в SQL-запрос в секцию GROUP BY. Таким образом, явное использование группировки в языке запросов 1С при работе с виртуальными таблицами является избыточным и может даже незначительно замедлить выполнение запроса, так как системе придется выполнять двойную работу.

Важное исключение: группировка СГРУППИРОВАТЬ ПО понадобится только в том случае, если вы используете агрегатные функции (например, СУММА или МАКСИМУМ) над полями, которые сами не являются ресурсами виртуальной таблицы, или если вы соединяете таблицу остатков с другими таблицами.

Работа с пустыми партиями

Часто возникает вопрос: как отобразить в отчете остатки, у которых партия не указана (например, товар был оприходован до начала ведения партионного учета)? В системе 1С пустая партия — это не NULL, а ПустаяСсылка соответствующего типа документа или справочника.

Рассмотрим, как сделать вывод данных более дружелюбным для пользователя с помощью функции ЕСТЬNULL или проверки на пустую ссылку:


ВЫБРАТЬ
    ОстаткиНаСкладе.Номенклатура КАК Номенклатура,
    ВЫБОР
        КОГДА ОстаткиНаСкладе.Партия = ЗНАЧЕНИЕ(Документ.ПоступлениеТоваровУслуг.ПустаяСсылка)
            ТОГДА "--- Без партии ---"
        ИНАЧЕ ОстаткиНаСкладе.Партия
    КОНЕЦ КАК ПартияПредставление,
    ОстаткиНаСкладе.КоличествоОстаток КАК Остаток
ИЗ
    РегистрНакопления.ЗапасыНаСкладах.Остатки(&Дата, ) КАК ОстаткиНаСкладе

Такой подход позволит четко отделить позиции без партий от остальных. Также стоит помнить, что использование ЕСТЬNULL(Партия, Неопределено) может быть полезно при левых соединениях, но в параметрах виртуальной таблицы остатков измерения обычно всегда заполнены хотя бы пустой ссылкой.

Оптимизация: Параметры виртуальной таблицы vs ГДЕ

Разберем критически важный аспект производительности. Никогда не используйте секцию ГДЕ для фильтрации данных по измерениям в виртуальных таблицах, если это можно сделать через параметры.

Сравним два подхода:

  1. Неправильно: ИЗ РегистрНакопления.Товары.Остатки() ГДЕ Номенклатура = &Номенклатура. В этом случае система сначала получит остатки ПО ВСЕМ товарам из базы, и только потом отфильтрует нужный.
  2. Правильно: ИЗ РегистрНакопления.Товары.Остатки(, Номенклатура = &Номенклатура). В этом случае фильтр накладывается на уровне SQL-запроса к таблицам итогов, что работает в десятки и сотни раз быстрее.

Проанализируем также порядок измерений. Индексы в таблицах итогов строятся по порядку измерений, заданному в метаданных. Старайтесь указывать фильтры в параметрах виртуальной таблицы в том же порядке, в котором измерения идут в самом регистре (обычно это Организация -> Номенклатура -> Склад).

Проблема "зависших" остатков

Иногда при переходе к учету по партиям программист видит странную картину: общий остаток по номенклатуре равен нулю, но при добавлении в запрос поля Партия вылезают строки с положительными и отрицательными значениями (например, Партия №1: +10 шт, Партия №2: -10 шт).

Выясним причину: это происходит из-за ошибок в логике проведения документов, когда приход был по одной партии, а списание — по другой (или вовсе без указания партии). Использование детализации по партиям в запросе — отличный способ выявить такие "хвосты" и провести корректировку регистров. Если бы мы не добавили Партия в ВЫБРАТЬ, виртуальная таблица просто сложила бы +10 и -10, показав нам итоговый ноль, что скрыло бы проблему в учете.

← На главную