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

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

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

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

Архитектурные ограничения платформы и транзакции СУБД

Проанализируем базовые правила работы транзакционного механизма платформы 1С:

  1. Доступность методов: методы НачатьТранзакцию(), ЗафиксироватьТранзакцию() и ОтменитьТранзакцию() доступны исключительно на стороне сервера. На тонком клиенте базы данных как таковой нет, и управлять транзакцией напрямую невозможно.
  2. Контекст серверного вызова: транзакция базы данных жестко привязана к потоку выполнения текущего серверного вызова. Как только управление возвращается на клиент (например, для открытия окна вопроса пользователю), серверный контекст завершается. Оставить открытую транзакцию СУБД «в режиме ожидания» ответа от клиента платформа не позволяет: при завершении серверного вызова с незафиксированной транзакцией будет вызвано исключение, а изменения будут автоматически откачены.
  3. Блокировки данных: удержание открытой транзакции во время интерактивных действий пользователя категорически запрещено стандартами разработки. Это приводит к длительным блокировкам таблиц СУБД, зависанию сеансов других пользователей и взаимным блокировкам (deadlocks).

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

Решение 1. Накопление данных и пакетная запись в единой транзакции (Рекомендуемый подход)

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

Разберем по шагам, как устроен этот механизм:

  1. На клиенте пользователь заполняет начальные параметры.
  2. Серверная процедура рассчитывает данные, формирует структуры для будущих документов, проверяет ошибки и возвращает клиенту список вопросов или параметров, требующих уточнения.
  3. Промежуточные данные сохраняются во временном хранилище формы с помощью ПоместитьВоВременноеХранилище().
  4. Клиент запрашивает недостающие данные у пользователя и снова передает их на сервер для обновления подготовленных структур.
  5. Когда все шаги успешно пройдены и все ответы получены, вызывается одна финальная серверная процедура, которая открывает транзакцию, создает все объекты и выполняет запись.

Посмотрим на пример реализации финальной серверной процедуры записи:


&НаСервере
Процедура ЗаписатьПакетДокументовНаСервере(АдресДанныхВХранилище)
    
    ДанныеДляЗаписи = ПолучитьИзВременногоХранилища(АдресДанныхВХранилище);
    УдалитьИзВременногоХранилища(АдресДанныхВХранилище);
    
    НачатьТранзакцию();
    
    Попытка
        
        Для Каждого ОписаниеДокумента Из ДанныеДляЗаписи Цикл
            
            ДокументОбъект = Документы[ОписаниеДокумента.ИмяМетаданных].СоздатьДокумент();
            ЗаполнитьЗначенияСвойств(ДокументОбъект, ОписаниеДокумента.Реквизиты);
            
            Для Каждого СтрокаТЧ Из ОписаниеДокумента.ТабличнаяЧасть Цикл
                НоваяСтрока = ДокументОбъект.Товары.Добавить();
                ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТЧ);
            КонецЦикла;
            
            ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
            
        КонецЦикла;
        
        ЗафиксироватьТранзакцию();
        
    Исключение
        
        ОтменитьТранзакцию();
        ЗаписьЖурналаРегистрации("СозданиеПакетаДокументов", УровеньЖурналаРегистрации.Ошибка,,, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        ВызватьИсключение "Ошибка при создании документов: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
        
    КонецПопытки;
    
КонецПроцедуры

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

Решение 2. Тестовая валидация через кратковременные транзакции

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

  1. На сервере вызывается НачатьТранзакцию().
  2. Создается и заполняется объект документа.
  3. Выполняется попытка записи документа в режиме проведения (РежимЗаписиДокумента.Проведение).
  4. Анализируются ошибки или результат проверки.
  5. Вызывается ОтменитьТранзакцию(), чтобы изменения ни при каких условиях не зафиксировались в базе данных.
  6. Результат проверки (список предупреждений, остатки, ошибки) возвращается на клиент для принятия решения пользователем.
  7. После согласования всех данных с пользователем выполняется итоговая запись всего пакета, как в первом решении.

Решение 3. Использование статусов (черновиков) или программная компенсация

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

  1. Документы создаются и записываются только в режиме РежимЗаписиДокумента.Запись (без проведения) со специальным статусом или признаком черновика.
  2. Массив ссылок на созданные черновики хранится в контексте формы.
  3. Если на очередном шаге пользователь прерывает операцию или возникает ошибка, запускается серверная процедура очистки, которая обходит накопленный массив ссылок и помечает объекты на удаление с помощью УстановитьПометкуУдаления(Истина) или удаляет их непосредственно методом Удалить().
  4. Если все этапы пройдены успешно, в единой транзакции НачатьТранзакцию() все черновики переводятся в рабочий статус и проводятся.

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

← На главную