При разработке складских и управленческих отчетов нередко возникает задача: для каждой номенклатурной позиции определить последний документ поступления и дату этой операции. Типичной ошибкой начинающих разработчиков становится попытка применить агрегатную функцию МАКСИМУМ(Регистратор). В результате запрос либо возвращает не тот документ, либо даты и документы рассинхронизируются.
Разберем детально, почему так происходит, какие ограничения накладывает платформа 1С на работу с составными типами и как корректно решить задачу с помощью запросов и возможностей системы компоновки данных (СКД).
Поле Регистратор в регистрах накопления и сведений имеет составной ссылочный тип данных, так как движения могут формироваться документами разных видов (например, Поступление товаров и услуг, Авансовый отчет, Оприходование товаров, Перемещение товаров).
Выясним причину некорректной работы функции МАКСИМУМ для составных полей. На уровне базы данных СУБД выполняет сравнение составных ссылок не по дате документа, а по следующим правилам:
Таким образом, выражение МАКСИМУМ(Регистратор) вернет документ того типа, чей код таблицы в структуре метаданных конфигурации оказался старше, а не тот, который был проведен позже по времени. Именно поэтому дата из МАКСИМУМ(Период) может указывать на вчерашний день, а полученный через МАКСИМУМ(Регистратор) документ будет датирован прошлым месяцем.
Кроме того, обращение к реквизитам через точку от составного поля (например, Регистратор.Дата) заставляет платформу формировать неявные левые соединения (LEFT JOIN) со всеми таблицами документов, входящими в составной тип, что существенно снижает общую производительность отчета.
Классический и наиболее надежный способ получения последнего документа на уровне единого запроса — разделение задачи на поиск максимального момента движения и последующее получение самого документа.
Разберем по шагам логику построения такого запроса:
МАКСИМУМ(Период)) в разрезе необходимых измерений (Номенклатура, Склад) с отбором по виду движения Приход.МАКСИМУМ(Регистратор) — внутри одной и той же секунды это безопасно и исключит дублирование строк.Посмотрим на практический пример реализации:
ВЫБРАТЬ
Партии.Номенклатура КАК Номенклатура,
Партии.Склад КАК Склад,
МАКСИМУМ(Партии.Период) КАК МаксПериод
ПОМЕСТИТЬ ВТ_МаксимальныеДаты
ИЗ
РегистрНакопления.ПартииТоваровНаСкладах КАК Партии
ГДЕ
Партии.Период <= &КонецПериода
И Партии.Склад В (&ВыбСклады)
И Партии.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
СГРУППИРОВАТЬ ПО
Партии.Номенклатура,
Партии.Склад
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_МаксимальныеДаты.Номенклатура КАК Номенклатура,
ВТ_МаксимальныеДаты.Склад КАК Склад,
ВТ_МаксимальныеДаты.МаксПериод КАК ДатаПрихода,
МАКСИМУМ(Партии.Регистратор) КАК ПоследнийДокумент
ПОМЕСТИТЬ ВТ_ПоследниеПриходы
ИЗ
ВТ_МаксимальныеДаты КАК ВТ_МаксимальныеДаты
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.ПартииТоваровНаСкладах КАК Партии
ПО ВТ_МаксимальныеДаты.Номенклатура = Партии.Номенклатура
И ВТ_МаксимальныеДаты.Склад = Партии.Склад
И ВТ_МаксимальныеДаты.МаксПериод = Партии.Период
ГДЕ
Партии.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
СГРУППИРОВАТЬ ПО
ВТ_МаксимальныеДаты.Номенклатура,
ВТ_МаксимальныеДаты.Склад,
ВТ_МаксимальныеДаты.МаксПериод
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_ПоследниеПриходы.Номенклатура КАК Номенклатура,
ВТ_ПоследниеПриходы.Склад КАК Склад,
ВТ_ПоследниеПриходы.ДатаПрихода КАК ДатаПрихода,
ВТ_ПоследниеПриходы.ПоследнийДокумент КАК ДокументПрихода
ИЗ
ВТ_ПоследниеПриходы КАК ВТ_ПоследниеПриходы
Обратите внимание: в условиях выборки мы фильтруем записи по полю Период, входящему в стандартный кластерный индекс регистра накопления, а не по реквизиту Регистратор.Дата. Это обеспечивает быстрое выполнение запроса даже на больших объемах данных.
Если отчет разрабатывается в системе компоновки данных, можно избежать громоздких соединений в тексте одного запроса. СКД умеет выполнять параметрические запросы для каждой строки с помощью механизма связей наборов данных.
Рассмотрим порядок настройки отчета:
ВЫБРАТЬ ПЕРВЫЕ 1
ПартииТоваров.Период КАК ДатаПрихода,
ПартииТоваров.Регистратор КАК ДокументПрихода
ИЗ
РегистрНакопления.ПартииТоваровНаСкладах КАК ПартииТоваров
ГДЕ
ПартииТоваров.Номенклатура = &Номенклатура
И ПартииТоваров.Склад = &Склад
И ПартииТоваров.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
И ПартииТоваров.Период <= &КонецПериода
УПОРЯДОЧИТЬ ПО
ПартииТоваров.Период УБЫВ,
ПартииТоваров.НомерСтроки УБЫВ
Номенклатура и Склад.&Номенклатура и &Склад.При таком подходе СКД при выводе каждой строки номенклатуры передает ее значение в параметры подчиненного запроса и получает ровно одну актуальную запись за счет конструкции ВЫБРАТЬ ПЕРВЫЕ 1 и сортировки по убыванию периода.
При реализации поиска документов поступления важно заранее согласовать бизнес-логику работы алгоритма:
Приход и отрицательным количеством, добавьте в условия отбора фильтр Партии.Количество > 0.Партии.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг) либо анализируйте реквизит КодОперации.