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

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

При разработке правил обмена в Конвертации данных 2.0 программисты часто сталкиваются с ситуацией, когда стандартная конструкция Попытка...Исключение ведет себя не так, как ожидалось. Типичный сценарий: вы пытаетесь записать или провести объект в блоке Попытка, и в случае неудачи хотите выполнить какие-то дополнительные действия в блоке Исключение (например, найти узел обмена, сделать запись в лог или изменить статус). Однако вместо выполнения вашего кода в блоке Исключение, платформа выбрасывает критическую ошибку: «В данной транзакции уже происходили ошибки!».

Рассмотрим подробнее, почему это происходит, как работает механизм транзакций в 1С и какими способами можно обойти это ограничение.

Разберем механизм «битой» транзакции

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

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

Проблема в том, что блок Попытка...Исключение перехватывает саму ошибку в коде 1С, но он не может восстановить состояние транзакции в базе данных. Если ошибка произошла внутри Попытки, то в блок Исключение вы попадете, но база данных для вас будет «закрыта» до выхода из текущей транзакции.

Особенности работы Конвертации данных (КД)

Почему в обычной обработке код может работать, а в Конвертации данных — нет? Разберем по шагам логику работы универсальной обработки загрузки данных XML:

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

Посмотрим на примере: как делать нельзя

Рассмотрим пример кода, который гарантированно приведет к ошибке, если документ не удастся провести:


Попытка
    Объект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
    // В этот момент транзакция БД уже помечена как ошибочная!
    // Любой поиск в базе данных вызовет исключение.
    УзелОбм = ПланыОбмена.ОбменУПП.НайтиПоКоду("УПП13"); 
    // ОШИБКА: В данной транзакции уже происходили ошибки!
КонецПопытки;

Как решить проблему: практические способы

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

Способ 1: Использование глобальных переменных и кэширование

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

Проанализируем правильный подход:


// В начале обработчика или в "Перед загрузкой данных"
// Кэшируем ссылку заранее
Если Не Параметры.Свойство("УзелУПП") Тогда
    Параметры.Вставить("УзелУПП", ПланыОбмена.ОбменУПП.НайтиПоКоду("УПП13"));
КонецЕсли;

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

Способ 2: Изменение настроек транзакции

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

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

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

Способ 3: Отделение записи от проведения

Часто ошибки возникают именно при проведении (из-за нехватки остатков, проверок в ОбработкаПроведения и т.д.). Запись объекта без проведения гораздо реже приводит к фатальным ошибкам транзакции.

Мы можем применить следующую тактику:

  1. Записываем объект в режиме Запись (это создаст ссылку и сохранит данные).
  2. Если запись прошла успешно, пытаемся провести документ.
  3. Если проведение не удалось, мы уже имеем записанный объект, и нам может не понадобиться выполнять НайтиПоКоду, так как данные уже под рукой.

Способ 4: Проверка статуса транзакции

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

Резюме

Подведем итог нашему анализу. Ошибка, с которой столкнулся автор, — это не «глюк» метода НайтиПоКоду, а фундаментальное поведение платформы 1С.

Основные правила работы:

← На главную