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