Как вывести список документов в иерархии подразделений на управляемой форме в 1С?

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

При разработке интерфейсов на управляемых формах нередко возникает задача сгруппировать документы не просто плоским списком, а в виде иерархической структуры подразделений, к которым эти документы относятся. Разберем особенности платформы 1С:Предприятие 8.3, выясним причины возникающих ограничений и рассмотрим эффективные варианты реализации такого интерфейса.

Анализ ограничений динамического списка

Для понимания архитектуры решения сначала проанализируем, как платформа 1С работает с древовидными структурами:

  1. Режим отображения «Дерево»: у элемента формы типа ДинамическийСписок свойство Отображение можно переключить в значение Дерево только в том случае, если в свойстве ОсновнаяТаблица указан объект метаданных со встроенной иерархией (например, иерархический справочник с предопределенным полем Родитель). Поскольку документы в платформе 1С не являются иерархическими объектами, перевести динамический список документов в режим полноценного дерева штатными средствами невозможно.
  2. Механизм пользовательских группировок: стандартная группировка динамического списка по реквизиту Подразделение сворачивает строки только по конкретным ссылкам. Если подразделения имеют многоуровневую структуру (например, «Департамент -> Отдел -> Сектор»), платформа сгруппирует документы по конечному подразделению, не разворачивая вышестоящие уровни иерархии справочника.
  3. Использование условий «В ИЕРАРХИИ»: условие В ИЕРАРХИИ допустимо применять в отборах и секции ГДЕ динамического списка, однако сам по себе текст запроса динамического списка не строит древовидное визуальное дерево для разнородных данных.

Рассмотрим проверенные на практике способы решения этой задачи.

Способ 1. Паттерн «Дерево — Список» (Связанные таблицы на форме)

Разберем классический подход, рекомендуемый стандартами разработки 1С. Вместо попытки объединить неиерархические документы и дерево подразделений в одной таблице, разделим форму на две связанные области.

Пошагово выполним настройку формы:

  1. Разместим на форме группу с горизонтальной ориентацией.
  2. В левую часть поместим таблицу, связанную с новым реквизитом формы ДеревоПодразделений (тип ДинамическийСписок с основной таблицей Справочник.Подразделения или Справочник.СтруктураПредприятия). Установим у этой таблицы свойство Отображение в значение Дерево.
  3. В правую часть поместим стандартную таблицу документов (динамический список Список).
  4. Для таблицы подразделений создадим обработчик события ПриАктивизацииСтроки. При выборе узла в дереве будем накладывать динамический отбор на список документов.

Посмотрим на пример программного кода в модуле управляемой формы:


&НаКлиенте
Процедура ДеревоПодразделенийПриАктивизацииСтроки(Элемент)
    ТекущиеДанные = Элемент.ТекущиеДанные;
    Если ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    УстановитьОтборПоПодразделению(ТекущиеДанные.Ссылка);
КонецПроцедуры

&НаКлиенте
Процедура УстановитьОтборПоПодразделению(ВыбранноеПодразделение)
    // Находим или создаем элемент отбора по полю Подразделение
    ЭлементОтбора = Неопределено;
    Для Каждого Элемент Из Список.Отбор.Элементы Цикл
        Если Элемент.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Подразделение") Тогда
            ЭлементОтбора = Элемент;
            Прервать;
        КонецЕсли;
    КонецЦикла;
    
    Если ЭлементОтбора = Неопределено Тогда
        ЭлементОтбора = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
        ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Подразделение");
    КонецЕсли;
    
    Если ЗначениеЗаполнено(ВыбранноеПодразделение) Тогда
        ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.ВИерархии;
        ЭлементОтбора.ПравоеЗначение = ВыбранноеПодразделение;
        ЭлементОтбора.Использование = Истина;
    Иначе
        ЭлементОтбора.Использование = Ложь;
    КонецЕсли;
КонецПроцедуры

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

Способ 2. Использование реквизита «ДеревоЗначений»

Если интерфейсные требования строго предписывают выводить документы внутри единого разворачивающегося дерева, воспользуемся универсальной коллекцией ДеревоЗначений.

Разберем по шагам реализацию этого механизма:

  1. Создадим реквизит управляемой формы ДеревоДокументов с типом ДеревоЗначений.
  2. Добавим колонки дерева: НаименованиеУзла (строка), СсылкаНаОбъект (произвольный тип или ссылка), Дата (дата), Номер (строка), СуммаДокумента (число).
  3. Перетащим реквизит в элементы формы и настроим отображение колонок.
  4. На сервере заполним дерево иерархией подразделений и привяжем соответствующие документы к нижним узлам.

Рассмотрим пример серверной процедуры формирования дерева:


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

Обратите внимание на ограничения: при использовании ДеревоЗначений полностью отключается динамическая подгрузка данных. При открытии формы все строки считываются в память сервера и передаются на клиент, что может замедлить работу при большом массиве документов. Кроме того, команды открытия форм, отборов и пиктограмм статусов документов потребуется реализовывать вручную.

Способ 3. Фиксированная многоуровневая группировка

Если структура подразделений в компании имеет фиксированную глубину вложенности (например, строго два уровня: филиал и отдел), настроим последовательную группировку в свойствах динамического списка.

  1. Откроем форму списка документов и выделим динамический список.
  2. В свойствах перейдем к настройке группировки (свойство НастройкиКомпоновкиДанных -> вкладка Группировка).
  3. Добавим группировки по иерархической цепочке через разыменование полей:
    1. Подразделение.Родитель (Верхний уровень)
    2. Подразделение (Нижний уровень)

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

← На главную