Как создать и провести документ при проведении другого документа, если проведение второго может завершиться ошибкой?

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

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

Проблема атомарности транзакций в 1С

Разберем ситуацию, с которой сталкивается большинство разработчиков. При попытке вызвать ЭтотОбъект.Записать(РежимЗаписиДокумента.Проведение) для второго документа внутри подписки на событие или в модуле первого документа, вся операция происходит внутри одной транзакции базы данных. Если во втором документе возникнет любая ошибка (не заполнено обязательное поле, сработал контроль остатков, возникла исключительная ситуация в коде), платформа выставит внутренний флаг «Транзакция испорчена».

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

Решение 1: Использование фоновых заданий

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

Рассмотрим по шагам, как это реализовать:

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

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


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

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


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

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

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

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

Выясним алгоритм работы этого метода:

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

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

Решение 3: Механизм оповещения пользователя

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

Рассмотрим способы решения этой задачи:

Решение 4: COM-соединение или HTTP-сервисы (для продвинутых)

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

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

Выводы и рекомендации

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

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

← На главную