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

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

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

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

Почему ОбработкаУдаленияПроведения не всегда подходит?

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

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

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

Решение 1. Использование ДополнительныхСвойств объекта

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

Рассмотрим по шагам, как реализовать этот алгоритм в модуле объекта документа.

Шаг 1. Перехватываем режим записи в обработчике ПередЗаписью

В процедуре ПередЗаписью мы анализируем системный параметр РежимЗаписи и сохраняем его в структуру дополнительных свойств нашего объекта:


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

Шаг 2. Анализируем режим записи в обработчике ПриЗаписи

В процедуре ПриЗаписи транзакция фиксации изменений в СУБД уже подходит к концу, документ уже записан в базу как непроведенный, и мы можем безопасно проверить, какое именно действие вызвало этот обработчик:


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

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

Архитектурные риски выполнения связанных действий внутри транзакции

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

  1. Взаимные блокировки (Deadlocks): Если документ «А» при отмене проведения начинает изменять документ «Б», а в это же время другой пользователь (или фоновое задание обмена) проводит документ «Б», который пытается обновить документ «А», СУБД заблокирует оба процесса, что приведет к аварийному завершению операции для одного из пользователей.
  2. Проблемы при обмене данными: Если ваша база данных участвует в распределенной схеме (РИБ) или обменивается данными с другими системами, документы могут загружаться в режиме «Загрузка = Истина». В этом случае обработчики событий могут не вызываться вовсе, либо вызываться в непредсказуемом порядке, порождая рассогласование связанных документов.
  3. Интерактивные задержки: Если сопутствующие действия занимают много времени (например, выполняется сложный расчет себестоимости или интеграционный запрос к API), пользователь будет вынужден ожидать завершения всей транзакции, созерцая зависший интерфейс программы.

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

Решение 2. Паттерн «Очередь задач» (Регистр сведений + Регламентное задание)

Этот метод является наиболее надежным и отказоустойчивым. Он активно применяется во всех современных типовых конфигурациях 1С (ERP, КА, УТ) на базе Библиотеки Стандартных Подсистем (БСП).

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

  1. Создаем независимый регистр сведений ОчередьЗадачПостобработки. В качестве измерений используем ДокументСсылка (тип: ЛюбаяСсылка), а в качестве ресурсов — признак СостояниеОбработки (тип: Булево или Перечисление) и ДатаРегистрации.
  2. В процедуре ПриЗаписи нашего документа при обнаружении отмены проведения мы записываем в этот регистр одну строку с ссылкой на текущий документ и статусом "Требует обработки".
  3. Если транзакция записи основного документа завершится успешно — запись в регистре сведений зафиксируется. Если произойдет сбой или откат транзакции — запись о задаче также автоматически исчезнет из БД благодаря механизму транзакционности.
  4. Настраиваем регламентное задание, которое запускается в фоне (например, раз в минуту или каждые 10 секунд). Оно считывает накопившиеся задачи из регистра ОчередьЗадачПостобработки, выполняет все тяжелые связанные расчеты и корректировки других документов вне транзакции основного документа, после чего удаляет обработанные записи из регистра.

Рассмотрим пример кода для записи в очередь в обработчике ПриЗаписи:


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

Решение 3. Использование клиентских событий формы (для интерактивной работы)

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

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

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


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

&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
	
	Если ПараметрыЗаписи.Свойство("РежимЗаписи") 
		И ПараметрыЗаписи.РежимЗаписи = РежимЗаписиДокумента.ОтменаПроведения Тогда
		
		// Сообщаем пользователю или открываем связанную форму
		ПоказатьПредупреждение(, "Проведение документа успешно отменено! Не забудьте проверить связанные кассовые ордера.");
		
	КонецЕсли;
	
КонецПроцедуры

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

← На главную