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