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

Ниже — оба способа, шаблон программного формирования, поэлементный вывод и пять примеров, которые я регулярно применяю в доработках.

Когда постобработка действительно нужна

Прежде чем писать код, проверьте, не решается ли задача настройками: условное оформление, параметры вывода на закладке «Другие настройки», макеты полей и группировок в схеме. Постобработка — это код, который придётся поддерживать, и он ломается при изменении структуры отчёта.

ЗадачаБез кодаС постобработкой
Цвет и шрифт по условиюУсловное оформлениеНе нужна
Свой текст заголовка колонкиЗаголовок поля, макет поляТолько для нетиповых шапок
Картинки в ячейкахПоле-картинка в актуальных версиях платформыОсобое размещение, масштаб
Нумерация колонок под шапкойНетДа
Колонтитулы, повтор шапки при печатиНетДа
Разрыв страницы по группировкеНетДа

Два способа перехватить формирование отчёта

Способ 1: своя команда в форме отчёта

Стандартную кнопку «Сформировать» заменяют своей командой. В ней вызывают метод формы СкомпоноватьРезультат в режиме Непосредственно, а затем дорабатывают реквизит Результат.

Минусы: отчёту нужна своя форма, формирование идёт синхронно (без фонового режима), а постобработку почти всегда приходится уносить на сервер отдельным вызовом. Для разовой задачи сойдёт, для типового решения — нет.

Способ 2: обработчик ПриКомпоновкеРезультата

В модуле объекта отчёта есть предопределённый обработчик ПриКомпоновкеРезультата. Если в нём отключить стандартную обработку, вся компоновка выполняется вашим кодом на сервере, и результат можно доработать до того, как его увидит пользователь. Работает и с формой по умолчанию, и с фоновым формированием.

Постобработка отчёта СКД: данные проходят компоновку, затем табличный документ дорабатывается кодом
Постобработка отчёта СКД: данные проходят компоновку, затем табличный документ дорабатывается кодом

Шаблон программного формирования отчёта СКД

1С
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)

	СтандартнаяОбработка = Ложь;
	ДокументРезультат.Очистить();

	// Итоговые настройки: вариант + пользовательские + фиксированные.
	Настройки = КомпоновщикНастроек.ПолучитьНастройки();

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

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

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

	// Дальше — постобработка готового документа.
	ДобавитьНумерациюКолонок(ДокументРезультат);

КонецПроцедуры

Важные моменты:

  • ДанныеРасшифровки передавайте и в компоновщик макета, и в процессор — иначе пропадёт расшифровка по двойному клику.
  • Настройки берите через ПолучитьНастройки(), а не напрямую из КомпоновщикНастроек.Настройки, иначе пользовательские отборы не применятся.
  • Четвёртый параметр Инициализировать разрешает использовать в выражениях схемы функции общих модулей; если они не нужны, его можно не передавать.

Поэлементный вывод

Вместо Вывести можно выводить результат по элементам. Это позволяет вмешаться в процесс: пропустить элемент, вставить что-то между ними, посчитать свои итоги.

1С
	ПроцессорВывода.НачатьВывод();

	Пока Истина Цикл
		ЭлементРезультата = ПроцессорКомпоновки.Следующий();
		Если ЭлементРезультата = Неопределено Тогда
			Прервать;
		КонецЕсли;
		// Здесь можно анализировать ЭлементРезультата и выводить дополнительные области.
		ПроцессорВывода.ВывестиЭлемент(ЭлементРезультата);
	КонецЦикла;

	ПроцессорВывода.ЗакончитьВывод();

Поэлементный вывод сложнее в отладке: элементы — это фрагменты макета, а не строки отчёта. Я использую его, только когда постобработка готового документа не подходит.

Пример 1: картинки товаров в прайс-листе

Идея: в отчёт выводится поле со ссылкой на номенклатуру, после формирования проходим по строкам, через расшифровку достаём значение поля и вставляем рисунок в ячейку.

1С
// Вставляет картинки номенклатуры в указанную колонку отчёта.
Процедура ВставитьКартинкиНоменклатуры(ДокументРезультат, ДанныеРасшифровки, НомерКолонки, ВысотаШапки)

	Для НомерСтроки = ВысотаШапки + 1 По ДокументРезультат.ВысотаТаблицы Цикл

		Ячейка = ДокументРезультат.Область(НомерСтроки, НомерКолонки, НомерСтроки, НомерКолонки);
		Если ТипЗнч(Ячейка.Расшифровка) <> Тип("ИдентификаторРасшифровкиКомпоновкиДанных") Тогда
			Продолжить;
		КонецЕсли;

		ПоляРасшифровки = ДанныеРасшифровки.Элементы[Ячейка.Расшифровка].ПолучитьПоля();
		ПолеНоменклатуры = ПоляРасшифровки.Найти("Номенклатура");
		Если ПолеНоменклатуры = Неопределено Тогда
			Продолжить;
		КонецЕсли;

		ДанныеКартинки = ПолучитьДанныеКартинки(ПолеНоменклатуры.Значение); // своя функция
		Если ДанныеКартинки = Неопределено Тогда
			Продолжить;
		КонецЕсли;

		ДокументРезультат.Область(НомерСтроки, , НомерСтроки).ВысотаСтроки = 50;

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

	КонецЦикла;

КонецПроцедуры

Функция ПолучитьДанныеКартинки зависит от конфигурации: в типовых решениях картинка обычно хранится в присоединённом файле, и её двоичные данные получают средствами БСП. Не тяните картинки запросом в сам набор данных: тяжёлые двоичные данные замедлят компоновку.

Пример 2: нумерация колонок под шапкой

Под шапкой вставляем строку и пишем номера колонок, как в регламентных формах.

1С
// Добавляет под шапкой строку с номерами колонок.
Процедура ДобавитьНумерациюКолонок(ДокументРезультат)

	СтрокаШапки = ДокументРезультат.ФиксацияСверху;
	Если СтрокаШапки = 0 Тогда
		Возврат;
	КонецЕсли;

	ОбластьШапки = ДокументРезультат.Область(СтрокаШапки, , СтрокаШапки);
	ОбластьВставки = ДокументРезультат.Область(СтрокаШапки + 1, , СтрокаШапки + 1);
	ДокументРезультат.ВставитьОбласть(ОбластьШапки, ОбластьВставки,
		ТипСмещенияТабличногоДокумента.ПоВертикали, Ложь);

	Для НомерКолонки = 1 По ДокументРезультат.ШиринаТаблицы Цикл
		Ячейка = ДокументРезультат.Область(СтрокаШапки + 1, НомерКолонки);
		Ячейка.Текст = Формат(НомерКолонки, "ЧГ=0");
		Ячейка.ГоризонтальноеПоложение = ГоризонтальноеПоложение.Центр;
		Ячейка.Расшифровка = Неопределено;
	КонецЦикла;

	ДокументРезультат.ФиксацияСверху = СтрокаШапки + 1;

КонецПроцедуры

Копируем строку шапки, чтобы унаследовать оформление и рамки, затем заменяем текст номерами. Расшифровку в новой строке обнуляем, иначе двойной клик по номеру откроет расшифровку заголовка.

Пример 3: повтор шапки и колонтитулы при печати

Для печати длинных отчётов шапку повторяют на каждой странице и добавляют колонтитулы.

1С
// Настраивает печать: повтор шапки и верхний колонтитул.
Процедура НастроитьПечатьОтчета(ДокументРезультат, ЗаголовокОтчета)

	Если ДокументРезультат.ФиксацияСверху > 0 Тогда
		ДокументРезультат.ПовторятьПриПечатиСтроки =
			ДокументРезультат.Область(1, , ДокументРезультат.ФиксацияСверху);
	КонецЕсли;

	Колонтитул = ДокументРезультат.ВерхнийКолонтитул;
	Колонтитул.Выводить = Истина;
	Колонтитул.НачальнаяСтраница = 2;
	Колонтитул.ТекстСлева = ЗаголовокОтчета;
	Колонтитул.ТекстВЦентре = "Стр. [&НомерСтраницы] из [&СтраницВсего]";
	Колонтитул.ТекстСправа = "Напечатано: [&Дата] [&Время]";

КонецПроцедуры

НачальнаяСтраница = 2 убирает колонтитул с первой страницы, где и так есть заголовок отчёта.

Пример 4: разрыв страницы по группировке

Типичная просьба бухгалтерии: каждый контрагент — с новой страницы. Надёжный способ без разбора макетов — сформировать отчёт во временный документ, найти строки, где начинается новая запись верхней группировки (через расшифровку, как в примере 1), а затем собрать итоговый документ по кускам:

  1. Вывести в итоговый документ шапку: ДокументРезультат.Вывести(Временный.ПолучитьОбласть(1, , ВысотаШапки)).
  2. Для каждого блока группировки вывести его область и после неё вызвать ДокументРезультат.ВывестиГоризонтальныйРазделительСтраниц().
  3. Восстановить ФиксацияСверху и параметры печати.

Расшифровка при копировании областей сохраняется, потому что идентификаторы ссылаются на те же ДанныеРасшифровки.

Постобработка отчёта СКД: разрывы страниц по группировке и повтор шапки на каждой странице
Постобработка отчёта СКД: разрывы страниц по группировке и повтор шапки на каждой странице

Пример 5: заголовки группировок колонок

Когда в таблице несколько уровней группировок колонок, СКД выводит заголовки так, как умеет: с дублями или с лишними строками. Часть проблем снимает параметр «Расположение полей группировок» на вложенных группировках колонок. Если и этого мало, после формирования объединяют ячейки шапки (Область(...).Объединить()), переписывают тексты и удаляют пустые строки шапки. Код здесь всегда привязан к конкретной структуре варианта, поэтому я фиксирую такой вариант как предопределённый и запрещаю пользователю менять структуру.

Типичные ошибки

  • Забыли СтандартнаяОбработка = Ложь. Отчёт формируется дважды или ваш результат затирается стандартным.
  • Берут КомпоновщикНастроек.Настройки. Пользовательские и фиксированные отборы игнорируются.
  • Жёсткие номера строк и колонок. Пользователь добавил поле — постобработка вставила картинку не туда. Ищите колонки по расшифровке или по тексту шапки.
  • Постобработка на клиенте. Работа с табличным документом на клиенте по ячейкам медленная и неудобная. Всё — на сервере.
  • Тяжёлые данные в наборе. Картинки и большие тексты тянут отдельно, только для выведенных строк.
Постобработка хороша, когда она тонкая: несколько точечных правок поверх отчёта, который корректно работает и без неё.

Если в отчётах накопилось много постобработки и они стали хрупкими, это повод пересобрать их по уму — в рамках разработки отчётов на СКД или доработки печатных форм 1С.

Коротко

  • Постобработка — доработка готового табличного документа, для неё отчёт формируется программно.
  • Основное место для кода — ПриКомпоновкеРезультата с СтандартнаяОбработка = Ложь.
  • Настройки берите через ПолучитьНастройки(), ДанныеРасшифровки передавайте в компоновщик и процессор.
  • Типовые задачи: картинки, нумерация колонок, колонтитулы, разрывы страниц, сложные заголовки.
  • Ищите колонки и строки по расшифровке, а не по жёстким номерам.