В процессе разработки механизмов загрузки данных в 1С программисты часто сталкиваются с ситуацией, когда стандартный метод Записать() возвращает ошибку «В данной транзакции уже происходили ошибки». Эта ошибка является одной из самых коварных в платформе 1С:Предприятие 8.3, так как она не указывает на первопричину проблемы, а лишь сообщает, что транзакция была «сломана» ранее. Особенно часто это происходит при работе со справочниками, интегрированными в сложные подсистемы, такие как «Номенклатура контрагентов» (или «Номенклатура поставщиков») в современных конфигурациях (ERP 2, УТ 11, КА 2).
В данной статье мы подробно разберем, почему возникает эта ошибка, как проанализировать стек вызовов и какие реквизиты необходимо заполнить, чтобы успешно создать элемент справочника программно.
Для начала проанализируем, что именно происходит в системе. Когда мы вызываем метод Записать(), платформа открывает транзакцию. Внутри этой транзакции срабатывают программные обработчики: ПередЗаписью (в модуле объекта), ПриЗаписи, а также многочисленные подписки на события. Если в любом из этих обработчиков код заключен в блок Попытка...Исключение и внутри этого блока происходит ошибка базы данных (например, нарушение уникальности или отказ в записи связанного регистра), транзакция помечается как «невалидная».
После этого любое последующее обращение к базе данных (даже простое чтение константы или получение ссылки) вызовет исключение «В данной транзакции уже происходили ошибки». Рассматривая ситуацию автора темы, мы видим, что ошибка возникает именно при записи, причем в стеке вызовов фигурируют общие модули, связанные с проверкой удаления объектов.
Рассмотрим типовой код, который изначально привел к ошибке:
&НаСервере
Процедура кнЗагрузитьНаСервере()
тзНомПост = РеквизитФормыВЗначение("Таб");
спрНоменклатура = Справочники.Номенклатура;
Для Каждого Стр Из тзНомПост Цикл
НайденнаяНоменклатура = спрНоменклатура.НайтиПоРеквизиту("Артикул", СокрЛП(Стр.НашКод));
Если Не НайденнаяНоменклатура.Пустая() Тогда
НовыйЭлемент = Справочники.НоменклатураКонтрагентов.СоздатьЭлемент();
НовыйЭлемент.Наименование = Стр.НаименованиеНоменклатурыПоставщика;
НовыйЭлемент.НаименованиеНоменклатуры = Стр.НаименованиеНоменклатурыПоставщика;
НовыйЭлемент.НаименованиеПолное = Стр.НаименованиеНоменклатурыПоставщика;
НовыйЭлемент.Артикул = Стр.КодПоставщика;
НовыйЭлемент.Номенклатура = НайденнаяНоменклатура.Ссылка;
НовыйЭлемент.Упаковка = НайденнаяНоменклатура.ЕдиницаИзмерения;
НовыйЭлемент.Владелец = ПоставщикВладелец.Ссылка;
НовыйЭлемент.ВладелецНоменклатуры = НовыйЭлемент.Владелец;
НовыйЭлемент.Записать(); // Здесь происходит фатальный сбой
КонецЕсли;
КонецЦикла;
КонецПроцедуры
На первый взгляд код выглядит корректным. Однако, как выяснилось в ходе обсуждения, ключевым моментом является установка реквизита ВладелецНоменклатуры. В современных конфигурациях этот реквизит имеет определяемый тип (например, ВладелецНоменклатурыБЭД), и его заполнение включает механизмы синхронизации данных для электронного документооборота (ЭДО).
Посмотрим на стек ошибки, который предоставил автор. В нем видно обращение к модулю УдалениеПомеченныхОбъектовСлужебный. Это сигнализирует о том, что при попытке записи объекта система запускает проверку ссылочной целостности. Если транзакция уже была повреждена в ПередЗаписью из-за нехватки данных, то любая проверка константы в ПриЗаписи выдает вторичную ошибку.
Почему заполнение Владельца провоцирует ошибку? В справочнике «Номенклатура контрагентов» при указании владельца система пытается выполнить поиск сопоставлений в регистрах сведений. Если при этом не заполнены служебные поля, идентификаторы или нарушена логика сопоставления «Партнер - Контрагент», типовой код выбрасывает исключение, которое «тихо» перехватывается, но портит транзакцию.
Как показала практика, для корректной записи элемента справочника «Номенклатура контрагентов» (особенно если он используется в подсистемах БЭД/ЭДО), необходимо заполнять реквизиты-идентификаторы. Эти поля используются системой для однозначной связи между вашей базой и данными поставщика.
Рассмотрим исправленный алгоритм заполнения объекта. Нам необходимо добавить инициализацию полей Идентификатор и ИдентификаторНоменклатуры. В качестве значения можно использовать уникальный идентификатор строки или GUID, полученный от поставщика. Если данных от поставщика нет, можно сгенерировать новый идентификатор или использовать строковое представление ссылки.
Ниже приведен фрагмент кода, который позволяет обойти ошибку и успешно записать элемент:
НовыйЭлемент = Справочники.НоменклатураКонтрагентов.СоздатьЭлемент();
НовыйЭлемент.Наименование = Стр.НаименованиеНоменклатурыПоставщика;
// ... заполнение основных реквизитов ...
НовыйЭлемент.Номенклатура = НайденнаяНоменклатура.Ссылка;
НовыйЭлемент.Владелец = ПоставщикВладелец.Ссылка;
НовыйЭлемент.ВладелецНоменклатуры = ПоставщикВладелец.Ссылка;
// Обязательные поля для предотвращения ошибок в транзакции:
НовыйЭлемент.Идентификатор = Строка(Новый УникальныйИдентификатор());
НовыйЭлемент.ИдентификаторНоменклатуры = Строка(Новый УникальныйИдентификатор());
Попытка
НовыйЭлемент.Записать();
Исключение
Инфо = ИнформацияОбОшибке();
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Ошибка записи: " + ПодробноеПредставлениеОшибки(Инфо));
КонецПопытки;
Важный нюанс: Если вы используете типовую конфигурацию, убедитесь, что в качестве Владельца передается объект правильного типа. В УТ 11/ERP владельцем справочника может быть Партнер, но в реквизитах сопоставления может ожидаться Контрагент. Проверьте настройки функциональных опций «Использовать партнеров и контрагентов как раздельные сущности».
Если вы добавили идентификаторы, но ошибка «В данной транзакции уже происходили ошибки» сохраняется, рекомендуем выполнить следующие шаги:
Отладка - Остановка по ошибке. Установите флаг «Останавливаться по ошибке». Критически важно: снимите галочку «Игнорировать ошибки в попытке». Это позволит вам «поймать» ту самую первичную ошибку, которую типовой код скрывает внутри блока Попытка...Исключение.НоменклатураКонтрагентов. Часто сторонние расширения или доработки вносят логику, которая не рассчитана на программное создание элементов без полной инициализации всех реквизитов формы.Подводя итог, отметим: ошибка «В данной транзакции уже происходили ошибки» почти всегда является симптомом того, что вы пропустили заполнение важного поля, которое проверяется в модуле объекта или подписках. Для справочников, связанных с ЭДО, такими полями являются идентификаторы связей.