Как программно добавлять колонки в табличную часть на управляемой форме и сохранять данные в регистр сведений?

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

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

Архитектура решения: почему данные не сохраняются сами?

Важно понимать ключевую особенность управляемых форм: программно добавленные через метод ИзменитьРеквизиты() элементы существуют только в контексте текущего сеанса работы формы. Они не имеют отражения в физических таблицах базы данных. Поэтому, если мы просто добавим колонку и пользователь введет в нее значение, при закрытии формы или записи объекта это значение будет безвозвратно утеряно, если мы заранее не позаботимся о его сохранении в Регистр сведений или иное хранилище.

Рассмотрим пошагово процесс реализации этой задачи.

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

Для того чтобы добавить колонки в таблицу, нам необходимо выполнить два действия: создать Реквизит формы (внутренняя структура данных) и создать Элемент формы (визуальное поле ввода). Разберем пример кода, который позволяет динамически формировать колонки в табличной части ТЧСписок.

Проанализируем процедуру, работающую на сервере:


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

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

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

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

Шаг 2. Группировка колонок

Как мы увидели в примере выше, для «склеивания» нескольких динамических реквизитов в один логический блок на форме используется объект ГруппаФормы с видом ВидГруппыФормы.ГруппаКолонок. Это позволяет визуально отделить один набор динамических данных от другого, что критично для удобства пользователя при большом количестве добавляемых полей.

Шаг 3. Сохранение данных в Регистр Сведений

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

Выясним причину, почему данные могут потеряться: если вы просто нажмете кнопку «Записать», платформа запишет только стандартные реквизиты табличной части. Динамические данные нужно извлечь программно в событии ПередЗаписьюНаСервере.


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

Шаг 4. Восстановление данных при открытии формы

Чтобы пользователь увидел свои данные при повторном открытии документа, нам необходимо выполнить обратную операцию в процедуре ПриЧтенииНаСервере или ПриСозданииНаСервере:

  1. Определить количество необходимых колонок (прочитать из регистра или настроек).
  2. Вызвать процедуру создания динамических колонок (см. Шаг 1).
  3. Запросом выгрузить данные из Регистра сведений.
  4. Распределить значения по ячейкам табличной части, используя обращение через имя колонки: СтрокаТаблицы[ИмяДинамическойКолонки] = ЗначениеИзРегистра;.

Важные рекомендации и итоги

Проанализируем основные риски этого подхода:

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

Таким образом, мы рассмотрели полный цикл работы с динамическими данными: от визуализации до обеспечения ссылочной целостности и хранения в базе данных. Этот механизм делает интерфейс системы гибким и адаптивным под любые задачи бизнеса.

← На главную