При разработке документов и печатных форм часто возникает задача отобразить изображение номенклатуры (например, в коммерческом предложении, прайс-листе или спецификации). Начинающие разработчики часто сталкиваются с ситуацией, когда текстовые и числовые реквизиты заполняются корректно, а поле с изображением остается пустым либо платформа генерирует исключение Поле объекта не обнаружено. Разберем подробно причины такого поведения, архитектурные особенности хранения файлов в 1С и пошаговый алгоритм решения задачи.
Выясним причину, почему прямой перенос значения реквизита номенклатуры не дает ожидаемого результата. В управляемом приложении и типовых конфигурациях графические данные не хранятся в строковых переменных или простых реквизитах справочника.
Номенклатура реквизит ФайлКартинки обычно содержит не сам графический файл, а ссылку на элемент справочника (например, СправочникСсылка.Файлы или СправочникСсылка.НоменклатураПрисоединенныеФайлы).ПрисоединенныеФайлы с ресурсом ХранимыйФайл типа ХранилищеЗначений), либо в томах на диске сервера. Чтобы получить картинку, необходимо извлечь двоичные данные методом Получить().Картинка напрямую в колонку формы документа без использования специализированных элементов отображения приводит к ошибкам или пустому результату. Изображение для коммерческого предложения должно выводиться непосредственно в макет печатной формы.Новый Картинка(ПрисоединенныеФайлы.ХранимыйФайл) вызывает ошибку Поле объекта не обнаружено, так как имя ПрисоединенныеФайлы без предварительного чтения конкретной записи регистра является менеджером, у которого нет ресурсов.Рассмотрим правильный подход к автозаполнению реквизитов строки при изменении номенклатуры. Вместо нескольких вызовов сервера объединим получение всех данных в одну функцию с директивой &НаСервереБезКонтекста, возвращающую структуру.
Посмотрим на пример клиент-серверного взаимодействия:
&НаКлиенте
Процедура ОборудованиеНоменклатураПриИзменении(Элемент)
Стр = Элементы.Оборудование.ТекущиеДанные;
Если Стр = Неопределено Тогда
Возврат;
КонецЕсли;
ДанныеНоменклатуры = ПолучитьРеквизитыНоменклатуры(Стр.Номенклатура);
Стр.ЕдиницаИзмерения = ДанныеНоменклатуры.ЕдиницаИзмерения;
Стр.Описание = ДанныеНоменклатуры.Описание;
Стр.Группа = ДанныеНоменклатуры.Родитель;
// В табличную часть достаточно сохранить ссылку на файл, а не саму картинку
Стр.ФайлКартинки = ДанныеНоменклатуры.ФайлКартинки;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьРеквизитыНоменклатуры(Знач СсылкаНоменклатура)
Результат = Новый Структура;
Результат.Вставить("ЕдиницаИзмерения", СсылкаНоменклатура.ЕдиницаИзмерения);
Результат.Вставить("Описание", СсылкаНоменклатура.Комментарий);
Результат.Вставить("Родитель", СсылкаНоменклатура.Родитель);
Результат.Вставить("ФайлКартинки", СсылкаНоменклатура.ФайлКартинки);
Возврат Результат;
КонецФункции
Обратите внимание: сохранять саму картинку или ее двоичные данные в табличную часть документа не требуется. Это неоправданно увеличивает объем базы данных. Достаточно иметь ссылку на номенклатуру или ссылку на файл, а само изображение извлекать непосредственно в момент формирования печатной формы.
Разберем по шагам, как извлечь двоичные данные изображения из базы перед выводом на печать.
Способ 1. Использование процедур БСП (Библиотеки Стандартных Подсистем)
Если конфигурация разработана на основе БСП, файлы могут храниться как в базе данных, так и в томах на сервере. Для универсального получения двоичных данных рекомендуется использовать функции подсистемы работы с файлами:
ДвоичныеДанные = Неопределено;
Если ЗначениеЗаполнено(СсылкаНаФайл) Тогда
// Использование стандартного модуля подсистемы работы с файлами
ДвоичныеДанные = ПрисоединенныеФайлы.ПолучитьДвоичныеДанныеФайла(СсылкаНаФайл);
КонецЕсли;
Способ 2. Прямое чтение из регистра сведений
Если хранение организовано в регистре сведений ПрисоединенныеФайлы, где измерением выступает ПрисоединенныйФайл, а ресурсом ХранимыйФайл с типом ХранилищеЗначений, проанализируем получение данных через отбор:
ДвоичныеДанные = Неопределено;
Если ЗначениеЗаполнено(СсылкаНаФайл) Тогда
Отбор = Новый Структура("ПрисоединенныйФайл", СсылкаНаФайл);
ЗначенияРесурсов = РегистрыСведений.ПрисоединенныеФайлы.Получить(Отбор);
Если ЗначенияРесурсов.ХранимыйФайл <> Неопределено Тогда
ДвоичныеДанные = ЗначенияРесурсов.ХранимыйФайл.Получить();
КонецЕсли;
КонецЕсли;
Рассмотрим подробнее процесс размещения картинки в табличном документе. Чтобы изображение корректно выводилось для каждой строки номенклатуры, необходимо соблюдать правила компоновки макета:
Оборудование добавьте графический объект (меню Таблица — Рисунки — Картинка).Фото.РазмерКартинкиТабличногоДокумента.Пропорционально, чтобы изображения с разным разрешением не растягивали макет и сохраняли исходные пропорции.Рисунки.Фото.Картинка до вызова метода ТабДок.Вывести().Посмотрим на реализацию процедуры печати в модуле менеджера документа:
Процедура Печать(ТабДок, Ссылка) Экспорт
Макет = Документы.КоммерческоеПредложение.ПолучитьМакет("Печать");
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КП.Номер КАК Номер,
| КП.Дата КАК Дата,
| КП.Контрагент КАК Контрагент,
| КП.Оборудование.(
| НомерСтроки КАК НомерСтроки,
| Номенклатура КАК Номенклатура,
| Номенклатура.ФайлКартинки КАК ФайлКартинки,
| Количество КАК Количество,
| Цена КАК Цена,
| Сумма КАК Сумма
| ) КАК ТЧ_Оборудование
|ИЗ
| Документ.КоммерческоеПредложение КАК КП
|ГДЕ
| КП.Ссылка В (&Ссылка)";
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Выборка = Запрос.Выполнить().Выбрать();
ОбластьЗаголовок = Макет.ПолучитьОбласть("Заголовок");
ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
ОбластьСтрокаШапка = Макет.ПолучитьОбласть("ОборудованиеШапка");
ОбластьОборудование = Макет.ПолучитьОбласть("Оборудование");
ТабДок.Очистить();
Пока Выборка.Следующий() Цикл
ТабДок.Вывести(ОбластьЗаголовок);
ОбластьШапка.Параметры.Заполнить(Выборка);
ТабДок.Вывести(ОбластьШапка);
ТабДок.Вывести(ОбластьСтрокаШапка);
ВыборкаСтрок = Выборка.ТЧ_Оборудование.Выбрать();
Пока ВыборкаСтрок.Следующий() Цикл
ОбластьОборудование.Параметры.Заполнить(ВыборкаСтрок);
// Сбрасываем картинку перед обработкой текущей строки
ОбластьОборудование.Рисунки.Фото.Картинка = Новый Картинка;
ОбластьОборудование.Рисунки.Фото.РазмерКартинки = РазмерКартинкиТабличногоДокумента.Пропорционально;
Если ЗначениеЗаполнено(ВыборкаСтрок.ФайлКартинки) Тогда
// Получаем запись из регистра сведений
Отбор = Новый Структура("ПрисоединенныйФайл", ВыборкаСтрок.ФайлКартинки);
ДанныеФайла = РегистрыСведений.ПрисоединенныеФайлы.Получить(Отбор);
Если ДанныеФайла.ХранимыйФайл <> Неопределено Тогда
ДвоичныеДанные = ДанныеФайла.ХранимыйФайл.Получить();
Если ТипЗнч(ДвоичныеДанные) = Тип("ДвоичныеДанные") Тогда
ОбластьОборудование.Рисунки.Фото.Картинка = Новый Картинка(ДвоичныеДанные);
КонецЕсли;
КонецЕсли;
КонецЕсли;
ТабДок.Вывести(ОбластьОборудование);
КонецЦикла;
КонецЦикла;
КонецПроцедуры
Такой подход исключает ошибки обращения к менеджерам объектов, защищает форму от утечек памяти и гарантирует корректное позиционирование изображений номенклатуры в готовом печатном документе.