Почему возникает ошибка при повторной записи документа после исключения при проведении в 1С?

Программист IT и автоматизация бизнеса
← На главную

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

Разберем, почему возникает эта проблема, как ведет себя платформа 1С и какими способами можно правильно выстроить алгоритм записи и проведения документов.

В чем заключается проблема

Проанализируем распространенную конструкцию кода:


Попытка
    РеализацияТоваровУслуг.Записать(РежимЗаписиДокумента.Проведение);
Исключение 
    Отказ = Истина;
    РеализацияТоваровУслуг.Записать(РежимЗаписиДокумента.Запись);
КонецПопытки;

При выполнении этого кода в момент вызова метода Записать(РежимЗаписиДокумента.Проведение) происходят следующие процессы:

  1. Платформа запускает обработку проведения документа. В процессе проведения заполняются коллекции наборов записей в свойстве Движения объекта, для которых устанавливается признак Записывать = Истина.
  2. Если в процессе проведения или в подписках на события происходит сбой (например, не хватает остатка товара или установлена отмена проведения), платформа генерирует исключение.
  3. Управление переходит в блок Исключение, однако объект документа в памяти сохраняет сформированные движения с установленным флагом записи.
  4. Повторный вызов Записать(РежимЗаписиДокумента.Запись) пытается записать накопившиеся движения в регистры. Это приводит к повторному контролю остатков, который снова завершается ошибкой, так как товар на складе уже был условно заблокирован или списан в памяти первой попытки проведения.

Причины сбоя на уровне платформы

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

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

Правильные способы решения проблемы

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

Способ 1. Двухэтапная схема (сначала запись, затем проведение)

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


// Шаг 1. Записываем документ в режиме обычного сохранения
РеализацияТоваровУслуг.Записать(РежимЗаписиДокумента.Запись);

// Шаг 2. Пытаемся провести документ
Попытка
    РеализацияТоваровУслуг.Записать(РежимЗаписиДокумента.Проведение);
Исключение
    // Документ уже гарантированно записан в базу непроведенным.
    // Выводим информацию об ошибке проведения в журнал
    ЗаписьЖурналаРегистрации("Проведение документа", 
        УровеньЖурналаРегистрации.Ошибка, 
        , 
        , 
        ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;

При таком подходе, если проведение завершается ошибкой, документ гарантированно остается сохраненным в базе данных со статусом "Не проведен", а движения по регистрам не повреждаются.

Способ 2. Очистка движений перед повторной записью

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


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

Способ 3. Перечтение объекта из базы данных

Чтобы полностью сбросить несохраняемое состояние объекта из памяти после исключения, воспользуемся методом Прочитать(). Он возвращает объект к состоянию, сохраненному в базе данных:


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

Диагностика ошибок проведения

Для детального анализа причин, по которым проведение попадает в блок исключения (даже если функция ОписаниеОшибки() возвращает пустую строку), используем встроенные средства платформы:

  1. ИнформацияОбОшибке() — возвращает объект с детальной информацией о произошедшем исключении.
  2. ПодробноеПредставлениеОшибки() — формирует подробный стек вызовов и текст ошибки для вывода в журнал регистрации.
  3. Сообщить() — выводит текстовое сообщение в пользовательский интерфейс, что позволяет увидеть реальную причину отказа при отладке.

Применяя двухэтапную схему проведения и контролируя состояние коллекции Движения, можно гарантировать стабильную работу алгоритмов проведения документов в 1С.

← На главную