При разработке интерфейсов на управляемых формах нередко возникает задача сгруппировать документы не просто плоским списком, а в виде иерархической структуры подразделений, к которым эти документы относятся. Разберем особенности платформы 1С:Предприятие 8.3, выясним причины возникающих ограничений и рассмотрим эффективные варианты реализации такого интерфейса.
Для понимания архитектуры решения сначала проанализируем, как платформа 1С работает с древовидными структурами:
ДинамическийСписок свойство Отображение можно переключить в значение Дерево только в том случае, если в свойстве ОсновнаяТаблица указан объект метаданных со встроенной иерархией (например, иерархический справочник с предопределенным полем Родитель). Поскольку документы в платформе 1С не являются иерархическими объектами, перевести динамический список документов в режим полноценного дерева штатными средствами невозможно.Подразделение сворачивает строки только по конкретным ссылкам. Если подразделения имеют многоуровневую структуру (например, «Департамент -> Отдел -> Сектор»), платформа сгруппирует документы по конечному подразделению, не разворачивая вышестоящие уровни иерархии справочника.В ИЕРАРХИИ допустимо применять в отборах и секции ГДЕ динамического списка, однако сам по себе текст запроса динамического списка не строит древовидное визуальное дерево для разнородных данных.Рассмотрим проверенные на практике способы решения этой задачи.
Разберем классический подход, рекомендуемый стандартами разработки 1С. Вместо попытки объединить неиерархические документы и дерево подразделений в одной таблице, разделим форму на две связанные области.
Пошагово выполним настройку формы:
ДеревоПодразделений (тип ДинамическийСписок с основной таблицей Справочник.Подразделения или Справочник.СтруктураПредприятия). Установим у этой таблицы свойство Отображение в значение Дерево.Список).ПриАктивизацииСтроки. При выборе узла в дереве будем накладывать динамический отбор на список документов.Посмотрим на пример программного кода в модуле управляемой формы:
&НаКлиенте
Процедура ДеревоПодразделенийПриАктивизацииСтроки(Элемент)
ТекущиеДанные = Элемент.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
УстановитьОтборПоПодразделению(ТекущиеДанные.Ссылка);
КонецПроцедуры
&НаКлиенте
Процедура УстановитьОтборПоПодразделению(ВыбранноеПодразделение)
// Находим или создаем элемент отбора по полю Подразделение
ЭлементОтбора = Неопределено;
Для Каждого Элемент Из Список.Отбор.Элементы Цикл
Если Элемент.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Подразделение") Тогда
ЭлементОтбора = Элемент;
Прервать;
КонецЕсли;
КонецЦикла;
Если ЭлементОтбора = Неопределено Тогда
ЭлементОтбора = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Подразделение");
КонецЕсли;
Если ЗначениеЗаполнено(ВыбранноеПодразделение) Тогда
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.ВИерархии;
ЭлементОтбора.ПравоеЗначение = ВыбранноеПодразделение;
ЭлементОтбора.Использование = Истина;
Иначе
ЭлементОтбора.Использование = Ложь;
КонецЕсли;
КонецПроцедуры
Преимущества подхода: сохраняется высокая скорость работы платформы за счет порционного динамического считывания данных, работают стандартные функции поиска, сортировки и контекстного меню документов.
Если интерфейсные требования строго предписывают выводить документы внутри единого разворачивающегося дерева, воспользуемся универсальной коллекцией ДеревоЗначений.
Разберем по шагам реализацию этого механизма:
ДеревоДокументов с типом ДеревоЗначений.НаименованиеУзла (строка), СсылкаНаОбъект (произвольный тип или ссылка), Дата (дата), Номер (строка), СуммаДокумента (число).Рассмотрим пример серверной процедуры формирования дерева:
&НаСервере
Процедура ЗаполнитьДеревоДокументовНаСервере()
ДеревоОбъект = РеквизитФормыВЗначение("ДеревоДокументов");
ДеревоОбъект.Строки.Очистить();
// Выбираем иерархию подразделений
ЗапросПодразделений = Новый Запрос;
ЗапросПодразделений.Текст =
"ВЫБРАТЬ
| Подразделения.Ссылка КАК Подразделение,
| Подразделения.Родитель КАК Родитель,
| Подразделения.Наименование КАК Наименование
|ИЗ
| Справочник.Подразделения КАК Подразделения
|ИТОГИ ПО
| Подразделение ИЕРАРХИЯ";
РезультатПодразделений = ЗапросПодразделений.Выполнить();
ДеревоОбъект = РезультатПодразделений.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
// Выбираем документы
ЗапросДокументов = Новый Запрос;
ЗапросДокументов.Текст =
"ВЫБРАТЬ
| Док.Ссылка КАК Ссылка,
| Док.Номер КАК Номер,
| Док.Дата КАК Дата,
| Док.Подразделение КАК Подразделение
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Док
|ГДЕ
| Док.Проведен = ИСТИНА";
ТЗДокументов = ЗапросДокументов.Выполнить().Выгрузить();
// Рекурсивно или поиском распределяем документы по узлам дерева
Для Каждого СтрДок Из ТЗДокументов Цикл
НайденнаяСтрока = ДеревоОбъект.Строки.Найти(СтрДок.Подразделение, "Подразделение", Истина);
Если НайденнаяСтрока <> Неопределено Тогда
НоваяСтрокаДок = НайденнаяСтрока.Строки.Добавить();
НоваяСтрокаДок.СсылкаНаОбъект = СтрДок.Ссылка;
НоваяСтрокаДок.Наименование = "Документ № " + СтрДок.Номер;
НоваяСтрокаДок.Дата = СтрДок.Дата;
КонецЕсли;
КонецЦикла;
ЗначениеВРеквизитФормы(ДеревоОбъект, "ДеревоДокументов");
КонецПроцедуры
Обратите внимание на ограничения: при использовании ДеревоЗначений полностью отключается динамическая подгрузка данных. При открытии формы все строки считываются в память сервера и передаются на клиент, что может замедлить работу при большом массиве документов. Кроме того, команды открытия форм, отборов и пиктограмм статусов документов потребуется реализовывать вручную.
Если структура подразделений в компании имеет фиксированную глубину вложенности (например, строго два уровня: филиал и отдел), настроим последовательную группировку в свойствах динамического списка.
НастройкиКомпоновкиДанных -> вкладка Группировка).Подразделение.Родитель (Верхний уровень)Подразделение (Нижний уровень)Этот способ не требует написания программного кода, однако применим только в условиях жестко регламентированной структуры справочника подразделений с одинаковым числом уровней вложенности.