Как в 1С отфильтровать остатки по реквизиту регистратора и сохранить вывод всех дат

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

При построении отчетов по складским остаткам программисты часто сталкиваются с задачей: нужно исключить из расчета остатков определенные документы (например, реализации без указанного заказа), но при этом продолжать выводить остатки на каждый день выбранного периода, даже если движений в этот день не было. Попытка решить это «в лоб» через условие в виртуальной таблице ОстаткиИОбороты приводит к тому, что из результата исчезают все дни, в которые не было оборотов по «разрешенным» документам.

Разберем детально, почему это происходит и какими способами можно добиться желаемого результата, сохранив при этом производительность системы.

Почему стандартный фильтр ГДЕ ломает отчет

Проанализируем работу виртуальной таблицы РегистрНакопления.ИмяРегистра.ОстаткиИОбороты. Когда мы указываем периодичность День, платформа 1С автоматически генерирует строки для каждой даты внутри периода, если на эту дату есть какой-либо остаток или движение. Однако, если мы добавляем условие в блоке ГДЕ, например: ГДЕ Регистратор.Сделка <> НЕОПРЕДЕЛЕНО, логика выборки меняется.

В те дни, когда движений не было, поле Регистратор содержит значение NULL. Любое сравнение NULL с каким-либо значением (в том числе с НЕОПРЕДЕЛЕНО) дает результат «Ложь». В итоге система отсекает все записи за «тихие» дни, и отчет превращается в список дат, когда были конкретные отфильтрованные движения. Чтобы этого избежать, нужно использовать альтернативные подходы к построению запроса.

Решение 1: Использование временных таблиц и таблицы оборотов

Самый надежный способ — это разделить получение начального остатка и расчет оборотов. Рассмотрим пошагово, как это реализовать:

  1. Получение начального остатка. В первую временную таблицу мы выбираем остаток на начало периода. Здесь нам не важны реквизиты регистратора, так как это накопленный итог.
  2. Получение оборотов. Во вторую временную таблицу мы выбираем данные из физической таблицы регистра (или таблицы оборотов), накладывая наше условие на Сделку.
  3. Соединение с календарем. Полученные данные соединяются с производственным календарем или таблицей дат, чтобы обеспечить непрерывность.

Пример кода запроса для реализации этой логики:


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

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

Проанализируем этот код: во второй временной таблице мы явно прописываем логику исключения «плохих» документов. Мы используем физическую таблицу регистра, так как только в ней мы можем гарантированно проверить реквизиты регистратора, не теряя производительность на тяжелых соединениях виртуальных таблиц.

Решение 2: Сборка итогового остатка на каждый день

После того как мы получили чистые обороты, нам нужно объединить их с начальным остатком и развернуть по дням. Для этого лучше всего использовать таблицу дат (например, из регламентированного производственного календаря).

Рассмотрим метод расчета остатка через соединение:


ВЫБРАТЬ
    Даты.ДатаКалендаря КАК Период,
    ВсеДанные.Склад,
    ВсеДанные.Номенклатура,
    СУММА(ВсеДанные.Количество) КАК КонечныйОстаток
ИЗ
    РегистрСведений.РегламентированныйПроизводственныйКалендарь КАК Даты
    ЛЕВОЕ СОЕДИНЕНИЕ (
        ВЫБРАТЬ Склад, Номенклатура, НачОстаток КАК Количество, &ДатаНачала КАК Период Из ВТ_НачальныйОстаток
        ОБЪЕДИНИТЬ ВСЕ
        ВЫБРАТЬ Склад, Номенклатура, Оборот, Период Из ВТ_Обороты
    ) КАК ВсеДанные
    ПО Даты.ДатаКалендаря >= ВсеДанные.Период
ГДЕ
    Даты.ДатаКалендаря МЕЖДУ &ДатаНачала И &ДатаОкончания
СГРУППИРОВАТЬ ПО
    Даты.ДатаКалендаря,
    ВсеДанные.Склад,
    ВсеДанные.Номенклатура

В этом запросе мы используем накопительный итог: для каждой даты календаря мы суммируем все движения, которые произошли ДО этой даты (включая начальный остаток). Это позволяет получить корректный остаток на каждый день, даже если в этот день не было разрешенных документов.

Важные технические нюансы

При реализации подобных отчетов необходимо учитывать несколько особенностей платформы 1С:

  1. Различие NULL и Неопределено. В языке запросов 1С поле Регистратор.Сделка может быть NULL (если регистратор не выбран или это виртуальная запись), ПустаяСсылка или Неопределено. Проверяйте тип значения через оператор ССЫЛКА или сравнивайте с пустой ссылкой конкретного типа, чтобы избежать логических ошибок.
  2. Производительность. Обращение через точку к реквизитам регистратора (Регистратор.Сделка) заставляет систему выполнять LEFT JOIN с таблицами всех возможных документов, входящих в тип Регистратор. Если документов много, это существенно замедлит запрос. В таких случаях рекомендуется сначала отобрать нужные регистраторы во временную таблицу по типам.
  3. Система компоновки данных (СКД). Если вы используете СКД, можно упростить задачу. На закладке «Настройки» для поля периода (День) выберите Тип дополнения — День. Это заставит СКД саму добавлять недостающие даты в отчет, даже если ваш запрос их не вернул из-за фильтра ГДЕ. Однако помните, что расчет остатков в этом случае должен быть настроен на закладке «Роли» для соответствующих полей.

Рекомендация по проектированию

Если задача «получать остатки без учета определенных документов» возникает регулярно, это может быть признаком того, что структура метаданных не вполне соответствует бизнес-задаче. Рассмотрим возможность добавления в регистр накопления дополнительного измерения (например, Проект или Заказ) или отдельного реквизита, по которому можно будет фильтровать данные без обращения к объекту-регистратору. Это значительно ускорит работу отчетов и упростит написание запросов.

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

← На главную