Как правильно отметить обработанные документы в 1С, чтобы исключить их повторную обработку?

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

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

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

Вариант 1. Добавление реквизита непосредственно в документ

Рассмотрим самый очевидный на первый взгляд способ: добавить в документ булевский реквизит Обработан и при выполнении алгоритма выставлять его значение в Истина. Если обработка не связана с перепроведением документа, запись объекта выполняется в режиме обычной записи.

Посмотрим на пример кода установки признака:


ОбъектДокумента = СсылкаНаДокумент.ПолучитьОбъект();
ОбъектДокумента.Обработан = Истина;
ОбъектДокумента.Записать(РежимЗаписиДокумента.Запись);

Несмотря на простоту реализации, этот вариант несет в себе серьезные архитектурные риски:

  1. Конфликты версий и блокировки: при вызове метода Записать() платформа перезаписывает реквизит ВерсияДанных. Если в этот момент пользователь редактирует данный документ в интерактивной форме, при попытке сохранения он получит ошибку несоответствия версии объекта.
  2. Каскадные обработчики: вызов записи объекта инициирует события ПередЗаписью, ПриЗаписи и все зарегистрированные подписки на события. Это может вызвать ненужные проверки заполнения, пересчеты и замедлить фоновый процесс.
  3. Побочные эффекты типовых механизмов: если в конфигурации включено версионирование объектов, каждая фиксация факта обработки будет плодить фиктивные версии истории. Кроме того, документ может повторно зарегистрироваться в планах обмена и уйти в связанные узлы распределенной базы.

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

Вариант 2. Регистр сведений обработанных документов

Проанализируем более гибкий и безопасный подход: создание непериодического независимого регистра сведений, например, ОбработанныеДокументы. В качестве измерения выступает Документ с типом ссылки на обрабатываемый объект метаданных.

При настройке регистра важно включить свойство Ведущее для измерения Документ. Это гарантирует, что при физическом удалении документа из базы платформа автоматически очистит связанную запись в регистре сведений, исключая появление «битых» ссылок.

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


ВЫБРАТЬ
    Док.Ссылка КАК Документ
ИЗ
    Документ.РеализацияТоваровУслуг КАК Док
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ОбработанныеДокументы КАК Обработанные
        ПО Док.Ссылка = Обработанные.Документ
ГДЕ
    Док.Проведен = ИСТИНА
    И Док.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
    И Обработанные.Документ ЕСТЬ NULL

Обратите внимание на соединение: использование конструкции ЛЕВОЕ СОЕДИНЕНИЕ с проверкой ЕСТЬ NULL работает значительно быстрее на больших объемах данных, чем подзапрос с условием ГДЕ Док.Ссылка НЕ В (ВЫБРАТЬ ...).

После успешного выполнения бизнес-логики мы фиксируем документ в регистре сведений с помощью набора записей:


НаборЗаписей = РегистрыСведений.ОбработанныеДокументы.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Документ.Установить(СсылкаНаДокумент);

НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.Документ = СсылкаНаДокумент;
НоваяЗапись.ПериодОбработки = ТекущаяДатаСеанса();

НаборЗаписей.Записать();

Этот вариант не блокирует объект документа, не вызывает системных подписок и полностью изолирует вспомогательный алгоритм от пользовательской активности.

Вариант 3. Регистр очереди («К обработке» со статусами)

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

Рассмотрим схему работы очереди:

  1. Создаем регистр сведений ОчередьОбработкиДокументов с измерением Документ и реквизитами Статус (Перечисление: «К обработке», «В обработке»), а также ДатаНачалаОбработки.
  2. В подписке на событие ОбработкаПроведения нужного документа добавляем запись в регистр со статусом К обработке. Документ попадает в очередь автоматически при проведении.
  3. Регламентное задание порциями (квантами по 10–50 штук) выбирает записи со статусом К обработке.
  4. Выбранные документы переводятся в статус В обработке, чтобы другие параллельные фоновые потоки не взяли их в работу.
  5. После завершения процедуры запись об объекте либо просто удаляется из регистра, либо переводится в статус «Завершен».

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


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

Запрос.УстановитьПараметр("ГраничноеВремя", ТекущаяДатаСеанса() - 1800);
Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл
    // Возвращаем статус обратно в "К обработке"
КонецЦикла;

Вариант 4. Альтернативные платформенные механизмы

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

  1. Локальный план обмена в роли очереди: создаем план обмена без настройки узлов распределенной базы. В его составе указываем целевой документ с авторегистрацией при проведении. Обработка считывает зарегистрированные изменения, выполняет действия, а затем очищает очередь с помощью метода ПланыОбмена.УдалитьРегистрациюИзменений(). Платформа берет на себя всю работу по поддержанию системных таблиц изменений.
  2. Использование объекта «Последовательность»: если документы должны обрабатываться строго в хронологическом порядке, последовательность позволяет сдвигать границу обработки вперед по мере выполнения алгоритма. Проведение документа автоматически сдвигает границу назад, если документ проведен «задним числом».

Итоги и рекомендации

Выбирая способ отметки обработанных документов, ориентируйтесь на архитектурные требования задачи:

  1. Если требуется простая и надежная фиксация без перегрузки объекта — создайте независимый регистр сведений обработанных документов с ведущим измерением.
  2. Если объем документов измеряется сотнями тысяч, а обработка выполняется регламентным заданием в фоне — реализуйте регистр очереди со статусами либо локальный план обмена с удалением отработанных записей.
  3. Избегайте добавления реквизитов в сам документ, если обновление этого реквизита происходит вне транзакции интерактивного проведения пользователем.
← На главную