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

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

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

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

Метод 1. Использование механизма ДополнительныеСвойства в подписках

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

Рассмотрим, как передать информацию о режиме записи между этими событиями. Для этого воспользуемся свойством объекта ДополнительныеСвойства, которое представляет собой структуру, существующую в памяти на протяжении всего цикла записи объекта.

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

  1. Подписка на событие «ПередЗаписью»: Здесь мы фиксируем намерение пользователя или системы.
  2. Подписка на событие «ПриЗаписи»: Здесь мы анализируем зафиксированное намерение и выполняем логику.

Проанализируем пример кода для общего модуля:


// Обработчик события ПередЗаписью
Процедура ПодпискаПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
    // Сохраняем режим записи в дополнительные свойства документа
    Источник.ДополнительныеСвойства.Вставить("ЭтоРежимПроведения", (РежимЗаписи = РежимЗаписиДокумента.Проведение));
КонецПроцедуры

// Обработчик события ПриЗаписи
Процедура ПодпискаПриЗаписи(Источник, Отказ) Экспорт
    // Проверяем, было ли инициировано проведение
    Если Источник.ДополнительныеСвойства.Свойство("ЭтоРежимПроведения") И Источник.ДополнительныеСвойства.ЭтоРежимПроведения Тогда
        // Выполняем действия после записи, но в рамках транзакции
        ОбновитьКарточкуСотрудника(Источник.Сотрудник);
    КонецЕсли;
КонецПроцедуры

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

Метод 2. Выполнение кода в конце процедуры ОбработкаПроведения

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

Этот метод гарантирует, что:

  1. Документ успешно прошел все проверки.
  2. Все движения сформированы.
  3. Действия выполняются в рамках транзакции проведения.

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

Метод 3. Асинхронное выполнение через ФоновыеЗадания

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

Для этого в подписке ПриЗаписи или ОбработкаПроведения мы инициируем фоновое задание.

Разберем алгоритм действий:

  1. В транзакции проведения проверяем, что все условия выполнены.
  2. Запускаем фоновое задание, передавая в него ссылку на документ.
  3. Внутри фонового задания первым делом проверяем, завершена ли транзакция в основном сеансе.

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


Процедура ЗапуститьОбновлениеКарточкиФоново(ДокументСсылка) Экспорт
    ПараметрыЗадания = Новый Массив;
    ПараметрыЗадания.Добавить(ДокументСсылка);
    
    ФоновыеЗадания.Выполнить("МойОбщийМодуль.ВыполнитьОбновлениеКарточки", ПараметрыЗадания);
КонецПроцедуры

Процедура ВыполнитьОбновлениеКарточки(ДокументСсылка) Экспорт
    // Нюанс: фоновое задание может стартовать быстрее, чем завершится коммит транзакции.
    // Попробуем установить блокировку, чтобы дождаться освобождения объекта.
    Блокировка = Новый БлокировкаДанных;
    ЭлементБлокировки = Блокировка.Добавить("Документ.КадровоеПеремещение");
    ЭлементБлокировки.УстановитьПараметр("Ссылка", ДокументСсылка);
    ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
    
    Попытка
        Блокировка.Заблокировать();
        // Если заблокировали — значит, основная транзакция завершена.
        // Здесь выполняем основную логику обновления.
    Исключение
        // Объект еще занят или возникла ошибка
    КонецПопытки;
КонецПроцедуры

Метод 4. Паттерн «Очередь обработки» (Регистр сведений)

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

Шаги реализации:

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

Преимущества этого подхода:

Подведем итоги

Выбор метода зависит от ваших требований к целостности данных и производительности системы:

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

← На главную