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