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