При автоматическом создании и проведении документов в 1С разработчики нередко сталкиваются с ситуацией, когда документ не проводится и выдает системное сообщение «В данной транзакции уже происходили ошибки». При этом ручное копирование или повторное интерактивное проведение того же самого документа может выполняться без каких-либо ошибок. Разберем подробно, почему возникает такое поведение платформы, как локализовать первопричину сбоя и как правильно выстроить программную обработку данных.
Сообщение о том, что в транзакции уже происходили ошибки, является вторичным. Платформа 1С работает по строгим правилам управления транзакциями: если в процессе записи или проведения документа (включая обработчики ПередЗаписью, ПриЗаписи, ОбработкаПроведения и подписки на события) произошла любая ошибка уровня СУБД, блокировок или необработанное исключение, транзакция помечается как недействительная (требующая безусловного отката).
Если после возникновения сбоя исполнение кода продолжается (например, ошибка была перехвачена блоком Попытка ... Исключение, но транзакция не была прервана) и управление доходит до вызова ЗафиксироватьТранзакцию() или системного завершения записи, платформа аварийно останавливает процесс с указанной формулировкой.
Проанализируем типичный фрагмент кода, где скрывается подобная логика:
Если Источник.Склад = Справочники.Склады.НайтиПоКоду("00015") Тогда
СписокРазрешеннойПродукции = Новый СписокЗначений;
Попытка
СписокРазрешеннойПродукции.Добавить(Справочники.Номенклатура.НайтиПоКоду("00214"));
СписокРазрешеннойПродукции.Добавить(Справочники.Номенклатура.НайтиПоКоду("00218"));
СписокРазрешеннойПродукции.Добавить(Справочники.Номенклатура.НайтиПоКоду("00215"));
Исключение
// Попытка подавить ошибку внутри транзакции не снимает флаг ошибки транзакции
КонецПопытки;
КонецЕсли;
Одной из частых причин сбоя при программной обработке становится некорректная работа метода НайтиПоКоду(). Разберем, почему поиск по коду может возвращать неожиданный результат:
Неопределено, а пустую ссылку соответствующего типа (Справочники.Склады.ПустаяСсылка()). Если в проверяемом документе поле Склад не заполнено, условие вида Источник.Склад = Справочники.Склады.НайтиПоКоду("00015") вернет Истина, так как пустая ссылка равна пустой ссылке. В результате алгоритм начинает выполняться для некорректных данных."00015", поиск завершится неудачей, так как реальный код в базе имеет вид "000000015".В типовых конфигурациях (таких как 1С:Бухгалтерия) при записи документов автоматически срабатывают подсистемы Библиотеки стандартных подсистем (БСП), например, версионирование объектов (РегистрСведений.ВерсииОбъектов) и интеграционные механизмы (РегистрСведений.ДокументыПоТребованиюФНС). Если документ создается программно и в нем не заполнены обязательные реквизиты (организация, дата, контрагент) либо реквизиты заполняются в нестандартной последовательности, в подписках на события происходит исключение «Поле объекта не обнаружено», которое и приводит к блокировке всей транзакции.
Разберем по шагам, как выявить истинное место ошибки и устранить ее:
Журнал регистрации с отбором по событию «Ошибка» и транзакции документа. В журнале фиксируется самое первое исключение с полным стеком вызова, которое и привело к повреждению транзакции.Отладка -> Остановка по ошибке и включим флаг «Останавливаться по ошибке». При повторном запуске обработки отладчик остановится точно в том месте, где возникает первое исключение, даже если оно находится внутри общих модулей подписок на события.ЗначениеЗаполнено().Объект.ОбменДанными.Загрузка = Истина. Это отключит выполнение лишних подписок и проверок при первичной записи.Посмотрим на пример надежного кода для поиска элементов и проверки условий перед проведением документа:
// 1. Получаем ссылку на нужный склад через безопасный поиск
ИскомыйСклад = Справочники.Склады.НайтиПоНаименованию("Основной склад");
Если Не ЗначениеЗаполнено(ИскомыйСклад) Тогда
// Запасной вариант поиска по коду с учетом формата
ИскомыйСклад = Справочники.Склады.НайтиПоКоду("000000015");
КонецЕсли;
// 2. Сравниваем только при условии, что и в источнике, и в поиске ссылки заполнены
Если ЗначениеЗаполнено(Источник.Склад) И ЗначениеЗаполнено(ИскомыйСклад) И Источник.Склад = ИскомыйСклад Тогда
СписокРазрешеннойПродукции = Новый СписокЗначений;
// Использование запроса для гарантированного поиска номенклатуры
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Код В(&МассивКодов)
| И НЕ Номенклатура.ПометкаУдаления";
МассивКодов = Новый Массив;
МассивКодов.Добавить("000000214");
МассивКодов.Добавить("000000218");
МассивКодов.Добавить("000000215");
Запрос.УстановитьПараметр("МассивКодов", МассивКодов);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
СписокРазрешеннойПродукции.Добавить(Выборка.Ссылка);
КонецЦикла;
КонецЕсли;
Применение выборки через Запрос и обязательная проверка метода ЗначениеЗаполнено() позволяют полностью исключить ложные срабатывания условий, предотвратить возникновение ошибок в подписках на события и обеспечить корректное проведение документов в автоматическом режиме.