Как правильно спроектировать регистр для отслеживания статусов и повторной отправки уведомлений?

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

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

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

Постановка задачи и анализ ошибок

Рассмотрим типичную ситуацию. У нас есть документ, например, РеализацияТоваровУслуг. Нам необходимо:

  1. При проведении отправить уведомление.
  2. Если через 5 дней статус документа не изменился (он не подписан), отправить повторное уведомление.

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

Почему это плохая идея?

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

Давайте разберем более эффективные подходы, предложенные опытными разработчиками.

Подход 1: Непериодический регистр сведений как «Очередь заданий»

Наиболее производительным и архитектурно верным решением является использование обычного (непериодического) регистра сведений. Он будет выступать в роли оперативной таблицы состояний. Историю изменений (логи) мы будем хранить отдельно, а регистр будет отвечать только за вопрос «Что нужно сделать прямо сейчас?».

Рекомендуемая структура регистра:

Логика работы:

При таком подходе мы отказываемся от анализа «прошло ли 5 дней» внутри запроса. Мы вычисляем дату следующего действия в момент записи в регистр.

  1. При проведении реализации мы пишем в регистр запись со статусом ОжидаетОтправки и ПлановаяДатаСледующейОбработки = ТекущаяДата().
  2. Регламентное задание выбирает записи, где ПлановаяДатаСледующейОбработки <= ТекущаяДата().
  3. После отправки уведомления мы обновляем запись: ставим статус Отправлено, а ПлановаяДатаСледующейОбработки устанавливаем как ТекущаяДата() + 5 дней.
  4. Если документ подписан, запись из регистра либо удаляется, либо переводится в финальный статус Завершено с пустой датой обработки, чтобы она больше не попадала в выборку.

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


ВЫБРАТЬ ПЕРВЫЕ 50
    Уведомления.ДокументОснование КАК Документ,
    Уведомления.Адресат КАК Адресат,
    Уведомления.КоличествоПопыток КАК КоличествоПопыток
ИЗ
    РегистрСведений.ОчередьУведомлений КАК Уведомления
ГДЕ
    Уведомления.ПлановаяДатаСледующейОбработки <= &ТекущаяДата
    И Уведомления.ПлановаяДатаСледующейОбработки <> ДАТАВРЕМЯ(1, 1, 1)
    И Уведомления.Статус <> ЗНАЧЕНИЕ(Перечисление.СтатусыУведомлений.Завершено)
ДЛЯ ИЗМЕНЕНИЯ

Обратите внимание на конструкцию ДЛЯ ИЗМЕНЕНИЯ (или использование блокировок), чтобы несколько потоков фонового задания не обрабатывали одну и ту же запись.

Подход 2: Разделение оперативного учета и истории

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

Давайте применим принцип разделения ответственности:

  1. Регистр сведений — это легкий и быстрый механизм маршрутизации. Он знает только КОГДА и КОМУ писать.
  2. Документы (Журнал) — хранят контент. Это могут быть документы ИсходящееПисьмо или ЭлектронноеПисьмоИсходящее (если используется БСП).

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

Это также решает проблему аудиторского следа. Если вам нужно посмотреть историю («сколько раз мы пинговали клиента?»), вы просто строите отчет по документам-письмам, связанным с реализацией.

Важные нюансы реализации

1. Индексирование

Чтобы выборка в фоновом задании работала быстро даже на миллионах записей, необходимо правильно настроить индексы. Если вы используете подход с полем ПлановаяДатаСледующейОбработки, обязательно проиндексируйте это поле (свойство «Индексировать» в метаданных).

Если же вы делаете отбор по статусу, то поле Статус должно участвовать в индексе. Помните золотое правило 1С: если измерение стоит первым (например, ДокументОснование), а отбор вы делаете по ресурсу или второму измерению без указания первого, стандартный кластерный индекс может не сработать эффективно. Поэтому для реквизитов, по которым идет частый отбор в регламентных заданиях (Статус, Дата), лучше установить признак «Индексировать».

2. Расчет даты (Рабочие дни)

Условие «через 5 дней» часто подразумевает 5 рабочих дней. Если вы просто прибавите 5 * 86400 секунд, уведомление может выпасть на воскресенье, и клиент его пропустит или оно затеряется в спаме за выходные.

Для корректного расчета используйте производственный календарь. В конфигурациях на базе БСП есть общие модули для работы с графиками. Примерный алгоритм:


// Пример получения даты через 5 рабочих дней
ДатаСледующейОтправки = КалендарныеГрафики.ПолучитьДатуПоКалендарю(
    ПроизводственныйКалендарь, 
    ТекущаяДата(), 
    5
);

3. Контроль «бесконечной назойливости»

Необходимо предусмотреть механизм остановки. Если клиент не подписывает документ после 5-го напоминания, возможно, стоит прекратить отправку писем и поставить задачу менеджеру «Позвонить клиенту». Для этого:

Альтернатива: Бизнес-процессы

Стоит упомянуть, что описанная логика «Действие -> Ожидание -> Проверка условия -> Действие» идеально ложится на карту маршрута бизнес-процесса (БП). В платформе 1С есть механизм задач, который позволяет «заморозить» процесс в точке маршрута на определенное время.

Когда стоит использовать БП, а когда регистр?

Итоговое резюме

Для реализации системы повторных уведомлений:

  1. Откажитесь от периодических регистров и СрезПоследних в пользу плоских таблиц (непериодических регистров).
  2. Используйте поле ПлановаяДата для построения эффективных очередей обработки.
  3. Разделяйте очередь (регистр) и историю (документы-письма).
  4. Учитывайте производственный календарь при расчете даты следующего касания.
  5. Не забывайте индексировать поля, по которым выполняется отбор в фоновом задании.
← На главную