При разработке интерфейсов на платформе 1С:Предприятие часто возникает задача улучшить визуальное восприятие иерархических данных. Стандартный механизм запросов с итогами отлично справляется с суммированием чисел, но не умеет агрегировать строки (например, названия проектов) в одну ячейку группировки.
В этой статье мы подробно разберем, как решить следующую задачу: имеется дерево значений (список заявок), сгруппированное по контрагентам. Необходимо в строке контрагента (верхний уровень) в колонке "Проект" вывести перечень всех проектов, которые содержатся в детальных записях, через запятую. При этом в детальных записях колонка должна оставаться ссылочной для удобной навигации.
Изначально колонка "Проект" в дереве значений обычно имеет тип СправочникСсылка.Проекты. Если вы попытаетесь программно записать в ячейку группировки строку вида "Проект А, Проект Б", платформа выдаст ошибку несоответствия типов, так как строка не является ссылкой.
Рассмотрим два основных способа решения этой задачи: "визуальный" (через условное оформление) и "структурный" (через изменение типа данных).
Этот метод является наиболее правильным с точки зрения архитектуры управляемых форм. Мы не ломаем типы данных в основной колонке, а лишь подменяем отображаемый текст.
Алгоритм действий:
СтрокаПроектов. Тип значения — Строка.СтрокаПроектов.Настройка Условного оформления:
В конфигураторе или программно (в процедуре ПриСозданииНаСервере) создадим элемент условного оформления:
СтрокаПроектов заполнена).Проект.СтрокаПроектов.Таким образом, платформа будет хранить в колонке "Проект" пустую ссылку (для группировки), но пользователю покажет текст из служебной колонки.
Если по каким-то причинам условное оформление не подходит, можно изменить тип самой колонки "Проект", чтобы она могла хранить и ссылки, и строки.
Это делается программно при инициализации дерева. Если дерево создается на основе запроса или выгружается из ТаблицыЗначений, колонки изначально имеют жесткий тип.
Рассмотрим, как изменить тип колонки программно:
// Создаем описание типов, содержащее и Ссылку, и Строку
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("СправочникСсылка.Проекты"));
МассивТипов.Добавить(Тип("Строка"));
// Присваиваем описание типов квалификаторам строки (длина, допустимая длина)
КвалификаторыСтроки = Новый КвалификаторыСтроки(0); // 0 - неограниченная длина
ОписаниеТиповПроект = Новый ОписаниеТипов(МассивТипов, , КвалификаторыСтроки);
// Изменяем тип колонки дерева значений (если работаем с объектом ДеревоЗначений на сервере)
ДеревоОбъект.Колонки.Проект.ТипЗначения = ОписаниеТиповПроект;
После этого в ячейку колонки Проект можно будет записывать как ссылку на справочник (для детальных строк), так и строку с перечислением (для группировок).
Поскольку язык запросов 1С не имеет функции агрегации строк (аналога GROUP_CONCAT из SQL), собирать строку с проектами придется постобработкой дерева. Это делается на сервере после того, как дерево уже заполнено данными из базы.
Давайте напишем универсальный алгоритм, который обходит дерево, собирает уникальные проекты и формирует строку.
// Процедура постобработки дерева значений
// Дерево - это ДанныеФормыДерево или ДеревоЗначений
Процедура ЗаполнитьГруппировкиПроектов(Дерево)
// Перебираем строки первого уровня (Группировки по Контрагенту)
Для Каждого СтрокаГруппировки Из Дерево.Строки Цикл
СписокПроектов = Новый Массив;
// Используем Соответствие для отбора уникальных значений (чтобы не было "Проект А, Проект А")
ОтборУникальных = Новый Соответствие;
// Перебираем детальные записи (дочерние строки)
Для Каждого ДетальнаяСтрока Из СтрокаГруппировки.Строки Цикл
ТекущийПроект = ДетальнаяСтрока.Проект;
// Если проект заполнен и мы его еще не добавили в список
Если ЗначениеЗаполнено(ТекущийПроект) И ОтборУникальных[ТекущийПроект] = Неопределено Тогда
// Добавляем строковое представление проекта в массив
СписокПроектов.Добавить(Строка(ТекущийПроект));
// Помечаем как обработанный
ОтборУникальных.Вставить(ТекущийПроект, Истина);
КонецЕсли;
КонецЦикла;
// Соединяем массив строк в одну строку через запятую
ИтоговаяСтрока = СтрСоединить(СписокПроектов, ", ");
// ВАРИАНТ 1: Записываем в служебную колонку (для Условного Оформления)
СтрокаГруппировки.ГруппаПроектов = ИтоговаяСтрока;
// ВАРИАНТ 2: Записываем в основную колонку (Если настроен Составной тип)
// СтрокаГруппировки.Проект = ИтоговаяСтрока;
КонецЦикла;
КонецПроцедуры
При внедрении этого решения обратите внимание на следующие моменты:
1. Рекурсия для многоуровневых деревьев
В примере выше мы рассмотрели дерево с одним уровнем вложенности. Если у вас многоуровневое дерево и группировки могут быть вложены друг в друга, вам потребуется вынести логику обхода в отдельную процедуру и вызывать её рекурсивно для каждого уровня.
2. Ограничение длины строки
Если заявок у контрагента очень много, строка с перечислением проектов может получиться огромной. Имеет смысл ограничить количество выводимых элементов или обрезать строку, добавляя многоточие. Пример:
Если СписокПроектов.Количество() > 5 Тогда
// Оставим первые 5 и добавим "..."
// ... код обрезки массива ...
ИтоговаяСтрока = ИтоговаяСтрока + "... и еще " + (Всего - 5);
КонецЕсли;
3. Интерактивность
Если вы используете Способ 2 (Составной тип), помните, что при клике на ячейку группировки платформа не откроет карточку проекта (так как там текст), но может войти в режим редактирования строки. Рекомендуется установить для группировок свойство ТолькоПросмотр в значение Истина при выводе строк.
Для вывода агрегированных строковых данных в группировках дерева значений используйте постобработку данных на сервере. Наиболее элегантное решение — использование механизма Условного оформления в связке со служебной колонкой. Это сохраняет типизацию данных и гибкость интерфейса.