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