Разработка отчетов и обработок выгрузки данных на базе Системы компоновки данных (СКД) в 1С — это мощный инструмент в арсенале любого разработчика. Однако при программной настройке СКД часто возникают неочевидные проблемы: то в выгружаемую таблицу попадают лишние строки итогов, то не удается построить сложную иерархию отборов, а то и вовсе платформа выбрасывает ошибку «Несоответствие типов» при попытке добавить новую группировку. В этой статье мы подробно разберем, как решать подобные проблемы программно, рассмотрим нюансы инициализации компоновщика настроек и научимся оптимизировать выгрузку результатов в формат JSON.
Когда мы разрабатываем отчет для пользователя, итоговые строки внизу таблицы или групп необходимы для наглядности. Но когда СКД используется в качестве источника данных для интеграции (например, для последующей выгрузки в JSON, XML или отправки по API), итоговые строки только мешают, так как они дублируют данные и ломают структуру обмена.
Попытка обратиться напрямую к элементам коллекции параметров вывода через квадратные скобки по имени настройки (например, ПараметрыВывода.Элементы["РасположениеИтогов"]) вызывает ошибку отсутствия поля объекта. Выясним причину: коллекция параметров вывода является специализированным объектом, и обращаться к ней нужно через предназначенные для этого методы.
Чтобы программно отключить общие итоги в СКД, воспользуемся методом УстановитьЗначениеПараметра у коллекции параметров вывода настроек компоновки. Рассмотрим рабочий пример кода:
// Получаем схему компоновки данных
СКД = Отчеты.Выработка.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
Настройки = СКД.НастройкиПоУмолчанию;
// Отключаем вертикальное расположение общих итогов
Настройки.ПараметрыВывода.УстановитьЗначениеПараметра("ВертикальноеРасположениеОбщихИтогов", РасположениеИтоговКомпоновкиДанных.Нет);
// Отключаем горизонтальное расположение общих итогов
Настройки.ПараметрыВывода.УстановитьЗначениеПараметра("ГоризонтальноеРасположениеОбщихИтогов", РасположениеИтоговКомпоновкиДанных.Нет);
// Отключаем расположение итогов в группировках
Настройки.ПараметрыВывода.УстановитьЗначениеПараметра("РасположениеИтогов", РасположениеИтоговКомпоновкиДанных.Нет);
Использование предопределенного системного перечисления РасположениеИтоговКомпоновкиДанных.Нет гарантирует, что при выводе результата в коллекцию значений (Таблицу значений или Дерево значений) итоговые строки формироваться не будут.
Часто требуется ограничить выборку данных сложным условием. Простые отборы по умолчанию объединяются логическим «И». Но что делать, если нужно реализовать условие вида (Склад = Склад1 ИЛИ Склад = Склад2)? Для этого необходимо программно создать группу элементов отбора с типом группы ГруппаИли.
Разберем по шагам, как правильно выстроить иерархию элементов отбора. Распространенная ошибка начинающих разработчиков — добавление элементов условий в общий корень отбора вместо того, чтобы подчинить их созданной группе. Проанализируем корректный алгоритм настройки отбора:
// 1. Создаем группу ИЛИ в коллекции элементов отбора
ГруппаИЛИ = Настройки.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
ГруппаИЛИ.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИли;
ГруппаИЛИ.Использование = Истина;
// 2. Добавляем первое условие внутрь созданной группы
ЭлементОтбора1 = ГруппаИЛИ.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора1.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Склад");
ЭлементОтбора1.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора1.ПравоеЗначение = Склад1;
ЭлементОтбора1.Использование = Истина;
// 3. Добавляем второе условие внутрь той же группы
ЭлементОтбора2 = ГруппаИЛИ.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора2.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Склад");
ЭлементОтбора2.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора2.ПравоеЗначение = Склад2;
ЭлементОтбора2.Использование = Истина;
Обратите внимание, что мы добавляем новые элементы методом ГруппаИЛИ.Элементы.Добавить(), а не через Настройки.Отбор.Элементы.Добавить(). Это обеспечивает правильную вложенность условий.
При программной работе в контексте управляемой формы (например, в модуле формы обработки) попытка напрямую изменить структуру компоновщика настроек, добавленного в реквизиты объекта или формы, может завершиться аварийно с ошибкой «Несоответствие типов» при выполнении метода Настройки.Structure.Add() (или Структура.Добавить()).
Выясним причину этой проблемы. Она кроется в особенностях клиент-серверного взаимодействия платформы 1С:
СхемаКомпоновкиДанных не может существовать на клиенте, так как он не сериализуется.Решением является инициализация компоновщика настроек через адрес во Временном хранилище. Этот подход признан стандартом разработки под управляемое приложение. Рассмотрим правильный шаблон инициализации:
&НаСервере
Процедура ИнициализироватьКомпоновщикНастроекНаСервере()
// Получаем схему компоновки данных
СКД = Отчеты.Выработка.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
// Помещаем схему во временное хранилище и связываем ее с уникальным идентификатором формы
АдресСхемы = ПоместитьВоВременноеХранилище(СКД, УникальныйИдентификатор);
// Инициализируем источник настроек, используя адрес в хранилище вместо самого объекта схемы
ИсточникНастроек = Новый ИсточникДоступныхНастроекКомпоновкиДанных(АдресСхемы);
// Инициализируем компоновщик настроек формы (или объекта)
КомпоновщикНастроек.Инициализировать(ИсточникНастроек);
// Загружаем настройки по умолчанию
КомпоновщикНастроек.ЗагрузитьНастройки(СКД.НастройкиПоУмолчанию);
КонецПроцедуры
Такой способ гарантирует, что при передаче управления на клиента компоновщик всегда сможет обратиться по ссылке-адресу к схеме данных, все доступные поля будут отображаться корректно, а программное добавление элементов структуры и группировок перестанет вызывать ошибку несовместимости типов.
Рассмотрим, как оптимизировать процесс преобразования данных СКД в формат JSON. На практике часто сначала выгружают результат СКД в промежуточную ТаблицуЗначений, а затем преобразуют её. Однако прямая сериализация таблицы значений в JSON встроенными средствами платформы не поддерживается. Для этого приходится обходить таблицу в цикле и собирать массив структур.
Посмотрим на пример классической сериализации через промежуточную таблицу значений:
// 1. Выводим данные в Таблицу Значений
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СКД, Настройки,,, Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);
ТаблицаРезультата = Новый ТаблицаЗначений;
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(ТаблицаРезультата);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
// 2. Преобразуем Таблицу Значений в сериализуемый массив структур
МассивОбъектов = Новый Массив;
Для Каждого СтрокаТЗ Из ТаблицаРезультата Цикл
СтруктураСтроки = Новый Структура;
Для Каждого Колонка Из ТаблицаРезультата.Колонки Цикл
СтруктураСтроки.Вставить(Колонка.Имя, СтрокаТЗ[Колонка.Имя]);
КонецЦикла;
МассивОбъектов.Добавить(СтруктураСтроки);
КонецЦикла;
// 3. Записываем в JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, МассивОбъектов);
РезультатJSON = ЗаписьJSON.Закрыть();
Важная рекомендация по оптимизации: Если выгружаемый отчет содержит десятки тысяч строк, создание промежуточной таблицы значений в памяти сервера нагрузит оперативную память дважды. Чтобы этого избежать, используйте потоковый вывод: обходите результаты компоновки построчно через ПроцессорКомпоновки.Следующий() и сразу же пишите данные в поток ЗаписьJSON. Это существенно ускорит выполнение операции и снизит потребление памяти сервера.