В процессе разработки в среде «1С:Предприятие 8.3» (особенно в конфигурациях на базе БСП, таких как «1С:Управление нашей фирмой») часто возникает задача предоставить пользователю выбор из ограниченного, динамически формируемого набора данных. Типичный пример — выбор единиц измерения в табличной части документа. Сложность возрастает, когда реквизит имеет составной тип данных: например, базовая единица измерения хранится в общем классификаторе, а дополнительные (упаковки, ящики, паллеты) — в подчиненном номенклатуре справочнике.
В этой статье мы подробно разберем, как правильно реализовать заполнение выпадающего списка в колонке табличной части, проанализируем критические ошибки в коде, которые часто допускают начинающие разработчики, и рассмотрим наиболее оптимальные и современные методы решения этой задачи на управляемых формах.
Рассмотрим ситуацию: в табличной части «Материалы» документа «Заявка в производство» у нас есть колонка «Единица измерения». Нам необходимо сделать так, чтобы при нажатии на кнопку выбора в этой колонке система считывала текущую номенклатуру строки, делала запрос к двум разным справочникам (классификатору и подчиненному справочнику упаковок), объединяла результаты и выводила их пользователю в виде простого выпадающего списка.
Для реализации этой логики на клиенте переопределяется стандартная обработка события выбора. Посмотрим на базовый вариант решения, который часто используют на практике:
&НаКлиенте
Процедура МатериалыЕдиницаИзмеренияНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// Получаем индекс текущей строки (Внимание: тут кроется опасная ошибка!)
НомерСтроки = ЭтотОбъект.ТекущийЭлемент.ТекущиеДанные.НомерСтроки - 1;
// Вызываем серверную функцию для получения массива доступных единиц
НаименованиеЗаказа = ПодборЕдИзм(НомерСтроки);
ДанныеВыбора = Новый СписокЗначений;
Для Каждого Массив Из НаименованиеЗаказа Цикл
Если Не Массив = NULL Тогда
ДанныеВыбора.Добавить(Массив);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
На первый взгляд, данный код решает задачу: стандартная форма выбора подавляется установкой СтандартнаяОбработка = Ложь, а в параметр ДанныеВыбора передается заполненный на сервере список значений. Однако этот код содержит критическую уязвимость, которая обязательно проявит себя в реальной работе пользователей.
Проанализируем строку кода: НомерСтроки = ЭтотОбъект.ТекущийЭлемент.ТекущиеДанные.НомерСтроки - 1; и последующее обращение к коллекции на сервере через полученный индекс Объект.Materials[НомерСтроки].
Вычисление индекса строки через визуальный номер строки — это грубейшая архитектурная ошибка в «1С». Выясним её причины:
Объект.Материалы на сервере останется прежним. В итоге программа считает номенклатуру совершенно из другой строки и предложит пользователю неверный список единиц измерения.Как сделать правильно? Нам вообще не нужно вычислять индекс строки. В управляемом интерфейсе 1С мы всегда можем получить непосредственно данные текущей строки через свойство ТекущиеДанные элемента формы (таблицы). Мы должны передавать на сервер конкретную ссылку на номенклатуру, а не индекс строки. Рассмотрим правильный способ получения данных:
ТекущаяСтрока = Элементы.Материалы.ТекущиеДанные;
Если ТекущаяСтрока <> Неопределено Тогда
// Напрямую получаем ссылку на номенклатуру из текущей строки
ВыбраннаяНоменклатура = ТекущаяСтрока.Номенклатура;
КонецЕсли;
Вызов серверных процедур из клиентского контекста — дорогостоящая операция для производительности системы. Если функция получения списка единиц измерения ПодборЕдИзм() объявлена с директивой компиляции &НаСервере, то при каждом её вызове на сервер неявно передается весь контекст формы (все реквизиты, элементы управления и вся табличная часть целиком). В условиях работы через тонкий клиент или веб-клиент по медленному интернет-соединению это вызовет ощутимые задержки интерфейса.
Для оптимизации процесса переведем функцию получения данных в бесконтекстный режим работы. Для этого используем директиву &НаСервереБезКонтекста. В такую функцию мы передаем только один параметр — ссылку на номенклатуру. Объём передаваемых на сервер данных в таком случае сокращается до нескольких байт.
&НаСервереБезКонтекста
Функция ПолучитьМассивЕдиницИзмерения(Знач НоменклатураСсылка)
МассивРезультат = Новый Массив;
// Если номенклатура не заполнена, возвращаем пустой массив
Если Не ЗначениеЗаполнено(НоменклатураСсылка) Тогда
Возврат МассивРезультат;
КонецЕсли;
// Пример запроса для получения базовой и дополнительных единиц измерения
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.ЕдиницаИзмерения КАК Единица
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка = &Номенклатура
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| ЕдиницыИзмерения.Ссылка
|ИЗ
| Справочник.ЕдиницыИзмерения КАК ЕдиницыИзмерения
|ГДЕ
| ЕдиницыИзмерения.Владелец = &Номенклатура";
Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Если ЗначениеЗаполнено(Выборка.Единица) Тогда
МассивРезультат.Добавить(Выборка.Единица);
КонецЕсли;
КонецЦикла;
Возврат МассивРезультат;
КонецФункции
Хотя событие НачалоВыбора решает задачу программного перехвата, платформа 1С v8.3 предоставляет более элегантное и специализированное событие для полей ввода — ПриПолученииДанныхВыбора.
Преимущество этого события заключается в том, что оно срабатывает автоматически не только при физическом нажатии на кнопку выпадающего списка, но и при начале ввода наименования единицы измерения с клавиатуры (ввод по строке). Это делает интерфейс решения бесшовным и удобным для быстрого ввода данных.
Рассмотрим, как будет выглядеть оптимальная реализация с использованием этого события и метода ЗагрузитьЗначения(), который позволяет избежать медленного обхода массива в цикле:
&НаКлиенте
Процедура МатериалыЕдиницаИзмеренияПриПолученииДанныхВыбора(Элемент, ДанныеВыбора, Параметры, СтандартнаяОбработка)
// Отменяем стандартную генерацию списка выбора платформой
СтандартнаяОбработка = Ложь;
ТекущиеДанные = Элементы.Материалы.ТекущиеДанные;
Если ТекущиеДанные <> Неопределено Тогда
// Быстрый бесконтекстный вызов сервера
МассивЕдиниц = ПолучитьМассивЕдиницИзмерения(ТекущиеДанные.Номенклатура);
// Инициализируем список выбора
ДанныеВыбора = Новый СписокЗначений;
// Быстрое заполнение списка напрямую из массива
ДанныеВыбора.ЗагрузитьЗначения(МассивЕдиниц);
КонецЕсли;
КонецПроцедуры
Чтобы элемент управления в колонке табличной части вел себя как полноценный выпадающий список (Combo Box), необходимо правильно настроить свойства этого элемента в палитре свойств конфигуратора:
МатериалыЕдиницаИзмерения.РежимВыбораИзСписка в значение Истина. Это заставит платформу при клике по полю сразу открывать сформированный нами список, избегая открытия стандартных диалогов выбора.КнопкаВыбора в значение Ложь. Это скроет кнопку с тремя точками «...», которая визуально ассоциируется с открытием полной формы справочника и может путать пользователя.КнопкаСпискаВыбора в значение Истина. Это отобразит в правой части поля аккуратную стрелочку вниз, указывающую на то, что поле является выпадающим списком.Проанализируем ситуацию: можно ли решить эту задачу вообще без написания кода? Платформа 1С поддерживает мощный механизм под названием Связи параметров выбора.
Если бы все единицы измерения хранились строго в одном подчиненном справочнике ЕдиницыИзмерения (где владельцем является Номенклатура), мы могли бы настроить фильтрацию штатными средствами:
ЕдиницаИзмерения.Связи параметров выбора и нажмите кнопку открытия.Объект.Материалы.Номенклатура, а в качестве имени параметра выбора выберите Отбор.Владелец.При такой настройке платформа автоматически при активизации ячейки будет передавать текущую номенклатуру в качестве отбора по владельцу в форму выбора справочника единиц измерения.
Почему в данном случае этот метод не подходит полностью? Из-за составного типа данных. Поскольку базовая единица измерения хранится в независимом общем классификаторе (ОКЕИ), а дополнительные — в подчиненном справочнике, простая связь по владельцу отсечет возможность выбора базовой единицы. Именно поэтому программный перехват через событие ПриПолученииДанныхВыбора (описанный в предыдущих разделах) является единственным верным и гибким архитектурным решением для объединения разнородных справочников в один список выбора.