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