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