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