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

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

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

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

Исходная задача и базовый вариант решения

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

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


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

На первый взгляд, данный код решает задачу: стандартная форма выбора подавляется установкой СтандартнаяОбработка = Ложь, а в параметр ДанныеВыбора передается заполненный на сервере список значений. Однако этот код содержит критическую уязвимость, которая обязательно проявит себя в реальной работе пользователей.

Анализ критической ошибки позиционирования: почему нельзя использовать НомерСтроки

Проанализируем строку кода: НомерСтроки = ЭтотОбъект.ТекущийЭлемент.ТекущиеДанные.НомерСтроки - 1; и последующее обращение к коллекции на сервере через полученный индекс Объект.Materials[НомерСтроки].

Вычисление индекса строки через визуальный номер строки — это грубейшая архитектурная ошибка в «1С». Выясним её причины:

  1. Интерактивная сортировка: Если пользователь отсортирует табличную часть по любой колонке (например, по алфавиту в колонке «Номенклатура»), визуальный порядок строк изменится. Номер строки на экране изменится, но физический индекс строки в коллекции Объект.Материалы на сервере останется прежним. В итоге программа считает номенклатуру совершенно из другой строки и предложит пользователю неверный список единиц измерения.
  2. Интерактивная фильтрация: При установке любого отбора в табличной части визуальные номера строк пересчитываются. Попытка получить строку по индексу, равному визуальному номеру минус один, приведет либо к получению неверных данных, либо к критической ошибке выхода за границы массива (Index out of bounds).
  3. Удаление строк: Если из середины табличной части удалить строку, индексы последующих строк в коллекции на сервере сместятся, а визуальные номера пересчитаются. Это также приведет к рассинхронизации данных.

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


ТекущаяСтрока = Элементы.Материалы.ТекущиеДанные;
Если ТекущаяСтрока <> Неопределено Тогда
    // Напрямую получаем ссылку на номенклатуру из текущей строки
    ВыбраннаяНоменклатура = ТекущаяСтрока.Номенклатура;
КонецЕсли;

Оптимизация клиент-серверного взаимодействия

Вызов серверных процедур из клиентского контекста — дорогостоящая операция для производительности системы. Если функция получения списка единиц измерения ПодборЕдИзм() объявлена с директивой компиляции &НаСервере, то при каждом её вызове на сервер неявно передается весь контекст формы (все реквизиты, элементы управления и вся табличная часть целиком). В условиях работы через тонкий клиент или веб-клиент по медленному интернет-соединению это вызовет ощутимые задержки интерфейса.

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


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

Использование современного события «ПриПолученииДанныхВыбора»

Хотя событие НачалоВыбора решает задачу программного перехвата, платформа 1С v8.3 предоставляет более элегантное и специализированное событие для полей ввода — ПриПолученииДанныхВыбора.

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

Рассмотрим, как будет выглядеть оптимальная реализация с использованием этого события и метода ЗагрузитьЗначения(), который позволяет избежать медленного обхода массива в цикле:


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

Настройка свойств элемента формы в конфигураторе

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

  1. Найдите элемент формы (колонку табличной части) МатериалыЕдиницаИзмерения.
  2. Установите свойство РежимВыбораИзСписка в значение Истина. Это заставит платформу при клике по полю сразу открывать сформированный нами список, избегая открытия стандартных диалогов выбора.
  3. Установите свойство КнопкаВыбора в значение Ложь. Это скроет кнопку с тремя точками «...», которая визуально ассоциируется с открытием полной формы справочника и может путать пользователя.
  4. Установите свойство КнопкаСпискаВыбора в значение Истина. Это отобразит в правой части поля аккуратную стрелочку вниз, указывающую на то, что поле является выпадающим списком.

Альтернативный вариант без кода: Связи параметров выбора

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

Если бы все единицы измерения хранились строго в одном подчиненном справочнике ЕдиницыИзмерения (где владельцем является Номенклатура), мы могли бы настроить фильтрацию штатными средствами:

  1. В окне конфигуратора откройте свойства реквизита табличной части ЕдиницаИзмерения.
  2. Найдите свойство Связи параметров выбора и нажмите кнопку открытия.
  3. Добавьте новую строку связи: укажите путь к данным — Объект.Материалы.Номенклатура, а в качестве имени параметра выбора выберите Отбор.Владелец.

При такой настройке платформа автоматически при активизации ячейки будет передавать текущую номенклатуру в качестве отбора по владельцу в форму выбора справочника единиц измерения.

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

← На главную