При разработке внешних обработок и кастомных механизмов заполнения документов на управляемых формах 1С разработчики часто сталкиваются с необходимостью получить данные, которые пользователь отфильтровал в динамическом списке на форме. Классический пример: на форме обработки выведен динамический список номенклатуры, пользователь установил сложные отборы (по группе, по свойствам, по наличию на складе), и теперь необходимо нажать кнопку и заполнить табличную часть документа только этими отобранными позициями.
Попытки решить эту задачу «в лоб» через получение компоновщика настроек динамического списка часто приводят к тому, что выгруженная ТаблицаЗначений оказывается пустой или не содержит нужных колонок (например, ссылок на элементы справочника). В этой статье мы подробно разберем причины такого поведения платформы, рассмотрим классическое решение через схему компоновки данных, изучим альтернативный высокопроизводительный метод и научимся использовать возможности Библиотеки Стандартных Подсистем (БСП).
Для начала проанализируем типичный код, который часто используют разработчики для выгрузки динамического списка в таблицу значений. Этот код базируется на получении исполняемой схемы и настроек динамического списка:
&НаСервере
Функция Получить_ТЗ_Из_ДинамическогоСписка()
Схема = Элементы.СписокНоменклатуры.ПолучитьИсполняемуюСхемуКомпоновкиДанных();
Настройки = Элементы.СписокНоменклатуры.ПолучитьИсполняемыеНастройкиКомпоновкиДанных();
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных();
МакетКомпоновки = КомпоновщикМакета.Выполнить(Схема, Настройки, , , Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ТЗ = Новый ТаблицаЗначений;
ПроцессорВывода.УстановитьОбъект(ТЗ);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
Возврат ТЗ;
КонецФункции
При запуске такого кода разработчики часто обнаруживают две проблемы:
Выясним причины такого поведения платформы 1С:
ПолучитьИсполняемыеНастройкиКомпоновкиДанных() оптимизирует запрос к СУБД. Если колонка «Ссылка» или «Номенклатура» скрыта на форме у пользователя (свойство Видимость = Ложь) или вообще не выведена в таблицу формы, платформа автоматически исключает это поле из выбранных полей компоновщика. В результате в результирующей таблице значений этого поля просто не будет.ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений мог правильно сформировать строки в коллекции, в структуре настроек СКД обязательно должна присутствовать хотя бы одна группировка. Если пользовательские настройки динамического списка не предполагают группировок, структура компоновщика оказывается пустой, генератор не создает детальные записи, и на выходе мы получаем пустую таблицу значений.---
Чтобы гарантированно получить таблицу значений с нужной колонкой (например, Ссылка) и заполненными строками, нам необходимо программно скорректировать настройки перед передачей их в компоновщик макета. Мы принудительно очистим выбранные поля, добавим туда только интересующие нас реквизиты и создадим структуру детальных записей.
Рассмотрим детальный и рабочий пример кода:
&НаСервере
Функция ПолучитьОтобраннуюНоменклатуруИзСписка() Экспорт
// 1. Получаем исполняемую схему и настройки динамического списка
Схема = Элементы.СписокНоменклатуры.ПолучитьИсполняемуюСхемуКомпоновкиДанных();
Настройки = Элементы.СписокНоменклатуры.ПолучитьИсполняемыеНастройкиКомпоновкиДанных();
// 2. Очищаем выбранные поля и принудительно добавляем только Ссылку.
// Это гарантирует наличие колонки в ТЗ, даже если она скрыта на форме.
Настройки.Выбор.Элементы.Очистить();
ВыбранноеПоле = Настройки.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("Ссылка");
ВыбранноеПоле.Использование = Истина;
// 3. Гарантируем структуру детальных записей, чтобы избежать пустой ТЗ
Настройки.Структура.Очистить();
ГруппаДетальныеЗаписи = Настройки.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
ГруппаДетальныеЗаписи.Использование = Истина;
// Добавляем автовыбранные поля для детальной группировки
АвтоПоле = ГруппаДетальныеЗаписи.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
АвтоПоле.Использование = Истина;
// 4. Компонуем макет для коллекции значений
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных();
МакетКомпоновки = КомпоновщикМакета.Выполнить(Схема, Настройки, , , Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
// 5. Инициализируем процессор компоновки
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);
// 6. Выводим результат в Таблицу Значений
ТаблицаРезультат = Новый ТаблицаЗначений;
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(ТаблицаРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
Возврат ТаблицаРезультат;
КонецФункции
Этот способ отлично работает в подавляющем большинстве случаев. На выходе мы гарантированно получаем таблицу значений с колонкой Ссылка, заполненную элементами справочника, которые соответствуют всем установленным пользователем отборам.
---
Прогон данных через связку ПроцессорКомпоновкиДанных и ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений является довольно тяжелой операцией для сервера 1С, особенно если справочник содержит десятки тысяч элементов. Платформа тратит много процессорного времени и оперативной памяти на абстракции компоновщика.
Если ваша единственная цель — получить массив или таблицу ссылок для дальнейшего использования в собственном запросе (например, для заполнения документа), мы можем оптимизировать этот процесс. Мы заставим СКД только сгенерировать текст SQL-запроса и заполнить параметры, а сам запрос выполним через стандартный и быстрый объект Запрос.
Разберем этот продвинутый метод по шагам:
&НаСервере
Функция ПолучитьМассивСсылокБыстрымСпособом()
Схема = Элементы.СписокНоменклатуры.ПолучитьИсполняемуюСхемуКомпоновкиДанных();
Настройки = Элементы.СписокНоменклатуры.ПолучитьИсполняемыеНастройкиКомпоновкиДанных();
// Принудительно выбираем только Ссылку
Настройки.Выбор.Элементы.Очистить();
ВыбранноеПоле = Настройки.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("Ссылка");
ВыбранноеПоле.Использование = Истина;
Настройки.Структура.Очистить();
ГруппаДетальныеЗаписи = Настройки.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
ГруппаДетальныеЗаписи.Использование = Истина;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных();
МакетКомпоновки = КомпоновщикМакета.Выполнить(Схема, Настройки, , , Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
// Извлекаем текст запроса, сгенерированный системой компоновки данных
ИмяНабораДанных = Схема.НаборыДанных[0].Имя; // Обычно это "НаборДанныхДинамическогоСписка" или "НаборДанных1"
ТекстЗапросаСКД = МакетКомпоновки.НаборыДанных[ИмяНабораДанных].Запрос;
// Создаем обычный запрос
Запрос = Новый Запрос;
Запрос.Текст = ТекстЗапросаСКД;
// Переносим параметры из макета компоновки в наш запрос
Для Каждого ПараметрМакета Из МакетКомпоновки.Параметры Цикл
Запрос.УстановитьПараметр(ПараметрМакета.Имя, ПараметрМакета.Значение);
КонецЦикла;
// Выполняем легкий запрос напрямую к СУБД
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Возврат РезультатЗапроса.Выгрузить().ВыгрузитьКолонку("Ссылка");
КонецЕсли;
Возврат Новый Массив;
КонецФункции
Этот подход работает в несколько раз быстрее первого метода, экономит оперативную память сервера и возвращает готовый одномерный массив ссылок Массив, который чрезвычайно удобно передавать в качестве параметра в другие запросы.
---
Если вы работаете в современной типовой конфигурации 1С (например, 1С:Бухгалтерия предприятия 3.0, 1С:ERP или 1С:Управление торговлей 11), в вашей системе уже развернута Библиотека Стандартных Подсистем (БСП). Разработчики БСП создали ряд полезных общих модулей для работы с отборами компоновщика.
Проанализируем возможности общего модуля КомпоновкаДанныхКлиентСервер. Если вам не нужно выгружать тысячи строк, а требуется лишь проанализировать, какие именно параметры или элементы отбора установил пользователь на форме (например, конкретную группу номенклатуры или конкретный склад), вы можете извлечь их напрямую, избегая тяжелых запросов к базе данных.
Пример получения структуры установленных пользователем отборов:
&НаСервере
Процедура АнализОтборовПользователя()
// Получаем пользовательские настройки динамического списка
ПользовательскиеНастройки = СписокНоменклатуры.КомпоновщикНастроек.ПользовательскиеНастройки;
// Используем функцию БСП для поиска элемента отбора по имени поля
ЭлементОтбора = КомпоновкаДанныхКлиентСервер.НайтиЭлементОтбора(ПользовательскиеНастройки, "Родитель");
Если ЭлементОтбора <> Неопределено И ЭлементОтбора.Использование Тогда
ЗначениеОтбора = ЭлементОтбора.ПравоеЗначение;
// Теперь мы знаем, какую группу номенклатуры выбрал пользователь в качестве отбора,
// и можем использовать эту группу напрямую в логике заполнения ТЧ.
ПоказатьПредупреждение(, "Выбран отбор по группе: " + ЗначениеОтбора);
КонецЕсли;
КонецПроцедуры
Такой подход исключает необходимость выполнения ресурсоемких операций компоновки и выгрузки всей таблицы номенклатуры, если логика вашей внешней обработки может быть оптимизирована простым знанием параметров фильтрации.
---
При работе с динамическими списками и выгрузкой их результатов обязательно учитывайте технические особенности платформы 1С:
rphost) и зависанию сеанса. Рекомендация: всегда проверяйте перед выгрузкой, установлены ли отборы, или программно ограничивайте количество выбираемых записей (например, используя конструкцию ВЫБРАТЬ ПЕРВЫЕ 1000 в схеме).ПолучитьИсполняемыеНастройкиКомпоновкиДанных() автоматически выполняет полнотекстовый поиск СУБД и неявно преобразует результаты поиска в отбор по списку уникальных идентификаторов (GUID). Это отработает корректно только в том случае, если на сервере 1С актуализирован индекс полнотекстового поиска. Если поиск не возвращает ожидаемых данных в ТЗ, проверьте состояние индекса ППС в администрировании базы данных.