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

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

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

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

Механика ошибки: почему платформа ругается на транзакцию?

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

  1. Внутри текущей активной транзакции (которая могла открыться автоматически при записи или явно через НачатьТранзакцию()) произошла некая ошибка базы данных или платформы. Например, сработал отказ при записи объекта, не прошла проверка заполнения реквизитов или нарушилась уникальность.
  2. Где-то глубоко в коде (чаще всего в типовых общих модулях) это исключение было перехвачено конструкцией Попытка ... Исключение, и код продолжил выполняться.
  3. Несмотря на перехват исключения, СУБД уже пометила эту транзакцию как «испорченную» (статус setRollbackOnly).
  4. Любая последующая попытка обратиться к базе данных в рамках этой же транзакции (даже простое чтение или наша попытка выполнить НоваяНоменклатураКА.Записать()) закономерно вызовет аварийное завершение с сообщением о том, что транзакция уже содержит ошибки.

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

Как правильно искать первопричину?

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

  1. Отладка обработчиков записи объекта: Зайдем отладчиком внутрь модуля записываемого справочника. Нам нужно поставить точки останова в процедурах ПередЗаписью, ОбработкаПроверкиЗаполнения и ПриЗаписи. Именно там типовой код конфигурации «Управление торговлей 11» проверяет заполнение реквизитов и формирует отказ.
  2. Использование Технологического журнала (ТЖ): В ТЖ фиксируется оригинальное событие СУБД, которое пометило транзакцию на откат. Если мы настроим сбор событий SDBL и найдем запись с пометкой setRollbackOnly, мы сразу увидим стек вызова и истинную строку с ошибкой.

Разбор практического примера: ошибка в УТ 11

Проанализируем пример кода, который вызывал ошибку в нашем случае:


Для Каждого СтрТЗ Из ТЗ Цикл
    НоваяНоменклатураКА = Справочники.НоменклатураКонтрагентов.СоздатьЭлемент();
    НоваяНоменклатураКА.Упаковка = СтрТЗ.Ссылка.ЕдиницаИзмерения;
    НоваяНоменклатураКА.ИдентификаторУпаковки = НоваяНоменклатураКА.Упаковка.Наименование;
    // ... заполнение остальных реквизитов
    НоваяНоменклатураКА.Артикул = СтрТЗ.Ссылка.Артикул;
    НоваяНоменклатураКА.Владелец = Справочники.Партнеры.НайтиПоНаименованию(СтрТЗ.СсылкаНаПартнера.Наименование);
    НоваяНоменклатураКА.Номенклатура = СтрТЗ.Ссылка;
    НоваяНоменклатураКА.Записать();
КонецЦикла;

В чем же была реальная проблема? Конфигурация «Управление торговлей» имеет строгий встроенный контроль. Запись отклоняется, если переданы неверные типы данных в критически важные поля. Как выяснилось при детальном разборе, ошибка скрывалась в реквизите Упаковка (единица измерения).

УТ 11 строго различает базовые единицы измерения (справочник ЕдиницыИзмерения / Классификатор ОКЕИ) и индивидуальные или общие упаковки (справочник УпаковкиЕдиницыИзмерения). Если напрямую присвоить упаковке значение несоответствующего типа или не найти правильную единицу измерения в нужном справочнике, платформа на этапе внутренних проверок генерирует исключение, ломает транзакцию и выдает нашу ошибку.

Решение: необходимо было корректно искать единицу измерения создаваемой карточке в справочнике единиц измерения (или упаковок, в зависимости от настроек номенклатуры), а не подставлять значения напрямую из исходной номенклатуры без проверки совместимости.

Антипаттерны: как писать код лучше

Посмотрим на другие недочеты в коде, которые следует исправлять, чтобы система работала надежно и быстро.

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

2. Ручное формирование служебных идентификаторов
Строки вида НоваяНоменклатураКА.ИдентификаторНоменклатуры = СтрТЗ.Ссылка.Наименование + "##" + ... являются небезопасными. Служебные поля в современных версиях конфигураций формируются и контролируются платформой или типовыми подписками на события. Ручное формирование таких строк может привести к непредсказуемым последствиям и дополнительным ошибкам при валидации данных.

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

← На главную