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