Как программно найти существующие группировки СКД, включая детальные записи, в отчетах 1С?

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

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

Предположим, мы работаем с отчетом «ДвиженияТоваров» в конфигурации 1С:Бухгалтерия Предприятия 3.0 и нам необходимо найти, например, группировку по полю «Документ» или определить наличие группировок детальных записей.

Понимание структуры настроек СКД

Прежде чем приступить к поиску, давайте разберем, как устроены настройки СКД. Настройки отчета СКД представлены объектом НастройкиКомпоновкиДанных. Этот объект содержит коллекцию Структура, которая и описывает иерархию элементов отчета. Элементы коллекции Структура могут быть разных типов:

  1. ГруппировкаКомпоновкиДанных: представляет собой стандартную группировку в отчете. У этого объекта есть свойство ПоляГруппировки, которое является коллекцией объектов ПолеГруппировкиКомпоновкиДанных. Эти объекты указывают, по каким полям происходит группировка.
  2. ТаблицаКомпоновкиДанных: представляет собой табличный отчет. Важно отметить, что у этого типа элемента группировки могут быть вложены в коллекции Строки и Колонки.
  3. ДиаграммаКомпоновкиДанных: представляет диаграмму. Группировки здесь могут быть в коллекциях Серии и Точки.
  4. И другие типы, специфичные для различных видов вывода данных.

Ключевой момент, который часто вызывает затруднения, связан с тем, что первый элемент коллекции Настройки.Структура не всегда является непосредственно ГруппировкаКомпоновкиДанных. Как правило, в сложных отчетах, он может быть ТаблицаКомпоновкиДанных или другим контейнером, а сами группировки находятся уже внутри его вложенных коллекций.

Что касается «детальных записей», то в терминах СКД это фактически группировка, у которой не указаны поля группировки. То есть, если мы находим объект типа ГруппировкаКомпоновкиДанных и его свойство ПоляГруппировки.Элементы.Количество равно 0, это означает, что перед нами группировка детальных записей.

Правильный способ получения настроек СКД

Изначальная попытка получить настройки отчета напрямую через макет (например, СКД.ВариантыНастроек[0].Настройки) может не учитывать пользовательские изменения, сохраненные в настройках отчета. Для того чтобы получить актуальные настройки, включая те, что были изменены или добавлены пользователем, необходимо использовать объект КомпоновщикНастроекКомпоновкиДанных.

Рассмотрим по шагам, как это сделать:

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

Посмотрим на пример кода:


Функция ПолучитьАктуальныеНастройкиОтчета(ИмяОтчета, ИмяВарианта = "") Экспорт
    СКД = Отчеты[ИмяОтчета].ПолучитьМакет(ИмяВарианта);

    Если СКД = Неопределено Тогда
        Возврат Неопределено;
    КонецЕсли;

    // Инициализируем КомпоновщикНастроекКомпоновкиДанных
    КомпоновщикНастроек = Новый КомпоновщикНастроекКомпоновкиДанных;
    КомпоновщикНастроек.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(СКД));
    
    // Получаем базовые настройки из макета
    НастройкиСКД = СКД.ВариантыНастроек[0].Настройки;
    КомпоновщикНастроек.ЗагрузитьНастройки(НастройкиСКД);

    // Возвращаем текущие настройки, которые могут включать пользовательские изменения
    Возврат КомпоновщикНастроек.ПолучитьНастройки();
КонецФункции

Теперь, когда у нас есть объект НастройкиКомпоновкиДанных, содержащий актуальные настройки, мы можем приступить к их обходу.

Рекурсивный обход структуры для поиска группировок

Поскольку структура настроек СКД может быть вложенной и содержать различные типы элементов, прямой доступ к первому элементу структуры (например, Настройки.Структура[0].ПоляГруппировки) часто приводит к ошибке «Поле объекта не обнаружено». Для универсального и надежного поиска нам потребуется рекурсивный обход всех элементов структуры и их вложенных коллекций. При необходимости упростить программную работу со сложной структурой можно использовать модуль-обёртку над объектной моделью СКД.

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


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

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

    Возврат СписокГруппировок;
КонецФункции

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

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

Разберем по шагам логику этих функций:

  1. Функция ПолучитьГруппировки является точкой входа. Она принимает в качестве параметра либо КомпоновщикНастроекКомпоновкиДанных, либо НастройкиКомпоновкиДанных, либо другой элемент структуры СКД (например, ТаблицаКомпоновкиДанных).
  2. В зависимости от типа входного параметра, функция определяет, какую коллекцию передать в процедуру ДобавитьГруппировки для дальнейшего обхода. Например, для ТаблицаКомпоновкиДанных это будут коллекции Строки и Колонки.
  3. Процедура ДобавитьГруппировки рекурсивно обходит переданную коллекцию элементов структуры.
  4. Для каждого ЭлементСтруктуры внутри коллекции, она проверяет его тип:
    • Если это ТаблицаКомпоновкиДанных или ДиаграммаКомпоновкиДанных, она рекурсивно вызывает саму себя для обхода их вложенных коллекций (Строки, Колонки, Серии, Точки).
    • Если это ГруппировкаКомпоновкиДанных, она добавляет этот объект в СписокГруппировок.
    • Далее, если текущий элемент имеет свойство Структура (то есть, может содержать вложенные элементы), процедура рекурсивно вызывает саму себя для обхода этой вложенной структуры.
  5. Таким образом, СписокГруппировок в итоге будет содержать все найденные объекты типа ГруппировкаКомпоновкиДанных из всей иерархии настроек.

Пример использования этих функций для поиска группировок:


// Предположим, у нас есть имя отчета и, возможно, имя варианта
ИмяОтчета = "ОтчетДвиженияТоваров"; // Замените на реальное имя вашего отчета
ИмяВарианта = "Основной"; // Или пустая строка, если вариант не важен

Настройки = ПолучитьАктуальныеНастройкиОтчета(ИмяОтчета, ИмяВарианта);

Если Настройки <> Неопределено Тогда
    СписокВсехГруппировок = ПолучитьГруппировки(Настройки);

    Для Каждого ЭлементСпискаГруппировок Из СписокВсехГруппировок Цикл
        Группировка = ЭлементСпискаГруппировок.Значение;

        // 1. Проверяем, является ли это группировкой детальных записей
        Если Группировка.ПоляГруппировки.Элементы.Количество = 0 Тогда
            Сообщить("Найдена группировка детальных записей. Идентификатор: " + Группировка.Идентификатор);
            // Здесь можно выполнить дополнительные действия с группировкой детальных записей
            // Например, добавить в нее новое поле, если это необходимо.
        КонецЕсли;

        // 2. Ищем группировку по конкретному полю, например "Регистратор" или "Документ"
        Для Каждого ПолеГруппировки Из Группировка.ПоляГруппировки.Элементы Цикл
            Если ПолеГруппировки.Поле.Имя = "Регистратор" Тогда
                Сообщить("Найдена группировка по полю 'Регистратор'. Идентификатор: " + Группировка.Идентификатор);
                // Дополнительные действия с найденной группировкой
                Прервать; // Выходим из цикла по полям, так как поле найдено
            КонецЕсли;

            Если ПолеГруппировки.Поле.Имя = "Документ" Тогда
                Сообщить("Найдена группировка по полю 'Документ'. Идентификатор: " + Группировка.Идентификатор);
                // Дополнительные действия с найденной группировкой
                Прервать;
            КонецЕсли;
        КонецЦикла;
    КонецЦикла;
Иначе
    Сообщить("Не удалось получить настройки отчета.");
КонецЕсли;

Использование встроенных функций БСП

В конфигурациях, разработанных на основе Библиотеки Стандартных Подсистем (БСП), таких как 1С:Бухгалтерия Предприятия 3.0, 1С:Управление Торговлей 11 и других, существуют общие модули, предоставляющие вспомогательные функции для работы с СКД. Это может быть модуль КомпоновкаДанныхКлиентСервер или специализированные модули отчетов, например, БухгалтерскиеОтчетыКлиентСервер для БП 3.0.

Например, в некоторых версиях БСП может существовать функция КомпоновкаДанныхКлиентСервер.ПолучитьГруппировки(КомпоновщикНастроек, Ложь), как было предложено в одном из сообщений. Эта функция предназначена для сбора всех группировок из настроек компоновщика.

Также может быть полезной функция НайтиГруппировку(СтруктураГруппировки, ИмяПоля), которая возвращает элемент структуры настроек, содержащий поле группировки с указанным именем. Поиск осуществляется по указанной структуре и всем её подчиненным структурам.

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

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


// Пример использования, если функция КомпоновкаДанныхКлиентСервер.ПолучитьГруппировки доступна
// ПодключитьКомпоновщикНастроек - это условное название функции, которая получает КомпоновщикНастроек
// КомпоновщикНастроек = ПодключитьКомпоновщикНастроек(ИмяОтчета, ИмяВарианта);
//
// Если КомпоновщикНастроек <> Неопределено Тогда
//     // Ложь означает, что мы не хотим получать группировки колонок таблиц, только строковые
//     СписокГруппировокБСП = КомпоновкаДанныхКлиентСервер.ПолучитьГруппировки(КомпоновщикНастроек, Ложь);
//
//     Для Каждого ГруппировкаСтруктуры Из СписокГруппировокБСП Цикл
//         // Проверяем на детальные записи
//         Если ТипЗнч(ГруппировкаСтруктуры.Значение) = Тип("ГруппировкаКомпоновкиДанных")
//             И ГруппировкаСтруктуры.Значение.ПоляГруппировки.Элементы.Количество = 0 Тогда
//             Сообщить("Найдена группировка детальных записей через БСП. Идентификатор: " + ГруппировкаСтруктуры.Значение.Идентификатор);
//         КонецЕсли;
//
//         // Ищем группировку по полю
//         Если ТипЗнч(ГруппировкаСтруктуры.Значение) = Тип("ГруппировкаКомпоновкиДанных") Тогда
//             Для Каждого ПолеГруппировки Из ГруппировкаСтруктуры.Значение.ПоляГруппировки.Элементы Цикл
//                 Если ПолеГруппировки.Поле.Имя = "Документ" Тогда
//                     Сообщить("Найдена группировка по полю 'Документ' через БСП. Идентификатор: " + ГруппировкаСтруктуры.Значение.Идентификатор);
//                     Прервать;
//                 КонецЕсли;
//             КонецЦикла;
//         КонецЕсли;
//     КонецЦикла;
// КонецЕсли;

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

Заключение

Поиск существующих группировок детальных записей в СКД требует глубокого понимания структуры настроек компоновки данных и использования правильных методов для их получения. Мы рассмотрели, как преодолеть типичные ошибки, связанные с прямым доступом к элементам структуры, и предложили универсальный механизм рекурсивного обхода настроек. Использование КомпоновщикНастроекКомпоновкиДанных позволяет работать с актуальными настройками отчета, включая пользовательские изменения. В зависимости от вашей конфигурации и версии БСП, вы можете либо использовать готовые функции платформы, либо реализовать собственный рекурсивный обход, который будет одинаково эффективно находить необходимые группировки.

← На главную