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

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

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

Разберем детально, почему возникает это поведение, как устроена работа с транзакциями в платформе 1С:Предприятие и как правильно организовать загрузку и проведение документов при обмене.

Механизм возникновения ошибки и модель транзакций 1С

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

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

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

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


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

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

Способы решения проблемы

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

1. Отказ от явного проведения в обработчиках загрузки

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


// В обработчике "После загрузки" выполняем только запись объекта
Объект.Записать(РежимЗаписиДокумента.Запись);

Если документ при загрузке не прошел критические проверки и не должен оставаться в базе данных, следует передать платформе отказ от обработки, установив флаг Отказ = Истина. Это прервет обработку текущего объекта штатным образом, не разрушая целостность транзакционного контекста.

2. Использование отложенного проведения (механизмы БСП)

В современных конфигурациях для минимизации блокировок и предотвращения транзакционных сбоев реализован механизм отложенного проведения:

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

3. Проведение пакета документов после фиксации транзакции

Если обмен настраивается вручную или через собственные алгоритмы, разделим процесс на два изолированных этапа:

  1. Этап загрузки: накапливаем ссылки на успешно записанные документы в массив или таблицу значений в рамках общей транзакции и фиксируем ее.
  2. Этап проведения: обходим накопленный массив уже после завершения внешней транзакции. Каждый документ проводится в собственной изолированной транзакции, где ошибка проведения одного документа не влияет на остальные.

Посмотрим на пример такого алгоритма:


// Этап 1: Фиксация транзакции загрузки объектов
ЗафиксироватьТранзакцию();

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

Методы диагностики и отладки «скрытых» ошибок

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

  1. Отладчик Конфигуратора: в меню Отладка -> Остановка по ошибке необходимо снять флаг «Останавливаться только при необработанных исключениях». Это заставит отладчик прервать выполнение кода ровно в момент возникновения первичной ошибки внутри секции Попытка, до того как транзакция будет помечена испорченной.
  2. Настройка количества объектов в транзакции: в настройках обработки обмена данными временно установим значение Количество объектов в одной транзакции = 1. Это локализует конкретный объект и строку файла обмена, вызывающую сбой.
  3. Настройка Технологического журнала: включим сбор событий EXCP и EXCPCNTX. В логе технологического журнала будет отражен точный контекст и текст первого возникшего исключения.
← На главную