Как реализовать расшифровку через вторую схему компоновки данных (СКД) в одном внешнем отчете

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

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

Постановка задачи и выбор стратегии

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

Шаг 1: Перехват события расшифровки на клиенте

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


&НаКлиенте
Процедура РезультатОбработкаРасшифровки(Элемент, Расшифровка, СтандартнаяОбработка, ДополнительныеПараметры)
    
    // Проверяем, что работаем с нужным вариантом
    Если КлючТекущегоВарианта = "Основной" Тогда
        
        // Отменяем стандартное окно выбора действия
        СтандартнаяОбработка = ЛОЖЬ;
        
        // Обрабатываем данные на сервере, чтобы понять, на что нажал пользователь
        ДанныеРасшифровкиСтруктура = ОбработкиРасшифровкуНаСервере(Расшифровка);
        
        Если ДанныеРасшифровкиСтруктура.Материал <> Неопределено Тогда
            Если ДанныеРасшифровкиСтруктура.РасшифровкаОстатка Тогда
                
                // Формируем параметры для открытия формы со второй СКД
                ПараметрыФормы = Новый Структура;
                ПараметрыФормы.Вставить("Дата", ТекущаяДата());
                ПараметрыФормы.Вставить("Номенклатура", ДанныеРасшифровкиСтруктура.Материал);
                
                // Открываем форму расшифровки, передавая параметры
                // Важно: указываем путь к форме нашего внешнего отчета
                ОткрытьФорму("ВнешнийОтчет.МойОтчёт.Форма.ФормаРасшифровкаОстаткиСерии", ПараметрыФормы);
                
            КонецЕсли;
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

Шаг 2: Извлечение данных из расшифровки на сервере

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

Рассмотрим серверную функцию, которая анализирует состав полей в строке. Мы будем использовать вспомогательную функцию для поиска значения нужного поля (например, "Материал"), даже если пользователь кликнул на ресурсный показатель (например, "Остаток").


&НаСервере
Функция ОбработкиРасшифровкуНаСервере(Расшифровка)
    
    РезультатСтруктура = Новый Структура("РасшифровкаОстатка, Материал", Ложь, Неопределено);
    
    // Получаем данные из хранилища
    ОбъектДанныеРасшифровки = ПолучитьИзВременногоХранилища(ДанныеРасшифровки);
    
    // Определяем, на какое поле нажали (проверяем первое поле в выбранном элементе)
    ЭлементРасшифровки = ОбъектДанныеРасшифровки.Элементы[Расшифровка];
    Если ТипЗнч(ЭлементРасшифровки) = Тип("ЭлементРасшифровкиКомпоновкиДанныхПоля") Тогда
        ПолеИмя = ЭлементРасшифровки.ПолучитьПоля()[0].Поле;
        РезультатСтруктура.РасшифровкаОстатка = (ПолеИмя = "Остаток");
    КонецЕсли;
    
    Если РезультатСтруктура.РасшифровкаОстатка Тогда
        // Список полей, которые могут встретиться в строке
        МассивПолей = Новый Массив;
        МассивПолей.Добавить("Остаток");
        МассивПолей.Добавить("Дефицит");
        
        // Вызываем поиск значения группировки "Материалы" в текущей строке
        РезультатСтруктура.Материал = ПолучитьЗначениеПоляВСтрокеНаСервере(Расшифровка, МассивПолей, "Материалы");
    КонецЕсли;
    
    Возврат РезультатСтруктура;
    
КонецФункции

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


&НаСервере
Функция ПолучитьЗначениеПоляВСтрокеНаСервере(Расшифровка, ИменаТекущихПолей, ИмяНужногоПоля)
    
    ДанныеОтчета = ПолучитьИзВременногоХранилища(ДанныеРасшифровки);
    КоличествоЭлементов = ДанныеОтчета.Элементы.Количество();
    
    // Идем "назад" от текущего индекса расшифровки в поиске значения группировки
    Счетчик = Расшифровка - 1;
    Пока Счетчик >= 0 Цикл
        ТекущийЭлемент = ДанныеОтчета.Элементы.Получить(Счетчик);
        
        Если ТипЗнч(ТекущийЭлемент) = Тип("ЭлементРасшифровкиКомпоновкиДанныхПоля") Тогда
            ПолеРасшифровки = ТекущийЭлемент.ПолучитьПоля()[0];
            Если ПолеРасшифровки.Поле = ИмяНужногоПоля Тогда
                Возврат ПолеРасшифровки.Значение;
            КонецЕсли;
        КонецЕсли;
        
        Счетчик = Счетчик - 1;
    КонецЦикла;
    
    Возврат Неопределено;
КонецФункции

Шаг 3: Программное формирование отчета по второй СКД

После того как мы получили параметры (Номенклатура и Дата) и передали их в новую форму расшифровки, нам нужно программно сформировать результат. В форме расшифровки создадим реквизит Результат с типом ТабличныйДокумент и опишем процедуру компоновки.

Проанализируем шаги программного вывода:

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

&НаСервере
Процедура СформироватьОтчётНаСервере()
    
    // Получаем доступ к макетам внешнего отчета
    ОтчетОбъект = РеквизитФормыВЗначение("Объект");
    СхемаКД = ОтчетОбъект.ПолучитьМакет("РасшифровкаОстаткиСерии");
    
    // Настройки по умолчанию
    Настройки = СхемаКД.НастройкиПоУмолчанию;
    
    // Инициализация компоновщика
    ИсточникНастроек = Новый ИсточникДоступныхНастроекКомпоновкиДанных(СхемаКД);
    Компоновщик = Новый КомпоновщикНастроекКомпоновкиДанных;
    Компоновщик.Инициализировать(ИсточникНастроек);
    Компоновщик.ЗагрузитьНастройки(Настройки);
    
    // Установка переданных параметров
    ПараметрДата = Компоновщик.Настройки.ПараметрыДанных.Элементы.Найти("Дата");
    Если ПараметрДата <> Неопределено Тогда
        ПараметрДата.Значение = Параметры.Дата;
        ПараметрДата.Использование = Истина;
    КонецЕсли;
    
    ПараметрНом = Компоновщик.Настройки.ПараметрыДанных.Элементы.Найти("Номенклатура");
    Если ПараметрНом <> Неопределено Тогда
        ПараметрНом.Значение = Параметры.Номенклатура;
        ПараметрНом.Использование = Истина;
    КонецЕсли;
    
    // Компоновка макета
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКД, Компоновщик.ПолучитьНастройки());
    
    // Инициализация процессора компоновки
    ПроцессорКД = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКД.Инициализировать(МакетКомпоновки);
    
    // Вывод в ТабличныйДокумент (Результат - это элемент формы)
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(Результат);
    ПроцессорВывода.Вывести(ПроцессорКД);
    
КонецПроцедуры

Важные рекомендации и тонкости

При работе с внешними отчетами помните, что сервер 1С не всегда "знает" о существовании вашего файла на диске в контексте сеанса. Поэтому использование РеквизитФормыВЗначение("Объект").ПолучитьМакет(...) является наиболее надежным способом получения схемы. Если вы попытаетесь создать новый объект ВнешнийОтчет по пути, это может привести к ошибкам прав доступа или блокировкам файла.

Также стоит обратить внимание на производительность функции поиска значений расшифровки. Если ваш отчет содержит тысячи строк и сложную иерархию, вместо ручного перебора индексов лучше использовать свойство Иерархия элементов расшифровки. Объект ДанныеРасшифровкиКомпоновкиДанных позволяет получить родительские элементы для текущего идентификатора, что дает возможность мгновенно определить значения всех группировок (например, Организация -> Склад -> Номенклатура), в которые входит ячейка. Для реализации таких механизмов обработки клика пригодится пример получения значений группировок через данные расшифровки СКД.

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

← На главную