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