Как реализовать в запросе 1С выбор последнего использованного счета поставщика с проверкой актуальности за 3 месяца?

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

В практике разработки на платформе 1С:Предприятие 8 часто возникает задача анализа активности контрагентов. Одной из типичных проблем является определение актуального (последнего использованного) счета поставщика. При этом логика часто усложняется дополнительными условиями: например, если счет не использовался более трех месяцев, он должен терять признак активности. Рассмотрим, как правильно составить такой запрос, избежав дублирования строк и лишней нагрузки на систему.

Анализ исходной задачи и типичных ошибок

Разберем ситуацию: нам необходимо пометить поле Отметка значением ИСТИНА для того счета, который фигурировал в самом последнем документе ЗаказПоставщику. Если же документ был создан давно (более 3 месяцев назад), отметка должна быть ЛОЖЬ. Основная сложность, с которой сталкиваются разработчики, — это получение корректной выборки без дублей, когда один и тот же счет попадает и в категорию "последний", и в категорию "старый".

Проанализируем первую ошибку — использование конструкции ОБЪЕДИНИТЬ ВСЕ для разделения логики "последний" и "остальные". Это приводит к тому, что система дважды сканирует таблицу документов, что крайне неэффективно. Вместо этого мы будем использовать соединение с временной таблицей и конструкцию ВЫБОР КОГДА.

Шаг 1. Получение последней даты активности

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

Рассмотрим правильный код для подготовки временной таблицы:


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

Обратите внимание на использование ИНДЕКСИРОВАТЬ ПО. Это критически важно для производительности при последующем соединении таблиц. Мы также добавили условие на проведенность документа, чтобы не учитывать черновики.

Шаг 2. Соединение данных и применение логики "3 месяца"

Теперь проанализируем, как связать полученные даты с конкретными счетами. Нам нужно получить срез последних данных. Выясним причину, по которой нельзя просто соединить таблицы по дате: у одного контрагента может быть несколько документов с одинаковой (максимальной) датой, но разными счетами. В таком случае рекомендуется использовать дополнительный фильтр по Ссылка, но для базовой задачи нам достаточно соединиться по паре Контрагент + Дата.

Разберем, как реализовать условие актуальности счета (не старше 3 месяцев) прямо в запросе, используя функцию ДОБАВИТЬКДАТЕ.


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

Шаг 3. Оптимизация и решение проблемы дублей

В приведенном выше коде мы используем ВНУТРЕННЕЕ СОЕДИНЕНИЕ, что автоматически отсекает все старые записи, оставляя только те счета, которые использовались в "последнюю дату". Это избавляет нас от необходимости использовать ОБЪЕДИНИТЬ ВСЕ.

Посмотрим на важные нюансы, которые помогут сделать запрос профессиональным:

  1. Обработка пустых счетов: Если в документе счет не указан, запрос вернет пустую ссылку. Рекомендуется добавить условие ГДЕ ЗаказПоставщику.СчетПоставщика <> ЗНАЧЕНИЕ(Справочник.БанковскиеСчета.ПустаяСсылка).
  2. Точность времени: В 1С Дата содержит время. Если необходимо найти счет, использованный "вчера" (весь день), используйте функции НАЧАЛОПЕРИОДА и КОНЕЦПЕРИОДА для параметров.
  3. Использование ТекущаяДатаСеанса: При работе в разных часовых поясах всегда передавайте в параметр &ТекущаяДата значение функции ТекущаяДатаСеанса(), а не серверное время.

Итоговый вариант эффективного запроса

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


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

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

В данном примере мы вынесли расчет "3 месяца назад" в параметр &ГраницаАктивности на стороне встроенного языка (используя ДобавитьМесяц(ТекущаяДата(), -3)), что является более предпочтительным для производительности SQL-сервера, так как избавляет его от лишних вычислений в каждой строке выборки.

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

← На главную