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

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

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

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

1. Архитектурный подход: Разделение хранения и отображения

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

  1. В базе данных (в СУБД) вся информация хранится в «плоском» (вертикальном) виде. Для этого используется классический архитектурный паттерн EAV (Entity-Attribute-Value — Сущность-Атрибут-Значение). Табличная часть содержит минимальный набор реквизитов: идентификатор строки, ключ (имя реквизита/колонки) и само значение.
  2. На управляемой форме при открытии объекта эти плоские данные программно трансформируются («разворачиваются») в двумерную таблицу значений, где ключи строк превращаются в динамические колонки. При записи документа происходит обратный процесс — «сворачивание» данных обратно в плоскую табличную часть.

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

НомерСтрокиДокумента ИмяРеквизита (Свойство) ЗначениеРеквизита
1 "Физлицо" Иванов
1 "Сумма" 50.00
2 "Физлицо" Петров
2 "Сумма" 250.00

При выводе на форму эта таблица превратится в привычную нам горизонтальную структуру с колонками "Физлицо" и "Сумма".

2. Пошаговая программная реализация на Управляемой Форме

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

Для манипуляции реквизитами формы используется пара методов: ИзменитьРеквизиты() для добавления/удаления структуры данных и Элементы.Добавить() для визуализации этой структуры.

Разберем по шагам серверный код, который будет выполняться, например, при нажатии кнопки «Добавить колонку»:


&НаСервере
Процедура ДобавитьДинамическуюКолонкуНаСервере(ИмяКолонки, ЗаголовокКолонки, ТипЗначения)

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

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

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


&НаСервере
Процедура УдалитьДинамическуюКолонкуНаСервере(ИмяКолонки)

    // 1. Удаляем элемент отображения с формы
    ИмяЭлемента = "ДинамическаяТаблица" + ИмяКолонки;
    ЭлементКолонка = Элементы.Найти(ИмяЭлемента);
    Если ЭлементКолонка <> Неопределено Тогда
        Элементы.Удалить(ЭлементКолонка);
    КонецЕсли;
    
    // 2. Удаляем сам реквизит из данных формы
    МассивУдаляемыхРеквизитов = Новый Массив;
    МассивУдаляемыхРеквизитов.Добавить("Объект.ДинамическаяТаблица." + ИмяКолонки);
    
    ИзменитьРеквизиты(, МассивУдаляемыхРеквизитов);

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

3. Трансформация данных: Заполнение и выгрузка обратно в БД

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

Рассмотрим пример процедуры заполнения динамической таблицы значений при инициализации формы:


&НаСервере
Процедура ЗаполнитьДинамическуюТаблицуПриОткрытии()

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

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

При сохранении документа или перед записью объекта на сервере выполняется обратная процедура свертки данных из динамической таблицы в плоскую ТЧ ДополнительныеЗначения. Мы полностью очищаем физическую ТЧ объекта и перезаписываем ее актуальными данными из нашей экранной формы, обходя динамические колонки в цикле.

4. Использование Плана видов характеристик (ПВХ)

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

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

5. Хранение динамических данных в формате JSON

Если вам не требуется строить сложные аналитические отчеты, делать выборки по динамическим колонкам в запросах или обеспечивать жесткую ссылочную целостность на уровне СУБД, рассмотрите альтернативный вариант — сериализацию данных в JSON.

При таком подходе в табличной части документа создается одна текстовая колонка неограниченной длины (или реквизит типа ХранилищеЗначения). При сохранении формы все динамические колонки текущей строки упаковываются в структуру и сериализуются в строку JSON. При открытии происходит десериализация. Этот подход существенно снижает количество записей в базе данных и ускоряет запись документов.

6. Важная оптимизация: Пул резервных колонок

Проанализируем критически важную проблему производительности. Метод ИзменитьРеквизиты() является крайне «тяжелым» для платформы 1С. При его вызове сервер полностью пересоздает контекст формы со всеми элементами управления. Если пользователь будет интенсивно добавлять или удалять колонки кнопками на форме, интерфейс начнет заметно зависать, ухудшая отзывчивость системы.

Чтобы избежать этой проблемы, применяется оптимизация под названием Пул резервных колонок (буфер колонок). Она актуальна, если бизнес-логика позволяет ограничить максимальное количество динамических колонок разумным числом (например, не более 15–20 одновременно отображаемых колонок).

Суть метода:

  1. В конфигураторе в табличную часть формы заранее добавляются невидимые колонки с запасом: РезервнаяКолонка1, РезервнаяКолонка2 ... РезервнаяКолонка20.
  2. При открытии формы все эти колонки скрыты (свойство Видимость = Ложь).
  3. Когда пользователь нажимает кнопку «Добавить колонку», мы не вызываем тяжелый серверный метод ИзменитьРеквизиты(). Вместо этого мы берем первую скрытую колонку из нашего пула, меняем ее программный заголовок на нужный нам текст и делаем ее видимой (Видимость = Истина).
  4. При удалении колонки мы просто очищаем ее данные и снова скрываем её.

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

← На главную