При разработке правил обмена в Конвертации данных 2.0 программисты часто сталкиваются с ситуацией, когда стандартная конструкция Попытка...Исключение ведет себя не так, как ожидалось. Типичный сценарий: вы пытаетесь записать или провести объект в блоке Попытка, и в случае неудачи хотите выполнить какие-то дополнительные действия в блоке Исключение (например, найти узел обмена, сделать запись в лог или изменить статус). Однако вместо выполнения вашего кода в блоке Исключение, платформа выбрасывает критическую ошибку: «В данной транзакции уже происходили ошибки!».
Рассмотрим подробнее, почему это происходит, как работает механизм транзакций в 1С и какими способами можно обойти это ограничение.
Главная причина кроется в архитектуре платформы «1С:Предприятие». Когда выполняется любой код, взаимодействующий с базой данных (запись объекта, проведение, удаление), платформа открывает транзакцию. Если внутри этой транзакции происходит ошибка уровня базы данных (например, нарушение уникальности индексов, отказ в обработчике ПередЗаписью, ошибка блокировки или неуспешная попытка проведения), транзакция помечается как невалидная (или «битая»).
С этого момента и до тех пор, пока транзакция не будет полностью отменена (вызван ОтменитьТранзакцию()), любое последующее обращение к базе данных будет вызывать исключение. Проанализируем ситуацию: метод НайтиПоКоду, ПолучитьОбъект или даже простой запрос к БД — всё это требует обращения к СУБД. Но поскольку текущая транзакция уже «сломана» ошибкой, платформа блокирует любые операции, чтобы предотвратить нарушение целостности данных.
Проблема в том, что блок Попытка...Исключение перехватывает саму ошибку в коде 1С, но он не может восстановить состояние транзакции в базе данных. Если ошибка произошла внутри Попытки, то в блок Исключение вы попадете, но база данных для вас будет «закрыта» до выхода из текущей транзакции.
Почему в обычной обработке код может работать, а в Конвертации данных — нет? Разберем по шагам логику работы универсальной обработки загрузки данных XML:
ПослеЗагрузкиОбъекта, ваш код выполняется внутри этой огромной транзакции.Объект.Записать(РежимЗаписиДокумента.Проведение) выдает ошибку, вся транзакция (включая предыдущие 4 объекта) помечается как ошибочная.Исключение пытается выполнить ПланыОбмена.НайтиПоКоду(...), обращается к «битой» транзакции и получает закономерный отказ.Рассмотрим пример кода, который гарантированно приведет к ошибке, если документ не удастся провести:
Попытка
Объект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
// В этот момент транзакция БД уже помечена как ошибочная!
// Любой поиск в базе данных вызовет исключение.
УзелОбм = ПланыОбмена.ОбменУПП.НайтиПоКоду("УПП13");
// ОШИБКА: В данной транзакции уже происходили ошибки!
КонецПопытки;
Выяснив причину, мы можем выбрать одну из стратегий решения задачи. Рассмотрим их от простых к более сложным.
Если вам нужны какие-то справочные данные или ссылки в блоке Исключение, получите их до того, как начнете операцию записи, которая может привести к ошибке. В Конвертации данных удобно использовать структуру Параметры или алгоритмы.
Проанализируем правильный подход:
// В начале обработчика или в "Перед загрузкой данных"
// Кэшируем ссылку заранее
Если Не Параметры.Свойство("УзелУПП") Тогда
Параметры.Вставить("УзелУПП", ПланыОбмена.ОбменУПП.НайтиПоКоду("УПП13"));
КонецЕсли;
Попытка
Объект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
// Ссылка уже есть в памяти, обращение к БД не требуется
УзелОбм = Параметры.УзелУПП;
// Здесь можно выполнить действия, не требующие записи в БД
// Например, сообщить пользователю подробности
Сообщить("Ошибка проведения документа: " + ОписаниеОшибки());
КонецПопытки;
Если вам критически важно, чтобы при ошибке одного объекта остальные загружались нормально, и вы могли выполнять поиск в БД после ошибки, нужно изменить настройки загрузки.
В обработке «Универсальный обмен данными XML» установите параметр «Количество объектов в транзакции» равным 1. В этом случае каждый объект будет записываться в своей отдельной транзакции. Если запись не удастся, транзакция этого конкретного объекта откатится, и в следующем объекте (или после выхода из процедуры записи текущего) вы снова сможете работать с БД.
Важно: этот метод существенно замедляет скорость загрузки данных, так как создание транзакции на каждый объект — дорогая операция для СУБД.
Часто ошибки возникают именно при проведении (из-за нехватки остатков, проверок в ОбработкаПроведения и т.д.). Запись объекта без проведения гораздо реже приводит к фатальным ошибкам транзакции.
Мы можем применить следующую тактику:
Запись (это создаст ссылку и сохранит данные).НайтиПоКоду, так как данные уже под рукой.В сложных алгоритмах полезно проверять, жива ли еще транзакция, прежде чем обращаться к базе данных. Для этого в языке 1С предусмотрена функция ТранзакцияАктивна(). Однако стоит помнить, что она показывает лишь наличие открытой транзакции, но не гарантирует её «здоровье». Если вы уже получили ошибку «В данной транзакции уже происходили ошибки!», то единственный выход — завершить текущий процесс и позволить платформе выполнить откат.
Подведем итог нашему анализу. Ошибка, с которой столкнулся автор, — это не «глюк» метода НайтиПоКоду, а фундаментальное поведение платформы 1С.
Основные правила работы:
Исключение, если в блоке Попытка произошла ошибка записи.Параметры.