Как реализовать отбор в поле выбора по значениям из табличной части другого документа

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

В процессе разработки конфигураций на платформе 1С:Предприятие 8.3 часто возникает задача ограничения списка доступных для выбора значений. Типичный пример: в документе приготовления блюда необходимо разрешить выбор только тех блюд, которые были ранее указаны в конкретном заказе клиента. Поскольку стандартные механизмы платформы («Связи параметров выбора») по умолчанию работают с реквизитами, а не с содержимым табличных частей связанных объектов, нам потребуется применить программный подход.

В этой статье мы подробно разберем, как реализовать такой функционал, используя управляемые формы, работу со списками выбора и оптимизированные запросы к базе данных. Мы пройдем путь от простого решения «в лоб» до профессиональных методов оптимизации кода.

Метод 1. Использование списка выбора и события «ПриИзменении»

Рассмотрим ситуацию, когда у нас есть документ ПриготовлениеБлюда. В нем присутствует реквизит Заказ (ссылка на документ Заказы) и реквизит Блюдо (ссылка на справочник ТехнологическиеКартыБлюд). Наша цель — чтобы при нажатии на поле Блюдо пользователь видел только те позиции, которые физически есть в табличной части выбранного заказа.

Проанализируем алгоритм реализации этого метода. Первым делом нам необходимо создать обработчик события ПриИзменении для поля Заказ. Это нужно для того, чтобы как только пользователь выбрал или сменил заказ, список доступных блюд мгновенно обновился.

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


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

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

КонецФункции

Теперь проанализируем процедуру, которая будет вызывать эту функцию на клиенте при выборе заказа:


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

КонецПроцедуры

Важный нюанс: Режим выбора из списка

Многие начинающие разработчики сталкиваются с тем, что код написан верно, но при нажатии на кнопку выбора всё равно открывается полный справочник. Выясним причину: для того чтобы поле работало как выпадающий список с нашими значениями, необходимо установить свойство РежимВыбораИзСписка в значение Истина. Это можно сделать двумя способами:

  1. В конфигураторе: откройте свойства элемента формы Блюдо и установите галочку «Выбирать из списка».
  2. Программно: добавьте в процедуру ПриСозданииНаСервере строку Элементы.Блюдо.РежимВыбораИзСписка = Истина;.

Метод 2. Сохранение данных при открытии формы

Разберем ситуацию: пользователь выбрал заказ, выбрал блюдо и сохранил документ. При следующем открытии документа список выбора элемента формы будет пустым, так как он не хранится в базе данных, а создается динамически. Если мы не заполним список выбора при открытии, система может некорректно отобразить значение или не позволить его изменить.

Для решения этой проблемы обязательно пропишем логику в процедуре ПриСозданииНаСервере:


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

Метод 3. Профессиональный подход (использование Запроса)

Рассмотрим более эффективный способ получения данных. Прямое обращение к объекту через СсылкаНаЗаказ.ЗаказываемыеБлюда заставляет платформу считывать весь документ целиком из базы данных в память сервера. Если документ «Заказ» содержит сотни строк или тяжелые реквизиты, это замедлит работу. Проанализируем, как сделать это лучше с помощью объекта Запрос.


&НаСервереБезКонтекста
Функция ПолучитьСписокБлюдЗапросом(Знач СсылкаНаЗаказ)
    
    Список = Новый СписокЗначений;
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ РАЗЛИЧНЫЕ
        |   ЗаказыЗаказываемыеБлюда.Блюдо КАК Блюдо
        |ИЗ
        |   Документ.Заказы.ЗаказываемыеБлюда КАК ЗаказыЗаказываемыеБлюда
        |ГДЕ
        |   ЗаказыЗаказываемыеБлюда.Ссылка = &Ссылка";
    
    Запрос.УстановитьПараметр("Ссылка", СсылкаНаЗаказ);
    
    РезультатЗапроса = Запрос.Выполнить();
    Если Не РезультатЗапроса.Пустой() Тогда
        Выборка = РезультатЗапроса.Выбрать();
        Пока Выборка.Следующий() Цикл
            Список.Добавить(Выборка.Блюдо);
        КонецЦикла;
    КонецЕсли;
    
    Возврат Список;

КонецФункции

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

Метод 4. Альтернатива через событие «НачалоВыбора»

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


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

КонецПроцедуры

В данном сценарии мы используем механизм фиксированных отборов. Когда мы передаем в параметр Отбор массив или список значений для поля Ссылка, платформа автоматически применяет вид сравнения «В списке», и пользователь увидит в справочнике только нужные позиции.

Резюме и рекомендации

Мы проанализировали несколько способов реализации отбора по табличной части. Подведем итоги выбора метода:

Применяя эти техники, вы создадите надежный и отзывчивый интерфейс, который минимизирует ошибки ввода данных и сделает работу пользователя в конфигурации «Кафе» максимально комфортной.

← На главную