Как программно создать колонки на управляемой форме с названиями номенклатуры (кросс-таблица)?

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

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

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

Почему нельзя изменять табличную часть объекта?

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

Вместо этого мы применим стандартный подход: линейное хранение данных в базе и матричное отображение на форме.

Суть архитектурного решения

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

  1. Склад
  2. Номенклатура
  3. Количество

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

Шаг 1. Подготовка данных на сервере

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


// Примерный алгоритм формирования исходной таблицы
ТаблицаМатрица = Новый ТаблицаЗначений;
ТаблицаМатрица.Колонки.Добавить("Склад", Новый ОписаниеТипов("СправочникСсылка.Склады"));

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

Шаг 2. Создание реквизитов управляемой формы

Управляемая форма не умеет напрямую отображать объект ТаблицаЗначений. Ей требуются специальные «Реквизиты формы». На этом этапе мы программно создадим массив реквизитов на основе подготовленной таблицы и применим их к форме с помощью метода ИзменитьРеквизиты.


МассивДобавляемыхРеквизитов = Новый Массив;

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

// Применяем изменения к форме
ИзменитьРеквизиты(МассивДобавляемыхРеквизитов);

Шаг 3. Отрисовка элементов на форме (Визуализация)

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


ЭлементТаблица = Элементы.ТаблицаНаФорме;

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

Шаг 4. Загрузка значений в реквизит формы

Когда визуальная структура готова, нам нужно заполнить ее данными. Для этого сформированную на первом шаге матрицу (со значениями количеств) мы переносим в созданный реквизит формы.

Используем системный метод ЗначениеВРеквизитФормы. После вызова этого метода пользователь увидит на экране полноценную кросс-таблицу, готовую к редактированию.

Шаг 5. Обратное преобразование при сохранении данных

Поскольку добавленная нами матрица существует лишь в оперативной памяти интерфейса, перед записью документа (в событии ПередЗаписьюНаСервере) необходимо сохранить введенные пользователем данные обратно в линейную структуру объекта базы данных.

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


ТаблицаМатрица = РеквизитФормыВЗначение("ТаблицаНаФорме");
Объект.Товары.Очистить();

Для Каждого СтрокаМатрицы Из ТаблицаМатрица Цикл
    Для Каждого КолонкаМатрицы Из ТаблицаМатрица.Колонки Цикл
        
        // Пропускаем колонку со складом
        Если КолонкаМатрицы.Имя = "Склад" Тогда Продолжить; КонецЕсли;
        
        Количество = СтрокаМатрицы[КолонкаМатрицы.Имя];
        Если Количество > 0 Тогда
            НоваяСтрока = Объект.Товары.Добавить();
            НоваяСтрока.Склад = СтрокаМатрицы.Склад;
            НоваяСтрока.Номенклатура = ПолучитьНоменклатуруИзИмени(КолонкаМатрицы.Имя);
            НоваяСтрока.Количество = Количество;
        КонецЕсли;
        
    КонецЦикла;
КонецЦикла;

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

← На главную