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