Как в отчете в процедуре «ПриКомпоновкеРезультата» получить значение параметра СКД?

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

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

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

Выясняем причину: почему параметры «теряются»?

Для того чтобы понять суть проблемы, нам необходимо проанализировать архитектуру хранения настроек СКД. В управляемых формах 1С настройки, которые пользователь видит и изменяет на форме, хранятся обособленно. Весь объект КомпоновщикНастроек состоит из нескольких независимых коллекций:

  1. КомпоновщикНастроек.Настройки — это базовые настройки схемы компоновки данных, которые заданы разработчиком по умолчанию в конфигураторе. Когда пользователь меняет параметры на форме, эти изменения в данную коллекцию напрямую не записываются. Именно поэтому обращение к ней возвращает пустые дефолтные значения.
  2. КомпоновщикНастроек.ПользовательскиеНастройки — это специальная плоская коллекция, куда попадают только те элементы (параметры, отборы, выбранные поля), которые разработчик вынес в область пользовательских настроек (отметил флажком «Включать в пользовательские настройки»). Именно здесь хранятся актуальные значения, введенные пользователем на форме.
  3. КомпоновщикНастроек.ФиксированныеНастройки — жестко заданные программно настройки, которые пользователь не может изменить на форме.

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

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

Способ 1. Использование платформенного метода «ПолучитьНастройки()»

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

Рассмотрим подробнее пример реализации этого способа в процедуре ПриКомпоновкеРезультата:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    // Шаг 1. Получаем объединенные настройки отчета
    ИтоговыеНастройки = КомпоновщикНастроек.ПолучитьНастройки();
    
    // Шаг 2. Отыскиваем параметр в результирующей коллекции
    ИмяПараметра = "НачалоПериода";
    ПолеПараметра = Новый ПараметрКомпоновкиДанных(ИмяПараметра);
    ПараметрОбъект = ИтоговыеНастройки.ПараметрыДанных.НайтиЗначениеПараметра(ПолеПараметра);
    
    // Шаг 3. Проверяем, найден ли параметр и включено ли его использование
    Если ПараметрОбъект <> Неопределено Тогда
        
        Если ПараметрОбъект.Использование Тогда
            ЗначениеПараметра = ПараметрОбъект.Значение;
            // Здесь выполняем необходимую логику с полученным значением
            Сообщить("Выбранная дата начала периода: " + ЗначениеПараметра);
        Иначе
            Сообщить("Параметр найден, но его использование отключено пользователем!");
        КонецЕсли;
        
    КонецЕсли;

КонецПроцедуры

Важный нюанс: всегда проверяйте свойство Использование у найденного параметра. Если пользователь на форме снимет галочку активности рядом с параметром, его значение физически может оставаться в поле, но компоновщик СКД при формировании отчета должен его проигнорировать. Ваш программный код также должен это учитывать.

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

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

Давайте посмотрим на пример использования функции получения параметра через БСП:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)

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

КонецПроцедуры

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

Способ 3. Универсальная функция ручного обхода коллекций

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

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


Функция ПолучитьЗначениеПараметраСКД(ОбъектНастройки, ИмяПараметра) Экспорт
    
    ЗначениеПараметра = Неопределено;
    ПолеПараметр = ?(ТипЗнч(ИмяПараметра) = Тип("Строка"), Новый ПараметрКомпоновкиДанных(ИмяПараметра), ИмяПараметра);
    
    // 1. Если передан объект НастройкиКомпоновкиДанных
    Если ТипЗнч(ОбъектНастройки) = Тип("НастройкиКомпоновкиДанных") Тогда
        ЗначениеПараметра = ОбъектНастройки.ПараметрыДанных.НайтиЗначениеПараметра(ПолеПараметр);
        
    // 2. Если передана непосредственно коллекция ПользовательскиеНастройкиКомпоновкиДанных
    ИначеЕсли ТипЗнч(ОбъектНастройки) = Тип("ПользовательскиеНастройкиКомпоновкиДанных") Тогда
        Для Каждого ЭлементНастройки Из ОбъектНастройки.Элементы Цикл
            Если ТипЗнч(ЭлементНастройки) = Тип("ЗначениеПараметраНастроекКомпоновкиДанных") 
                И ЭлементНастройки.Параметр = ПолеПараметр Тогда
                ЗначениеПараметра = ЭлементНастройки;
                Прервать;
            КонецЕсли;
        КонецЦикла;
        
    // 3. Если передан целиком КомпоновщикНастроекКомпоновкиДанных
    ИначеЕсли ТипЗнч(ОбъектНастройки) = Тип("КомпоновщикНастроекКомпоновкиДанных") Тогда
        // Сначала ищем в пользовательских настройках формы
        Для Каждого ЭлементНастройки Из ОбъектНастройки.ПользовательскиеНастройки.Элементы Цикл
            Если ТипЗнч(ЭлементНастройки) = Тип("ЗначениеПараметраНастроекКомпоновкиДанных") 
                И ЭлементНастройки.Параметр = ПолеПараметр Тогда
                ЗначениеПараметра = ЭлементНастройки;
                Прервать;
            КонецЕсли;
        КонецЦикла;
        
        // Если в пользовательских настройках не нашли, смотрим основные настройки схемы
        Если ЗначениеПараметра = Неопределено Тогда
            ЗначениеПараметра = ОбъектНастройки.Настройки.ПараметрыДанных.НайтиЗначениеПараметра(ПолеПараметр);
        КонецЕсли;
        
        // Если и там нет, проверяем фиксированные настройки
        Если ЗначениеПараметра = Неопределено Тогда
            ЗначениеПараметра = ОбъектНастройки.ФиксированныеНастройки.ПараметрыДанных.НайтиЗначениеПараметра(ПолеПараметр);
        КонецЕсли;
        
    // 4. Если передан вложенный объект настройки
    ИначеЕсли ТипЗнч(ОбъектНастройки) = Тип("НастройкиВложенногоОбъектаКомпоновкиДанных") Тогда
        ЗначениеПараметра = ОбъектНастройки.Настройки.ПараметрыДанных.НайтиЗначениеПараметра(ПолеПараметр);
        
    // 5. Обработка данных расшифровки
    ИначеЕсли ТипЗнч(ОбъектНастройки) = Тип("ДанныеРасшифровкиКомпоновкиДанных") Тогда
        ЗначениеПараметра = ОбъектНастройки.Настройки.ПараметрыДанных.НайтиЗначениеПараметра(ПолеПараметр);
        
    // 6. Прямой поиск в коллекции параметров
    ИначеЕсли ТипЗнч(ОбъектНастройки) = Тип("КоллекцияЗначенийПараметровКомпоновкиДанных") Тогда
        ЗначениеПараметра = ОбъектНастройки.Найти(ПолеПараметр);
        
    // 7. Поиск в оформлении компоновки данных
    ИначеЕсли ТипЗнч(ОбъектНастройки) = Тип("ОформлениеКомпоновкиДанных") Тогда
        ЗначениеПараметра = ОбъектНастройки.НайтиЗначениеПараметра(ПолеПараметр);
    КонецЕсли;
    
    Возврат ЗначениеПараметра;
    
КонецФункции

Для вызова этой функции в процедуре ПриКомпоновкеРезультата достаточно передать в нее сам компоновщик настроек отчета:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)

    ПараметрОбъект = ПолучитьЗначениеПараметраСКД(КомпоновщикНастроек, "НачалоПериода");
    Если ПараметрОбъект <> Неопределено И ПараметрОбъект.Использование Тогда
        А = ПараметрОбъект.Value; // Получили искомое значение
    КонецЕсли;

КонецПроцедуры

Как связать основную и пользовательскую настройку через Идентификатор?

Разберем еще один теоретически важный механизм связывания настроек. Если вы хотите точечно найти элемент в ПользовательскиеНастройки, зная только имя параметра в основной схеме, вам нужно пройти через цепочку идентификаторов. Каждая пользовательская настройка привязана к основной через свойство ИдентификаторПользовательскойНастройки.

Посмотрим на практическую реализацию этого алгоритма:


// 1. Находим описание параметра в основных настройках компоновщика
ПараметрОсновной = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("НачалоПериода");

Если ПараметрОсновной <> Неопределено Тогда
    
    // 2. Получаем уникальный GUID, связывающий основную настройку с пользовательской
    ИдентификаторСвязи = ПараметрОсновной.ИдентификаторПользовательскойНастройки;
    
    // 3. Выполняем точечный поиск в пользовательских настройках по этому GUID
    ЭлементПользовательский = КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.Найти(ИдентификаторСвязи);
    
    Если ЭлементПользовательский <> Неопределено И ЭлементПользовательский.Использование Тогда
        ЗначениеПараметра = ЭлементПользовательский.Значение;
    КонецЕсли;
    
КонецЕсли;

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

Важные рекомендации по работе с параметрами в ПриКомпоновкеРезультата

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

  1. Программный вывод отчета: Если в процедуре ПриКомпоновкеРезультата вы устанавливаете свойство СтандартнаяОбработка = Ложь (например, для программного вывода данных в таблицу значений, дерево или для кастомного формирования макета), помните, что при инициализации процессора компоновки ПроцессорКомпоновкиДанных.Инициализировать() вы должны передавать настройки, полученные исключительно через метод КомпоновщикНастроек.ПолучитьНастройки(). Если вы передадите туда напрямую свойство КомпоновщикНастроек.Настройки, то все изменения параметров и отборов, внесенные пользователем интерактивно на форме, будут полностью проигнорированы отчетом.
  2. Проверка заполнения: Если параметр является обязательным для выполнения запроса в СКД, перед получением его значения убедитесь, что оно заполнено. Непустое значение даты проверяется как ЗначениеЗаполнено(ВыбраннаяДата).
  3. Изменение параметров «на лету»: Если вам требуется не просто прочитать значение, а переопределить параметр перед выводом отчета (например, программно установить границу конца дня для даты окончания периода), лучше всего вносить изменения также в объединенную коллекцию настроек непосредственно перед генерацией макета компоновки.

Использование описанных подходов гарантирует корректную работу ваших отчетов СКД в любых конфигурациях на платформе 1С:Предприятие 8.3. Дополнительные практические сценарии формирования отчетов можно посмотреть в примерах программной работы со схемой компоновки данных и управляемыми формами.

← На главную