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