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