Как правильно установить отбор по полю составного типа в динамическом списке на управляемых формах 1С?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей IT и автоматизация бизнеса
← На главную

При работе с динамическими списками в системе 1С:Предприятие 8.3, особенно на управляемых формах, часто возникает необходимость устанавливать отборы по полям, которые имеют составной тип. Например, поле Контрагент может хранить ссылки как на справочник Справочник.Контрагенты, так и на Справочник.Организации. Попытка установить стандартный отбор программно или интерактивно для такого поля может привести к ошибкам типизации или некорректной фильтрации данных. В данном материале мы подробно рассмотрим, почему возникают такие проблемы и как их эффективно решить. Стоит отметить, что настройка отборов — это лишь одна из многих задач по кастомизации интерфейса (поможет готовая настраиваемая панель фильтров для форм списков); другой популярной задачей является, например, реализация выбора строк через флажки в динамическом списке.

Суть проблемы: стандартный механизм отбора компоновки данных, используемый динамическим списком, не всегда может автоматически определить корректный тип значения для сравнения, если поле имеет составной тип. Это особенно проявляется, когда мы пытаемся установить отбор на значение, которое не соответствует первому типу в списке составных типов поля, или когда мы ожидаем интерактивной фильтрации по единому значению, которое может быть любого из допустимых составных типов.

Давайте разберем основные методы решения этой задачи.

Решение 1: Использование параметров запроса динамического списка

Этот подход является наиболее чистым и рекомендуемым для установки отбора по одному конкретному значению составного типа. Суть метода заключается в добавлении параметра непосредственно в текст запроса динамического списка и последующей программной установке значения этого параметра.

Шаг 1: Добавляем параметр в запрос динамического списка

Прежде всего, нам необходимо модифицировать текст запроса, на котором основан наш динамический список. Мы добавим условие ГДЕ, которое будет ссылаться на наш новый параметр.

Предположим, у нас есть запрос, который выбирает данные из какого-либо документа или регистра, и в нем присутствует поле Контрагент составного типа.


ВЫБРАТЬ
    Документ.Ссылка,
    Документ.Номер,
    Документ.Дата,
    Документ.Контрагент
ИЗ
    Документ.РеализацияТоваровУслуг КАК Документ
ГДЕ
    Документ.Контрагент = &ВыбранныйОбъектОтбора

В этом примере &ВыбранныйОбъектОтбора — это наш параметр. Система компоновки данных (СКД), которая лежит в основе динамического списка, автоматически определит, что этот параметр может принимать значения различных типов, соответствующих полю Документ.Контрагент.

Шаг 2: Программно устанавливаем значение параметра

После того как параметр добавлен в запрос, мы можем управлять его значением программно из клиентского кода формы. Это делается с помощью метода ДинамическийСписок.Параметры.УстановитьЗначение().

Предположим, у нас на форме есть поле выбора (например, реквизит формы ВыбранныйКонтрагентНаФорме), куда пользователь вводит нужное значение для отбора. При изменении этого поля мы будем устанавливать параметр динамического списка.


&НаКлиенте
Процедура ПриИзмененииВыбранногоКонтрагента(Элемент)
    // Получаем значение для отбора из реквизита формы
    // Этот реквизит формы должен иметь тип, соответствующий составному типу поля Контрагент
    // (например, составной тип, включающий Справочник.Контрагенты и Справочник.Организации)
    ЗначениеОтбора = ВыбранныйКонтрагентНаФорме; 

    Если ЗначениеЗаполнено(ЗначениеОтбора) Тогда
        // Устанавливаем значение параметра динамического списка
        ДинамическийСписок.Параметры.УстановитьЗначение("ВыбранныйОбъектОтбора", ЗначениеОтбора);
    Иначе
        // Если значение не заполнено, сбрасываем отбор
        ДинамическийСписок.Параметры.УстановитьЗначение("ВыбранныйОбъектОтбора", Неопределено);
    КонецЕсли;
    
    // Обновляем динамический список для применения нового отбора
    ДинамическийСписок.ОбновитьСписок();
КонецПроцедуры

Преимущества этого метода:

  1. Простота и чистота: код получается лаконичным, а отбор осуществляется на уровне запроса, что часто более производительно.
  2. Корректная типизация: СКД сама заботится о правильной интерпретации типа передаваемого значения, если оно соответствует одному из типов составного поля.
  3. Интерактивность: позволяет пользователю выбирать одно значение для отбора, которое может быть любого из допустимых составных типов.

Важно: убедитесь, что реквизит формы ВыбранныйКонтрагентНаФорме имеет подходящий тип (например, составной тип, который включает все возможные типы для поля Контрагент), чтобы пользователь мог выбрать как контрагента, так и организацию.

Решение 2: Модификация запроса с использованием псевдо-поля (виртуального реквизита)

Этот метод полезен, когда мы хотим использовать стандартный механизм отбора динамического списка (через ДинамическийСписок.Отбор.Элементы.Добавить()) или интерактивный отбор пользователем, но при этом столкнулись с проблемой составного типа. Идея заключается в том, чтобы создать в запросе динамического списка новое "псевдо-поле", которое всегда будет иметь определенный, либо явно заданный, либо унифицированный тип, по которому уже можно легко отбирать.

Шаг 1: Создаем псевдо-поле в запросе

Мы используем конструкцию ВЫБОР КОГДА ... ТОГДА ... КОНЕЦ КАК для создания виртуального реквизита в запросе. Это позволит нам явно указать, как интерпретировать поле составного типа для целей отбора.


ВЫБРАТЬ
    Документ.Ссылка,
    Документ.Номер,
    Документ.Дата,
    Документ.Контрагент,
    ВЫБОР
        КОГДА Документ.Контрагент ССЫЛКА Справочник.Контрагенты ТОГДА Документ.Контрагент
        КОГДА Документ.Контрагент ССЫЛКА Справочник.Организации ТОГДА Документ.Контрагент
        ИНАЧЕ NULL
    КОНЕЦ КАК ОбъектОтбораПоКонтрагенту
ИЗ
    Документ.РеализацияТоваровУслуг КАК Документ

Здесь мы создали поле ОбъектОтбораПоКонтрагенту. Это поле будет иметь такой же составной тип, как и Документ.Контрагент. Однако, иногда этого может быть недостаточно, если СКД всё равно "путается" при интерактивном отборе. В более сложных случаях можно преобразовать его к строковому представлению или к общему типу-характеристике, но это усложнит интерактивный выбор.

В некоторых случаях, если нам нужен именно интерактивный отбор по *одному* значению, и мы хотим, чтобы это значение могло быть либо Контрагентом, либо Организацией, то вышеприведенный запрос с псевдо-полем, которое является "чистым" полем составного типа, может оказаться вполне рабочим. Главное — это чтобы само поле Документ.Контрагент было корректно определено в схеме компоновки данных как составное поле.

Шаг 2: Установка отбора по псевдо-полю

Теперь, когда у нас есть поле ОбъектОтбораПоКонтрагенту в динамическом списке, мы можем использовать его для стандартного отбора:


// Предполагаем, что ВыбранныйКонтрагентНаФорме - это реквизит формы с составным типом
// или выбранное значение
ЗначениеОтбора = ВыбранныйКонтрагентНаФорме; 

Если ЗначениеЗаполнено(ЗначениеОтбора) Тогда
    ЭлементОтбора = ДинамическийСписок.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
    ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ОбъектОтбораПоКонтрагенту");
    ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
    ЭлементОтбора.ПравоеЗначение = ЗначениеОтбора;
    ЭлементОтбора.Использование = Истина;
Иначе
    // Если нужно сбросить отбор, то ищем и удаляем соответствующий элемент
    Для Каждого ЭлОтбора Из ДинамическийСписок.Отбор.Элементы Цикл
        Если ТипЗнч(ЭлОтбора) = Тип("ЭлементОтбораКомпоновкиДанных") И ЭлОтбора.ЛевоеЗначение.ИмяПоля = "ОбъектОтбораПоКонтрагенту" Тогда
            ДинамическийСписок.Отбор.Элементы.Удалить(ЭлОтбора);
            Прервать;
        КонецЕсли;
    КонецЦикла;
КонецЕсли;

Этот подход позволяет использовать более стандартные методы работы с отборами, но требует модификации запроса. Процесс программного создания элементов отбора можно сделать более лаконичным, используя готовые обертки, например, СКДБилдер, который упрощает программное создание СКД. Иногда это может быть менее гибко, чем использование параметров, если сценарий отбора очень динамический.

Решение 3: Группировка отбора по каждому типу (для множественного выбора или специфичных сценариев)

В некоторых случаях, особенно если нам нужно отбирать по нескольким значениям или по разным типам одновременно (например, "выбери этого контрагента ИЛИ эту организацию"), можно создать группу отбора с типом "ИЛИ". Этот метод может быть менее удобным для интерактивного выбора одного значения, но полезен для программной установки сложных отборов.

Рассмотрим пример, где нам нужно отобрать записи, связанные с конкретным контрагентом ИЛИ с конкретной организацией:


// Сначала очистим существующий отбор, если он есть, чтобы не было дублирования
ДинамическийСписок.Отбор.Элементы.Очистить();

// Создаем элемент отбора для Контрагента
ЭлементОтбораКонтрагент = ДинамическийСписок.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбораКонтрагент.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбораКонтрагент.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Контрагент");
ЭлементОтбораКонтрагент.ПравоеЗначение = Справочники.Контрагенты.НайтиПоКоду("000000001"); // Пример: конкретный контрагент
ЭлементОтбораКонтрагент.Использование = Истина;

// Создаем элемент отбора для Организации
ЭлементОтбораОрганизация = ДинамическийСписок.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбораОрганизация.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбораОрганизация.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Контрагент");
ЭлементОтбораОрганизация.ПравоеЗначение = Справочники.Организации.НайтиПоКоду("000000002"); // Пример: конкретная организация
ЭлементОтбораОрганизация.Использование = Истина;

// Создаем группу отбора с типом "ИЛИ" и добавляем в нее наши элементы
ГруппаОтбора = ДинамическийСписок.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
ГруппаОтбора.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИли;
ГруппаОтбора.Элементы.Добавить(ЭлементОтбораКонтрагент);
ГруппаОтбора.Элементы.Добавить(ЭлементОтбораОрганизация);
ГруппаОтбора.Использование = Истина;

ДинамическийСписок.ОбновитьСписок();

Недостатки этого метода для интерактивного выбора одного значения:

  1. Сложность управления: для интерактивного выбора одного значения пользователю будет сложно работать с такой структурой.
  2. Избыточность: для простой фильтрации по одному значению этот подход избыточен.

Этот метод подходит, скорее, для заранее определенных, сложных фильтров, которые программно устанавливаются и редко меняются пользователем.

Особенности работы с СКД и составными типами

Важно понимать, что система компоновки данных (СКД), которая лежит в основе динамических списков, обладает некоторыми особенностями при работе с полями составных типов. Это мощный механизм, позволяющий решать даже такие нестандартные задачи, как использование двух разных схем в одном отчете СКД. Когда вы формируете запрос для динамического списка, СКД анализирует его структуру. Если в запросе поле явно определено как составной тип (например, реквизит справочника или документа), СКД обычно корректно обрабатывает его. Однако проблемы могут возникнуть в следующих случаях:

  1. Результат объединения или сложных выражений: если поле составного типа является результатом сложного выражения, ЛЕВОГО СОЕДИНЕНИЯ, или ОБЪЕДИНИТЬ ВСЕ, где типы могут быть неявно преобразованы или потеряны, СКД может некорректно интерпретировать его тип для целей отбора.
  2. Несоответствие типов: если вы пытаетесь установить отбор по значению, тип которого *не входит* в составной тип поля, даже если оно логически похоже (например, попытка отбора по строке, когда поле ожидает ссылку), возникнет ошибка.

Поэтому всегда старайтесь максимально явно определять типы полей в запросе, особенно если они используются для отбора. При использовании параметров запроса СКД получает значение параметра и пытается сопоставить его с одним из типов поля в условии ГДЕ, что обычно работает успешно.

Заключение

Наиболее эффективным и рекомендованным способом для установки отбора по полю составного типа в динамическом списке является использование параметров запроса. Этот метод обеспечивает чистоту кода, корректную обработку типов и хорошую производительность. Если же возникает необходимость в более сложной логике отбора или требуется интерактивная работа с самим объектом отбора, можно рассмотреть создание псевдо-поля в запросе или, в крайних случаях, группировку отборов по типу "ИЛИ". Всегда анализируйте свой конкретный сценарий и выбирайте тот подход, который наилучшим образом соответствует вашим требованиям к гибкости, производительности и удобству использования.

Мы настоятельно рекомендуем начинать с первого метода, основанного на параметрах запроса, как самого универсального и надежного. Только если он по какой-то причине не подходит для вашей специфической задачи, следует рассматривать более сложные модификации запроса или программное формирование групп отбора.

← На главную