Как программно сохранить список значений в реквизите регистра сведений

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

В процессе разработки на платформе 1С:Предприятие 8 программисты часто сталкиваются с ограничением: реквизиты регистра сведений не могут напрямую содержать такие типы данных, как СписокЗначений, Массив или Структура. Это связано с особенностями хранения данных в реляционных таблицах СУБД. Однако задачи, требующие привязки набора элементов (например, списка номенклатуры) к одной записи регистра, встречаются повсеместно — от настроек регламентных заданий до сложных фильтров в аналитических отчетах.

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

Способ 1. Использование типа ХранилищеЗначения

Это классический метод, позволяющий упаковать любой объект 1С (включая СписокЗначений) в бинарный формат и сохранить его в базе данных. Рассмотрим подробнее, как реализовать этот механизм в форме записи регистра.

При использовании этого метода сам реквизит регистра должен иметь тип ХранилищеЗначения. Поскольку напрямую редактировать этот тип на форме нельзя, нам потребуется вспомогательный реквизит формы.

Алгоритм реализации:

  1. Создадим в регистре реквизит ДанныеСписка с типом ХранилищеЗначения — для этой задачи подойдет универсальный редактор записей регистров с поддержкой Хранилища значения.
  2. На форме записи регистра создадим реквизит формы СписокДляРедактирования с типом СписокЗначений.
  3. При чтении данных (событие ПриЧтенииНаСервере) будем извлекать данные из хранилища, используя распаковщик для работы с данными.
  4. При записи (событие ПередЗаписьюНаСервере) — упаковывать обратно.

Проанализируем пример кода для формы записи:


&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
    // Извлекаем список из хранилища значения
    ЗначениеИзХранилища = ТекущийОбъект.ДанныеСписка.Получить();
    Если ТипЗнч(ЗначениеИзХранилища) = Тип("СписокЗначений") Тогда
        ЗначениеВРеквизитФормы(ЗначениеИзХранилища, "СписокДляРедактирования");
    КонецЕсли;
КонецПроцедуры

&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    // Помещаем список обратно в хранилище перед сохранением
    ТекущийОбъект.ДанныеСписка = Новый ХранилищеЗначения(РеквизитФормыВЗначение("СписокДляРедактирования"));
КонецПроцедуры

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

Недостатки: Данные в ХранилищеЗначения недоступны для использования в условиях запроса (нельзя написать ГДЕ Номенклатура В Рег.ДанныеСписка). Также невозможно вывести содержимое списка в стандартный динамический список без дополнительных ухищрений.

Способ 2. Сериализация в XML или JSON (строковый реквизит)

Если нам нужно хранить список ссылок (например, массив Номенклатура), мы можем преобразовать его в текстовую строку. В современных версиях платформы 1С 8.3 для этого лучше всего подходит формат JSON, так как он более компактен по сравнению с XML.

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

Рассмотрим пример программной упаковки данных через XDTO (как предлагалось в обсуждении) или экспорт данных в JSON:


// Пример сериализации списка в XML строку
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
СериализаторXDTO.ЗаписатьXML(ЗаписьXML, СписокДляСохранения);
СтрокаДляБазы = ЗаписьXML.Закрыть();

Однако мы рекомендуем использовать более современный JSON. Посмотрим на пример:


// Упаковка в JSON
МассивСсылок = СписокЗначений.ВыгрузитьЗначения();
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, МассивСсылок);
СтрокаJSON = ЗаписьJSON.Закрыть();

// Распаковка из JSON
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
МассивСсылок = ПрочитатьJSON(ЧтениеJSON);
СписокЗначений.ЗагрузитьЗначения(МассивСсылок);

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

Способ 3. Архитектурный подход: «Одна запись — один элемент»

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

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

Решение: Создание формы набора записей с виртуальной таблицей.

  1. Создаем регистр, где Номенклатура является измерением.
  2. Создаем вспомогательную форму (не форму записи, а форму, имитирующую редактирование объекта).
  3. На форму выводим таблицу, в которую пользователь может подобрать список номенклатуры (через стандартную команду «Подбор»).
  4. При нажатии кнопки «Записать» программа очищает старые записи по данному ключу и записывает новые строки.

Проанализируем код записи такого набора:


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

Для Каждого Стр Из ТаблицаНаФорме Цикл
    НоваяЗапись = НаборЗаписей.Добавить();
    НоваяЗапись.КлючНастройки = ТекущийКлюч;
    НоваяЗапись.Номенклатура = Стр.Номенклатура;
КонецЦикла;

НаборЗаписей.Записать();

Это решение — самое производительное. Оно позволяет использовать данные регистра в любых соединениях и условиях запросов без предварительной обработки в коде.

Визуализация списка в форме списка регистра

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

Для отображения содержимого рекомендуется завести дополнительный реквизит ПредставлениеСписка (Тип: Строка, длина например 150-200 символов). При записи объекта мы будем программно формировать строку из первых нескольких элементов списка (например: "Ботинки, Туфли, Кроссовки...").

Рассмотрим, как это помогает производительности:

Проблема ссылочной целостности

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

Как минимизировать риски:

  1. Использовать архитектуру «Одна запись — один элемент» (Способ 3). В этом случае платформа сама запретит удаление номенклатуры, если на нее есть ссылка в измерении регистра.
  2. Подписка на событие: Если выбран способ со строкой/хранилищем, необходимо создать подписку на событие ПередУдалением для справочника номенклатуры, которая будет проверять наличие ссылок в ваших специфических хранилищах. Однако это технически сложно и замедляет работу системы.
  3. Очистка помеченных на удаление: Регулярно проверять данные регламентным заданием и вычищать несуществующие ссылки из сериализованных данных — для этого есть утилита выявления и удаления битых ссылок.

Резюме и рекомендации

Выбор метода зависит от ваших целей:

  1. Если список нужен только для программного чтения в регламентном задании и он не слишком велик — используйте ХранилищеЗначения. Это проще всего в реализации.
  2. Если вы работаете в современной конфигурации и важна читаемость данных — используйте сериализацию в JSON.
  3. Если по списку нужно строить отчеты или фильтровать запросы — используйте классическую структуру регистра (одна запись на каждый элемент списка) с доработкой интерфейса для удобства пользователя.

Помните, что удобство интерфейса (UX) можно обеспечить даже при самой строгой и «неудобной» структуре данных, если правильно использовать возможности форм и наборов записей.

← На главную