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

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

В практике разработки на платформе 1С часто возникает задача: среди множества документов (например, оплат) найти самый первый или самый последний для конкретного объекта (заказа, договора или контрагента). На первый взгляд задача кажется тривиальной и решаемой через функцию МИНИМУМ, однако начинающие разработчики часто сталкиваются с тем, что запрос продолжает возвращать все документы вместо одного. В этой статье мы подробно разберем, почему это происходит, и как правильно составить запрос для получения корректного результата.

Анализ типичной ошибки группировки

Рассмотрим ситуацию, с которой столкнулся автор темы. В системе есть Заказ клиента и несколько документов Безналичная оплата, привязанных к нему. Цель — вывести в отчет только самую раннюю оплату. Типичный ошибочный запрос выглядит следующим образом:


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

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

Правильный подход: использование вложенных запросов

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

  1. Создаем вложенный запрос (или временную таблицу), в котором получаем только идентификатор объекта (Заказ) и минимальную дату (МИНИМУМ(Дата)). В этом запросе не должно быть никаких лишних полей, кроме тех, по которым мы ищем минимум.
  2. Соединяем полученный результат с основной таблицей регистра по двум полям: по объекту и по дате. Это позволит "отфильтровать" только те записи, дата которых совпала с минимальной.

Рассмотрим пример кода, реализующий эту логику:


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

В этом примере первый подзапрос определяет "когда была первая оплата", а соединение находит "какой именно документ был в это время".

Проблема "одинаковых секунд" и способы её решения

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

  1. Найти минимальную дату для каждого заказа.
  2. Найти минимальную ссылку (МИНИМУМ(Ссылка)) среди всех документов, которые имеют эту минимальную дату.
  3. И только после этого получить данные (сумму и прочее) по этой конкретной ссылке.

Это гарантирует уникальность результата, так как ссылка в системе 1С всегда уникальна.

Оптимизация: использование временных таблиц

При работе с большими объемами данных в реальных конфигурациях (УТ 11, ERP 2.5) использование вложенных запросов может замедлять работу системы, так как СУБД сложнее построить оптимальный план выполнения. Профессиональным стандартом считается использование временных таблиц. Перепишем наше решение в пакетный запрос:


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

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

Такой подход работает быстрее, так как платформа создает индексы по полям временных таблиц, что значительно ускоряет операцию соединения.

Нюансы работы в конфигурации УТ 11

При решении задачи именно в контексте Управления торговлей 11 стоит помнить о специфике регистров. Автор темы использует ДенежныеСредстваБезналичные. Однако, если задача стоит в анализе оплат по конкретным заказам, более правильным может быть обращение к регистру РасчетыСКлиентами или РасчетыСКлиентамиПоДокументам. Почему это важно?

Во-первых, в регистре денежных средств отражается движение по банковскому счету, но там не всегда есть привязка к заказу (например, если платеж был общим авансом). Во-вторых, в регистрах расчетов хранится информация о том, как программа распределила (зачла) оплату. Часто бывает, что платежка пришла вчера, а к заказу её привязали сегодня — в этом случае "первая оплата заказа" с точки зрения учета может отличаться от фактического поступления денег на расчетный счет.

Также не забывайте фильтровать записи. В регистрах накопления могут присутствовать движения с видом Расход (например, возврат денег клиенту). Если не указать условие на вид движения или тип хозяйственной операции, возврат может попасть в выборку как "первая оплата", что исказит отчетность.

Заключение

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

← На главную