Как программно обработать данные и загрузить таблицу значений в отчет на СКД

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

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

Архитектура решения: Набор данных «Объект»

Для реализации подобного сценария в схеме компоновки данных создается набор данных с типом Объект. В свойствах этого набора указывается Имя объекта, содержащего данные (например, ЗарплатаПоОбъектам). Поля набора данных описываются вручную с указанием точных типов.

В модуле объекта отчета вся логика переопределяется в обработчике события ПриКомпоновкеРезультата. Разберем по шагам, как устроен этот процесс:

  1. Отключаем стандартное формирование отчета установкой флага СтандартнаяОбработка = Ложь.
  2. Получаем первичные данные запросом к базе или через внешние интеграции.
  3. Обрабатываем полученные строки алгоритмически и формируем типизированную таблицу значений.
  4. Связываем сформированную таблицу значений со схемой через структуру ВнешниеНаборыДанных.
  5. Инициализируем процессор компоновки и выводим готовый отчет в табличный документ.

Пример программной подготовки данных в процедуре ПриКомпоновкеРезультата

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


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    ОтражениеЗарплаты.ФизическоеЛицо,
        |    ОтражениеЗарплаты.СпособОтраженияЗарплатыВБухучете,
        |    СУММА(ОтражениеЗарплаты.Сумма) КАК Сумма
        |ИЗ
        |    Документ.ОтражениеЗарплатыВБухучете.НачисленнаяЗарплатаИВзносы КАК ОтражениеЗарплаты
        |ГДЕ
        |    ОтражениеЗарплаты.Ссылка.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
        |СГРУППИРОВАТЬ ПО
        |    ОтражениеЗарплаты.СпособОтраженияЗарплатыВБухучете,
        |    ОтражениеЗарплаты.ФизическоеЛицо";
    
    Запрос.УстановитьПараметр("ДатаНачала", НачалоДня(ДатаНачала));
    Запрос.УстановитьПараметр("ДатаОкончания", КонецДня(ДатаОкончания));
    
    // Создаем структуру и типизацию колонок таблицы значений
    ТЗ = Новый ТаблицаЗначений;
    ТЗ.Колонки.Добавить("ОбъектВыполнения", Новый ОписаниеТипов("СправочникСсылка.ОбъектыВыполнения"));
    ТЗ.Колонки.Добавить("СтатьяЗатрат", Новый ОписаниеТипов("Строка"));
    ТЗ.Колонки.Добавить("ФизическоеЛицо", Новый ОписаниеТипов("СправочникСсылка.ФизическиеЛица"));
    
    КвалификаторыЧисла = Новый КвалификаторыЧисла(15, 2, ДопустимыйЗнак.Неотрицательный);
    ОписаниеЧисла = Новый ОписаниеТипов("Число", КвалификаторыЧисла);
    ТЗ.Колонки.Добавить("Сумма", ОписаниеЧисла);
    
    РезультатЗапроса = Запрос.Выполнить();
    Выборка = РезультатЗапроса.Выбрать();
    
    Пока Выборка.Следующий() Цикл
        СпособОтражения = СокрЛП(Выборка.СпособОтраженияЗарплатыВБухучете);
        Счет = Лев(СпособОтражения, 2);
        
        Если Счет = "26" Тогда
            ОбъектВыполненияКод = "АУП";
            СтатьяЗатрат = "Зарплата";
        ИначеЕсли Счет = "25" Тогда
            ОбъектВыполненияКод = "ИТР";
            СтатьяЗатрат = "Зарплата";
        ИначеЕсли Счет = "20" Тогда
            ОбъектВыполненияКод = Сред(СпособОтражения, 7, 4);
            СтатьяЗатрат = ПолучитьСтатьюЗатратПоКоду(Прав(СпособОтражения, 3));
        Иначе
            ОбъектВыполненияКод = "";
            СтатьяЗатрат = "";
        КонецЕсли;
        
        НоваяСтрока = ТЗ.Добавить();
        НоваяСтрока.ОбъектВыполнения = Справочники.ОбъектыВыполнения.НайтиПоКоду(ОбъектВыполненияКод);
        НоваяСтрока.СтатьяЗатрат = СтатьяЗатрат;
        НоваяСтрока.ФизическоеЛицо = Выборка.ФизическоеЛицо;
        НоваяСтрока.Сумма = Выборка.Сумма;
    КонецЦикла;
    
    // Передача таблицы во внешние наборы данных
    ВнешниеНаборыДанных = Новый Структура;
    ВнешниеНаборыДанных.Вставить("ЗарплатаПоОбъектам", ТЗ);
    
    // Формирование макета компоновки
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    Настройки = ЭтотОбъект.КомпоновщикНастроек.ПолучитьНастройки();
    МакетКомпоновки = КомпоновщикМакета.Выполнить(ЭтотОбъект.СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки); 
    
    // Инициализация процессора компоновки
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки);
    
    // Вывод результата в табличный документ
    ДокументРезультат.Очистить();
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);
    
КонецПроцедуры

Как правильно работать с отборами и настройками СКД

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

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

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


// Получаем копию настроек для генерации макета
Настройки = ЭтотОбъект.КомпоновщикНастроек.ПолучитьНастройки();

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

// Передаем изолированные настройки в компоновщик макета
МакетКомпоновки = КомпоновщикМакета.Выполнить(ЭтотОбъект.СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);

Оптимизация и ограничения платформы

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

  1. Строгое соответствие колонок: Названия колонок таблицы значений должны посимвольно совпадать с именами полей в наборе данных схемы (с учетом регистра). Колонки должны быть строго типизированы, а числовые ресурсы не должны содержать значений Null.
  2. Исключение запросов в цикле: Вызов методов вроде НайтиПоКоду() внутри обхода выборки приводит к сильному замедлению формирования отчета. Оптимально собрать уникальные коды в массив и получить соответствующие ссылки одним запросом перед заполнением таблицы.
  3. Ограничения COM-соединения: Библиотека V83.COMConnector является 32-разрядной. Если сервер 1С работает под 64-битной версией Windows, прямое создание COM-объекта в коде сервера вызовет ошибку без предварительной настройки пула приложений в службе компонентов (COM+). На серверах под управлением Linux COM-соединение не поддерживается вовсе. В современных конфигурациях рекомендуется использовать интеграцию через HTTP-сервисы или REST API.
  4. Внешние функции как альтернатива: Если единственная задача — распарсить строковое поле, не обязательно полностью переходить на внешний набор данных. Можно включить параметр ВозможностьИспользованияВнешнихФункций в четвертом параметре метода ПроцессорКомпоновки.Инициализировать() и вызывать функцию парсинга прямо в выражениях вычисляемых полей СКД.
← На главную