Почему не сохраняется программно созданный реквизит формы в 1С и как решить эту проблему?

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

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

Анализ проблемы: реквизит формы против реквизита объекта

Рассмотрим частую ошибку в коде создания реквизита формы:


НовыеРеквизиты = Новый Массив;
НовыйРеквизит = Новый РеквизитФормы(Результат.Имя, Результат.Свойство, , , Истина);
НовыеРеквизиты.Добавить(НовыйРеквизит);
ЭтаФорма.ИзменитьРеквизиты(НовыеРеквизиты);

Элемент = ЭтаФорма.Элементы.Добавить(Результат.Имя, Тип("ПолеФормы"));
Элемент.Вид = ВидПоляФормы.ПолеВвода;
Элемент.ПутьКДанным = Результат.Имя;

Выясним причину, по которой введенное пользователем значение не сохраняется в базе данных:

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

Способ 1. Использование плана видов характеристик и табличной части (типовой механизм дополнительных реквизитов)

Если перед нами стоит задача дать пользователю возможность самостоятельно добавлять произвольные поля к справочнику или документу с выбором типа и сохранением значений, стандартный подход 1С заключается в использовании механизма свойств на базе Плана видов характеристик (ПВХ).

Разберем структуру этого решения:

  1. План видов характеристик: создаем в конфигурации объект (например, ПланВидовХарактеристик.СвойстваОбъектов), в котором задаются наименования пользовательских реквизитов и типы их значений (Число, Строка, Дата, Булево, ссылки на справочники).
  2. Табличная часть объекта: у объекта (например, справочника) создаем табличную часть ДополнительныеРеквизиты с двумя основными колонками:
    • Свойство — тип ПланВидовХарактеристикСсылка.СвойстваОбъектов;
    • Значение — тип Характеристика.СвойстваОбъектов.
  3. Регистр сведений (альтернатива): если табличную часть в объект добавить нельзя, данные можно сохранять в непериодическом регистре сведений с измерениями Объект, Свойство и ресурсом Значение.

Способ 2. Правильное управление жизненным циклом динамических реквизитов в форме

Если мы создаем реквизиты на форме программно, необходимо связать их с сохраненными данными в объекте. Разберем по шагам, как построить логику в модуле формы объекта.

Шаг 1. Создание реквизитов и элементов при открытии формы

В обработчике ПриСозданииНаСервере считываем перечень свойств из базы, динамически добавляем реквизиты формы и визуальные элементы ввода, после чего подставляем сохраненные значения:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    СоздатьДинамическиеРеквизитыИЭлементы();
КонецПроцедуры

&НаСервере
Процедура СоздатьДинамическиеРеквизитыИЭлементы()
    МассивНовыхРеквизитов = Новый Массив;
    
    // Обходим строки табличной части или записи регистра
    Для Каждого Стр Из Объект.ДополнительныеРеквизиты Цикл
        ИмяРеквизита = "Реквизит_" + Стр.Свойство.УникальныйИдентификатор();
        
        // Получаем тип значения характеристики
        ОписаниеТипа = Стр.Свойство.ТипЗначения;
        
        Реквизит = Новый РеквизитФормы(ИмяРеквизита, ОписаниеТипа, , Стр.Свойство.Наименование, Истина);
        МассивНовыхРеквизитов.Добавить(Реквизит);
    КонецЦикла;
    
    // Регистрируем реквизиты в контексте формы
    ИзменитьРеквизиты(МассивНовыхРеквизитов);
    
    // Создаем элементы интерфейса и заполняем значения
    Для Каждого Стр Из Объект.ДополнительныеРеквизиты Цикл
        ИмяРеквизита = "Реквизит_" + Стр.Свойство.УникальныйИдентификатор();
        
        Элемент = Элементы.Добавить(ИмяРеквизита, Тип("ПолеФормы"), ЭтаФорма);
        Элемент.Вид = ВидПоляФормы.ПолеВвода;
        Элемент.ПутьКДанным = ИмяРеквизита;
        
        // Переносим значение из объекта в реквизит формы
        ЭтаФорма[ИмяРеквизита] = Стр.Значение;
    КонецЦикла;
КонецПроцедуры

Шаг 2. Обработка перечитывания данных

При выполнении команды «Перечитать» программные элементы формы могут быть удалены или сброшены. Проверим их наличие и восстановим значения в событии ПриЧтенииНаСервере:


&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
    // Восстанавливаем актуальные значения реквизитов из перечитанного объекта
    Для Каждого Стр Из ТекущийОбъект.ДополнительныеРеквизиты Цикл
        ИмяРеквизита = "Реквизит_" + Стр.Свойство.УникальныйИдентификатор();
        Если Элементы.Найти(ИмяРеквизита) <> Неопределено Тогда
            ЭтаФорма[ИмяРеквизита] = Стр.Значение;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Шаг 3. Сохранение данных в объект перед записью

Чтобы изменения, внесенные пользователем в программно созданные поля, попали в базу данных, перед записью переносим их обратно в табличную часть объекта:


&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    Для Каждого Стр Из ТекущийОбъект.ДополнительныеРеквизиты Цикл
        ИмяРеквизита = "Реквизит_" + Стр.Свойство.УникальныйИдентификатор();
        Если Элементы.Найти(ИмяРеквизита) <> Неопределено Тогда
            Стр.Значение = ЭтаФорма[ИмяРеквизита];
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Способ 3. Хранение произвольных наборов данных в ХранилищеЗначения

Если для объекта требуется сохранять произвольные динамические поля, но поиск или фильтрация запросом по этим полям в базе не требуются, можно применить хранение в сериализованном виде:

  1. Добавляем в конфигураторе реквизит объекта с типом ХранилищеЗначения (например, ДополнительныеДанныеХранилище).
  2. При записи формы упаковываем все значения динамических полей в структуру или соответствие:
    
    &НаСервере
    Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
        СтруктураДанных = Новый Структура;
        СтруктураДанных.Вставить("Поле1", ЭтаФорма.Реквизит1);
        СтруктураДанных.Вставить("Поле2", ЭтаФорма.Реквизит2);
        
        ТекущийОбъект.ДополнительныеДанныеХранилище = Новый ХранилищеЗначения(СтруктураДанных);
    КонецПроцедуры
    
  3. При чтении на сервере распаковываем структуру методом Получить() и присваиваем значения реквизитам формы:
    
    &НаСервере
    Процедура ПриЧтенииНаСервере(ТекущийОбъект)
        Хранилище = ТекущийОбъект.ДополнительныеДанныеХранилище;
        Если ЗначениеЗаполнено(Хранилище) Тогда
            СтруктураДанных = Хранилище.Получить();
            Если ТипЗнч(СтруктураДанных) = Тип("Структура") Тогда
                ЗаполнитьЗначенияСвойств(ЭтаФорма, СтруктураДанных);
            КонецЕсли;
        КонецЕсли;
    КонецПроцедуры
    

Таким образом, реквизиты формы служат исключительно промежуточным интерфейсным слоем. Для надежного сохранения данных всегда требуется связывать их с постоянными объектами метаданных информационной базы: табличными частями, регистрами сведений или реквизитами типа ХранилищеЗначения.

← На главную