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