При разработке прикладных решений на платформе «1С:Предприятие 8» разработчики часто сталкиваются с иерархическими связями или подчиненными справочниками. Одна из классических задач — ограничить выборку элементов подчиненного справочника в зависимости от свойств его владельца. Например, у нас есть справочник Версии, который подчинен справочнику ПО. У владельца (справочника ПО) есть реквизит ВидПО (Перечисление). Нам необходимо сделать так, чтобы при выборе версии на форме отображались только те элементы, у владельцев которых ВидПО соответствует определенному значению (например, Платформа1С).
Попытка решить задачу «в лоб» через стандартную структуру отбора формы выбора (передав в качестве ключа связи Владелец.ВидПО) приводит к системной ошибке. Платформа ожидает в структуре отбора только непосредственные реквизиты выбираемого объекта и не умеет автоматически разыменовывать ссылки через точку.
В этой статье мы подробно разберем причины этого ограничения и рассмотрим три надежных практических способа решения данной задачи.
Давайте проанализируем ситуацию. Когда мы пишем следующий код:
ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("Отбор", Новый Структура("Владелец.ВидПО", ПредопределенноеЗначение("Перечисление.ВидыПО.Платформа1С")));
ОткрытьФорму("Справочник.Версии.ФормаВыбора", ПараметрыФормы, Элемент);
Платформа генерирует ошибку, поскольку ключ структуры отбора анализируется как имя поля основной таблицы динамического списка формы выбора. Поля Владелец.ВидПО в физической таблице справочника Версии не существует — это реквизит связанной таблицы. Платформа 1С в параметре Отбор поддерживает обращение исключительно к «плоским» реквизитам текущей таблицы (например, просто Владелец или ПометкаУдаления).
Аналогичное ограничение действует и для механизма «Связи параметров выбора» в свойствах элементов формы в конфигураторе. Путь к данным не может содержать более одной точки, поэтому напрямую прописать сложную связь параметров выбора в свойствах поля у нас также не получится.
К счастью, обойти это ограничение можно несколькими путями. Рассмотрим их подробнее.
Это наиболее гибкий и современный способ, который активно используется в управляемых формах. Динамические списки, на базе которых строятся формы выбора, работают под управлением Системы компоновки данных. Компоновщик настроек СКД, в отличие от простых структур платформы, нативно поддерживает сложные пути к данным и разыменование ссылок через точку.
Для реализации этого способа мы перехватим стандартное событие начала выбора у поля ввода на форме и программно передадим в открываемую форму структуру фиксированных настроек компоновки.
Разберем по шагам реализацию в коде формы:
НачалоВыбора у нашего реквизита на форме.СтандартнаяОбработка = Ложь, чтобы предотвратить обычное открытие формы.ПолеКомпоновкиДанных.Посмотрим на пример программной реализации:
&НаКлиенте
Процедура РекомендованнаяВерсияНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
// Отменяем стандартное открытие формы выбора
СтандартнаяОбработка = Ложь;
// Создаем структуру параметров для открываемой формы
ПараметрыФормы = Новый Структура;
// Инициализируем настройки компоновки данных
НастройкиКД = Новый НастройкиКомпоновкиДанных;
// Добавляем новый элемент отбора
ЭлементОтбора = НастройкиКД.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
// Указываем путь к реквизиту владельца через точку
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Владелец.ВидПО");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
// Задаем целевое значение отбора
ЭлементОтбора.ПравоеЗначение = ПредопределенноеЗначение("Перечисление.ВидыПО.Платформа1С");
ЭлементОтбора.Использование = Истина;
// Делаем отбор скрытым для пользователя, чтобы он не мог его случайно отключить
ЭлементОтбора.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный;
// Помещаем настройки в параметры под специальным системным ключом
ПараметрыФормы.Вставить("ФиксированныеНастройки", НастройкиКД);
// Открываем форму выбора справочника версий с нашими настройками
ОткрытьФорму("Справочник.Версии.ФормаВыбора", ПараметрыФормы, Элемент);
КонецПроцедуры
Этот способ идеален тем, что вся фильтрация происходит на уровне СУБД в момент динамического считывания данных списка. Мы не перегружаем память сервера и клиента лишними выборками ссылок.
Если по каким-то причинам использование фиксированных настроек СКД кажется вам избыточным или вы хотите оставить пользователю возможность визуально видеть структуру отбора по владельцам, можно пойти по другому пути. Мы можем предварительно запросить из базы данных список всех владельцев (элементов справочника ПО), удовлетворяющих условию, а затем передать этот массив в стандартный отбор по реквизиту Владелец.
Платформа 1С устроена таким образом, что если в структуру Отбор по какому-либо реквизиту передается массив значений, система автоматически преобразует этот отбор в условие соответствия «В списке».
Рассмотрим пример реализации данного подхода:
&НаКлиенте
Процедура РекомендованнаяВерсияНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// Шаг 1. Получаем массив подходящих владельцев (справочник ПО) на сервере
МассивВладельцев = ПолучитьСписокВладельцевПлатформы1С();
// Шаг 2. Формируем стандартную структуру отбора
ПараметрыФормы = Новый Структура;
ОтборСтруктура = Новый Структура("Владелец", МассивВладельцев);
ПараметрыФормы.Вставить("Отбор", ОтборСтруктура);
// Шаг 3. Открываем форму с подготовленным списком владельцев
ОткрытьФорму("Справочник.Версии.ФормаВыбора", ПараметрыФормы, Элемент);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьСписокВладельцевПлатформы1С()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПО.Ссылка КАК Ссылка
|ИЗ
| Справочник.ПО КАК ПО
|ГДЕ
| ПО.ВидПО = ЗНАЧЕНИЕ(Перечисление.ВидыПО.Платформа1С)";
РезультатЗапроса = Запрос.Выполнить();
Возврат РезультатЗапроса.Выгрузить().ВыгрузитьКолонку("Ссылка");
КонецФункции
Важное предупреждение: этот способ отлично работает, если количество подходящих владельцев в вашей информационной базе относительно невелико (в пределах нескольких десятков или сотен). Если же справочник владельцев содержит десятки тысяч элементов, данный метод может вызвать серьезные просадки производительности при передаче огромного массива по сети между сервером и клиентом, а также может упереться в ограничения СУБД по количеству параметров в запросе.
Предыдущие два способа отлично решают задачу на конкретной форме элемента. Но что делать, если пользователь пытается ввести наименование версии вручную прямо в поле ввода (быстрый выбор по строке), или если этот отбор должен работать абсолютно во всех формах конфигурации, где выбирается рекомендованная версия?
В концепции современной разработки на платформе 1С наиболее архитектурно правильным решением является перенос бизнес-логики фильтрации данных из модулей конкретных форм в Модуль менеджера соответствующего справочника. Для этого используется системный обработчик ОбработкаПолученияДанныхВыбора.
Давайте реализуем эту логику. В модуле менеджера справочника Версии пропишем перехват получения данных выбора:
// Помещаем код в Модуль менеджера справочника Версии
Процедура ОбработкаПолученияДанныхВыбора(ДанныеВыбора, Параметры, СтандартнаяОбработка)
// Проверяем наличие нашего специализированного параметра фильтрации
Если Параметры.Свойство("ОтборПоВидуПО") Тогда
// Отменяем стандартную генерацию списка выбора платформой
СтандартнаяОбработка = Ложь;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ
| Версии.Ссылка КАК Ссылка,
| Версии.Наименование КАК Представление
|ИЗ
| Справочник.Версии КАК Версии
|ГДЕ
| Версии.Владелец.ВидПО = &ВидПО
| И НЕ Версии.ПометкаУдаления";
Запрос.УстановитьПараметр("ВидПО", ПредопределенноеЗначение("Перечисление.ВидыПО.Платформа1С"));
// Учитываем текст, который пользователь уже начал вводить в поле ввода
Если ЗначениеЗаполнено(Параметры.СтрокаПоиска) Тогда
Запрос.Текст = Запрос.Текст + " И Версии.Наименование ПОДОБНО &СтрокаПоиска";
Запрос.УстановитьПараметр("СтрокаПоиска", "%" + Параметры.СтрокаПоиска + "%");
КонецЕсли;
РезультатЗапроса = Запрос.Выполнить();
ТаблицаДанных = РезультатЗапроса.Выгрузить();
СписокВыбора = Новый СписокЗначений;
Для Каждого Строка Из ТаблицаДанных Цикл
СписокВыбора.Добавить(Строка.Ссылка, Строка.Представление);
КонецЦикла;
ДанныеВыбора = СписокВыбора;
КонецЕсли;
КонецПроцедуры
Теперь на форме элемента, где располагается поле выбора рекомендованной версии, нам достаточно при начале выбора или при автоподборе просто передать этот параметр в структуру параметров выбора:
&НаКлиенте
Процедура РекомендованнаяВерсияНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
ПараметрыВыбора = Новый Структура;
ПараметрыВыбора.Вставить("ОтборПоВидуПО", Истина);
ПараметрыВыбора.Вставить("СтрокаПоиска", Элемент.ТекстРедактирования);
// Передаем параметр "ОтборПоВидуПО", который будет обработан модулем менеджера
ОткрытьФорму("Справочник.Версии.ФормаВыбора", ПараметрыВыбора, Элемент);
КонецПроцедуры
Использование обработчика ОбработкаПолученияДанныхВыбора гарантирует единообразие работы интерфейса: отбор будет корректно применяться как при открытии полноценного диалогового окна выбора, так и при обычном наборе текста с клавиатуры (вводе по строке).
Мы детально изучили три эффективных метода решения проблемы отбора по реквизитам владельца через точку:
Выбирайте наиболее подходящий для вашего проекта вариант, ориентируясь на объемы данных в информационных базах ваших пользователей и требования к кастомизации интерфейса.