Как вывести список значений из табличной части в динамический список журнала документов 1С?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке и кастомизации интерфейсов в 1С:Предприятии часто возникает типовая бизнес-задача: отобразить в строке журнала документов (списке документов) данные, которые хранятся не в шапке, а в строках табличной части. Классический пример — журнал документов «Чек ККМ» в конфигурациях «1С:Управление торговлей» или «1С:Розница», когда в рамках одного чека разные позиции могут продавать разные продавцы-консультанты, и требуется вывести всех задействованных продавцов одной строкой через запятую или точку с запятой.

Разберем природу ограничений платформы 1С при работе с динамическими списками и подробно рассмотрим все возможные варианты решения этой задачи.

Ограничения языка запросов 1С: почему нельзя «склеить» строки на лету

Проанализируем сначала возможность получения такой строки прямо в запросе динамического списка. В СУБД (например, MS SQL Server или PostgreSQL) существуют агрегатные функции вроде STRING_AGG() или STRING_AGG / GROUP_CONCAT. Однако в стандартном языке запросов 1С функции агрегации строк отсутствуют. Функция СоединитьСтроки() доступна исключительно в выражениях системы компоновки данных (СКД) при построении отчетов, но в произвольном тексте запроса динамического списка использовать её невозможно.

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

Вариант 1. Добавление реквизита в шапку документа (рекомендуемый способ)

Наиболее производительным и надежным способом для динамических списков является денормализация данных: сохранение готовой сводной строки прямо в шапку документа в момент его записи.

Разберем реализацию по шагам:

  1. В метаданных документа (например, Документ.ЧекККМ) добавляем новый реквизит шапки СписокПродавцов. Важно: не рекомендуется использовать тип Строка неограниченной длины, так как по таким полям в динамических списках СУБД не может строить индексы, а платформа 1С отключает стандартные отборы и сортировку. Задайте фиксированную длину, например, Строка(250) или Строка(500).
  2. В модуле объекта документа в обработчике события ПередЗаписью формируем итоговую строку по табличной части.
  3. В запросе динамического списка или в свойствах формы журнала просто выводим новое поле СписокПродавцов.

Посмотрим на пример кода формирования строки продавцов в модуле объекта документа:


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

Преимущества: максимальная скорость работы формы списка, возможность настраивать стандартный поиск, отбор и сортировку по колонке без просадки производительности СУБД.

Вариант 2. Использование события «ПриПолученииДанныхНаСервере»

Если конфигурация находится на типовой поддержке и добавлять реквизиты в шапку документа нежелательно, можно сформировать колонку динамически исключительно на уровне формы журнала.

Разберем, как работает этот механизм:

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

Рассмотрим пример кода в модуле формы журнала:


&НаСервере
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
    
    МассивСсылок = Строки.ПолучитьКлючи();
    
    Если МассивСсылок.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ РАЗЛИЧНЫЕ
        |   ЧекККМТовары.Ссылка КАК Ссылка,
        |   ПРЕДСТАВЛЕНИЕ(ЧекККМТовары.Продавец) КАК ПродавецПредставление
        |ИЗ
        |   Документ.ЧекККМ.Товары КАК ЧекККМТовары
        |ГДЕ
        |   ЧекККМТовары.Ссылка В(&МассивСсылок)
        |   И ЧекККМТовары.Продавец <> ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)";
    
    Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
    Выборка = Запрос.Выполнить().Выгрузить();
    
    // Группируем продавцов по документам
    СоответствиеДанных = Новый Соответствие;
    Для Каждого Стр Из Выборка Цикл
        ТекСписок = СоответствиеДанных[Стр.Ссылка];
        Если ТекСписок = Неопределено Тогда
            ТекСписок = Новый Массив;
            СоответствиеДанных.Вставить(Стр.Ссылка, ТекСписок);
        КонецЕсли;
        ТекСписок.Добавить(Стр.ПродавецПредставление);
    КонецЦикла;
    
    // Заполняем ячейки отображаемых строк
    Для Каждого КлючСтроки Из МассивСсылок Цикл
        ДанныеСтроки = Строки[КлючСтроки];
        МассивИмен = СоответствиеДанных[КлючСтроки];
        
        Если МассивИмен <> Неопределено Тогда
            ДанныеСтроки.ДанныеОформления["ПродавцыСтрокой"].Значение = СтрСоединить(МассивИмен, "; ");
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

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

Вариант 3. Использование независимого регистра сведений

Если конфигурацию нельзя снимать с поддержки (или изменения вносятся через расширение), хорошим компромиссом выступает создание непериодического регистра сведений:

  1. Создаем регистр сведений СводныеДанныеЧеков с измерением Документ (тип ДокументСсылка.ЧекККМ) и ресурсом СписокПродавцов (тип Строка(250)).
  2. Заполняем запись регистра в подписке на событие ПриЗаписи документа.
  3. В динамическом списке журнала настраиваем левое соединение с виртуальной таблицей регистра сведений.

Этот способ объединяет плюсы первого и второго вариантов: структура самого типового документа остается нетронутой, при этом в динамическом списке сохраняется высокая скорость чтения, возможность сортировки и платформенного отбора.

← На главную