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