Как получить подчиненные элементы для выделенной группы в динамическом списке 1С?

Программист 1С v8.3 (Управляемые формы)
← На главную

В разработке на платформе 1С часто возникает задача: у нас есть динамический список с настроенной группировкой, который отображается пользователю в виде дерева. Необходимо при выборе пользователем строки-группы (например, контрагента) получить в коде все дочерние элементы этой группы (например, все заказы этого контрагента). Попытка получить их через свойство ВыделенныеСтроки или ТекущиеДанные возвращает объект типа СтрокаГруппировкиДинамическогоСписка, у которого нет прямого метода для доступа к подчиненным записям. Разберемся, как правильно решить эту задачу.

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

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

Подход 1: Простой серверный запрос

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

Проанализируем шаги реализации:

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

Посмотрим на пример кода. Допустим, наш динамический список называется `Список`, а группировка установлена по полю `Контрагент`.

Код на клиенте (в модуле формы):


&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
    
    ТекущиеДанные = Элемент.ТекущиеДанные;
    
    // Если ТекущиеДанные не определены (например, список пуст) или
    // это не строка группировки (определяем по наличию ссылки на документ),
    // то выходим.
    Если ТекущиеДанные = Неопределено ИЛИ ТекущиеДанные.Ссылка <> Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Получаем значение группировки. В ТекущиеДанные для строки-группы
    // будет доступно значение поля, по которому она сгруппирована.
    КонтрагентГруппы = ТекущиеДанные.Контрагент;
    
    // Вызываем сервер для получения подчиненных документов
    МассивДокументов = ПолучитьЗаказыПоКонтрагентуНаСервере(КонтрагентГруппы);
    
    // Далее можно обработать полученный массив
    Сообщить("Найдено заказов: " + МассивДокументов.Количество());
    
КонецПроцедуры

Код на сервере (в модуле той же формы):


&НаСервере
Функция ПолучитьЗаказыПоКонтрагентуНаСервере(КонтрагентГруппы)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    ЗаказПокупателя.Ссылка
        |ИЗ
        |    Документ.ЗаказПокупателя КАК ЗаказПокупателя
        |ГДЕ
        |    ЗаказПокупателя.Контрагент = &Контрагент";
        
    Запрос.УстановитьПараметр("Контрагент", КонтрагентГруппы);
    
    Результат = Запрос.Выполнить();
    
    Возврат Результат.Выгрузить().ВыгрузитьКолонку("Ссылка");
    
КонецФункции

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

Подход 2: Использование настроек компоновки данных (СКД)

Это более сложный, но "правильный" и универсальный метод. Он позволяет получить данные, которые в точности соответствуют текущему отображению в динамическом списке, со всеми пользовательскими отборами, сортировками и другими настройками. Суть метода — мы не создаем запрос с нуля, а берем текущие настройки компоновки данных динамического списка, программно добавляем к ним свой отбор по значению из выбранной группы и выполняем компоновку для получения результата. При необходимости выгрузки данных списка с сохранением фильтров в отчет можно использовать инструмент для формирования отчета СКД из динамического списка.

Разберем этот процесс по шагам.

  1. Получаем значение группировки на клиенте, как и в первом подходе, и передаем его на сервер.
  2. На сервере получаем исполняемые настройки СКД. Для этого у объекта динамического списка есть специальные методы:
    • ПолучитьИсполняемуюСхемуКомпоновкиДанных() — возвращает схему компоновки.
    • ПолучитьИсполняемыеНастройкиКомпоновкиДанных() — возвращает все текущие настройки, включая те, что применил пользователь.
  3. Программно создаем новый элемент отбора. Это будет объект типа ЭлементОтбораКомпоновкиДанных. Мы указываем в нем левое значение (поле для фильтрации, например, `Контрагент`), вид сравнения (`Равно`) и правое значение (ссылка на контрагента, полученная с клиента).
  4. Добавляем наш отбор в коллекцию. Новый элемент отбора помещается в коллекцию Настройки.Отбор.Элементы.
  5. Выполняем компоновку данных. С помощью объектов КомпоновщикМакетаКомпоновкиДанных, ПроцессорКомпоновкиДанных и ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений мы "исполняем" измененные настройки и получаем результат в виде таблицы значений.

Посмотрим, как это выглядит в коде. Клиентская часть остается такой же, как в первом примере. Изменится только серверная функция.

Код на сервере (в модуле формы):


&НаСервере
Функция ПолучитьЗаказыПоКонтрагентуНаСервере(КонтрагентГруппы)
    
    // 1. Получаем схему и текущие настройки динамического списка
    СхемаКомпоновки = ЭтотОбъект.Список.ПолучитьИсполняемуюСхемуКомпоновкиДанных();
    Настройки = ЭтотОбъект.Список.ПолучитьИсполняемыеНастройкиКомпоновкиДанных();
    
    // 2. Создаем новый элемент отбора по нашему контрагенту
    НовыйОтбор = Настройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
    НовыйОтбор.ЛевоеЗначение  = Новый ПолеКомпоновкиДанных("Контрагент");
    НовыйОтбор.ВидСравнения   = ВидСравненияКомпоновкиДанных.Равно;
    НовыйОтбор.ПравоеЗначение = КонтрагентГруппы;
    НовыйОтбор.Использование  = Истина;

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

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

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

← На главную