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