Когда запрос в СКД перестает "помещаться в голове" становится слишком громоздким (ЗиУП конечно не в счет) и ситуацию не получается исправить различными фичами самой СКД, то можно использовать данный метод.
Сразу оговорюсь: иногда представленную методику можно использовать даже для простых случаев, с целью ускорения выполнения формирования отчета (это касается, например, случаев многкратного соединения больших объемов данных в запросе).
Общая идея:
1. Получаем необходимые данные при помощи первой схемы компоновки данных (можно конечно получить данные обычным запросом, но тогда нам самим придется описывать все поля условий, ресурсы и т.д., а также организовывать ввод пользовательских данных). В качестве данной СКД можно использовать основную схему компоновки данных отчета.
2. Обрабатываем полученную на первом этапе выборку используя язык программирования 1С (это обычно нагляднее чем сделать тоже самое, например, в запросе, к тому же в этом случае можно снабдить текст комментариями, не боясь того что их "проглотит" конструктор запросов).
3. Полученные на втором этапе данные - выводим при помощи другой схемы компоновки данных, используя всю (почти всю) мощь СКД в части вывода в табличный документ.
Проиллюстрируем на примере содания отчета ABC анализ номенклатуры, клиентов, уже построенного по этой методике:
1. Данные получаем при помощи СКД запросом к регистру Выручка и себестоимость продаж, будем использовать два варианта настройки (группировка по Номенклатуре и группировка по Клиентам)
2. Производим ABC анализ по методу, реализованонму в ТиС 7.7, и классическому методу ABC анализа
3. Выводим результаты используя другую СКД.
Начнем пример:
1. Создаем новый внешний отчет, открываем основную схему комноновки данных, и формируем СКД для первоначальной выборки данных:
Здесь намеренно используется простой (неоптимизированный) запрос:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
ВИСПОбороты.КоличествоОборот КАК Количество,
ВИСПОбороты.СуммаВыручкиОборот КАК Выручка,
ВИСПОбороты.СебестоимостьОборот + ВИСПОбороты.СуммаДополнительныхРасходовОборот КАК СебестоимостьСДР,
ВИСПОбороты.АналитикаУчетаНоменклатуры.Номенклатура КАК Номенклатура,
ВИСПОбороты.АналитикаУчетаНоменклатуры.Склад КАК Склад, ВИСПОбороты.СуммаВыручкиОборот - ВИСПОбороты.СебестоимостьОборот - ВИСПОбороты.СуммаДополнительныхРасходовОборот КАК Прибыль,
ВЫБОР
КОГДА ВИСПОбороты.СебестоимостьОборот + ВИСПОбороты.СуммаДополнительныхРасходовОборот = 0
ТОГДА 0
ИНАЧЕ 100 * (ВИСПОбороты.СуммаВыручкиОборот - ВИСПОбороты.СебестоимостьОборот - ВИСПОбороты.СуммаДополнительныхРасходовОборот)
/ (ВИСПОбороты.СебестоимостьОборот + ВИСПОбороты.СуммаДополнительныхРасходовОборот)
КОНЕЦ КАК Прибыльность, ВИСПОбороты.АналитикаУчетаПоПартнерам.Партнер КАК Партнер,
ВИСПОбороты.АналитикаУчетаПоПартнерам.Организация КАК Организация,
ВИСПОбороты.АналитикаУчетаПоПартнерам.Контрагент КАК Контрагент
ИЗ РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(&НачалоПериода, &КонецПериода, Авто,
НЕ АналитикаУчетаПоПартнерам.Партнер = ЗНАЧЕНИЕ(Справочник.Партнеры.НашеПредприятие)) КАК ВИСПОбороты
Ресурсы:
Параметры:
Настройки:
Т.е. построена обычная СКД для выборки данных.
2. Добавляем основную форму отчета (удаляем с нее все элементы (это важно!)), и заново добавляем на нее Пользователские настройки компоновщика настроек отчета:
(если не отображаются названия колонок этой таблицы - включаем их отображение).
Наполняем содержимым форму (блок ABC):
Добавляем команду Сформировать и формируем соотвествующую кнопку.
3. Создаем СКД для вывода информации в табличный документ (добавляем соответствующий макет), описание полей:
Вычисляемые поля:
Ресурсы:
Вариант настроек БезГрупп:
Вариант настроек Классы:
Т.е. это тоже вполне обычная СКД, которая "заточена" под конкретные колонки таблицы значений, которую мы будем в нее передавать.
3. Наполняем содержимым команду Сформировать:
&НаКлиенте
Процедура Сформировать(Команда)
СформироватьНаСервере();
КонецПроцедуры
&НаСервере
Процедура СформироватьНаСервере()
ОтчетОбъект = ДанныеФормыВЗначение(Отчет, Тип("ОтчетОбъект"));
//Первый этап: получаем данные при помощи СКД
СКД = ОтчетОбъект.СхемаКомпоновкиДанных;
ПользовательскиеНастройки = Отчет.КомпоновщикНастроек.ПользовательскиеНастройки; //Используем пользовательские настройки с формы отчета;
Отчет.КомпоновщикНастроек.ЗагрузитьНастройки(СКД.ВариантыНастроек[ОбъектАнализа].Настройки); //Используем нужный вариант
Настройки = Отчет.КомпоновщикНастроек.Настройки;// Тут уже готовый вариант настроек
//При необходимости корректируем поле группировки в соответствии с настройками
//Это нужно когда выбрано значение СвойстоОбъектаАнализа
//и анализ производится на основании значения реквизита соответствующего справочника
Если ЗначениеЗаполнено(СвойствоОбъектаАнализа) Тогда
ЭлементыПолейГруппировки = Настройки.Структура[0].ПоляГруппировки.Элементы;
ЭлементыПолейГруппировки.Очистить();
НовыйЭлементГруппировки = ЭлементыПолейГруппировки.Добавить(Тип("ПолеГруппировкиКомпоновкиДанных"));
НовыйЭлементГруппировки.Поле = Новый ПолеКомпоновкиДанных(?(ОбъектАнализа = "Клиенты", "Партнер", ОбъектАнализа) + "." + СвойствоОбъектаАнализа);
НовыйЭлементГруппировки.Использование = Истина;
КонецЕсли;
//Копируем пользовательские настройки в основные настройки отчета
Для Каждого ЭлементПользовательскихНастроек Из ПользовательскиеНастройки.Элементы Цикл
Если ТипЗнч(ЭлементПользовательскихНастроек) = Тип("ЗначениеПараметраНастроекКомпоновкиДанных") Тогда
ЭлементНастройки = Настройки.ПараметрыДанных.Элементы.Найти(ЭлементПользовательскихНастроек.Параметр);
ЗаполнитьЗначенияСвойств(ЭлементНастройки, ЭлементПользовательскихНастроек);
ИначеЕсли ТипЗнч(ЭлементПользовательскихНастроек) = Тип("ЭлементОтбораКомпоновкиДанных") Тогда
ЗаполнитьЭлементОтбораПоИД(Настройки.Отбор.Элементы, ЭлементПользовательскихНастроек); //Процедура определена в модуле формы
КонецЕсли;
КонецЦикла;
//^^^^^^ все готово, чтобы получить исходную выборку
//Следующие 8 строк - получение выборки в ТаблицуЗначений ТЗ:
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СКД, Настройки, , , Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, , );
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ТЗ = Новый ТаблицаЗначений;
ПроцессорВывода.УстановитьОбъект(ТЗ);
ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных, Ложь);
//Конец Первого этапа: Выборка уже есть в ТЗ
//Второй этап: Необходимые преобразования, для последующего вывода на экран
//Тут реализуется алгоритм расчета для ABC анализа, взятый из ТиС 7.7, и классический ABC анализ
//Здесь: Последняя сторка ТЗ - строка итогов, запомним числовые значения итогов в Структуре
Итоги = Новый Структура();
Для Каждого КолонкаТЗ Из ТЗ.Колонки Цикл
ЗначениеЭлементаТЗ = ТЗ[ТЗ.Количество() - 1][КолонкаТЗ.Имя];
Если Не ТипЗнч(ЗначениеЭлементаТЗ) = Тип("Число") Тогда Продолжить; КонецЕсли;
Итоги.Вставить(КолонкаТЗ.Имя, ЗначениеЭлементаТЗ);
КонецЦикла;
ТЗ.Удалить(ТЗ.Количество() - 1); //Удалим строку итогов из ТЗ
ТЗ.Колонки.Добавить("ПроцентОтОбщего"); //Добавим новые колонки в ТЗ
ТЗ.Колонки.Добавить("Объект");
ПоказательСтрокой = ?(Показатель = "Выручка по отгрузке", "Выручка", ?(Показатель = "Прибыль по отгрузке", "Прибыль", "Прибыльность"));
ОбъектАнализаСтрокой = ?(ОбъектАнализа = "Клиенты", "Партнер", ОбъектАнализа) + СокрЛП(СвойствоОбъектаАнализа);
Для Каждого СтрокаТЗ Из ТЗ Цикл
СтрокаТЗ.ПроцентОтОбщего = 100 * СтрокаТЗ[ПоказательСтрокой] / Итоги[ПоказательСтрокой];
СтрокаТЗ.Объект = СтрокаТЗ[ОбъектАнализаСтрокой];
КонецЦикла;
//Обработка
Если СпособАнализа = 0 Тогда
ПорядокСтрокой = ?(Порядок = "убывания", "Убыв", "Возр");
ТЗ.Сортировать(ПоказательСтрокой + " " + ПорядокСтрокой);
КоличествоНужных = Окр(ТЗ.Количество() * Мин(ПроцентОбъектов, 100) / 100);
Для Индекс = КоличествоНужных + 1 По ТЗ.Количество() - 1 Цикл
ТЗ[Индекс].Объект = "";
КонецЦикла;
ИначеЕсли СпособАнализа = 1 Тогда
ПорядокСтрокой = "Убыв";
ТЗ.Сортировать(ПоказательСтрокой + " " + ПорядокСтрокой);
ОставитьСумму = Итоги[ПоказательСтрокой] * Мин(ПроцентСумм, 100) / 100;
НакопленоСумма = 0;
Для Каждого СтрокаТЗ Из ТЗ Цикл
Если НакопленоСумма > ОставитьСумму Тогда
СтрокаТЗ.Объект = "";
КонецЕсли;
НакопленоСумма = НакопленоСумма + СтрокаТЗ[ПоказательСтрокой];
КонецЦикла;
Иначе
ПорядокСтрокой = "Убыв";
ТЗ.Сортировать(ПоказательСтрокой + " " + ПорядокСтрокой);
ТЗ.Колонки.Добавить("Класс");
НакопленоСумма = 0;
СуммаA = Итоги[ПоказательСтрокой] * ПроцентA / 100;
СуммаAB = СуммаA + Итоги[ПоказательСтрокой] * ПроцентB / 100;
СуммаABC = СуммаAB + Итоги[ПоказательСтрокой] * ПроцентC / 100; //Все остальное D
ТекущийКласс = "A";
Для Каждого СтрокаТЗ Из ТЗ Цикл
СтрокаТЗ.Класс = ТекущийКласс;
НакопленоСумма = НакопленоСумма + СтрокаТЗ[ПоказательСтрокой];
Если ТекущийКласс = "A" И НакопленоСумма > СуммаA Тогда ТекущийКласс = "B";
ИначеЕсли ТекущийКласс = "B" И НакопленоСумма > СуммаAB Тогда ТекущийКласс = "C";
ИначеЕсли ТекущийКласс = "C" И НакопленоСумма > СуммаABC Тогда ТекущийКласс = "D";
КонецЕсли;
КонецЦикла;
КонецЕсли;
//Конец Второго этапа: ТЗ преобразована и готова к выводу на экран.
//Третий этап: Вывод через СКД в табличный документ, расположенный на форме
Результат.Очистить(); //Чистим Табличный документ от старых данных
ВнешниеНаборыДанных = Новый Структура("ТЗ", ТЗ); //Готовим внешний набор который передадим СКД
СКДВ = ОтчетОбъект.ПолучитьМакет("СКДДляВывода"); //Получаем СКД Для Вывода из макетов Отчета;
//Выбор нужного варианта настроек:
НастройкиВ = СКДВ.ВариантыНастроек[?(СпособАнализа = 2, "Классы", "БезГрупп")].Настройки;
//Настройка варианта графического отображения:
НастройкиВ.Структура[1].Использование = (Диаграмма = "Гистограмма");
НастройкиВ.Структура[1].Выбор.Элементы[0].Поле = Новый ПолеКомпоновкиДанных(ПоказательСтрокой);
НастройкиВ.Структура[2].Использование = (Диаграмма = "Круговая");
НастройкиВ.Структура[2].Выбор.Элементы[0].Поле = Новый ПолеКомпоновкиДанных(ПоказательСтрокой);
//Настройка сортировки:
ЭлементыСортировки = НастройкиВ.Порядок.Элементы;
ЭлементыСортировки.Очистить();
ЭлементСортировки = ЭлементыСортировки.Добавить(Тип("ЭлементПорядкаКомпоновкиДанных"));
ЭлементСортировки.Поле = Новый ПолеКомпоновкиДанных(ПоказательСтрокой);
ЭлементСортировки.ТипУпорядочивания = НаправлениеСортировкиКомпоновкиДанных[ПорядокСтрокой];
ЭлементСортировки.Использование = Истина;
//Чтобы в итоговом отчете была Расшифровка, придется предпринять ряд шагов:
ДанныеРасшифровкиКомпоновкиДанныхВ = Новый ДанныеРасшифровкиКомпоновкиДанных; //Сюда будут складываться данные для расшифровки
КомпоновщикМакетаВ = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновкиВ = КомпоновщикМакетаВ.Выполнить(СКДВ, НастройкиВ, ДанныеРасшифровкиКомпоновкиДанныхВ); //не забываем: данные для расшифровки
ПроцессорКомпоновкиДанныхВ = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанныхВ.Инициализировать(МакетКомпоновкиВ, ВнешниеНаборыДанных, ДанныеРасшифровкиКомпоновкиДанныхВ, Истина);
//^^^^ Предпоследний параметр: данные для расшифровки
//^^^^Последний параметр - возможность использования внешних функций в компоновке, хотя они и не используются
//Следующие 4 строки - вывод ТЗ в табличный документ:
ПроцессорВыводаВ = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВыводаВ.УстановитьДокумент(Результат);
ПроцессорВыводаВ.ОтображатьПроцентВывода = Истина;
ПроцессорВыводаВ.Вывести(ПроцессорКомпоновкиДанныхВ);
//Передача через элемент формы, т.е. запоминаем адрес хранилища в параметре формы ДанныеРасшифровки
ДанныеРасшифровки = ПоместитьВоВременноеХранилище(ДанныеРасшифровкиКомпоновкиДанныхВ, ЭтаФорма.УникальныйИдентификатор);
//Избавление от надписи... скорее всего можно побороть как-то еще.
ОтображениеСостояния = ЭтаФорма.Элементы.Результат.ОтображениеСостояния;
ОтображениеСостояния.Видимость = Ложь;
ОтображениеСостояния.ДополнительныйРежимОтображения = ДополнительныйРежимОтображения.НеИспользовать;
//Установка текущей страницы
Элементы.Страницы.ТекущаяСтраница = Элементы.Результаты;
//Конец третьего этапа, Табличный документ заполнен результатами расчета
КонецПроцедуры
Это все. Тут не описано некоторое количество вспомогательных функций, которые можно увидеть непосредствено в реализации.