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