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

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

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

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

Физика процесса: что такое «сломанная» транзакция?

Данная ошибка — это классическое проявление механизма СУБД, который переводит текущую транзакцию в статус «только для отмены» (rollback-only). Это происходит в тот момент, когда внутри транзакции выполняется запрос или операция записи, приводящая к ошибке на уровне базы данных (например, нарушение уникальности индекса, попытка записи пустого значения в обязательное поле или конфликт блокировок).

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

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


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    
    // Начало неявной транзакции проведения документа
    Попытка
        // Выполняется некорректный запрос к СУБД или запись в поврежденный регистр
        РегистрыСведений.МойРегистр.СоздатьНаборЗаписей().Записать(); 
    Исключение
        // Ошибка перехвачена, транзакция на уровне СУБД уже помечена как rollback-only!
        ЗаписатьВЛогОшибок("Что-то пошло не так, но продолжаем проведение...");
    КонецПопытки;
    
    // Любое последующее действие с базой данных вызовет фатальный сбой
    Движения.Хозрасчетный.Записать(); // Здесь платформа выбросит ошибку транзакции!

КонецПроцедуры

В данном примере истинной проблемой была неудачная запись в МойРегистр, но программист увидит в журнале регистрации и на экране лишь сообщение о том, что не удалось записать движения по регистру Хозрасчетный из-за ранее произошедшей ошибки.

Почему при ручном проведении ошибки нет?

Разница в поведении системы при ручном проведении и при групповой обработке заключается в транзакционном контексте:

Метод 1. Локализация ошибки с помощью отладочного скрипта

Если стандартный отладчик не помогает, мы можем изолировать процесс проведения документов от транзакционного контекста групповой обработки. Для этого напишем простейший алгоритм проведения документов в цикле. Мы можем запустить его через обработку «Групповая обработка объектов» или создать внешнюю обработку со следующим кодом:


Выборка = Документы.КорректировкаРеализации.Выбрать(НачалоПериода, КонецПериода);
Пока Выборка.Следующий() Цикл
    Если Выборка.Проведен Тогда
        ДокОбъект = Выборка.ПолучитьОбъект();
        Попытка
            // Проводим документ вне транзакционных механизмов групповой обработки
            ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
            Сообщить("Документ " + ДокОбъект + " успешно перепроведен.");
        Исключение
            // Здесь мы поймаем ИСТИННУЮ причину сбоя до того, как транзакция заблокируется
            Сообщить("Критическая ошибка проведения документа " + ДокОбъект + ": " + ОписаниеОшибки());
        КонецПопытки;
    КонецЕсли;
КонецЦикла;

Этот простой шаг позволяет запустить проведение каждого документа в индивидуальной транзакции. Если сбой происходит из-за битых данных конкретного документа, данный скрипт немедленно выведет реальную ошибку в окно сообщений.

Метод 2. Тонкая настройка встроенного отладчика 1С

Стандартная настройка отладчика «Останавливаться по ошибке» часто игнорирует первопричину, так как она происходит внутри системных блоков перехвата исключений, либо выполняется в фоновом режиме на сервере. Выполним правильную настройку отладки по шагам:

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

Метод 3. Использование Технологического Журнала (ТЖ) для поиска setRollbackOnly

Если база данных работает в режиме клиент-сервера и развернута на продуктивном сервере, где ручная отладка затруднена или невозможна, самым надежным способом будет сбор логов Технологического Журнала 1С. Поскольку транзакция помечается испорченной на уровне СУБД, платформа делает системный вызов функции отката.

Выясним точное место сбоя. Для этого в каталоге настроек платформы на сервере (например, C:\Program Files\1cv8\conf\ для Windows-сервера) создадим конфигурационный файл logcfg.xml следующего содержания:


<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://v8.1c.ru/v8/tech-log">
  <log location="C:\1C_logs\trans_errors" history="24">
    <event>
      <eq property="name" value="SDBL"/>
      <eq property="Func" value="setRollbackOnly"/>
    </event>
    <property name="all"/>
  </log>
</config>

После создания файла запустим групповое перепроведение и дождемся воспроизведения ошибки. Сразу после этого откроем лог-файлы в папке C:\1C_logs\trans_errors. В файле будет зафиксировано событие SDBL с функцией setRollbackOnly. ТЖ предоставит нам полный стек вызовов на встроенном языке 1С с указанием модулей и конкретных номеров строк кода. Это и будет точное место, где произошла первая скрытая ошибка.

Метод 4. Проверка базы данных через Тестирование и Исправление (ТиИС)

Часто в измененных конфигурациях нарушается логическая целостность данных. Например, в измерениях регистров сведений могут появиться «битые» ссылки (объекты, которые были удалены напрямую из базы данных без контроля ссылочной целостности), или реквизиты документов могут содержать недопустимые типы данных. При индивидуальном проведении эти регистры могут не затрагиваться, но при групповом перепроведении система обращается к ним для пересчета итогов.

Рекомендуется выполнить следующие действия (предварительно сделав резервную копию базы данных!):

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

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

Правильный шаблон написания кода с использованием транзакций выглядит следующим образом:


НачатьТранзакцию();
Попытка
    // Выполняем запись данных в регистры или документы
    МойОбъектЗаписи.Записать();
    
    // Если всё прошло успешно, фиксируем изменения в СУБД
    ЗафиксироватьТранзакцию();
Исключение
    // В случае ЛЮБОЙ ошибки СУБД мы ОБЯЗАНЫ сразу отменить транзакцию
    ОтменитьТранзакцию();
    
    // Логируем ошибку для администраторов
    ЗаписатьПеременнуюВЖурналРегистрации(
        "ОшибкаЗаписиДанных",
        УровеньЖурналаРегистрации.Ошибка,
        ,
        ,
        ОписаниеОшибки()
    );
    
    // Обязательно пробрасываем ошибку дальше по стеку, чтобы остановить процесс проведения
    ВызватьИсключение "Критический сбой записи данных: " + ОписаниеОшибки();
КонецПопытки;

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

← На главную