Как вывести изображения товаров с учетом характеристик (цвета) в произвольном отчете 1С:Розница на СКД?

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

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

Рассмотрим подробнее, как мы можем решить эту задачу, комбинируя программные подходы и механизмы СКД.

Основы работы с СКД и Табличным Документом для вывода изображений

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

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

Идентификация данных для картинки: Номенклатура и Характеристика (Цвет)

Одной из главных сложностей в этой задаче является определение, к какому товару и какой характеристике (например, цвету) относится текущая строка или ячейка отчета. Произвольный отчет подразумевает, что пользователь может изменять его структуру, добавлять или удалять колонки, группировки. В такой ситуации простой обход по индексам колонок становится ненадежным. Здесь на помощь приходит механизм расшифровки.

  1. Использование расшифровки ячеек: Каждая ячейка в табличном документе, содержащая ссылочные данные (например, ссылку на элемент справочника Номенклатура), может иметь связанные с ней данные расшифровки. Эти данные позволяют получить исходный объект, который был выведен в ячейке. Для получения данных расшифровки мы можем использовать свойства и методы табличного документа, например, Область.Расшифровка. При обходе табличного документа мы можем проверять, содержит ли текущая ячейка расшифровку, и если да, то извлекать из нее необходимую информацию.
  2. Получение данных из расшифровки: После получения идентификатора расшифровки (например, Область.Расшифровка), мы можем использовать объект ДанныеРасшифровки (который хранится во временном хранилище, если речь идет об управляемых формах) для получения конкретных полей и их значений, таких как ссылка на Номенклатуру или значение Характеристики. Это позволяет нам точно определить, какой товар и с какой характеристикой находится в данной строке отчета, независимо от ее визуального расположения.
  3. Определение цвета из характеристики: Если цвет является характеристикой номенклатуры, то в запросе СКД мы должны предусмотреть вывод этой характеристики. В конструкторе запросов СКД на закладке "Характеристики" мы можем указать таблицу, откуда берутся значения характеристик, и связать их с номенклатурой. После того как характеристика "Цвет" будет выведена в отчет (явно или неявно через данные расшифровки), мы сможем получить ее значение. Например, из расшифровки мы получаем ссылку на объект Номенклатура и значение Характеристики, которое в нашем случае является ссылкой на элемент справочника "ЗначенияСвойствОбъектов" или "ВидыНоменклатуры". Из этого элемента мы уже можем получить наименование цвета.
  4. Анализ структуры отчета: Как альтернатива или дополнение к расшифровке, мы можем анализировать структуру отчета через компоновщик макета (КомпоновщикМакета). Это позволяет программно определить, какие поля были выведены в какие колонки и группировки, что может быть полезно для более сложной логики размещения изображений, но часто менее гибко при произвольных изменениях отчета пользователем.

Получение и вставка картинки в Табличный Документ

Когда мы определили, для какой номенклатуры и какого цвета нам нужна картинка, следующим шагом будет ее получение и вставка в табличный документ.

  1. Источники картинок:
    • Типовые реквизиты: В многих конфигурациях 1С у справочника Номенклатура есть реквизит ФайлКартинки или аналогичный, который ссылается на присоединенные файлы (например, из справочника НоменклатураПрисоединенныеФайлы).
    • Внешние источники: Как упомянуто в исходной теме, картинки могут браться с сайта. В этом случае потребуется использовать HTTP-запросы для загрузки двоичных данных изображения по URL, который формируется на основе артикула и цвета (например, "сайт.ру/images/" + Артикул + "_" + Цвет + ".jpg").
    • Хранилище двоичных данных: Двоичные данные картинок могут храниться в регистрах сведений или других таблицах в формате ХранилищеЗначения.
  2. Работа с объектом РисунокТабличногоДокумента: Для вставки изображения в табличный документ используется объект РисунокТабличногоДокумента. Мы создаем новый экземпляр этого объекта, устанавливаем его свойство Картинка и позиционируем его в нужной области.
  3. Получение объекта Картинка: Объект Картинка может быть создан из двоичных данных (полученных из файла, из базы данных или по HTTP) с помощью метода Новый Картинка(<ДвоичныеДанные>) или из общих картинок конфигурации.
  4. Размещение картинки: Метод Расположить() объекта РисунокТабличногоДокумента позволяет указать область ячеек, в которой будет размещено изображение. Мы также можем настраивать его размер, пропорции, привязку к ячейкам и другие свойства.

Пошаговая реализация

Рассмотрим по шагам, как можно реализовать эту функциональность.

Шаг 1: Подготовка запроса СКД

Прежде всего, в схеме компоновки данных (СКД) необходимо убедиться, что в запрос включены следующие поля:

  1. Номенклатура: Ссылка на элемент справочника Номенклатура (или его код/артикул, если он уникален и используется для формирования URL картинки).
  2. Характеристика (Цвет): Ссылка на значение характеристики "Цвет" или его наименование (если характеристика предопределена и не меняется). Если характеристики хранятся в отдельных регистрах сведений, их нужно присоединить к запросу по Номенклатуре.

Пример фрагмента запроса (псевдокод):


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура,
    Номенклатура.Артикул КАК Артикул,
    ЗначенияСвойствОбъектов.Значение КАК Цвет
ИЗ
    Справочник.Номенклатура КАК Номенклатура
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
        ПО Номенклатура.Ссылка = ЗначенияСвойствОбъектов.Объект
        И ЗначенияСвойствОбъектов.Свойство = &ВидСвойстваЦвет

Здесь &ВидСвойстваЦвет – это параметр, который будет содержать ссылку на элемент справочника "ВидыСвойствОбъектов" с наименованием "Цвет".

Шаг 2: Модификация модуля отчета

Откроем модуль объекта нашего отчета СКД и найдем процедуру ПриКомпоновкеРезультата().


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

    // Отменяем стандартную обработку, чтобы вручную сформировать или модифицировать отчет
    СтандартнаяОбработка = Ложь;

    // Сначала формируем стандартный отчет, чтобы получить его структуру и данные
    // Можно использовать КомпоновщикМакета и ПроцессорКомпоновки,
    // или же позволить СКД скомпоновать, а потом модифицировать.
    // Для простоты, здесь мы предполагаем, что СКД уже скомпоновала ДокументРезультат
    // (если не устанавливать СтандартнаяОбработка = Ложь сразу, а только после получения результата).
    // Более правильный подход:

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

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

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

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

    // Теперь ДокументРезультат содержит отчет, сгенерированный СКД.
    // Мы можем его обходить и добавлять картинки.

    // Вызовем нашу функцию для добавления картинок
    ДобавитьКартинкиВОтчет(ДокументРезультат, ДанныеРасшифровки, КомпоновщикНастроек.Настройки);

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

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

Шаг 3: Обход табличного документа и анализ расшифровки

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


// Вспомогательная процедура для добавления картинок в табличный документ
Процедура ДобавитьКартинкиВОтчет(ТабДок, ДанныеРасшифровки, НастройкиОтчета)

    // Проверяем, включена ли опция "с картинками"
    Если Не НастройкиОтчета.ДополнительныеСвойства.Свойство("ВыводитьКартинки") ИЛИ 
       Не НастройкиОтчета.ДополнительныеСвойства.ВыводитьКартинки Тогда
        Возврат; // Если опция не включена, выходим
    КонецЕсли;

    Для Каждого ОбластьСтрок ИЗ ТабДок.Области Цикл
        // Проверяем, является ли текущая область строкой или группировкой
        Если ОбластьСтрок.ТипОбласти <> ТипОбластиТабличногоДокумента.Строка Тогда
            Продолжить;
        КонецЕсли;

        // Обходим ячейки в текущей строке
        Для ИндексКолонки = 0 ПО ОбластьСтрок.Ширина - 1 Цикл
            Ячейка = ОбластьСтрок.ПолучитьОбласть(ОбластьСтрок.Верх + 1, ОбластьСтрок.Лево + ИндексКолонки + 1,
                                                ОбластьСтрок.Верх + 1, ОбластьСтрок.Лево + ИндексКолонки + 1);

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

                // Получаем поля из элемента расшифровки
                ПоляРасшифровки = ЭлементРасшифровки.ПолучитьПоля();
                Если ПоляРасшифровки.Количество() = 0 Тогда
                    Продолжить;
                КонецЕсли;

                // Ищем Номенклатуру и Цвет среди полей расшифровки
                Номенклатура = Неопределено;
                ЦветНаименование = Неопределено;

                Для Каждого ПолеРасшифровки ИЗ ПоляРасшифровки Цикл
                    Если ПолеРасшифровки.Поле = "Номенклатура" Тогда // Имя поля, которое мы вывели в СКД
                        Номенклатура = ПолеРасшифровки.Значение;
                    ИначеЕсли ПолеРасшифровки.Поле = "Цвет" Тогда // Имя поля для характеристики "Цвет"
                        ЦветНаименование = ПолеРасшифровки.Значение;
                    КонецЕсли;
                КонецЦикла;

                Если Номенклатура <> Неопределено И ЦветНаименование <> Неопределено Тогда
                    // Здесь мы нашли Номенклатуру и Цвет.
                    // Теперь нужно получить картинку и вставить ее.
                    ВставитьКартинкуВЯчейку(ТабДок, Ячейка, Номенклатура, ЦветНаименование);
                КонецЕсли;
            КонецЕсли;
        КонецЦикла;
    КонецЦикла;

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

Шаг 4: Получение двоичных данных картинки

Создадим функцию, которая будет получать двоичные данные картинки на основе номенклатуры и цвета. Этот пример покажет получение из внешней ссылки.


// Вспомогательная функция для получения двоичных данных картинки
Функция ПолучитьДвоичныеДанныеКартинки(НоменклатураСсылка, ЦветНаименование)

    // Формируем URL для картинки (пример: Артикул + _ + Цвет.jpg)
    Артикул = НоменклатураСсылка.Артикул;
    Если ЗначениеЗаполнено(Артикул) И ЗначениеЗаполнено(ЦветНаименование) Тогда
        // Пример формирования URL с внешнего сайта
        URLКартинки = "https://example.com/images/" + СокрЛП(Артикул) + "_" + СокрЛП(ЦветНаименование) + ".jpg";

        Попытка
            HTTPЗапрос = Новый HTTPЗапрос(URLКартинки);
            HTTPСоединение = Новый HTTPСоединение("example.com"); // Указываем домен сайта

            HTTPОтвет = HTTPСоединение.Получить(HTTPЗапрос);

            Если HTTPОтвет.КодСостояния = 200 Тогда // Успешный ответ
                Возврат HTTPОтвет.ПолучитьТелоКакДвоичныеДанные();
            Иначе
                ЗаписатьЖурналРегистрации("Не удалось получить картинку: " + URLКартинки + ". Код состояния: " + HTTPОтвет.КодСостояния, УровеньЖурналаРегистрации.Ошибка);
            КонецЕсли;
        Исключение
            ЗаписатьЖурналРегистрации("Ошибка при получении картинки: " + ОписаниеОшибки(), УровеньЖурналаРегистрации.Ошибка);
        КонецПопытки;
    КонецЕсли;

    Возврат Неопределено;

КонецФункции

// Пример для получения из справочника присоединенных файлов (НоменклатураПрисоединенныеФайлы)
Функция ПолучитьДвоичныеДанныеКартинкиИзБазы(НоменклатураСсылка, ЦветСсылка = Неопределено)
    // Здесь нужна более сложная логика, т.к. в типовых нет прямого связывания "картинка + характеристика".
    // Обычно основная картинка хранится в НоменклатураПрисоединенныеФайлы для самой Номенклатуры.
    // Если есть отдельные картинки для характеристик, их придется хранить в отдельном регистре
    // с привязкой к Номенклатуре и Характеристике.

    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |    Файлы.ДвоичныеДанныеФайла КАК ДвоичныеДанныеФайла
    |ИЗ
    |    Справочник.НоменклатураПрисоединенныеФайлы КАК Файлы
    |ГДЕ
    |    Файлы.ВладелецФайла = &НоменклатураСсылка
    |    И Файлы.ПометкаУдаления = ЛОЖЬ
    |    И Файлы.ЭтоКартинка = ИСТИНА
    |{ГДЕ
    |    Файлы.Описание = &ОписаниеЦвета}"; // Можно использовать поле "Описание" для указания цвета

    Запрос.УстановитьПараметр("НоменклатураСсылка", НоменклатураСсылка);
    Если ЗначениеЗаполнено(ЦветСсылка) Тогда
        // Если цвет тоже ссылка, используем ее.
        // Или если 'Описание' содержит наименование цвета.
        Запрос.УстановитьПараметр("ОписаниеЦвета", ЦветСсылка.Наименование);
    КонецЕсли;

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

    Возврат Неопределено;
КонецФункции

Шаг 5: Вставка картинки в табличный документ

И наконец, функция для вставки полученной картинки в отчет.


// Вспомогательная процедура для вставки картинки в ячейку
Процедура ВставитьКартинкуВЯчейку(ТабДок, Ячейка, Номенклатура, ЦветНаименование)

    ДвоичныеДанные = ПолучитьДвоичныеДанныеКартинки(Номенклатура, ЦветНаименование); // Используем внешнее получение

    Если ДвоичныеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;

    Попытка
        КартинкаОбъект = Новый Картинка(ДвоичныеДанные);
    Исключение
        ЗаписатьЖурналРегистрации("Ошибка при создании объекта Картинка: " + ОписаниеОшибки(), УровеньЖурналаРегистрации.Ошибка);
        Возврат;
    КонецПопытки;

    // Определяем область для вставки картинки (например, после ячейки с номенклатурой)
    // Можно создать новую колонку специально для картинок или вставить в существующую
    // Предположим, мы хотим вставить картинку в ячейку рядом с номенклатурой
    // Если мы хотим вставить в саму ячейку или создать новую колонку:

    // Для примера, увеличим высоту строки и вставим картинку в новую колонку справа от текущей ячейки
    ТабДок.ВысотаСтроки(Ячейка.Верх, Ячейка.Высота + 5); // Увеличим высоту строки для картинки

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

    // Располагаем картинку. Например, в следующей колонке справа от текущей ячейки
    // Или в самой ячейке, если она достаточно широкая
    // Здесь мы создадим новую область, которая занимает 2 колонки и такую же высоту как текущая строка
    // Сдвинем все ячейки справа от Номенклатуры на 2 колонки
    // Более простой вариант - найти пустую колонку или добавить новую
    
    // Для простоты, разместим картинку в текущей ячейке, если она пуста или если мы хотим заменить текст
    // Или создадим новую область, если хотим добавить картинку отдельно.

    // Пример: вставка картинки в новую, пустую область справа от ячейки с товаром
    ЛевоКартинки = Ячейка.Лево + Ячейка.Ширина;
    ВерхКартинки = Ячейка.Верх;
    ШиринаКартинки = 5; // Условная ширина в ячейках
    ВысотаКартинки = Ячейка.Высота + 5; // Высота картинки

    // Добавляем ячейки, если нужно, чтобы создать место для картинки
    // ТабДок.ВставитьКолонки(ЛевоКартинки, ШиринаКартинки);

    НовыйРисунок.Расположить(ТабДок.Область(ВерхКартинки, ЛевоКартинки, ВерхКартинки + ВысотаКартинки -1, ЛевоКартинки + ШиринаКартинки - 1));
    
    // Настройки отображения
    НовыйРисунок.РазмерКартинки = РазмерКартинки.АвтоРазмер; // Или Пропорционально, РеальныйРазмер
    НовыйРисунок.ГраницаСлева = Новый Линия(ТипЛинииРисунка.Нет);
    НовыйРисунок.ГраницаСверху = Новый Линия(ТипЛинииРисунка.Нет);
    НовыйРисунок.ГраницаСправа = Новый Линия(ТипЛинииРисунка.Нет);
    НовыйРисунок.ГраницаСнизу = Новый Линия(ТипЛинииРисунка.Нет);

    // Можно также очистить текст в ячейке, если картинка вставляется вместо него
    // Ячейка.Текст = "";

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

Важные моменты и подводные камни

  1. Произвольные отчеты: Если пользователи могут значительно менять структуру отчета (добавлять/удалять поля, группировки), то привязка к конкретным индексам колонок становится ненадежной. Использование механизма расшифровки является более универсальным и устойчивым к изменениям структуры отчета.
  2. Производительность: Загрузка большого количества изображений (особенно с внешних сайтов) может существенно замедлить формирование отчета.
    • Кэширование: Рассмотрите возможность кэширования двоичных данных изображений (например, во временном хранилище или в базе данных), чтобы не загружать их при каждом формировании отчета.
    • Асинхронная загрузка: В некоторых случаях можно рассмотреть асинхронную загрузку картинок, но это значительно усложняет код.
  3. Размер и масштабирование: Изображения могут быть разного размера. Важно корректно настроить свойства РисунокТабличногоДокумента, такие как РазмерКартинки и ПривязкаКОбласти, чтобы они адекватно отображались в отчете и не "разъезжались". Возможно, потребуется программно регулировать высоту строк или ширину колонок под размер изображения.
  4. Хранение картинок: Определите оптимальный способ хранения картинок. Хранение в базе данных (например, в регистре сведений) удобно для целостности, но может увеличить размер ИБ. Хранение на диске или внешнем сайте требует правильного управления путями/URL.
  5. Пользовательский интерфейс: Реализуйте "галочку" в настройках отчета ("с картинками") для управления видимостью изображений. Это позволит пользователям включать/выключать функционал по необходимости. Для этого можно использовать дополнительные свойства настроек СКД.
  6. Ошибки и исключения: Всегда предусматривайте обработку ошибок при работе с файлами, HTTP-запросами или базами данных, чтобы отчет не "падал" при возникновении проблем.

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

← На главную