К статье
Практический материал

Разработка и Конфигурация · 10 мин

Как сохранить табличный документ в хранилище значений в 1С

Как сохранить табличный документ в хранилище значений в 1С: код на встроенном языке, чтение обратно, типовые ошибки и ограничения.

Хранилище значений в 1С — универсальный механизм для сохранения произвольных данных прямо в базе: в реквизите объекта, в ресурсе регистра сведений или в константе. Табличный документ (объект ТабличныйДокумент) можно поместить в хранилище целиком, вместе с форматированием, картинками и областями, и потом извлечь его в исходном виде. Это удобно для архива печатных форм, фиксации «снимка» отчёта на дату или передачи документа между сеансами.

Ключевая идея проста: объект ХранилищеЗначения создаётся конструктором Новый ХранилищеЗначения(Значение), куда передаётся сам табличный документ. При этом платформа сериализует объект и упаковывает его во внутренний формат. Обратное чтение выполняется методом Получить(), который возвращает исходный объект.

Ниже разобраны рабочие примеры кода, варианты хранения (реквизит справочника, регистр сведений, временное хранилище), ограничения по объёму и типичные ошибки, из-за которых документ «не читается» обратно.

Как работает хранилище значений с табличным документом

Тип ХранилищеЗначения предназначен для упаковки любых сериализуемых значений — от строк и массивов до сложных объектов вроде табличного документа или картинки. При создании хранилища платформа преобразует объект в поток байт, при чтении — восстанавливает его обратно. Для разработчика это выглядит как «чёрный ящик»: положили документ — получили документ.

Важно понимать, что хранилище значений — не ссылочный тип. Его нельзя «развернуть» запросом или посмотреть содержимое в пользовательском режиме. Единственный способ получить данные — вызвать метод Получить() в коде. Поэтому хранилище подходит для архивов и вложений, но не для данных, по которым нужно строить отборы и отчёты.

Табличный документ сериализуется целиком: значения ячеек, форматирование, объединённые области, вложенные картинки, параметры страницы. После извлечения документ можно показать пользователю, напечатать или сохранить в файл (MXL, Excel, PDF) штатными методами Записать().

Базовый код: сохранить и прочитать документ

Минимальный сценарий — сохранить сформированный табличный документ в реквизит объекта с типом ХранилищеЗначения. Например, в справочнике «ПечатныеФормы» создан реквизит ДанныеДокумента нужного типа.

// Сохранение

ТабДок = Новый ТабличныйДокумент;

// ... заполнение документа ...

НовыйЭлемент = Справочники.ПечатныеФормы.СоздатьЭлемент();

НовыйЭлемент.Наименование = "Счёт № 12 от клиента";

НовыйЭлемент.ДанныеДокумента = Новый ХранилищеЗначения(ТабДок);

НовыйЭлемент.Записать();

// Чтение

Запрос = Новый Запрос("ВЫБРАТЬ ДанныеДокумента ИЗ Справочник.ПечатныеФормы ГДЕ Ссылка = &Ссылка");

Запрос.УстановитьПараметр("Ссылка", НужнаяСсылка);

Выборка = Запрос.Выполнить().Выбрать();

Если Выборка.Следующий() Тогда

ТабДок = Выборка.ДанныеДокумента.Получить();

Если ТипЗнч(ТабДок) = Тип("ТабличныйДокумент") Тогда

ТабДок.Показать("Архивная печатная форма");

КонецЕсли;

КонецЕсли;

Обратите внимание на проверку типа после Получить(). Если реквизит не заполнен, метод вернёт Неопределено, и попытка работать с ним как с табличным документом вызовет ошибку. Проверка типа — обязательная страховка при чтении любых данных из хранилища.

Порядок действий
  1. 1
    Создайте реквизит объекта или ресурс регистра сведений с типом ХранилищеЗначения
  2. 2
    Сформируйте табличный документ программно или получите его из макета
  3. 3
    Присвойте реквизиту значение Новый ХранилищеЗначения(ТабДок)
  4. 4
    Запишите объект в базу
  5. 5
    При чтении вызовите Получить() и проверьте тип результата перед использованием

Где хранить: реквизит объекта, регистр сведений или временное хранилище

Выбор места хранения зависит от задачи. Если табличный документ привязан к конкретному объекту (документу, элементу справочника), логично добавить реквизит с типом ХранилищеЗначения прямо в этот объект. Если нужен архив печатных форм по множеству объектов с версиями — удобнее подчинённый регистр сведений, где измерение — ссылка на объект, а ресурс — хранилище.

Третий сценарий — временное хранилище. Оно не пишет данные в базу, а размещает их в памяти сервера и возвращает адрес (строку навигационной ссылки). Это стандартный способ передать табличный документ с сервера на клиент в управляемых формах, например для показа результата отчёта без сохранения в базу.

// Помещение во временное хранилище (серверная процедура формы)

АдресВХранилище = ПоместитьВоВременноеХранилище(ТабДок, УникальныйИдентификаторФормы);

// Получение обратно

ТабДок = ПолучитьИзВременногоХранилища(АдресВХранилище);

Сравнение вариантов хранения

Реквизит объекта

Подходит, когда документ жёстко привязан к одному объекту. Ограничение: одна версия на объект, при перезаписи старая теряется.

Регистр сведений

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

Временное хранилище

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

Сжатие и объём хранимых данных

Табличные документы с большим числом строк и оформлением могут занимать заметный объём. Конструктор хранилища поддерживает второй параметр — степень сжатия: Новый ХранилищеЗначения(ТабДок, Новый СжатиеДанных(9)). Сжатие уменьшает размер записи в базе, но немного замедляет запись и чтение. Для архивов, которые пишутся редко, а хранятся долго, сжатие обычно оправдано.

Точного универсального лимита на размер хранилища платформа не фиксирует — практическая граница определяется СУБД и настройками поля. Однако хранение десятков мегабайт в одном реквизите замедляет выборки, репликацию и обмены. Если документы крупные, разумнее сохранять их в файлы (MXL или PDF) в томе хранения файлов, а в базе держать только ссылку на файл.

Типичные ошибки и как их избежать

Чаще всего проблемы возникают не при записи, а при чтении. Первая классическая ошибка — забыть вызвать Получить() и попытаться работать с самим хранилищем как с табличным документом: у объекта ХранилищеЗначения нет методов Показать() или Записать() для MXL. Вторая — отсутствие проверки на Неопределено для пустого реквизита.

Отдельная группа ошибок связана с контекстом выполнения. В тонком клиенте объект ТабличныйДокумент недоступен на клиенте в полном объёме — формировать и упаковывать его нужно в серверной процедуре. Также нельзя записать в хранилище несериализуемые объекты (например, COMОбъект или объект запроса): конструктор выдаст ошибку о невозможности сериализации.

Типичные ошибки
01
Работа с хранилищем без вызова Получить() — методы табличного документа недоступны, код падает с ошибкой
02
Чтение пустого реквизита без проверки на Неопределено — исключение при обращении к свойствам
03
Попытка сформировать ТабличныйДокумент на клиенте тонкого клиента — объект недоступен, нужен серверный вызов
04
Запись несериализуемого объекта в хранилище — ошибка сериализации в конструкторе
05
Хранение крупных документов без сжатия — раздувание базы и медленные выборки

Проверка результата и альтернативы

После записи убедитесь, что данные действительно читаются: выполните чтение в консоли кода или в отладчике, вызовите Получить() и проверьте, что вернулся объект нужного типа и документ открывается с сохранённым оформлением. Полезный контрольный приём — сразу после записи прочитать значение обратно и сравнить, например, количество строк области данных.

Если задача — не архив, а просто передача печатной формы пользователю, хранилище значений может быть избыточным. Альтернативы: запись документа в файл MXL командой ТабДок.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.MXL), использование механизма присоединённых файлов БСП, либо хранение готового PDF для документов, которые больше не будут редактироваться.

Контрольные точки

Как проверить результат

Тип после Получить()
Норма

Возвращается ТабличныйДокумент

Тревожный признак

Неопределено или другой тип — реквизит пуст или записано не то

Содержимое документа
Норма

Области, форматирование и картинки на месте

Тревожный признак

Пустой документ — записали незаполненный ТабДок

Размер базы после массовой записи
Норма

Рост соответствует числу записей и сжатию

Тревожный признак

Резкий рост — нет сжатия или пишутся лишние копии

FAQ: частые вопросы

Можно ли сохранить в хранилище значений несколько табличных документов сразу?

Да. Упакуйте их в массив или структуру и передайте эту коллекцию в конструктор хранилища. При чтении Получить() вернёт ту же коллекцию, из которой документы извлекаются по индексу или ключу.

Чем хранилище значений отличается от временного хранилища?

Хранилище значений — объект для записи данных в базу на постоянной основе. Временное хранилище — область памяти сервера с адресом-ссылкой, живущая ограниченное время; оно предназначено для передачи данных между сервером и клиентом, а не для архива.

Почему после Получить() возвращается Неопределено?

Чаще всего реквизит просто не был заполнен при записи объекта, либо запись не выполнилась из-за ошибки или отмены транзакции. Проверьте, что конструктор хранилища вызывался до метода Записать() и что объект успешно записан.

Сохранится ли форматирование и картинки табличного документа?

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

Можно ли найти нужный документ запросом по содержимому хранилища?

Нет, содержимое хранилища недоступно для отборов и поиска в запросах. Если нужен поиск, храните ключевые реквизиты (номер, дату, контрагента) в отдельных полях объекта рядом с хранилищем.

Что лучше для архива печатных форм — хранилище значений или файлы?

Для небольшого числа документов хранилище проще: всё лежит в базе и копируется вместе с ней. При большом объёме архива выгоднее файловое хранилище или присоединённые файлы — база остаётся компактной, а файлы можно вынести на отдельный диск.

Итог

Что важно запомнить

Табличный документ упаковывается конструктором Новый ХранилищеЗначения(ТабДок) и извлекается методом Получить() с обязательной проверкой типа. Место хранения выбирайте по задаче: реквизит объекта, регистр сведений или временное хранилище. Для крупных документов включайте сжатие или переходите на файловое хранение.