Как программно создать и провести документ на основании другого без ошибки транзакции в 1С?

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

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

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


НовДокумент = Документы.ЗаявкаНаРасходованиеДенежныхСредств.СоздатьДокумент();
НовДокумент.Заполнить(ЭтотОбъект.Ссылка); 
НовДокумент.Записать(РежимЗаписиДокумента.Проведение, РежимПроведенияДокумента.Оперативный);

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

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

Почему возникает ошибка «В данной транзакции происходили ошибки»?

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

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

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

Решение 1: Предварительная проверка условий (Pre-flight check)

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

Разберем по шагам данный подход:

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

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

Решение 2: Отложенное проведение (Очередь через Регистр Сведений)

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

Создадим механизм отложенной обработки:

  1. Создаем непериодический независимый Регистр сведений, например, ОчередьСозданияЗаявокНаОплату. В измерениях указываем ссылку на первичный документ (ДокументОснование).
  2. В обработчике проведения исходного документа мы не создаем подчиненные документы. Мы делаем лишь быструю запись в созданный регистр сведений (добавляем документ-основание в очередь). Это занимает миллисекунды и не нагружает транзакцию. Также, при отмене проведения основного документа, необходимо удалять эту запись или менять ее статус.
  3. Создаем Регламентное задание, которое запускается по расписанию (например, каждую минуту) или Фоновое задание, которое стартует сразу после фиксации транзакции первичного документа.
  4. Обработчик фонового задания считывает необработанные записи из регистра сведений. Вне всяких транзакций пользователя он создает новые документы заявок, пытается их провести.
  5. Если документ провелся успешно, запись удаляется из очереди или помечается как «Обработана». Если произошла ошибка (превышен лимит), в регистр записывается статус ошибки, которую потом сможет увидеть пользователь в специальном рабочем месте.

Важное замечание: перед созданием фонового задания имеет смысл прочитать данные СУБД и оценить, требуется ли вообще создание заявки для данного документа-основания. Это позволит избежать запуска лишних фоновых процессов при массовом перепроведении (исключит так называемые «ложные срабатывания»).

Решение 3: Создание документов в обработчиках Формы

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

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


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

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

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

← На главную