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