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