При разработке отчетов на системе компоновки данных (СКД) периодически возникают задачи, которые сложно или невозможно решить стандартным языком запросов. Например, требуется сложно распарсить строковые поля, объединить результат с данными из внешней информационной базы по COM или HTTP-сервису, либо применить нетривиальные алгоритмы расчета. Рассмотрим подробнее, как перехватить процесс формирования отчета в процедуре ПриКомпоновкеРезультата, заполнить внешнюю таблицу значений и корректно передать ее в макет СКД.
Для реализации подобного сценария в схеме компоновки данных создается набор данных с типом Объект. В свойствах этого набора указывается Имя объекта, содержащего данные (например, ЗарплатаПоОбъектам). Поля набора данных описываются вручную с указанием точных типов.
В модуле объекта отчета вся логика переопределяется в обработчике события ПриКомпоновкеРезультата. Разберем по шагам, как устроен этот процесс:
СтандартнаяОбработка = Ложь.ВнешниеНаборыДанных.Посмотрим на практический пример кода в модуле объекта отчета, где выполняется выборка данных по начислениям, парсинг способа отражения и передача подготовленной таблицы в процессор компоновки:
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ОтражениеЗарплаты.ФизическоеЛицо,
| ОтражениеЗарплаты.СпособОтраженияЗарплатыВБухучете,
| СУММА(ОтражениеЗарплаты.Сумма) КАК Сумма
|ИЗ
| Документ.ОтражениеЗарплатыВБухучете.НачисленнаяЗарплатаИВзносы КАК ОтражениеЗарплаты
|ГДЕ
| ОтражениеЗарплаты.Ссылка.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
|СГРУППИРОВАТЬ ПО
| ОтражениеЗарплаты.СпособОтраженияЗарплатыВБухучете,
| ОтражениеЗарплаты.ФизическоеЛицо";
Запрос.УстановитьПараметр("ДатаНачала", НачалоДня(ДатаНачала));
Запрос.УстановитьПараметр("ДатаОкончания", КонецДня(ДатаОкончания));
// Создаем структуру и типизацию колонок таблицы значений
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("ОбъектВыполнения", Новый ОписаниеТипов("СправочникСсылка.ОбъектыВыполнения"));
ТЗ.Колонки.Добавить("СтатьяЗатрат", Новый ОписаниеТипов("Строка"));
ТЗ.Колонки.Добавить("ФизическоеЛицо", Новый ОписаниеТипов("СправочникСсылка.ФизическиеЛица"));
КвалификаторыЧисла = Новый КвалификаторыЧисла(15, 2, ДопустимыйЗнак.Неотрицательный);
ОписаниеЧисла = Новый ОписаниеТипов("Число", КвалификаторыЧисла);
ТЗ.Колонки.Добавить("Сумма", ОписаниеЧисла);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
СпособОтражения = СокрЛП(Выборка.СпособОтраженияЗарплатыВБухучете);
Счет = Лев(СпособОтражения, 2);
Если Счет = "26" Тогда
ОбъектВыполненияКод = "АУП";
СтатьяЗатрат = "Зарплата";
ИначеЕсли Счет = "25" Тогда
ОбъектВыполненияКод = "ИТР";
СтатьяЗатрат = "Зарплата";
ИначеЕсли Счет = "20" Тогда
ОбъектВыполненияКод = Сред(СпособОтражения, 7, 4);
СтатьяЗатрат = ПолучитьСтатьюЗатратПоКоду(Прав(СпособОтражения, 3));
Иначе
ОбъектВыполненияКод = "";
СтатьяЗатрат = "";
КонецЕсли;
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.ОбъектВыполнения = Справочники.ОбъектыВыполнения.НайтиПоКоду(ОбъектВыполненияКод);
НоваяСтрока.СтатьяЗатрат = СтатьяЗатрат;
НоваяСтрока.ФизическоеЛицо = Выборка.ФизическоеЛицо;
НоваяСтрока.Сумма = Выборка.Сумма;
КонецЦикла;
// Передача таблицы во внешние наборы данных
ВнешниеНаборыДанных = Новый Структура;
ВнешниеНаборыДанных.Вставить("ЗарплатаПоОбъектам", ТЗ);
// Формирование макета компоновки
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
Настройки = ЭтотОбъект.КомпоновщикНастроек.ПолучитьНастройки();
МакетКомпоновки = КомпоновщикМакета.Выполнить(ЭтотОбъект.СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
// Инициализация процессора компоновки
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки);
// Вывод результата в табличный документ
ДокументРезультат.Очистить();
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецПроцедуры
Нередко разработчики пытаются вручную фильтровать данные перед добавлением в таблицу, анализируя пользовательские отборы через обход коллекции КомпоновщикНастроек.Настройки.Отбор.Элементы, а затем удаляют этот отбор методом Удалить(). Проанализируем, почему такой подход несет скрытые проблемы:
КомпоновщикНастроек.Настройки физически удаляет отбор из текущего варианта. Если пользователь повторно нажмет кнопку «Сформировать» или откроет окно настроек, добавленное им поле пропадет из интерфейса.ГруппаЭлементовОтбораКомпоновкиДанных) или вывести его в быстрые пользовательские настройки (КомпоновщикНастроек.ПользовательскиеНастройки). Простой перебор верхнего уровня коллекции не найдет такой фильтр.ОбъектВыполнения присутствует в схеме как поле набора данных «Объект», платформа выполняет фильтрацию по ней автоматически. Ручная фильтрация строк перед заполнением таблицы значений не требуется — СКД сама отсечет лишние записи в момент компоновки.Если же фильтр действительно нужно применить только на этапе сбора данных и исключить его дальнейшее влияние на макет компоновки, необходимо модифицировать не исходные настройки компоновщика, а их изолированную копию:
// Получаем копию настроек для генерации макета
Настройки = ЭтотОбъект.КомпоновщикНастроек.ПолучитьНастройки();
// Отключаем или удаляем нужный фильтр только в локальной переменной
Для Каждого Элемент Из Настройки.Отбор.Элементы Цикл
Если ТипЗнч(Элемент) = Тип("ЭлементОтбораКомпоновкиДанных")
И Элемент.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ОбъектВыполнения") Тогда
Элемент.Использование = Ложь;
КонецЕсли;
КонецЦикла;
// Передаем изолированные настройки в компоновщик макета
МакетКомпоновки = КомпоновщикМакета.Выполнить(ЭтотОбъект.СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
При использовании описанной схемы важно учитывать следующие технические нюансы платформы 1С:Предприятие:
Null.НайтиПоКоду() внутри обхода выборки приводит к сильному замедлению формирования отчета. Оптимально собрать уникальные коды в массив и получить соответствующие ссылки одним запросом перед заполнением таблицы.V83.COMConnector является 32-разрядной. Если сервер 1С работает под 64-битной версией Windows, прямое создание COM-объекта в коде сервера вызовет ошибку без предварительной настройки пула приложений в службе компонентов (COM+). На серверах под управлением Linux COM-соединение не поддерживается вовсе. В современных конфигурациях рекомендуется использовать интеграцию через HTTP-сервисы или REST API.ВозможностьИспользованияВнешнихФункций в четвертом параметре метода ПроцессорКомпоновки.Инициализировать() и вызывать функцию парсинга прямо в выражениях вычисляемых полей СКД.