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

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

Разберем ситуацию, с которой часто сталкиваются разработчики при работе с управляемыми формами: имеется табличная часть, в которой есть реквизит с составным типом данных (например, тип ДокументСсылка, включающий в себя все документы конфигурации). При стандартной настройке одиночный выбор работает идеально — платформа открывает диалог выбора типа, а затем список нужного документа. Однако, если пользователю требуется выбрать сразу несколько документов, возникают трудности. Даже если в форме подбора зажать клавишу Ctrl и выделить несколько строк, в исходную форму попадет только один элемент.

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

Шаг 1. Перехват события начала выбора

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


&НаКлиенте
Процедура ТоварыДокументНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
    
    // Отключаем стандартное окно выбора платформы
    СтандартнаяОбработка = Ложь;
    
    // Вызываем нашу экспортную процедуру для инициализации выбора типа
    ИнициироватьВыборТипаДокумента(Элемент);
    
КонецПроцедуры

Шаг 2. Программное формирование списка типов

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


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

Для получения синонима нам понадобится небольшая серверная функция:


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

Шаг 3. Асинхронный выбор типа документа

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


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

Шаг 4. Открытие формы выбора с параметром множественного выбора

После того как пользователь выбрал нужный тип из предложенного списка, сработает наше оповещение. В процедуре обработки оповещения мы определим имя нужной формы списка и откроем её. Ключевым моментом здесь является передача структуры параметров в метод ОткрытьФорму. Мы обязательно передаем параметр МножественныйВыбор со значением Истина. Также можно передать параметр ЗакрыватьПриВыборе, установив его в Ложь, если мы хотим, чтобы форма оставалась открытой для нескольких последовательных подборов.


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

Шаг 5. Обработка массива выбранных значений

Проанализируем итоговый этап. Когда у нас включен флаг множественного выбора, платформа передает в событие ОбработкаВыбора не одиночную ссылку, а массив выбранных значений. Обратите внимание: поскольку владельцем формы мы указали сам элемент табличной части, нам нужно корректно обработать этот массив, чтобы заполнить строки.

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


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

Альтернативный вариант: Универсальная форма подбора

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

  1. В эту форму при открытии передается описание доступных типов или АдресВХранилище.
  2. Форма сама отвечает за отрисовку списка доступных видов документов в виде дерева или списка.
  3. При выборе узла дерева форма показывает динамический список конкретного документа.
  4. Пользователь собирает выбранные документы в специальную корзину (табличную часть на форме подбора).
  5. По кнопке "Перенести в документ" форма закрывается и возвращает в ОповещениеОВыборе массив всех собранных ссылок.

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

← На главную