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

Программист 1С v8.3 (Обычные формы) Бухгалтерский учет IT и автоматизация бизнеса
← На главную

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

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

Анализируем последовательность событий при записи и проведении

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

  1. [Форма, Клиент] ПередЗаписью — самое первое событие. Здесь транзакция еще не началась. Мы можем задать вопрос пользователю (например, через диалоговое окно) или отменить запись.
  2. [Форма, Сервер] ПередЗаписьюНаСервере — подготовка данных формы к передаче в объект. Транзакция всё ещё не открыта.
  3. [Объект, Сервер] ОбработкаПроверкиЗаполнения — проверка обязательных реквизитов на уровне платформы.
  4. СТАРТ ТРАНЗАКЦИИ БАЗЫ ДАННЫХ. Все последующие действия выполняются строго внутри одной неделимой транзакции. Если на любом из следующих этапов произойдет ошибка, СУБД откатит все изменения к исходному состоянию.
  5. [Объект, Сервер] ПередЗаписью — первая точка расширения внутри транзакции. Здесь можно дозаполнить реквизиты объекта перед его физической записью в СУБД.
  6. [Объект, Сервер] ПриЗаписи — в этот момент объект физически записывается в базу данных. Ссылка для нового документа уже создана и доступна (ЭтотОбъект.Ссылка), но транзакция еще не зафиксирована.
  7. [Объект, Сервер] ОбработкаПроведения — выполняется только в том случае, если документ записывается в режиме проведения. Здесь формируются движения по регистрам.
  8. [Форма, Сервер] ПриЗаписиНаСервере — событие формы, выполняющееся внутри транзакции, но уже после того, как объект записан и проведен.
  9. ФИКСАЦИЯ (КОММИТ) ТРАНЗАКЦИИ. Данные окончательно сохраняются в СУБД. Блокировки таблиц снимаются.
  10. [Форма, Сервер] ПослеЗаписиНаСервере — транзакция завершена, данные в безопасности.
  11. [Форма, Клиент] ПослеЗаписи — окончание интерактивного цикла.

Важное примечание: Изменить этот порядок на уровне платформы невозможно. Событие ПриЗаписи в модуле объекта всегда выполняется до ОбработкаПроведения. Если вам кажется, что у вас сначала срабатывает проведение, а потом запись — скорее всего, вы перепутали событие модуля формы ПриЗаписиНаСервере (или ПослеЗаписи) с аналогичными событиями модуля объекта.

Почему проведение второго документа внутри транзакции первого — это плохая идея?

Представим ситуацию: вы хотите провести Документ №1 (основной) только тогда, когда успешно провелся созданный на его основании Документ №2 (подчиненный). Вы решаете разместить код создания и проведения Документа №2 внутри события ПриЗаписи или ОбработкаПроведения Документа №1.

С технической точки зрения это сработает: если при проведении Документа №2 возникнет ошибка (например, не хватит остатков на складе), система взведет флаг Отказ = Истина, транзакция откатится, и Документ №1 тоже не запишется и не проведется. Однако такой подход грубо нарушает стандарты разработки "1С" по следующим причинам:

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

Решение 1. Использование механизма подписок на события (Best Practice)

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

Выполним следующие шаги для реализации:

  1. Создадим новый общий модуль, например, ОтражениеДокументовСобытия. Установим свойства: Сервер, Вызов сервера.
  2. Создадим новый объект метаданных Подписка на событие:
    • Имя: СоздатьДокументБУПриПроведении
    • Источник: ДокументОбъект.ИмяВашегоОсновногоДокумента
    • Событие: ОбработкаПроведения
    • Обработчик: Выбираем процедуру из нашего общего модуля.

Проанализируем пример кода для обработчика подписки:


Процедура СоздатьДокументБУПриПроведенииОбработкаПроведения(Источник, Отказ, РежимПроведения) Экспорт

	// Если проведение основного документа уже отменено на предыдущих этапах, ничего не делаем
	Если Отказ Тогда
		Возврат;
	КонецЕсли;

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

	// Заполняем реквизиты нового документа на основании источника
	ПодчиненныйДокОбъект.Дата = Источник.Дата;
	ПодчиненныйДокОбъект.Организация = Источник.Организация;
	// [Заполнение остальных реквизитов...]

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

КонецПроцедуры

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

Решение 2. Асинхронное проведение через очередь задач (Рекомендуется для высоконагруженных систем)

Если ваша база данных работает в режиме высокой интенсивности и проведение документов БУ занимает значительное время, идеальным решением будет **асинхронное разделение процессов**. Это полностью исключит взаимные блокировки таблиц.

Рассмотрим этот паттерн по шагам:

  1. Создайте регистр сведений ОчередьОтраженияВУчете с измерениями ДокументСсылка (Тип: ДокументСсылка) и ресурсом Состояние (Тип: ПеречислениеСсылка).
  2. В подписке на событие ПриЗаписи или ОбработкаПроведения основного документа просто делайте быструю запись в этот регистр:

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

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

Резюме

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

← На главную