Как получить период и значения соседних полей в обработке расшифровки СКД?

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

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

В процессе реализации этой задачи разработчики сталкиваются с двумя основными трудностями:

  1. Как получить глобальные параметры отчета (например, НачалоПериода и КонецПериода), установленные пользователем на форме?
  2. Как получить значения соседних колонок (других полей) той строки, на которую нажал пользователь?

В этой статье мы подробно рассмотрим и разберем по шагам надежные решения этих задач с примерами кода на платформе «1С:Предприятие 8».


Часть 1. Безопасное получение параметров периода из Компоновщика настроек

В процедуре РезультатОбработкаРасшифровки напрямую обратиться к схеме компоновки для получения параметров не получится — в свойствах схемы значения параметров будут пустыми, так как они заполняются пользователем динамически в пользовательских настройках. Нам необходимо обратиться к свойству КомпоновщикНастроек формы отчета.

Обращение к элементам пользовательских настроек по фиксированному индексу (например, через Элементы[0]) крайне опасно. Если пользователь или разработчик изменит состав настроек на форме, добавит новые отборы или параметры, порядок элементов нарушится, и код начнет возвращать неверные типы данных или вызовет ошибку выполнения.

Рассмотрим правильный и безопасный алгоритм поиска значения параметра по его имени:


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

КонецФункции

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


ЗначениеНачалаПериода = КомпоновкаДанныхКлиентСервер.ПолучитьЗначениеПараметра(КомпоновщикНастроек, "НачалоПериода");
ЗначениеКонцаПериода  = КомпоновкаДанныхКлиентСервер.ПолучитьЗначениеПараметра(КомпоновщикНастроек, "КонецПериода");

Часть 2. Исправление ошибки получения данных из временного хранилища

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

«Неправильный путь к файлу "ДанныеРасшифровкиКомпоновкиДанных" при вызове метода ПолучитьИзВременногоХранилища()»

Выясним причину этой ошибки. Процедура РезультатОбработкаРасшифровки выполняется на стороне клиента (директива &НаКлиенте). Параметр ДанныеРасшифровки, передаваемый в эту процедуру, на клиенте представляет собой обычную строку — уникальный адрес, по которому сам двоичный объект расшифровки временно сохранен на сервере.

Метод ПолучитьИзВременногоХранилища не может быть выполнен на клиенте для получения сложных объектов. Для работы с объектом расшифровки нам необходимо передать этот адрес на сервер. Проанализируем правильную архитектуру вызова:


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

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

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

КонецФункции

Часть 3. Получение значений «соседних» полей строки отчета

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

Способ А. Использование макетов полей в СКД (Декларативный путь)

Этот способ является наиболее красивым и не требует сложного программного обхода табличного документа.

Разберем по шагам настройку макета:

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

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

Способ Б. Рекурсивный обход родительских группировок (Для иерархических отчетов)

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

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


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

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

Эту процедуру можно запустить внутри серверной функции получения данных:


&НаСервереБезКонтекста
Функция ПолучитьПолныеДанныеСтроки(Val АдресХранилища, Val ИдентификаторРасшифровки)
    
    ОбъектРасшифровки = ПолучитьИзВременногоХранилища(АдресХранилища);
    СтруктураПолей = Новый Структура;
    
    СобратьПоляСтрокиРекурсивно(ОбъектРасшифровки, ИдентификаторРасшифровки, СтруктураПолей);
    
    Возврат СтруктураПолей;

КонецФункции

Этот метод гарантирует, что вы получите полные контекстные данные строки, даже если клик произошел на самом детальном уровне отчета.

Способ В. Программный обход ячеек строки Табличного Документа

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

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

Реализуем данный метод:


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

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

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

КонецФункции

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

← На главную