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