Как выполнить действия после завершения транзакции записи документа или использовать актуальные данные в СКД внутри транзакции

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

В практике разработки на платформе 1С:Предприятие 8.3 часто возникает классическая дилемма: необходимо выполнить определенную логику на основе данных объекта, который в данный момент записывается или проводится. Однако стандартные события модуля объекта, такие как ПередЗаписью, ПриЗаписи или ОбработкаПроведения, исполняются внутри одной неделимой транзакции. Если мы попытаемся запустить сложный отчет СКД или выполнить запрос к базе данных внутри этой транзакции, мы можем столкнуться с тем, что данные еще не зафиксированы или система вернет старое состояние объекта из-за уровней изоляции транзакций.

Рассмотрим подробнее, как обойти эти ограничения, разобрав сценарии выполнения действий строго после завершения транзакции и методы работы с «новыми» данными объекта, когда транзакция еще открыта.

Проблема транзакционности и видимости данных

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

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

Решение 1: Выполнение кода после транзакции через фоновые задания

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

Разберем алгоритм реализации этого метода:

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

Посмотрим на пример кода в модуле объекта:


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

В общем модуле МойОбщийМодуль (с установленными флажками «Сервер» и «Вызов сервера») опишем процедуру:


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

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

Решение 2: Работа с СКД через внешний набор данных (без записи в БД)

Если же ваша цель — получить результат работы СКД, учитывающий текущие изменения в документе (например, для заполнения табличной части самого документа), то «ждать» завершения транзакции нельзя. В этом случае мы воспользуемся механизмом внешних наборов данных.

Вместо того чтобы позволять СКД выполнять запрос к таблице документа в базе данных, мы передадим ей текущее состояние объекта (из оперативной памяти) в виде таблицы значений. Выясним, как это сделать:

  1. Создаем схему компоновки данных.
  2. В схеме определяем «Набор данных - объект».
  3. В коде модуля объекта выгружаем нужные данные (например, табличную часть) в ТаблицуЗначений.
  4. Передаем эту таблицу в процессор компоновки.

Проанализируем пример кода реализации в модуле объекта:


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

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

Решение 3: Использование обработчика ожидания (для интерактивной записи)

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

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


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

&НаКлиенте
Процедура ВыполнитьДействиеПослеТранзакции()
    
    // Здесь вызываем серверный метод для финальной обработки
    ФинальнаяОбработкаНаСервере(Объект.Ссылка);
    
КонецПроцедуры

Преимущество этого метода в его простоте. Недостаток — он работает только при интерактивной работе пользователя и не поможет при программной записи объекта (например, в рамках обмена данными или регламентного задания).

Рекомендации и выводы

При выборе метода «прицепки» к транзакции руководствуйтесь следующими правилами:

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

← На главную