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