Как вывести одну картинку для нескольких строк характеристик в отчете СКД?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Способ 1. Программная постобработка сформированного табличного документа (в два прохода)

Наиболее гибким и универсальным подходом является использование постобработки результирующего табличного документа. Проанализируем алгоритм работы в два прохода:

  1. Первый проход: СКД стандартно выводит строки отчета в ТабличныйДокумент. В строках выводятся характеристики и служебная информация о номенклатуре (или ссылка на нее в расшифровке ячейки).
  2. Второй проход: программным кодом выполняется обход строк готового документа, определяются границы строк для каждого товара, выполняется объединение нужных ячеек в колонке картинок и выполняется размещение объекта РисунокТабличногоДокумента.

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


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    // 1. Стандартная инициализация компоновки
    Настройки = КомпоновщикНастроек.ПолучитьНастройки();
    
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
    
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки, Истина);
    
    ДокументРезультат.Очистить();
    ДокументРезультат.Рисунки.Очистить();
    
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);
    
    // 2. Постобработка: объединение ячеек и привязка картинок
    ОбработатьВыводКартинок(ДокументРезультат);
    
КонецПроцедуры

Теперь подробно разберем саму процедуру постобработки ОбработатьВыводКартинок. Выясним, как динамически определять начальные и конечные строки для каждого товара:


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

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


Процедура РазместитьКартинкуВОбласти(ДокументРезультат, СтрНач, СтрКон, Колонка, НоменклатураСсылка, КэшКартинок)
    
    // 1. Формируем область и объединяем ячейки
    ОбластьДляКартинки = ДокументРезультат.Область(СтрНач, Колонка, СтрКон, Колонка);
    ОбластьДляКартинки.Объединить();
    
    // 2. Получаем двоичные данные изображения (с кэшированием)
    ДвоичныеДанные = КэшКартинок[НоменклатураСсылка];
    Если ДвоичныеДанные = Неопределено Тогда
        ДвоичныеДанные = ПолучитьДвоичныеДанныеКартинки(НоменклатураСсылка);
        КэшКартинок.Вставить(НоменклатураСсылка, ДвоичныеДанные);
    КонецЕсли;
    
    Если ДвоичныеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // 3. Создаем и настраиваем объект РисунокТабличногоДокумента
    Рис = ДокументРезультат.Рисунки.Добавить(ТипРисункаТабличногоДокумента.Картинка);
    Рис.Картинка = Новый Картинка(ДвоичныеДанные);
    
    // Важно: Пропорционально вписывает картинку без искажения пропорций
    Рис.РазмерКартинки = РазмерКартинки.Пропорционально;
    
    // Убираем рамку вокруг рисунка при необходимости
    Рис.Линия = Новый Линия(ТипЛинииРисункаТабличногоДокумента.НетЛинии);
    
    // 4. Размещаем рисунок поверх объединенной области
    Рис.Расположить(ОбластьДляКартинки);
    
КонецПроцедуры

Способ 2. Использование событий процессора компоновки данных

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

Разберем по шагам, как использовать свойства ТипЭлементаРезультатаКомпоновкиДанных:


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

Способ 3. Изменение структуры настроек СКД (Без программного кода)

Если требования заказчика допускают иерархическое отображение, рассмотрим чисто декларативный способ настройки структуры отчета в схеме компоновки:

  1. В настройках отчета создаем группировку верхнего уровня: Номенклатура.
  2. В выбранных полях группировки «Номенклатура» указываем поле картинки и наименование товара. В свойствах расположения полей задаем Отдельно.
  3. Внутрь группировки «Номенклатура» добавляем подчиненную группировку: Характеристика (или детальные записи).

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

Важные рекомендации по оптимизации работы с изображениями

При выводе графики в отчеты обязательно учитываем следующие моменты:

  1. Очистка коллекции рисунков: перед повторным формированием всегда вызываем ДокументРезультат.Рисунки.Очистить(), иначе старые рисунки останутся в памяти и наложатся друг на друга.
  2. Пропорции и масштабирование: всегда устанавливаем свойство Рис.РазмерКартинки = РазмерКартинки.Пропорционально, чтобы изображения не искажались при изменении высоты строк объединенной области.
  3. Сжатие и генерация превью: не выводим в отчет исходные фотографии высокого разрешения. Сжимаем их на стороне сервера до размера миниатюры перед созданием объекта Картинка, чтобы избежать переполнения оперативной памяти сеанса и зависания отчета.
← На главную