Как в отчете 1С без СКД вывести данные в шахматную таблицу с колонками по месяцам и строками по номенклатуре

Программист 1С v8.3 (Обычные формы) Бухгалтерский учет Промышленность, строительство и АПК
← На главную

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

Оптимизация и исправление исходного запроса к регистру бухгалтерии

Прежде чем приступать к выводу данных, проанализируем текст запроса к виртуальной таблице РегистрБухгалтерии.Хозрасчетный.Обороты. В исходном варианте присутствуют типичные архитектурные ошибки, замедляющие работу и искажающие итоги.

Во-первых, числовые поля КоличествоКорОборот и КоличествоКорОборотДт ошибочно помещены в секцию группировки предложения ИТОГИ ПО. Из-за этого платформа пытается группировать строки по уникальным числовым значениям, а не суммировать их. Числовые ресурсы необходимо оборачивать в агрегатную функцию СУММА().

Во-вторых, фильтрация по счетам наложена в блоке ГДЕ. Для виртуальных таблиц это приводит к считыванию избыточного объема записей из базы данных. Условия по счетам и субконто всегда следует передавать непосредственно в параметры самой виртуальной таблицы.

Посмотрим на исправленный вариант запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|	ХозрасчетныйОбороты.Период КАК Период,
|	ХозрасчетныйОбороты.КорСубконто1 КАК Номенклатура,
|	ХозрасчетныйОбороты.КоличествоКорОборотДт КАК Количество
|ИЗ
|	РегистрБухгалтерии.Хозрасчетный.Обороты(
|			&ДатаНачала,
|			&ДатаОкончания,
|			Месяц,
|			Счет В ИЕРАРХИИ (&Счет),
|			,
|			КорСчет В ИЕРАРХИИ (&КорСчет),
|			,
|			) КАК ХозрасчетныйОбороты
|ИТОГИ
|	СУММА(Количество)
|ПО
|	ОБЩИЕ,
|	Номенклатура,
|	Период ПЕРИОДАМИ(МЕСЯЦ, &ДатаНачала, &ДатаОкончания)";

Запрос.УстановитьПараметр("ДатаНачала", НачалоДня(ДатаНачала));
Запрос.УстановитьПараметр("ДатаОкончания", КонецДня(ДатаОкончания));
Запрос.УстановитьПараметр("Счет", ПланыСчетов.Хозрасчетный.ОсновноеПроизводствоНеОблагаемоеЕНВД);
Запрос.УстановитьПараметр("КорСчет", ПланыСчетов.Хозрасчетный.СырьеИМатериалы);

РезультатЗапроса = Запрос.Выполнить();

Способ 1. Классический вывод в ТабличныйДокумент через секции «Вывести» и «Присоединить»

Рассмотрим базовый алгоритм ручного вывода кросс-таблицы. Для этого в макете отчета создадим следующие пересекающиеся области:

  1. Область шапки строки: Шапка|КолонкаНоменклатура
  2. Область шапки месяца: Шапка|КолонкаПериод
  3. Область строки данных: Строка|КолонкаНоменклатура
  4. Область ячейки данных на пересечении: Строка|КолонкаПериод

Для корректного выравнивания таблицы сначала получим массив всех месяцев периода, чтобы колонки формировались последовательно, даже если в конкретном месяце оборотов не было. Для быстрого поиска значений воспользуемся индексированной таблицей значений или структурой соответствия.

Разберем реализацию алгоритма в коде:


ТабДок = Новый ТабличныйДокумент;
Макет = ПолучитьМакет("Макет");

// Формируем массив всех месяцев периода
МассивМесяцев = Новый Массив;
ТекДата = НачалоМесяца(ДатаНачала);
Пока ТекДата <= ДатаОкончания Цикл
	МассивМесяцев.Добавить(ТекДата);
	ТекДата = ДобавитьМесяц(ТекДата, 1);
КонецЦикла;

// Выгружаем данные и создаем индекс для быстрого поиска
ТаблицаДанных = РезультатЗапроса.Выгрузить();
ТаблицаДанных.Индексы.Добавить("Номенклатура, Период");

// Получаем уникальный список номенклатуры
ТаблицаНоменклатуры = ТаблицаДанных.Скопировать(, "Номенклатура");
ТаблицаНоменклатуры.Свернуть("Номенклатура");
ТаблицаНоменклатуры.Сортировать("Номенклатура Возр");

// 1. Вывод шапки таблицы
ОблШапкаНоменклатура = Макет.ПолучитьОбласть("Шапка|КолонкаНоменклатура");
ОблШапкаПериод = Макет.ПолучитьОбласть("Шапка|КолонкаПериод");

ТабДок.Вывести(ОблШапкаНоменклатура);
Для Каждого МесяцКолонки Из МассивМесяцев Цикл
	ОблШапкаПериод.Параметры.Период = Формат(МесяцКолонки, "ДФ='ММММ гггг'");
	ТабДок.Присоединить(ОблШапкаПериод);
КонецЦикла;

// 2. Вывод строк данных
ОблСтрокаНоменклатура = Макет.ПолучитьОбласть("Строка|КолонкаНоменклатура");
ОблСтрокаПериод = Макет.ПолучитьОбласть("Строка|КолонкаПериод");

Отбор = Новый Структура("Номенклатура, Период");

Для Каждого СтрНоменклатура Из ТаблицаНоменклатуры Цикл
	ОблСтрокаНоменклатура.Параметры.Номенклатура = СтрНоменклатура.Номенклатура;
	ТабДок.Вывести(ОблСтрокаНоменклатура);
	
	Отбор.Номенклатура = СтрНоменклатура.Номенклатура;
	
	Для Каждого МесяцКолонки Из МассивМесяцев Цикл
		Отбор.Период = МесяцКолонки;
		НайденныеСтроки = ТаблицаДанных.НайтиСтроки(Отбор);
		
		Если НайденныеСтроки.Количество() > 0 Тогда
			ОблСтрокаПериод.Параметры.Количество = НайденныеСтроки[0].Количество;
		Иначе
			ОблСтрокаПериод.Параметры.Количество = 0;
		КонецЕсли;
		
		ТабДок.Присоединить(ОблСтрокаПериод);
	КонецЦикла;
КонецЦикла;

Способ 2. Использование объекта «СводнаяТаблица»

Если отчет разрабатывается в интерфейсе обычных форм, можно использовать встроенный визуальный объект СводнаяТаблица. Он позволяет автоматически разворачивать строки и колонки, а также рассчитывать итоги без ручной отрисовки областей макета.

Посмотрим на пример заполнения поля табличного документа через встроенную сводную таблицу:


// В запросе обязательно должны быть определены итоги по группировкам
Результат = Запрос.Выполнить();

СводТаб = ЭлементыФормы.ПолеТабличногоДокумента.ВстроенныеТаблицы.СводнаяТаблица;
СводТаб.ИсточникДанных = Результат;
СводТаб.ОтображатьЗаголовок = Ложь;

// Настройка измерений
СводТаб.Строки.Очистить();
СводТаб.Колонки.Очистить();
СводТаб.Данные.Очистить();

СводТаб.Строки.Добавить("Номенклатура");
СводТаб.Колонки.Добавить("Период");
СводТаб.Данные.Добавить("Количество");

Способ 3. Построитель отчета

Еще один эффективный инструмент платформы для вывода шахматных отчетов — ПостроительОтчета. Он умеет принимать в качестве источника данных результат запроса или таблицу значений и автоматически генерировать кросс-таблицу с итогами в табличный документ.

Разберем по шагам программную настройку построителя:


Построитель = Новый ПостроительОтчета;
Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(РезультатЗапроса);

// Настраиваем строки, колонки и ресурсы
Построитель.ИзмеренияСтроки.Очистить();
Построитель.ИзмеренияСтроки.Добавить("Номенклатура");

Построитель.ИзмеренияКолонки.Очистить();
Построитель.ИзмеренияКолонки.Добавить("Период");

Построитель.ВыбранныеПоля.Очистить();
Построитель.ВыбранныеПоля.Добавить("Количество");

// Формируем результат в табличный документ
ТабДок = Новый ТабличныйДокумент;
Построитель.Вывести(ТабДок);

Способ 4. Программная передача ТаблицыЗначений во внешний набор данных СКД

Если причина отказа от схемы компоновки данных заключалась только в сложности выборки исходных показателей, оптимальным компромиссом является передача программно подготовленной таблицы значений в СКД в качестве внешнего набора данных. В таком случае программист сохраняет полный контроль над логикой расчетов в коде 1С, а отрисовку матрицы колонок, строк и итогов передает ядру компоновки.

  1. В схеме компоновки данных создаем Набор данных - объект с именем ТаблицаОборотов.
  2. Добавляем поля: Номенклатура, Период, Количество.
  3. В настройках схемы создаем структуру: группировка строк по Номенклатура, группировка колонок по Период (с типом дополнения периода «Месяц»), в выбранные поля добавляем ресурс Количество.
  4. Передаем сформированную в коде таблицу значений в процессор компоновки данных:

ВнешниеНаборы = Новый Структура("ТаблицаОборотов", ТаблицаДанных);

КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.Настройки);

ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборы);

ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ТабДок);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);

Таким образом, для реализации кросс-отчета без СКД наиболее гибким вариантом остается ручной обход с использованием ТабДок.Вывести() и ТабДок.Присоединить(). Для максимального сокращения объема кода в обычных формах рекомендуется использовать ПостроительОтчета или объект СводнаяТаблица.

← На главную