В разработке на платформе 1С часто возникает задача отобразить в динамическом списке не только основные реквизиты документа, но и краткое содержимое его табличной части. Например, пользователь хочет видеть список товаров прямо в списке реализаций, не заходя в каждую форму. Однако язык запросов 1С не поддерживает агрегацию строк (аналог STRING_AGG в SQL), что делает задачу нетривиальной.
В данной статье мы разберем несколько эффективных способов реализации этой задачи, проанализируем их плюсы и минусы, а также выясним, какой метод лучше всего подходит для высоконагруженных систем.
Этот метод считается наиболее профессиональным и производительным, особенно если список товаров в строке должен быть доступен для поиска и фильтрации. Рассмотрим его подробнее.
Суть заключается в том, что мы добавляем в сам объект (документ или справочник) дополнительный реквизит КраткийСостав с типом Строка (неограниченной длины или фиксированной, например, 250 символов). Этот реквизит будет обновляться автоматически при записи объекта.
Разберем реализацию по шагам:
КраткийСостав в метаданные объекта.ПередЗаписью пропишите алгоритм формирования строки.Пример кода для модуля объекта:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Ограничим количество выводимых товаров, например, первыми пятью
МаксКоличество = 5;
Счетчик = 0;
НоваяСтрокаСостава = "";
Для Каждого СтрокаТЧ Из Товары Цикл
Счетчик = Счетчик + 1;
Если Счетчик > МаксКоличество Тогда
НоваяСтрокаСостава = НоваяСтрокаСостава + "...";
Прервать;
КонецЕсли;
Разделитель = ?(ЗначениеЗаполнено(НоваяСтрокаСостава), ", ", "");
НоваяСтрокаСостава = НоваяСтрокаСостава + Разделитель + Строка(СтрокаТЧ.Номенклатура);
КонецЦикла;
КраткийСостав = НоваяСтрокаСостава;
КонецПроцедуры
Преимущества: Высочайшая скорость работы динамического списка. По этому полю работают стандартные механизмы поиска и отбора платформы 1С.
Недостатки: Требуется перепополнение данных для уже существующих документов и небольшое увеличение объема базы данных.
Если вносить изменения в структуру метаданных нельзя, мы можем воспользоваться событием формы ПриПолученииДанныхНаСервере. Этот метод позволяет динамически "дорисовывать" нужные данные в строки, которые в данный момент отображаются на экране у пользователя.
Проанализируем ситуацию: просто выполнять запрос в цикле внутри этого события нельзя — это создаст колоссальную нагрузку на сервер (проблема N+1 запроса). Правильный подход — собрать все ссылки отображаемых строк и получить данные одним запросом.
Рассмотрим пример реализации в модуле формы:
&НаСервере
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
// Получаем массив ссылок текущих строк списка
МассивСсылок = Строки.ПолучитьКлючи();
// Запросом получаем данные табличных частей для всех ссылок разом
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Ссылка КАК Ссылка,
| Товары.Номенклатура.Наименование КАК Товар
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК Товары
|ГДЕ
| Товары.Ссылка В(&МассивСсылок)
|УПОРЯДОЧИТЬ ПО
| Товары.Ссылка,
| Товары.НомерСтроки";
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
РезультатЗапроса = Запрос.Выполнить();
// Группируем результат в соответствии для быстрого поиска
ДанныеПоСсылке = Новый Соответствие;
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
ТекущийТекст = ДанныеПоСсылке.Получить(Выборка.Ссылка);
Если ТекущийТекст = Неопределено Тогда
ДанныеПоСсылке.Вставить(Выборка.Ссылка, Выборка.Товар);
Иначе
// Ограничим длину строки для производительности интерфейса
Если СтрДлина(ТекущийТекст) < 150 Тогда
ДанныеПоСсылке.Вставить(Выборка.Ссылка, ТекущийТекст + ", " + Выборка.Товар);
КонецЕсли;
КонецЕсли;
КонецЦикла;
// Заполняем колонки в оформляемых строках
Для Каждого КлючИЗначение Из Строки Цикл
ДанныеСтроки = ДанныеПоСсылке.Получить(КлючИЗначение.Ключ);
Если ДанныеСтроки <> Неопределено Тогда
КлючИЗначение.Значение.Данные["КраткийСостав"] = ДанныеСтроки;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Важный нюанс: Для работы этого кода в динамическом списке должно быть добавлено произвольное поле КраткийСостав (например, через текст запроса списка как "" КАК КраткийСостав).
Если мы точно знаем, что нам нужно вывести, например, строго первые 3 или 5 товаров, мы можем реализовать это непосредственно в тексте запроса динамического списка. Выясним причину, почему это редко используется: запрос становится громоздким и может замедлять работу базы при больших объемах ТЧ.
Проанализируем пример запроса для вывода первых двух товаров:
ВЫБРАТЬ
Док.Ссылка КАК Ссылка,
Док.Дата КАК Дата,
Док.Номер КАК Номер,
ЕСТЬNULL(ТЧ1.Номенклатура.Наименование, "")
+ ВЫБОР КОГДА ТЧ2.Номенклатура ЕСТЬ НЕ NULL ТОГДА ", " + ТЧ2.Номенклатура.Наименование ИНАЧЕ "" КОНЕЦ КАК Состав
ИЗ
Документ.РеализацияТоваровУслуг КАК Док
ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.Товары КАК ТЧ1
ПО Док.Ссылка = ТЧ1.Ссылка И ТЧ1.НомерСтроки = 1
ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.Товары КАК ТЧ2
ПО Док.Ссылка = ТЧ2.Ссылка И ТЧ2.НомерСтроки = 2
Преимущества: Работает "из коробки" в динамическом списке без написания кода на языке 1С.
Недостатки: Резкое падение производительности при увеличении количества соединений. Невозможность вывести произвольное количество строк (динамический список начнет дублировать строки, если не ограничивать НомерСтроки).
Мы рассмотрели три принципиально разных подхода к решению задачи. Выбор метода зависит от ваших приоритетов:
Помните, что динамический список предназначен для быстрого отображения порций данных. Любое усложнение его основного запроса или добавление тяжелых вычислений в события отрисовки напрямую влияет на комфорт работы пользователя.