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