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