При разработке расширений и доработке управляемых форм документов разработчики нередко сталкиваются с ситуацией: в обработчике формы данные табличной части или реквизитов модифицируются непосредственно перед записью, в отладчике на форме всё выглядит корректно, однако в событие модуля объекта ПередЗаписью и в саму базу данных изменения не попадают. Разберем механизм работы платформы "1С:Предприятие" при записи формы и выясним, как правильно реализовать модификацию данных.
Рассмотрим последовательность действий платформы при интерактивной записи документа из формы. До момента вызова серверного обработчика ПередЗаписьюНаСервере платформа уже выполняет конвертацию данных управляемой формы в прикладной объект информационной базы (процесс, аналогичный вызову метода ДанныеФормыВЗначение). Этот сконвертированный экземпляр передается в процедуру в качестве параметра ТекущийОбъект.
Если внутри процедуры ПередЗаписьюНаСервере мы обращаемся к реквизиту формы Объект (например, меняем реквизит шапки или строки его табличной части), платформа игнорирует эти правки для текущей транзакции записи. В модуль объекта для выполнения проверок и последующей записи передается именно ссылка на экземпляр из параметра ТекущийОбъект.
Посмотрим на показательный пример ошибки и правильного подхода:
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
// ОШИБКА: Изменение реквизита формы Объект не попадет в базу данных при текущей записи!
Объект.Комментарий = "Эта идея обречена на провал";
// ПРАВИЛЬНО: Модифицируем экземпляр прикладного объекта, переданный в параметре процедуры
ТекущийОбъект.Комментарий = "Это хорошая идея";
КонецПроцедуры
Таким образом, если необходимо изменить реквизиты или строки табличной части в процедуре ПередЗаписьюНаСервере, модифицировать нужно исключительно коллекцию и поля параметра ТекущийОбъект.
Разберем важные нюансы взаимодействия платформы с объектом в данном обработчике:
ТекущийОбъект обратно в данные формы Объект. Однако если в процессе записи произойдет ошибка, будет вызвано исключение или разработчик установит Отказ = Истина, транзакция отменится, а все изменения, внесенные в ТекущийОбъект, будут безвозвратно утрачены и на клиенте не отобразятся.ТекущийОбъект на стороне сервера выполняется значительно быстрее, чем построчная обработка коллекции Объект.<ИмяТЧ> на клиенте, так как исключаются лишние накладные расходы на передачу контекста и сериализацию данных формы.В зависимости от архитектуры вашего решения и бизнес-требований модификацию данных документа можно организовать несколькими надежными способами.
Если критически важно, чтобы пользователь сразу увидел изменения на форме до начала серверных вызовов записи, логику следует выполнять в обработчике формы ПередЗаписью на клиенте. Если для модификации требуются серверные алгоритмы, вызовем отдельную неконтекстную функцию:
&НаСервереБезКонтекста
Функция ПолучитьРассчитанныеДанныеТЧ(ДанныеДляРасчета)
// Выполняем выборку или расчет без контекста формы
Возврат РезультатРасчета;
КонецФункции
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
// Получаем необходимые значения и модифицируем Объект на клиенте
НовыеДанные = ПолучитьРассчитанныеДанныеТЧ(Объект.Ссылка);
// Заполняем Объект.<ИмяТЧ>
// После этого платформа штатно сконвертирует Объект в ТекущийОбъект с новыми данными
КонецПроцедуры
Модификация данных исключительно в модуле формы имеет существенный архитектурный недостаток: алгоритм не сработает при программной записи документа (например, при загрузке данных из внешних систем, обмене через планы обмена, выполнении регламентных заданий). Чтобы обеспечить гарантированную целостность данных, перенесем логику в модуль самого объекта конфигурации.
В расширении подключимся к событию модуля объекта с помощью аннотации &Перед:
&Перед("ПередЗаписью")
Процедура Расш_ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Модифицируем табличную часть непосредственно в объекте
Для Каждого СтрТЧ Из ЭтотОбъект.Товары Цикл
// Выполняем заполнение реквизитов строк
КонецЦикла;
КонецПроцедуры
Если для алгоритма в модуле объекта требуются специфические параметры, флаги или настройки, которые выбраны пользователем на форме и не хранятся в реквизитах базы данных, воспользуемся свойством ДополнительныеСвойства.
Посмотрим, как организовать передачу данных по шагам:
ПередЗаписьюНаСервере заполняем структуру дополнительных свойств объекта:
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
ТекущийОбъект.ДополнительныеСвойства.Вставить("ПересчитыватьАвтоматически", Истина);
КонецПроцедуры
&Перед("ПередЗаписью")
Процедура Расш_ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
ФлагПересчета = Ложь;
Если ЭтотОбъект.ДополнительныеСвойства.Свойство("ПересчитыватьАвтоматически", ФлагПересчета) И ФлагПересчета Тогда
// Выполняем модификацию табличной части
КонецЕсли;
КонецПроцедуры
Применение правильного объекта для модификации данных исключает потерю пользовательских правок и гарантирует корректную запись документов в системе.