Почему документ проводится программно с ошибками, а интерактивно выдает отказ?

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

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

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

В чем кроется корень проблемы?

Чтобы понять, почему так происходит, давайте разберем разницу между интерактивным (ручным) и программным проведением документа.

1. Интерактивное проведение (из формы или списка)

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

Кроме того, при проведении из открытой формы отрабатывают события модуля формы, такие как ПередЗаписью и ПриЗаписиНаСервере. В типовых конфигурациях (например, в "Бухгалтерия предприятия 3.0") разработчики часто помещают в модуль формы специфические проверки или дозаполнение скрытых служебных реквизитов.

2. Программное проведение

Посмотрим на пример кода, который часто пишут разработчики (и который привел автор темы):


Попытка
    НовыйДокумент.Записать(РежимЗаписиДокумента.Проведение);
Исключение
    НовыйДокумент.Записать(РежимЗаписиДокумента.Запись);
    ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Ошибка проведения");
КонецПопытки;

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

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

Как решить проблему: правильный алгоритм проведения

Чтобы программный код работал корректно и реагировал на пустые реквизиты так же, как интерактивное проведение, нам необходимо принудительно инициировать эту проверку перед попыткой записи. Для этого в платформе предусмотрен метод объекта ПроверитьЗаполнение().

Рассмотрим подробнее, как должен выглядеть правильный код. Данный метод запускает платформенную проверку реквизитов, а также вызывает событие ОбработкаПроверкиЗаполнения в модуле объекта, собирает все ошибки и возвращает Ложь, если найдены нарушения.


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

Внедрение одной лишь конструкции с ПроверитьЗаполнение() полностью решает описанную проблему: если документ "кривой", он просто не дойдет до стадии проведения в коде обработки.

Дополнительные технические нюансы (БСП и ОбменДанными)

В ходе обсуждения проблемы автор упомянул, что пытался установить свойство ОбменДанными.Загрузка = Ложь. Разберем, почему это не помогло и на что это свойство влияет на самом деле.

  1. Свойство ОбменДанными.Загрузка по умолчанию всегда имеет значение Ложь. Поэтому явное присвоение этого значения в коде ничего не меняет в логике работы платформы.
  2. Если бы мы установили ОбменДанными.Загрузка = Истина, то ситуация стала бы еще хуже. Этот флаг говорит платформе: "Это системная загрузка данных, игнорируй вообще все проверки, не выполняй код обработчиков подписок на события и просто запиши данные в таблицы базы данных как есть". Документ записался бы со статусом проведенного гарантированно, даже если бы там не было вообще никаких данных.

Особенности работы в современных конфигурациях (на базе БСП)

Если мы работаем с современными конфигурациями, такими как Бухгалтерия предприятия 3.0, ERP или УТ 11, стоит учитывать механизмы Библиотеки стандартных подсистем (БСП).

В БСП разработчики часто анализируют программный контекст. Например, в событии ОбработкаПроверкиЗаполнения может проверяться коллекция ДополнительныеСвойства объекта документа. В зависимости от наличия определенных флагов в этой структуре, массив проверяемых реквизитов может динамически изменяться.

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


МассивДокументов = Новый Массив;
МассивДокументов.Добавить(НовыйДокумент.Ссылка);

// Вызов стандартной процедуры БСП для проведения
ОбщегоНазначенияВызовСервера.ПровестиДокументы(МассивДокументов);

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

Резюме: При написании собственных обработок всегда помните, что методы объектов работают "в лоб" и не эмулируют поведение пользовательского интерфейса. Хотите, чтобы документ проверялся перед записью программно — обязательно вызывайте ПроверитьЗаполнение().

← На главную