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

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

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

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

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

Для реализации задачи создадим в документе скрытую табличную часть (назовем ее ДанныеДинамическихКолонок), которая будет иметь «плоскую» структуру:

  1. Клиент — ссылка на справочник клиентов;
  2. НоменклатурнаяГруппа — ссылка на группу номенклатуры;
  3. ИмяПоказателя — строка (например, «Цена» или «Количество»);
  4. Значение — число (или другой нужный тип).

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

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

Чтобы форма «увидела» новые колонки, нам необходимо использовать метод ИзменитьРеквизиты(). Выясним причину, по которой это делается на сервере: реквизиты формы являются частью метаданных объекта формы, и их изменение требует пересборки структуры формы.

Рассмотрим пример кода, который создает реквизиты для каждой группы номенклатуры из списка значений:


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

Важно помнить, что третий параметр конструктора РеквизитФормы указывает на путь к данным (в нашем случае это основная табличная часть документа Объект.Товары).

Этап 2. Создание визуальных элементов (Группы и Поля ввода)

После того как данные (реквизиты) созданы, их нужно отобразить. Проанализируем ситуацию: автору требуется, чтобы показатели «Цена» и «Количество» визуально находились под общим заголовком номенклатурной группы. Для этого мы будем использовать ГруппаФормы с видом ГруппаКолонок.

Разберем процесс создания элементов по шагам:

  1. Находим основной элемент таблицы на форме.
  2. Для каждой группы номенклатуры создаем визуальную группу колонок.
  3. Внутри этой группы создаем поля ввода, связанные с ранее созданными реквизитами.

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

Этап 3. Обработка ввода данных и синхронизация

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

Для этого мы проанализируем имя элемента, вызвавшего событие:


&НаКлиенте
Процедура ДинамическоеПолеПриИзменении(Элемент)
    ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
    ИмяЭлемента = Элемент.Имя; // Например: "Группа_UUID_Цена"
    
    // Здесь можно выполнить дополнительные расчеты, например, суммы
    // Сумма = ТекущаяСтрока[ИмяГруппы + "_Цена"] * ТекущаяСтрока[ИмяГруппы + "_Кол"];
КонецПроцедуры

Важный момент: при записи документа нам нужно выполнить обратную операцию — собрать данные из всех динамических колонок каждой строки и записать их в нашу «плоскую» табличную часть ДанныеДинамическихКолонок или напрямую в регистр сведений.

Этап 4. Запись в регистр сведений

Рассмотрим подробнее процесс записи данных в регистр сведений. Это лучше всего делать в процедуре ПередЗаписьюНаСервере. Мы перебираем строки основной табличной части и для каждой строки обходим список динамических колонок.


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

Советы по оптимизации и удобству

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

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

← На главную