При разработке сложных интерфейсов на управляемых формах 1С (есть готовый конструктор адаптивных интерфейсов и рабочих столов) мы часто сталкиваемся с необходимостью отфильтровать ДинамическийСписок по списку значений, который формируется программно, или вовсе использовать внешнюю таблицу данных как источник для списка. Часто при этом требуется не просто вывести данные, но и максимально прокачать список, добавив дополнительную информацию или новые колонки через расширения. Однако, если мы попытаемся передать объект ТаблицаЗначений напрямую в параметры запроса динамического списка, платформа выдаст ошибку или просто не отобразит данные.
Это происходит из-за архитектурных особенностей работы динамических списков: они исполняются в отдельном сеансе или транзакции СУБД, и контекст "толстого" клиента или даже серверного вызова формы им не всегда доступен напрямую в виде сложных объектов. В этой статье мы подробно разберем, как обойти это ограничение и реализовать динамический список по таблице значений (через временное хранилище).
Давайте проанализируем ситуацию. У нас есть ТаблицаЗначений, сформированная, например, из файла Excel или в результате сложных вычислений, которые невозможно уложить в один запрос. Мы хотим отобразить эти данные в списке, возможно, даже реализовав флажок в динамическом списке для интерактивного выбора строк, или использовать их для отбора (JOIN) с данными базы.
При попытке выполнить код вида Список.Параметры.УстановитьЗначениеПараметра("МояТЗ", ТЗ) система не сможет сериализовать таблицу для использования внутри механизма компоновки данных динамического списка, особенно если список обновляется автоматически. Для понимания механики процесса полезно изучить основы взаимодействия механизма компоновки данных с данными во временном хранилище.
Для решения этой задачи мы воспользуемся механизмом Временного хранилища и специальным синтаксисом языка запросов.
Разберем по шагам схему реализации. Нам необходимо выполнить три основных действия:
В процессе работы с внешними данными также может потребоваться программное перемещение колонок динамического списка для адаптации интерфейса под структуру загруженной таблицы.
Сначала подготовим данные. Важно помнить, что временное хранилище должно быть доступно на протяжении всей жизни формы. Поэтому при помещении данных мы будем использовать Уникальный Идентификатор формы (УИД), чтобы данные очистились автоматически при закрытии формы, либо, наоборот, чтобы они "жили" достаточно долго.
Посмотрим на пример кода, который мы разместим в модуле формы, например, в процедуре ПриСозданииНаСервере или в обработчике команды обновления:
&НаСервере
Процедура ЗагрузитьДанныеВСписок()
// 1. Создаем или получаем нашу таблицу значений
// В реальной задаче здесь может быть загрузка из файла или расчет
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
// Заполним тестовыми данными для примера
Для Счетчик = 1 По 10 Цикл
НоваяСтрока = ТЗ.Добавить();
// Заполнение строк...
КонецЦикла;
// 2. Помещаем ТЗ во временное хранилище
// Важно: привязываем к УникальномуИдентификатору формы,
// чтобы хранилище не очистилось после серверного вызова
АдресВХранилище = ПоместитьВоВременноеХранилище(ТЗ, УникальныйИдентификатор);
// 3. Устанавливаем адрес как параметр динамического списка
// Предполагаем, что у списка есть параметр "АдресДанных"
Элементы.НашДинамическийСписок.ТекстЗапроса = ПолучитьТекстЗапросаСписка();
НашДинамическийСписок.Параметры.УстановитьЗначениеПараметра("АдресДанных", АдресВХранилище);
КонецПроцедуры
Обратите внимание на использование метода ПоместитьВоВременноеХранилище. Второй параметр (УИД формы) критически важен. Если его не указать, данные могут быть удалены сборщиком мусора сразу после возврата управления с сервера на клиент, и при следующем обновлении списка пользователь увидит ошибку или пустой список.
Теперь нам нужно научить запрос динамического списка работать с этим адресом. В языке запросов 1С есть возможность обращаться к внешним данным, используя гибкую настройку списка, которая позволяет реализовать практически любые пожелания к отображению данных. В некоторых случаях требуется программное управление текстом запроса и параметрами для динамической подмены источника — для этого подойдёт отладка запросов и механизмов СКД.
Рассмотрим, как должен выглядеть текст запроса. Мы не можем просто написать "ВЫБРАТЬ ... ИЗ &Адрес". Нам нужно описать структуру получаемых данных, иначе система не поймет, какие поля есть во внешней таблице.
&НаСервере
Функция ПолучитьТекстЗапросаСписка()
Текст =
"ВЫБРАТЬ
| ВнешниеДанные.Номенклатура КАК Номенклатура,
| ВнешниеДанные.Количество КАК Количество
|ИЗ
| &АдресДанных КАК ВнешниеДанные";
Возврат Текст;
Функция
Если вы используете Произвольный запрос в настройках Динамического Списка в конфигураторе, платформа может "ругаться" при проверке запроса, так как она не знает типа параметра &АдресДанных. В этом случае текст запроса лучше устанавливать программно при создании формы, как показано выше.
Чаще всего внешняя ТЗ нужна не сама по себе, а для фильтрации данных из базы. Например, "показать продажи только по товарам из моего Excel-файла". В этом случае мы используем ПОЛНОЕ СОЕДИНЕНИЕ или ВНУТРЕННЕЕ СОЕДИНЕНИЕ.
Давайте разберем более сложный пример, где мы отбираем документы РеализацияТоваровУслуг, в которых есть товары из нашей ТЗ.
ТекстЗапроса =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| Док.Ссылка КАК Ссылка,
| Док.Дата,
| Док.Номер
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК Док
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ (
| ВЫБРАТЬ
| ТЗ.Номенклатура КАК Товар
| ИЗ
| &АдресТЗ КАК ТЗ
| ) КАК Фильтр
| ПО Док.Номенклатура = Фильтр.Товар";
В этом примере мы используем вложенный запрос к параметру &АдресТЗ как обычную таблицу. Это мощный инструмент, позволяющий строить очень гибкие отборы.
При работе с внешними источниками в запросах есть одна особенность: платформа не всегда корректно определяет типы полей. Если ваш динамический список глючит (не работает сортировка, не открываются ссылки), вам может потребоваться явно типизировать поля во временной таблице или внутри запроса.
Однако, для Динамического Списка, работающего через временное хранилище, "золотым правилом" является жесткая типизация самой ТаблицыЗначений перед помещением её в хранилище. Убедитесь, что вы создали колонки с правильными объектами ОписаниеТипов:
// Правильно:
ТЗ.Колонки.Добавить("Ссылка", Новый ОписаниеТипов("СправочникСсылка.Клиенты"));
// Неправильно (тип будет произвольным, запрос может не понять):
ТЗ.Колонки.Добавить("Ссылка");
Если ваша задача — просто отфильтровать список по набору ссылок (например, 100-200 конкретных документов), и вам не нужны дополнительные колонки, то использование таблицы значений может быть избыточным. В таких ситуациях можно применить отбор СКД по таблице значений или табличной части через стандартные механизмы компоновки.
Проанализируем более простой метод. Динамический список прекрасно принимает в параметры массивы и списки значений для оператора В (...).
// Создаем массив ссылок
МассивСсылок = Новый Массив;
МассивСсылок.Добавить(Ссылка1);
МассивСсылок.Добавить(Ссылка2);
// Передаем в параметр
НашДинамическийСписок.Параметры.УстановитьЗначениеПараметра("СписокОтбора", МассивСсылок);
И в тексте запроса:
ВЫБРАТЬ ...
ИЗ Справочник.Товары
ГДЕ Ссылка В (&СписокОтбора)
Этот метод работает быстрее и проще, но он не подходит, если массив очень большой или требуется соединение по нескольким полям.
Иногда при использовании временных таблиц внутри динамического списка можно встретить ошибки уровня СУБД (SDBL). Это часто связано с тем, что менеджер временных таблиц для динамического списка управляется платформой автоматически.
Запомните правило: не пытайтесь назначить динамическому списку свой МенеджерВременныхТаблиц. Изучите анализ причин возникновения ошибок SDBL при работе с временными таблицами, чтобы избежать подобных проблем. Используйте подход с передачей адреса временного хранилища — это штатный механизм для управляемых форм.
Мы выяснили, что прямая передача ТаблицыЗначений в динамический список невозможна. Для решения задачи мы создаем типизированную таблицу на сервере, помещаем её в хранилище и обращаемся к ней в запросе.
Используйте эти современные подходы к использованию внешних источников данных для создания гибких и функциональных интерфейсов. А если вам понадобится вывести результат работы пользователя в печатную форму, вы можете легко преобразовать настроенный динамический список в отчет СКД со всеми наложенными отборами.