Как реализовать иерархию документов в динамическом списке

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

Вопрос построения иерархии для объектов, которые изначально ее не поддерживают (например, документы), часто возникает при разработке сложных интерфейсов. В системе 1С:Предприятие 8 документы оптимизированы для линейного отображения и работы в хронологическом порядке. Однако на практике часто требуется отобразить документы в виде дерева, где один документ является основанием или "родителем" для другого. Рассмотрим подробнее, почему стандартный динамический список не позволяет сделать это "в лоб" и какие существуют обходные пути для решения этой задачи.

Почему динамический список не строит иерархию документов автоматически?

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

Важный технический нюанс: в свойствах ДинамическийСписок есть поле "Основная таблица". Если в этом поле указан объект, у которого в метаданных не включена иерархия (как у документов), свойство таблицы формы "Режим просмотра" в значениях "Дерево" или "Иерархический список" будет либо недоступно, либо не будет функционировать. Платформа 1С опирается на индексы СУБД, которые создаются специально для иерархических справочников. Для документов таких индексов по полю ДокументРодитель по умолчанию не существует.

Способ 1. Использование группировок в динамическом списке

Самый простой, но ограниченный метод — это использование стандартных настроек компоновки данных. Мы можем настроить группировку по реквизиту ДокументРодитель. Разберем, как это работает:

  1. Откроем настройки динамического списка в конфигураторе или программно через свойство КомпоновщикНастроек.
  2. Добавим в "Группировки" поле ДокументРодитель.
  3. В этом случае система выведет список, где документы будут сгруппированы по родителю.

Ограничение метода: мы получим только один уровень вложенности. Построить рекурсивное дерево неограниченной глубины таким способом невозможно. Если в вашей задаче иерархия строго ограничена схемой "один родитель — много потомков" и потомки не могут быть родителями для других документов, этот метод будет наиболее производительным.

Способ 2. Дерево значений и механизм СКД (как в типовых конфигурациях)

В современных конфигурациях (например, УТ 11 или ERP 2) для вывода иерархии документов (журналы взаимодействий, цепочки документов) часто используется ДеревоЗначений. Выясним причину такого выбора. Система компоновки данных (СКД) обладает мощным механизмом построения иерархических связей даже для неиерархических таблиц через вкладку "Связи наборов данных".

Рассмотрим алгоритм реализации:

1. Создадим схему компоновки данных, где в качестве источника выступает запрос к документам.

2. Настроим связь набора данных "сам на себя": поле Ссылка свяжем с полем ДокументРодитель.

3. На форме создадим реквизит с типом ДеревоЗначений и соответствующий элемент формы "Таблица".

4. Программно выполним компоновку и выгрузим результат в дерево:


// Пример программного вывода иерархии через СКД
Схема = ПолучитьМакет("СхемаКомпоновки");
Настройки = Схема.НастройкиПоУмолчанию;

КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(Схема, Настройки, , , Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));

ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);

ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(ДеревоРезультат); // Реквизит формы с типом ДеревоЗначений
ПроцессорВывода.Вывести(ПроцессорКомпоновки);

Плюсы: полноценное отображение иерархии любой сложности. Минусы: это не динамический список. Если в базе миллион документов, система "зависнет", пытаясь прочитать их все и построить дерево в памяти сервера и клиента.

Способ 3. Вспомогательный иерархический справочник (Shadow Table)

Если нам критически важно сохранить динамическую загрузку (порционность) и при этом отображать документы деревом, проанализируем вариант с созданием "справочника-зеркала". Это наиболее профессиональный и масштабируемый подход.

Создадим новый справочник ИерархияДокументов со следующими настройками:

Теперь мы можем построить динамический список, указав этот справочник в качестве основной таблицы. В запросе динамического списка мы будем соединять справочник с таблицей документов для получения нужных реквизитов (дата, номер, сумма).


ВЫБРАТЬ
    Иерархия.Ссылка КАК СсылкаНаЭлементСправочника,
    Иерархия.Родитель КАК Родитель,
    Анкета.Ссылка КАК ДокументСсылка,
    Анкета.Дата КАК Дата,
    Анкета.Номер КАК Номер
ИЗ
    Справочник.ИерархияДокументов КАК Иерархия
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.Анкета КАК Анкета
    ПО Иерархия.ДокументСсылка = Анкета.Ссылка

Чтобы поддерживать актуальность этого дерева, необходимо настроить подписки на события для документа Анкета. Рассмотрим пример логики в подписке ПриЗаписи:


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

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

Выводы и рекомендации

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

Помните, что попытки "обмануть" динамический список, подсовывая ему сложные запросы с иерархией без основной таблицы, часто приводят к падению производительности, так как платформа в этом случае не может эффективно использовать индексы и транслировать запрос в оптимальный SQL-код.

← На главную