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

Программист 1С v8.3 (Обычные формы) Бухгалтерский учет
← На главную

В конфигурациях на обычных формах, таких как 1С:УПП 1.3 или 1С:КА 1.1, разработчики часто сталкиваются с задачей ограничения списка выбора. Типичный пример: при выборе основного средства в документе необходимо показать только те объекты, которые числятся за конкретным подразделением. Поскольку информация о местонахождении ОС хранится в периодическом регистре сведений, стандартные настройки отбора в конфигураторе не позволяют решить задачу напрямую. Разберем, как правильно реализовать этот механизм программно.

Анализ проблемы: почему стандартные методы не работают

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

Также часто пытаются добавить в форму выбора «виртуальную» колонку через событие ПриВыводеСтроки и записывать туда данные. Однако попытка присвоить значение в ДанныеСтроки.ИмяРеквизита вызовет ошибку «Поле объекта недоступно для записи», так как ДанныеСтроки предоставляют доступ к объекту базы данных в режиме «только чтение» для целей отображения.

Решение 1: Программное открытие формы с предопределенным отбором

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

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


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

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

Важные нюансы реализации

При реализации этого кода обратите внимание на следующие критические моменты:

  1. Владелец формы: В методе ПолучитьФормуВыбора вторым параметром мы передаем Элемент (само поле ввода). Это гарантирует, что при выборе значения в справочнике оно автоматически вернется в нужную ячейку табличной части документа. Если этого не сделать, форма откроется, но выбор в документ не «пробросится».
  2. СтандартнаяОбработка: Установка СтандартнаяОбработка = Ложь обязательна. Если этого не сделать, система сначала откроет вашу форму с отбором, а затем сразу же откроет стандартную форму без отбора поверх первой.
  3. Тип значения в отборе: При использовании ВидСравнения.ВСписке значением отбора обязательно должен быть объект типа СписокЗначений. Передача массива или колонки таблицы значений напрямую вызовет ошибку «Неверный тип значения».

Решение 2: Оптимизация отображения данных в списке

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

Выясним причину, по которой ПриВыводеСтроки может замедлять работу: если в списке 50 строк, запрос в этом событии выполнится 50 раз. В ПриПолученииДанных мы выполняем один запрос для всей пачки строк.


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

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

Подведение итогов

Проанализировав ситуацию, мы видим, что работа с отборами в обычных формах требует четкого понимания событий жизненного цикла формы. Главное правило: если данные для фильтрации лежат вне самого объекта (в регистрах), используйте программное формирование списка ссылок через НачалоВыбора. Если же вам нужно только отображение — используйте ПриПолученииДанных с пакетной обработкой ссылок для сохранения высокой производительности интерфейса.

← На главную