Почему не сохраняется значение реквизита при проведении документа из списка и как это исправить

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

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

Анализ проблемы: разница между проведением из формы и из списка

Давайте проанализируем, что происходит в оперативной памяти и в базе данных (SQL) в обоих случаях. Когда пользователь открывает форму документа, создается программный объект, связанный с этой формой. При нажатии кнопки «Провести» в форме происходит следующее:

  1. Платформа инициирует транзакцию записи.
  2. Выполняются процедуры модуля объекта.
  3. В процедуре ОбработкаПроведения вы меняете значение реквизита ЭтотОбъект.ВашРеквизит = Значение;.
  4. Транзакция завершается, данные записываются в регистры.
  5. Важный момент: после завершения серверного вызова измененный в памяти объект возвращается в форму. Платформа синхронизирует данные формы с этим объектом.

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

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

Жизненный цикл документа при записи и проведении

Для понимания правильного решения нам необходимо рассмотреть последовательность событий, которые генерирует платформа «1С:Предприятие» при записи документа с флагом проведения. Выясним причину, изучив порядок вызовов:

  1. ПередЗаписью (модуль объекта): идеальное место для расчета и установки реквизитов самого документа. Транзакция уже открыта, данные еще не записаны в SQL.
  2. Запись данных: платформа записывает значения реквизитов и табличных частей в основные таблицы базы данных.
  3. ПриЗаписи (модуль объекта): данные уже в базе, но транзакция еще не зафиксирована.
  4. ОбработкаПроведения (модуль объекта): это событие предназначено исключительно для создания движений по регистрам.
  5. Фиксация транзакции: все изменения (и документа, и регистров) окончательно сохраняются в базе данных.

Как мы видим, попытка изменить реквизит документа в пункте 4 является логической ошибкой, так как пункт 2 (запись самого документа) уже пройден.

Правильное решение: использование события ПередЗаписью

Чтобы реквизит гарантированно сохранялся вне зависимости от способа проведения документа, расчет логики необходимо перенести в обработчик ПередЗаписью. Рассмотрим пример кода, который часто ошибочно размещают в модуле проведения, и как его правильно переделать.

Неправильный подход (в модуле объекта):


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    
    // ОШИБКА: Изменение реквизита объекта в этой процедуре не сохранится в БД
    Для Каждого СтрокаТЧ Из Товары Цикл
        СтрокаТЧ.Партия = Справочники.Партии.СоздатьПартию(ЭтотОбъект, СтрокаТЧ.Номенклатура);
    КонецЦикла;
    
    // Движения по регистрам (здесь всё будет работать)
    Движения.Остатки.Записывать = Истина;
    // ... заполнение движений ...
    
КонецПроцедуры

Правильный подход (в модуле объекта):

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


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

КонецПроцедуры

Почему нельзя вызывать Записать() внутри ОбработкаПроведения?

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

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

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

Если вы все же столкнулись с тем, что данные в форме не обновляются (даже при правильной записи), обратите внимание на свойство Модифицированность. В управляемом приложении после выполнения серверного кода форма может считать, что данные изменились, и предложить пользователю сохранить их еще раз.

Проанализируем ситуацию, когда расчет реквизитов зависит от итогов проведения (например, расчет себестоимости по регистру). В классической схеме «1С» документ сначала записывается, а потом проводит сам себя. Если вам жизненно необходимо записать данные в документ, полученные после анализа движений, вам придется выполнить запись в два этапа, но это крайне редкая и архитектурно сомнительная задача. В 99% случаев достаточно перенести расчет в ПередЗаписью.

Резюме по шагам

Для исправления проблемы «невидимого» или «несохраняющегося» реквизита выполните следующие действия:

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

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

← На главную