Как сохранить программно добавляемые реквизиты и их значения в 1С, когда их количество и имена неизвестны заранее?

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

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

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

1. Основы динамического создания реквизитов и элементов формы в 1С

Прежде чем перейти к методам сохранения данных, давайте разберемся, как вообще происходит программное создание элементов на управляемой форме и связанных с ними реквизитов.

  1. Программное создание реквизитов формы: Мы можем динамически добавлять реквизиты к форме с помощью метода ИзменитьРеквизиты(). Для этого используется объект РеквизитФормы, который позволяет указать имя, тип и свойство Сохраняемый.
    
    ЭтотОбъект.ИзменитьРеквизиты(
        Новый Массив(Новый РеквизитФормы("МойДинамическийРеквизит", Новый ОписаниеТипов("Строка"), Истина)), // Истина означает, что реквизит сохраняемый
        Новый Массив() // Если нужно удалить существующие реквизиты
    );
    

    Свойство Сохраняемый (третий параметр в конструкторе Новый РеквизитФормы()) позволяет платформе автоматически сохранять и восстанавливать значения этих реквизитов между сеансами для текущего пользователя. Однако важно понимать, что этот механизм сохраняет значения для формы, а не для объекта базы данных. То есть, если мы откроем ту же форму для другого документа или для того же документа, но в другой базе данных (или если сам реквизит был переименован/изменен), значения не будут автоматически восстановлены. Для постоянного хранения данных нам потребуется работать с базой данных.

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

    Такой подход позволяет нам полностью контролировать структуру формы и её содержимое в процессе выполнения.

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

Механизм дополнительных реквизитов и сведений (ДРС) в Библиотеке Стандартных Подсистем (БСП) — это мощный и гибкий инструмент, позволяющий расширять структуру данных объектов конфигурации без прямого изменения метаданных. Форумчане не зря настаивают на его рассмотрении, так как он является типовым решением для подобных задач.

Как работает БСП для дополнительных реквизитов:

  1. Планы видов характеристик (ПВХ): В основе механизма ДРС лежит объект ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения. Он определяет возможные "типы" или "виды" ваших дополнительных реквизитов. Каждый элемент этого плана видов характеристик представляет собой описание одного свойства: его наименование (которое может быть именем вашего динамического реквизита), тип значения, возможность заполнения и другие параметры.
  2. Регистры сведений: Фактические значения этих характеристик для конкретных объектов (документов, справочников и т.д.) хранятся в специальных регистрах сведений, обычно это РегистрСведений.ДополнительныеСведения. Каждая запись в этом регистре связывает объект-владелец (например, наш документ), конкретный вид характеристики (элемент ПВХ) и значение этой характеристики.

Стандартное использование механизма БСП предполагает, что администратор или разработчик заранее создает необходимые виды характеристик в справочнике "Наборы дополнительных реквизитов и сведений" или напрямую в ПВХ. Затем эти характеристики автоматически отображаются на форме объекта в специальной группе "Дополнительные реквизиты".

Адаптация БСП для динамического создания реквизитов

Наша задача сложнее: мы не знаем заранее ни количество, ни имена реквизитов. Более того, мы хотим создавать не просто поля, а целые блоки UI (страницы, группы, декорации, поля) многократно. Для решения этой задачи мы должны программно управлять элементами Плана Видов Характеристик и Регистром Сведений.

Рассмотрим пошаговый алгоритм:

  1. Создание/поиск элемента Плана Видов Характеристик: Когда пользователь инициирует создание нового динамического реквизита (например, нажимая кнопку "Добавить блок реквизитов"), мы должны определить уникальное имя для каждого нового реквизита в этом блоке (например, "МойРеквизит_1_Поле1", "МойРеквизит_1_Поле2", "МойРеквизит_2_Поле1" и т.д., где "1" и "2" – это номера добавленных блоков). Для каждого такого уникального имени мы должны проверить, существует ли соответствующий элемент в ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения. Если нет, мы программно создаем новый элемент ПВХ.
  2. Создание реквизитов формы: Для отображения и редактирования значений этих динамических реквизитов на форме, мы, как и в первом случае, будем программно создавать реквизиты формы (Новый РеквизитФормы(...)). Эти реквизиты формы будут служить буфером для значений, которые потом будут записаны в базу данных.
  3. Динамическое создание элементов формы: Мы продолжаем использовать Элементы.Добавить() для создания страниц, групп, полей и привязываем их к соответствующим динамическим реквизитам формы с помощью ПутьКДанным.
  4. Чтение данных при открытии формы (ПриЧтенииНаСервере): При открытии документа нам нужно прочитать все ранее сохраненные динамические реквизиты из РегистраСведений.ДополнительныеСведения, которые относятся к текущему документу. Для каждой найденной записи:
    • Мы получаем наименование вида характеристики (это будет имя нашего динамического реквизита).
    • Создаем соответствующий реквизит формы и элемент формы (поле ввода).
    • Присваиваем значение из регистра реквизиту формы, чтобы оно отобразилось на экране.
  5. Запись данных при сохранении формы (ПриЗаписиНаСервере): При записи документа нам необходимо обойти все динамически созданные реквизиты формы (их ссылки или имена мы можем хранить в специальной коллекции, например, в Соответствие или Массив в реквизите формы). Для каждого такого реквизита:
    • Мы получаем его текущее значение с формы.
    • Находим (или убеждаемся в существовании) соответствующий элемент в ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.
    • Записываем или обновляем запись в РегистреСведений.ДополнительныеСведения, указывая текущий документ в качестве объекта, ссылку на элемент ПВХ в качестве свойства и текущее значение реквизита формы.

Давайте посмотрим на концептуальный пример кода для реализации такого подхода:


// Пример реквизита формы для хранения информации о динамических элементах
// В свойствах формы (через "Все действия" -> "Изменить форму" -> "Реквизиты"):
// Создайте реквизит "ДополнительныеДинамическиеРеквизиты" типа "Соответствие" или "Массив",
// не сохраняемый на форме. Он будет хранить ссылки на динамически созданные реквизиты и их метаданные.

// В модуле формы. Процедура, которая добавляет новый динамический блок реквизитов.
// В вашем случае, она будет вызываться по нажатию кнопки.
Процедура ДобавитьНовыйБлокРеквизитовНаФорму()
    // Предположим, у нас есть счетчик блоков для уникальных имен
    Если ОбъектФормы.СчетчикБлоков = Неопределено Тогда
        ОбъектФормы.СчетчикБлоков = 0;
    КонецЕсли;
    ОбъектФормы.СчетчикБлоков = ОбъектФормы.СчетчикБлоков + 1;
    ИмяБлока = "Блок_" + ОбъектФормы.СчетчикБлоков;

    // Создаем новую страницу для блока
    НоваяСтраница = Элементы.Добавить("Страница" + ИмяБлока, ТипЭлементаФормы.Страница, Элементы.Страницы);
    НоваяСтраница.Заголовок = "Блок реквизитов " + ОбъектФормы.СчетчикБлоков;

    // Создаем группу на странице
    НоваяГруппа = Элементы.Добавить("Группа" + ИмяБлока, ТипЭлементаФормы.ГруппаФормы, НоваяСтраница);
    НоваяГруппа.Вид = ВидГруппыФормы.ОбычнаяГруппа;
    НоваяГруппа.Заголовок = "Данные блока";

    // Для примера, добавим 3 поля в каждый блок
    Для НомерПоля = 1 По 3 Цикл
        ИмяРеквизита = ИмяБлока + "_Поле" + НомерПоля;
        ТипЗначенияРеквизитаСтрокой = "Строка"; // Или "Число", "Дата", в зависимости от задачи

        // 1. Создаем (или находим) элемент Плана Видов Характеристик
        ВидХарактеристики = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию(ИмяРеквизита);
        Если ВидХарактеристики = Неопределено Тогда
            НовыйВид = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.СоздатьЭлемент();
            НовыйВид.Наименование = ИмяРеквизита;
            // Устанавливаем тип значения характеристики, например, "Строка"
            // В типовых конфигурациях есть служебные функции для поиска ТипаЗначенияХарактеристики по строке
            // Например, УправлениеСвойствами.ПолучитьТипЗначенияХарактеристикиПоСтроке(ТипЗначенияРеквизитаСтрокой)
            НовыйВид.Записать();
            ВидХарактеристики = НовыйВид.Ссылка;
        КонецЕсли;

        // 2. Добавляем реквизит формы для отображения значения
        ЭтотОбъект.ИзменитьРеквизиты(
            Новый Массив(Новый РеквизитФормы(ИмяРеквизита, Новый ОписаниеТипов(ТипЗначенияРеквизитаСтрокой), Истина)), // Истина = сохраняемый на форме
            Новый Массив()
        );

        // 3. Добавляем элемент формы (поле ввода)
        НовоеПоле = Элементы.Добавить("Поле" + ИмяРеквизита, ТипЭлементаФормы.ПолеВвода, НоваяГруппа);
        НовоеПоле.Вид = ВидПоляФормы.ПолеВвода;
        НовоеПоле.ПутьКДанным = ИмяРеквизита;
        НовоеПоле.Заголовок = "Поле " + НомерПоля;

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

    КонецЦикла;

КонецПроцедура

// В обработчике ПриЧтенииНаСервере(ТекущийОбъект)
Процедура ПриЧтенииНаСервере(ТекущийОбъект)

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

    // Инициализируем счетчик блоков для корректного именования при дальнейшем добавлении
    ОбъектФормы.СчетчикБлоков = 0;
    // Коллекция для временного хранения информации о блоках и их полях,
    // чтобы правильно восстановить страницы и группы
    КоллекцияБлоков = Новый Соответствие();

    // Запрос для получения всех дополнительных сведений для текущего объекта
    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ
                   |    ДополнительныеСведения.Свойство.Наименование КАК ИмяСвойства,
                   |    ДополнительныеСведения.Свойство КАК Свойство,
                   |    ДополнительныеСведения.Значение КАК Значение
                   |ИЗ
                   |    РегистрСведений.ДополнительныеСведения КАК ДополнительныеСведения
                   |ГДЕ
                   |    ДополнительныеСведения.Объект = &Объект";
    Запрос.УстановитьПараметр("Объект", ТекущийОбъект.Ссылка);
    Выборка = Запрос.Выполнить().Выбрать();

    Пока Выборка.Следующий() Цикл
        // Из имени свойства "Блок_N_ПолеM" извлекаем N и M
        ИмяРеквизита = Выборка.ИмяСвойства;
        МассивЧастей = СтрРазделить(ИмяРеквизита, "_", Ложь); // Ожидаем "Блок", N, "Поле", M
        Если МассивЧастей.Количество() = 4 Тогда
            НомерБлока = Число(МассивЧастей[1]);
            НомерПоля = Число(МассивЧастей[3]);
        Иначе
            Продолжить; // Пропускаем, если имя не соответствует ожидаемому формату
        КонецЕсли;

        // Обновляем счетчик блоков, чтобы новые блоки не пересекались по именам
        Если НомерБлока > ОбъектФормы.СчетчикБлоков Тогда
            ОбъектФормы.СчетчикБлоков = НомерБлока;
        КонецЕсли;

        Если Не КоллекцияБлоков.Содержит(НомерБлока) Тогда
            КоллекцияБлоков.Вставить(НомерБлока, Новый Структура("Страница, Группа, Поля", Неопределено, Неопределено, Новый Соответствие()));
        КонецЕсли;
        ДанныеБлока = КоллекцияБлоков.Получить(НомерБлока);

        Если ДанныеБлока.Страница = Неопределено Тогда
            ИмяБлока = "Блок_" + НомерБлока;
            НоваяСтраница = Элементы.Добавить("Страница" + ИмяБлока, ТипЭлементаФормы.Страница, Элементы.Страницы);
            НоваяСтраница.Заголовок = "Блок реквизитов " + НомерБлока;
            НоваяГруппа = Элементы.Добавить("Группа" + ИмяБлока, ТипЭлементаФормы.ГруппаФормы, НоваяСтраница);
            НоваяГруппа.Вид = ВидГруппыФормы.ОбычнаяГруппа;
            НоваяГруппа.Заголовок = "Данные блока";
            ДанныеБлока.Страница = НоваяСтраница;
            ДанныеБлока.Группа = НоваяГруппа;
        КонецЕсли;

        // Создаем реквизит формы
        ТипЗначенияРеквизитаСтрокой = Выборка.Значение.Тип().Имя(); // Извлекаем тип из сохраненного значения
        ЭтотОбъект.ИзменитьРеквизиты(
            Новый Массив(Новый РеквизитФормы(ИмяРеквизита, Новый ОписаниеТипов(ТипЗначенияРеквизитаСтрокой), Истина)),
            Новый Массив()
        );

        // Создаем поле формы
        НовоеПоле = Элементы.Добавить("Поле" + ИмяРеквизита, ТипЭлементаФормы.ПолеВвода, ДанныеБлока.Группа);
        НовоеПоле.Вид = ВидПоляФормы.ПолеВвода;
        НовоеПоле.ПутьКДанным = ИмяРеквизита;
        НовоеПоле.Заголовок = "Поле " + НомерПоля;

        // Присваиваем значение реквизиту формы
        ЭтотОбъект[ИмяРеквизита] = Выборка.Значение;

        // Сохраняем информацию о динамически созданном реквизите
        ОбъектФормы.ДополнительныеДинамическиеРеквизиты.Вставить(ИмяРеквизита, Новый Структура("ВидХарактеристики, РеквизитФормы, ЭлементФормы", Выборка.Свойство, ИмяРеквизита, НовоеПоле));

    КонецЦикла;

КонецПроцедура

// В обработчике ПриЗаписиНаСервере(ТекущийОбъект)
Процедура ПриЗаписиНаСервере(ТекущийОбъект)

    Если ОбъектФормы.ДополнительныеДинамическиеРеквизиты <> Неопределено Тогда
        Для Каждого ЭлементСоответствия Из ОбъектФормы.ДополнительныеДинамическиеРеквизиты Цикл
            ИмяРеквизитаФормы = ЭлементСоответствия.Ключ;
            СтруктураИнфоРеквизита = ЭлементСоответствия.Значение;

            ВидХарактеристики = СтруктураИнфоРеквизита.ВидХарактеристики;
            ЗначениеРеквизита = ЭтотОбъект[ИмяРеквизитаФормы]; // Получаем значение из реквизита формы

            // Создаем или получаем набор записей регистра сведений
            НаборЗаписей = РегистрыСведений.ДополнительныеСведения.СоздатьНаборЗаписей();
            НаборЗаписей.Отбор.Объект.Установить(ТекущийОбъект.Ссылка);
            НаборЗаписей.Отбор.Свойство.Установить(ВидХарактеристики);
            НаборЗаписей.Прочитать(); // Пробуем прочитать существующие записи

            Если НаборЗаписей.Количество() = 0 Тогда
                // Если записи нет, создаем новую
                НоваяЗапись = НаборЗаписей.Добавить();
                НоваяЗапись.Объект = ТекущийОбъект.Ссылка;
                НоваяЗапись.Свойство = ВидХарактеристики;
            Иначе
                // Если запись есть, берем первую (должна быть одна по отбору)
                НоваяЗапись = НаборЗаписей[0];
            КонецЕсли;

            НоваяЗапись.Значение = ЗначениеРеквизита; // Устанавливаем новое значение
            НаборЗаписей.Записать(); // Записываем изменения в регистр
        КонецЦикла;
    КонецЕсли;

КонецПроцедура

Этот подход требует тщательной проработки, особенно в части определения типов значений для ПВХ и обработки различных видов полей. Однако он обеспечивает структурированное хранение данных, что важно для построения отчетов и выполнения запросов.

3. Альтернативный подход: Использование "Хранилища значений"

Если структура ваших динамически создаваемых реквизитов чрезвычайно сложна и сильно варьируется от документа к документу, или вы не хотите глубоко интегрироваться с механизмом ПВХ БСП, мы можем прибегнуть к типу данных ХранилищеЗначения. Этот подход обеспечивает максимальную гибкость в хранении произвольных структур.

Как это работает:

  1. Создание реквизита объекта: В вашем объекте конфигурации (например, в документе) мы создаем дополнительный реквизит с типом ХранилищеЗначения.
  2. Сбор данных в структуру: На форме, при каждом создании нового блока реквизитов, мы не просто создаем реквизиты формы, но и собираем информацию о всей динамически созданной структуре (страницы, группы, поля, их имена, типы и текущие значения) в одну сложную структуру данных. Это может быть Структура, содержащая Массивы, внутри которых хранятся другие Структуры или ТаблицыЗначений.
  3. Сериализация и сохранение: Эту сложную структуру данных мы сериализуем (платформа 1С делает это автоматически при помещении объекта в ХранилищеЗначения) и сохраняем в реквизит объекта документа типа ХранилищеЗначения.
    
    // В модуле формы при записи на сервере
    Процедура ПриЗаписиНаСервере(ТекущийОбъект)
        // Создаем структуру для хранения всей информации о динамических элементах
        // Например, массив структур, каждая из которых описывает блок реквизитов (страницу, группы, поля)
        Если ОбъектФормы.ДинамическиеБлокиДанных = Неопределено Тогда
            ОбъектФормы.ДинамическиеБлокиДанных = Новый Массив();
        КонецЕсли;
    
        // Пример: собираем данные из всех динамически созданных блоков.
        // Предположим, что у нас есть коллекция, где мы храним метаданные и значения
        // динамически созданных элементов формы
        МассивСохраняемыхДанных = Новый Массив();
        Для Каждого ЭлементБлока Из ОбъектФормы.ДинамическиеБлокиДанных Цикл // Это ваша внутренняя коллекция блоков
            СтруктураБлока = Новый Структура();
            СтруктураБлока.Вставить("ИмяСтраницы", ЭлементБлока.ИмяСтраницы);
            // ... другие метаданные страницы, группы
    
            ПоляБлока = Новый Массив();
            Для Каждого ПолеБлока Из ЭлементБлока.Поля Цикл // Например, коллекция полей в блоке
                СтруктураПоля = Новый Структура();
                СтруктураПоля.Вставить("Имя", ПолеБлока.ИмяРеквизитаФормы);
                СтруктураПоля.Вставить("Значение", ЭтотОбъект[ПолеБлока.ИмяРеквизитаФормы]); // Получаем значение с формы
                СтруктураПоля.Вставить("Тип", ПолеБлока.ТипРеквизита); // Сохраняем тип
                ПоляБлока.Добавить(СтруктураПоля);
            КонецЦикла;
            СтруктураБлока.Вставить("Поля", ПоляБлока);
            МассивСохраняемыхДанных.Добавить(СтруктураБлока);
        КонецЦикла;
    
        // Сериализуем структуру и сохраняем в реквизит ХранилищеЗначения объекта
        ТекущийОбъект.МойРеквизитХранилищеЗначения = Новый ХранилищеЗначения(МассивСохраняемыхДанных);
    
    КонецПроцедуры
    
  4. Десериализация и восстановление UI: При открытии документа мы извлекаем данные из ХранилищаЗначения с помощью метода Получить(). Полученная структура десериализуется, и на её основе мы заново программно формируем страницы, группы, реквизиты формы и поля ввода, присваивая им сохраненные значения.
    
    // В модуле формы при чтении на сервере
    Процедура ПриЧтенииНаСервере(ТекущийОбъект)
        // Перед восстановлением очищаем ранее созданные динамические элементы
        // (логика удаления аналогична примеру с БСП)
        // ...
    
        // Если есть сохраненные данные в ХранилищеЗначения
        Если ТекущийОбъект.МойРеквизитХранилищеЗначения.Получить() <> Неопределено Тогда
            МассивСохраненныхДанных = ТекущийОбъект.МойРеквизитХранилищеЗначения.Получить();
    
            // Инициализируем коллекции для отслеживания динамических элементов
            ОбъектФормы.ДинамическиеБлокиДанных = Новый Массив();
            ОбъектФормы.СчетчикБлоков = 0; // Сбрасываем счетчик для корректного именования
    
            Для Каждого СтруктураБлока Из МассивСохраненныхДанных Цикл
                ОбъектФормы.СчетчикБлоков = ОбъектФормы.СчетчикБлоков + 1;
                ИмяБлока = "Блок_" + ОбъектФормы.СчетчикБлоков;
    
                // Восстанавливаем элементы формы
                НоваяСтраница = Элементы.Добавить("Страница" + ИмяБлока, ТипЭлементаФормы.Страница, Элементы.Страницы);
                НоваяСтраница.Заголовок = СтруктураБлока.ИмяСтраницы; // или генерируем из ИмяБлока
                НоваяГруппа = Элементы.Добавить("Группа" + ИмяБлока, ТипЭлементаФормы.ГруппаФормы, НоваяСтраница);
                НоваяГруппа.Вид = ВидГруппыФормы.ОбычнаяГруппа;
                НоваяГруппа.Заголовок = "Данные блока";
    
                // Создаем коллекцию полей для текущего блока
                ТекущиеПоляБлока = Новый Массив();
    
                Для Каждого СтруктураПоля Из СтруктураБлока.Поля Цикл
                    ИмяРеквизитаФормы = СтруктураПоля.Имя;
                    ЗначениеРеквизита = СтруктураПоля.Значение;
                    ТипРеквизитаСтрокой = СтруктураПоля.Тип;
    
                    // Создаем реквизит формы
                    ЭтотОбъект.ИзменитьРеквизиты(
                        Новый Массив(Новый РеквизитФормы(ИмяРеквизитаФормы, Новый ОписаниеТипов(ТипРеквизитаСтрокой), Истина)),
                        Новый Массив()
                    );
    
                    // Создаем поле формы
                    НовоеПоле = Элементы.Добавить("Поле" + ИмяРеквизитаФормы, ТипЭлементаФормы.ПолеВвода, НоваяГруппа);
                    НовоеПоле.ПутьКДанным = ИмяРеквизитаФормы;
                    НовоеПоле.Заголовок = ИмяРеквизитаФормы; // или другое название
    
                    // Присваиваем значение
                    ЭтотОбъект[ИмяРеквизитаФормы] = ЗначениеРеквизита;
    
                    // Добавляем информацию о поле в текущий блок
                    ТекущиеПоляБлока.Добавить(Новый Структура("ИмяРеквизитаФормы, ТипРеквизита", ИмяРеквизитаФормы, ТипРеквизитаСтрокой));
                КонецЦикла;
    
                // Сохраняем информацию о восстановленном блоке
                ОбъектФормы.ДинамическиеБлокиДанных.Добавить(Новый Структура("ИмяСтраницы, Группа, Поля", НоваяСтраница, НоваяГруппа, ТекущиеПоляБлока));
    
            КонецЦикла;
        КонецЕсли;
    
    КонецПроцедура
    

Преимущества подхода с "Хранилищем значений":

Недостатки подхода с "Хранилищем значений":

Заключение

Для нашей задачи сохранения программно добавленных реквизитов и их значений, когда количество и имена реквизитов неизвестны заранее, существуют два основных подхода:

  1. Адаптированный механизм БСП (Дополнительные реквизиты и сведения): Этот метод является предпочтительным, если вам нужно структурированное хранение данных, возможность их использования в запросах, отчетах, а также если вы планируете масштабировать систему. Он требует более сложной начальной настройки и программного управления Планами Видов Характеристик, но обеспечивает мощную и гибкую базу данных.
  2. Использование "Хранилища значений": Этот подход предлагает максимальную гибкость и простоту сохранения произвольных структур, что идеально подходит для очень специфичных и нерегулярных данных. Однако он сопряжен с ограничениями в части анализа данных и потенциальными проблемами с производительностью и обслуживанием.

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

← На главную