Почему возникает ошибка «В данной транзакции уже происходили ошибки» при проведении документов и как ее исправить?

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

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

Механизм возникновения ошибки транзакции в платформе 1С

Сообщение о том, что в транзакции уже происходили ошибки, является вторичным. Платформа 1С работает по строгим правилам управления транзакциями: если в процессе записи или проведения документа (включая обработчики ПередЗаписью, ПриЗаписи, ОбработкаПроведения и подписки на события) произошла любая ошибка уровня СУБД, блокировок или необработанное исключение, транзакция помечается как недействительная (требующая безусловного отката).

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

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


Если Источник.Склад = Справочники.Склады.НайтиПоКоду("00015") Тогда
    СписокРазрешеннойПродукции = Новый СписокЗначений;
    Попытка
        СписокРазрешеннойПродукции.Добавить(Справочники.Номенклатура.НайтиПоКоду("00214"));
        СписокРазрешеннойПродукции.Добавить(Справочники.Номенклатура.НайтиПоКоду("00218"));
        СписокРазрешеннойПродукции.Добавить(Справочники.Номенклатура.НайтиПоКоду("00215"));
    Исключение
        // Попытка подавить ошибку внутри транзакции не снимает флаг ошибки транзакции
    КонецПопытки;
КонецЕсли;

Особенности и скрытые ловушки метода «НайтиПоКоду»

Одной из частых причин сбоя при программной обработке становится некорректная работа метода НайтиПоКоду(). Разберем, почему поиск по коду может возвращать неожиданный результат:

  1. Возврат пустой ссылки вместо отсутствия значения: если элемент с заданным кодом не найден в базе, метод возвращает не Неопределено, а пустую ссылку соответствующего типа (Справочники.Склады.ПустаяСсылка()). Если в проверяемом документе поле Склад не заполнено, условие вида Источник.Склад = Справочники.Склады.НайтиПоКоду("00015") вернет Истина, так как пустая ссылка равна пустой ссылке. В результате алгоритм начинает выполняться для некорректных данных.
  2. Длина кода и ведущие нули: поиск по коду строго учитывает длину строкового реквизита, заданную в метаданных конфигурации. Если длина кода склада составляет 9 символов, а поиск выполняется по строке "00015", поиск завершится неудачей, так как реальный код в базе имеет вид "000000015".
  3. Префиксы информационных баз и РИБ: при использовании распределенных информационных баз или нумерации по организациям к кодам справочников автоматически добавляются префиксы. В этом случае жестко прописанный в коде номер перестает определяться.

Сбои в подписках на события БСП

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

Пошаговый алгоритм диагностики и решения проблемы

Разберем по шагам, как выявить истинное место ошибки и устранить ее:

  1. Анализ системного журнала регистрации: откроем Журнал регистрации с отбором по событию «Ошибка» и транзакции документа. В журнале фиксируется самое первое исключение с полным стеком вызова, которое и привело к повреждению транзакции.
  2. Использование точки останова с остановкой по ошибке: в конфигураторе перейдем в меню Отладка -> Остановка по ошибке и включим флаг «Останавливаться по ошибке». При повторном запуске обработки отладчик остановится точно в том месте, где возникает первое исключение, даже если оно находится внутри общих модулей подписок на события.
  3. Проверка заполненности найденных ссылок: заменим прямое сравнение ссылок на обязательную проверку валидности через ЗначениеЗаполнено().
  4. Использование отключения проверок при программной загрузке: если документ формируется обработкой загрузки и должен быть сначала записан технически, установим признак загрузки данных: Объект.ОбменДанными.Загрузка = Истина. Это отключит выполнение лишних подписок и проверок при первичной записи.
  5. Отказ от жесткого кодирования строковых кодов: настроим поиск элементов через запросы, поиск по наименованию или использование предопределенных элементов и констант.

Пример правильной реализации проверки и поиска элементов

Посмотрим на пример надежного кода для поиска элементов и проверки условий перед проведением документа:


// 1. Получаем ссылку на нужный склад через безопасный поиск
ИскомыйСклад = Справочники.Склады.НайтиПоНаименованию("Основной склад");

Если Не ЗначениеЗаполнено(ИскомыйСклад) Тогда
    // Запасной вариант поиска по коду с учетом формата
    ИскомыйСклад = Справочники.Склады.НайтиПоКоду("000000015");
КонецЕсли;

// 2. Сравниваем только при условии, что и в источнике, и в поиске ссылки заполнены
Если ЗначениеЗаполнено(Источник.Склад) И ЗначениеЗаполнено(ИскомыйСклад) И Источник.Склад = ИскомыйСклад Тогда
    
    СписокРазрешеннойПродукции = Новый СписокЗначений;
    
    // Использование запроса для гарантированного поиска номенклатуры
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	Номенклатура.Ссылка КАК Ссылка
        |ИЗ
        |	Справочник.Номенклатура КАК Номенклатура
        |ГДЕ
        |	Номенклатура.Код В(&МассивКодов)
        |	И НЕ Номенклатура.ПометкаУдаления";
    
    МассивКодов = Новый Массив;
    МассивКодов.Добавить("000000214");
    МассивКодов.Добавить("000000218");
    МассивКодов.Добавить("000000215");
    
    Запрос.УстановитьПараметр("МассивКодов", МассивКодов);
    Выборка = Запрос.Выполнить().Выбрать();
    
    Пока Выборка.Следующий() Цикл
        СписокРазрешеннойПродукции.Добавить(Выборка.Ссылка);
    КонецЦикла;
    
КонецЕсли;

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

← На главную