Зачем?
Динамический Список - это очень мощный и удобный инструмент пользователя для работы с данными базы. Но у него есть ограничение - работа только с данными базы. Хотя иногда хочется их дополнительно "обогатить".
Например, при подборе товара хочется видеть не только доступный складской остаток, но и визуально уменьшать его на позиции, которые уже добавлены в документ. Или при подборе сделок во время разнесения платежей по контрагенту хочется видеть состояние взаиморасчетов с учетом уже внесенных данных (часто финансисты даже одну сделку разносят на десяток статей затрат или направлений деятельности). Или, предположим, что мы делаем рабочее место по групповому изменению данных и хотим получить "предварительный просмотр" будущих изменений...
Подобные задачи прямо просятся на простое решение с помощью Таблицы Значений, содержимое которой переносим во временную таблицу, с которой далее уже делаем все, что нам нужно. В запросах это отлично отрабатывает. И в отчетах на СКД (система компоновки данных) тоже не является проблемой закинуть к источникам содержимое произвольной таблицы (а ведь Динамический Список - это же почти СКД).
Мой опыт и анализ форумов по ключевым словам показывают, что есть две основные альтернативы для решения данного класса задач:
- Отказаться от Динамического Списка в пользу тандема из табличного поля на форме и таблицы значений как реквизит формы (реже дерева значения).
- Применяя Динамический Список, получать требуемые произвольные данные из служебного регистра сведений (разработчики даже делятся лайфхаками по использованию своих номеров сеанса в качестве измерений, чтобы не мешать работе пользователей избыточными блокировками).
Но первый вариант сильно ограничивает возможности для пользователя и для "очевидных вещей" потребуется написать много не очевидного для пользователей кода (что потом обычно вызывает удивление на этапе оплаты трудозатрат). Второй вариант хоть и решает задачу, но замедляет общую скорость работы из-за постоянного взаимодействия с СУБД и затрат на поддержку транзакций при записи служебной информации (особенно, если разработчик плохо продумал структуру хранения).
Почему нет?
Обычно в запросах для передачи Таблицы Значений для заполнения Временной Таблицы мы используем установку этой таблицы в качестве параметра, а далее используем этот параметр в секции ИЗ нашей Временной Таблицы. Если мы попробуем сделать такое с Динамическим Списком, то получим ошибку еще при попытке внести данные в конструкторе этого реквизита формы:
Проблема заключена в типе ТаблицаЗначений ('{http://v8.1c.ru/8.1/data/core}ValueTable'), который необъяснимо невзлюбили и запретили перемещать между клиентом и сервером (состояние на версию 8.3.22).
По этой причине уже десятилетие разработчики для передачи данных между серверным и клиентским контекстами выкручиваются с помощью массива структур, где ключи повторяют колонки исходной таблицы. Но в случае с Динамическим Списком - это не подходит. Ведь сама попытка использования Таблицы Значений в качестве параметра, предполагает её сохранение в компоновщике настроек, но компоновщик настроек Динамического Списка обязан свободно перемещаться между серверным и клиентским контекстом и потому не имеет права содержать в себе элементы типа ТаблицаЗначений.
Верность моего утверждения можно проверить, если в качестве текста запроса для Динамического Списка установить запрос, который ранее не получилось указать в редакторе формы. При попытке возврата на клиент получим ошибку передачи Таблицы Значений (даже если не будем ее устанавливать). Дело в том, что компоновщик настроек самостоятельно анализирует текст запроса, видит параметр в секции ИЗ и формирует его описание именно как значение типа ТаблицаЗначений (другие типы параметров не могут выступать источниками данных в запросах):
Что нам доступно?
Как известно, инициировать Временную Таблицу можно не только параметром типа ТаблицаЗначений, но и с помощью результата выполнения другого запроса, если есть возможность передать менеджер временных таблиц. Но, к сожалению, Динамический Список в отличие от Запроса не поддерживает свойство МенеджерВременныхТаблиц.
Так же, несмотря на то, что Динамический Список использует технологию СКД, но он использует её ограниченно - в качестве источника данных можно передавать исключительно текст запроса. Никаких Таблиц Значений, Табличных Частей, Выборок из Результатов Запроса и тому подобного!
Итого мы имеем - нам доступно использование Временных Таблиц и нам доступно использование параметров, которые могут быть примитивных или ссылочных типов.
В принципе этого достаточно для решения нашей задачи. Напомню, что в описании запроса обязательной секцией является только ВЫБРАТЬ, но вовсе не ИЗ. Так же напомню, что ТаблицаЗначений - это всего лишь двухмерный массив, где на пересечении строк и колонок хранятся значения, которым разрешено быть параметрами Динамического Списка.
Описание концепции
Если нельзя напрямую использовать Таблицу Значений, то будем ее эмулировать с помощью программной верстки текста Временной Таблицы, где для каждой строки будет подзапрос, в котором в секции ВЫБРАТЬ будут перечислены параметры запроса с псевдонимами соответствующих колонок. В результате выполнения такого запроса мы получим требуемую Временную Таблицу, которой "обогатим" данные основного запроса.
Следовательно, мы можем написать функцию для общего модуля, которой на вход передадим Таблицу Значений, а на выходе получим текст запроса для Временной Таблицы и коллекцию параметров этого запроса с их значениями. Чтобы получить универсальный механизм, дополнительно передадим в эту функцию название для Временной Таблицы и текст запроса заглушки, который следует использовать для пустых таблиц.
В модуле формы с Динамическим Списком мы можем написать процедуру, которая будет передавать в общий модуль требуемые параметры и получать на выход данные для модификации Динамического Списка. Данную процедуру можно вызывать только раз при создании формы, если это была подчиненная форма с фиксированной Таблицей Значений, или при каждом редактировании данных в элементах формы, от значения которых должен зависеть Динамический Список.
Чтобы облегчить нашу работу с Динамическим Списком, в режиме редактора формы сразу зададим запрос-заглушку, который будет делать пустую Временную Таблицу. Тут же сделаем объединение основного источника с заглушкой и выполним то, ради чего все и затевали - добавим новые колонки или изменим данные в существующих. Потом в процедуре доработки Динамического Списка по маркеру ";" (разделитель запросов в пакете) мы с легкостью разделим исходный текст на заменяемую секцию Временной Таблицы и основную секцию, которую соединим с результатами функции общего модуля. Останется только установить новые параметры и получить требуемый результат.
Реализация
Для демонстрации выберем задачу "видеть актуальные складские остатки во время заполнения документа". В качестве данных нам потребуется справочник "Товары" и два документа - "Приход товаров" и "Расход товаров". Складские остатки будем фиксировать в регистре накопления "Наличие товаров" (оба документа будут регистраторами).
Пусть при создании или редактировании документа "Расход товара" мы хотим видеть актуальные складские остатки (остатки на текущую дату), которые будут уменьшаться на позиции, которые уже введены в документ. При этом механизм должен работать и для новых документов и для ранее записанных. При этом учтем, что документ может быть проведен и уже сделал движения расхода.
Начнем с главного в нашей концепции - универсальной функции формирования. Возвращать будем структуру с двумя элементами - текстом запроса и параметрами для этого запроса. Параметры так же будут структурой, где ключами будут названия параметров, а значения - значениями параметров. Текст процедуры ниже:
Далее, согласно концепции, нужно сделать запрос для Динамического Списка с заглушкой. Для текущей задачи с учетом требований к актуальности остатков независимо от состояния документа запрос будет иметь следующий вид:
Поскольку нам нужно компенсировать возможные движения документа, то передадим в Динамический Список ссылку на данный документ (даже если она пустая). Так же для реализации "подбора в документ" сделаем в динамическом списке обработку события выбора, по которому будем заполнять состав документа. Дополнительно отследим изменение содержимого документа, чтобы сообщить динамическому списку новые настройки.
В результате получили механизм, который работает точно так же как нам и требовалось. При этом правила верстки форм просты, понятны и легки в сопровождении. И в отличие от использования табличного поля у пользователя есть инструменты настройки формы, где он может сделать свои отборы, группировки, сортировки и условное оформление - т.е. пользователь самостоятельно настраивает свое рабочее место не мешая другим.