Как в 1С найти контрагентов без активности за определенный период: пошаговое руководство по созданию запросов и отчетов в СКД

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

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

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

Постановка задачи и логическая схема

Представим ситуацию: нам нужно найти контрагентов, которые были активны в период с 2001 года по 01.10.2019, но полностью прекратили активность в период с 01.10.2019 по 04.10.2019. Если контрагент "засветился" в обоих периодах, он считается активным и не должен попасть в отчет. Если его нет ни в одном периоде — он нам не интересен. Нам нужны только те, кто есть во втором (старом) периоде, но отсутствует в первом (новом).

Рассмотрим основные этапы реализации:

  1. Сбор данных об активности из различных документов (Реализации, Заказы, Поступления).
  2. Разделение данных на два временных интервала.
  3. Сопоставление списков и исключение активных контрагентов.
  4. Оптимизация производительности запроса.

Шаг 1. Сбор данных через объединение документов

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


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

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ЗаказПокупателя.Контрагент,
    ЗаказПокупателя.Дата
ИЗ
    Документ.ЗаказПокупателя КАК ЗаказПокупателя
ГДЕ
    ЗаказПокупателя.Проведен = ИСТИНА

ОБЪЕДИНИТЬ ВСЕ

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

Важный момент: Мы используем временную таблицу ВТ_ВсеДвижения, чтобы в дальнейшем было удобнее работать с накопленными данными. Также обратите внимание на фильтр по проведенным документам, чтобы не учитывать черновики.

Шаг 2. Выделение "черного списка" активных контрагентов

Теперь нам нужно понять, кто из этих контрагентов совершал действия в "запретном" периоде (последние X дней). Этих клиентов мы будем исключать. Создадим вторую временную таблицу:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    Т.Контрагент КАК Контрагент
ПОМЕСТИТЬ ВТ_Активные
ИЗ
    ВТ_ВсеДвижения КАК Т
ГДЕ
    Т.Дата МЕЖДУ &ДатаНачалаСвежегоПериода И &ДатаКонцаСвежегоПериода
ИНДЕКСИРОВАТЬ ПО
    Контрагент
;

Здесь мы используем ВЫБРАТЬ РАЗЛИЧНЫЕ, так как нам важен сам факт наличия хотя бы одной записи. Индексация по полю Контрагент критически важна для быстродействия последующего соединения.

Шаг 3. Итоговый отбор "забытых" клиентов

На финальном этапе мы выбираем тех контрагентов, которые есть в нашей общей базе (или имели движения в старом периоде), но отсутствуют в таблице ВТ_Активные. Разберем два способа реализации: через подзапрос и через левое соединение.

Вариант с ЛЕВОЕ СОЕДИНЕНИЕ (рекомендуется):


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

Проанализируем ситуацию: мы присоединяем активных клиентов к общему списку. Если клиент активен, поле Т_Активные.Контрагент будет заполнено. Если нет — там будет NULL. Условие ГДЕ ... ЕСТЬ NULL эффективно отсекает всех лишних.

Реализация задачи в Системе компоновки данных (СКД)

Если вы хотите сделать это в виде отчета, СКД предоставляет более элегантные механизмы. Вместо написания одного огромного запроса, мы можем использовать Связи наборов данных.

Рассмотрим пошагово, как настроить такую схему в СКД:

  1. Создаем Набор данных 1 (СтараяАктивность): Запрос, который выбирает контрагентов и дату их последней активности до определенной даты.
  2. Создаем Набор данных 2 (СвежаяАктивность): Запрос, который выбирает только тех контрагентов, у которых были движения в периоде "X дней назад — Сегодня".
  3. Настраиваем связь: Переходим на закладку "Связи наборов данных".
    • Источник связи: СтараяАктивность.
    • Приемник связи: СвежаяАктивность.
    • Условие связи: Контрагент = Контрагент.
    • Параметры: Здесь можно передать дату из первого набора во второй.
  4. Настройка отбора: В настройках отчета (вкладка "Отбор") добавляем условие: СвежаяАктивность.Контрагент ЕСТЬ NULL.

Этот метод позволяет избежать написания сложных временных таблиц вручную — 1С сама сформирует оптимальный SQL-запрос.

Оптимизация: использование регистров вместо документов

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

Преимущества работы с регистрами:

Пример запроса к регистру оборотов:


ВЫБРАТЬ
    ПродажиОбороты.Контрагент КАК Контрагент,
    ПродажиОбороты.Период КАК Дата
ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, День, ) КАК ПродажиОбороты

Типичные ошибки при реализации

Рассмотрим критическую ошибку, которая часто встречается у начинающих (и упоминалась в обсуждении на форуме) — Запрос в цикле. Посмотрим на этот пример кода (так делать нельзя!):


// ОШИБОЧНЫЙ КОД
Выборка = Справочники.Контрагенты.Выбрать();
Пока Выборка.Следующий() Цикл
    Запрос = Новый Запрос("ВЫБРАТЬ ... ГДЕ Контрагент = &Контрагент");
    Запрос.УстановитьПараметр("Контрагент", Выборка.Ссылка);
    // Выполнение запроса...
КонецЦикла;

Выясним причину, почему это плохо: если у вас 5000 контрагентов, программа сделает 5000 отдельных обращений к базе данных. Это создаст колоссальную нагрузку. Всегда стремитесь получить данные одним запросом, используя соединения или временные таблицы.

Как динамически вычислять период "X дней назад"

Чтобы отчет был удобным, пользователю не обязательно вводить четыре даты. Можно использовать параметр "Количество дней простоя" и вычислять дату отсечения прямо в запросе или в настройках СКД.

В языке запросов 1С для этого используется функция ДОБАВИТЬКДАТЕ:


ГДЕ
    Документ.Дата > ДОБАВИТЬКДАТЕ(&ТекущаяДата, ДЕНЬ, -&КоличествоДней)

В СКД это лучше реализовать через "Выражение" в параметрах: ДобавитьКДате(ТекущаяДата(), "День", -1 * &КоличествоДней).

Подведем итог: Для решения задачи поиска неактивных контрагентов наиболее эффективным является использование временных таблиц с последующим левым соединением и проверкой на NULL. При возможности всегда используйте виртуальные таблицы регистров вместо таблиц документов, а логику отбора в отчетах доверяйте механизмам связей наборов данных в СКД.

← На главную