Как программно добавить динамические столбцы в табличную часть документа в 1С?

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

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

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

Суть проблемы и архитектурный подход

Сначала давайте проанализируем ситуацию. Автор задачи хочет, чтобы при выборе периода (например, с января по март) в документе появлялись три колонки для ввода часов. Если период меняется на год — должно появиться 12 колонок.

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

Поэтому решение всегда сводится к разделению хранения данных и их отображения. Этот подход часто называют паттерном EAV (Entity-Attribute-Value) или, в терминах 1С, «сверткой» и «разверткой» таблицы.

  1. Хранение (База данных): В табличной части документа мы используем структуру «длинной и узкой» таблицы. Например: Сотрудник, ДатаМесяца, КоличествоЧасов. Каждая ячейка будущей широкой таблицы здесь хранится отдельной строкой.
  2. Отображение (Форма): На форме мы программно создаем таблицу значений с нужным количеством колонок (Сотрудник, Январь, Февраль, Март...) и показываем её пользователю. Подробнее этот механизм описан в публикации про программный вывод произвольной таблицы значений на управляемую форму.

Этап 1: Проектирование структуры хранения

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

Этап 2: Программное создание реквизитов формы

Самое интересное происходит при открытии формы или при изменении периода. Нам нужно использовать механизм ИзменитьРеквизиты. Если вам приходится создавать много элементов, удобно использовать текучий конструктор для программной модификации форм, который упрощает написание однотипного кода.

Рассмотрим алгоритм перестроения формы:


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

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

Этап 3: Чтение данных (Развертка)

При открытии документа (событие ПриЧтенииНаСервере) данные лежат в «узкой» табличной части объекта. Нам нужно переложить их в нашу новую красивую таблицу на форме.

Разберем логику:

  1. Делаем запрос к табличной части Объект.ДанныеУчета.
  2. Выгружаем результат в Таблицу Значений.
  3. Создаем строки в реквизите формы ТаблицаНаФорме (заполняя сотрудников).
  4. Обходим выборку из запроса. На основании поля Период вычисляем имя колонки (по тому же алгоритму, что и при создании: "Колонка_20231001").
  5. Записываем Значение в соответствующую ячейку строки таблицы формы.

Этап 4: Сохранение данных (Свертка)

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


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

Нюансы и рекомендации

При реализации данного метода следует учесть несколько важных моментов, которые помогут избежать ошибок и проблем с производительностью:

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

Альтернативное решение: Табличный документ

Если предполагается огромное количество колонок или сложная верстка (объединение ячеек, сложные шапки), имеет смысл посмотреть в сторону использования ПоляТабличногоДокумента на форме.

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

Резюме: Для задачи «Столбцы по месяцам» (обычно 1-12 колонок) идеально подходит метод с динамическими реквизитами формы (ИзменитьРеквизиты) и хранением данных в вертикальной табличной части документа.

← На главную