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