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