При разработке интерфейсов в конфигурациях на 1С:Предприятии 8 (особенно на обычных формах в толстом клиенте) часто возникает задача обеспечения удобного и безошибочного ввода данных для оператора. Типичный сценарий: в табличной части внешней или внутренней обработки пользователю нужно выбрать элемент справочника Номенклатура. При этом доступный для выбора список номенклатуры должен быть строго ограничен элементами, которые привязаны к другому справочнику (например, Номенклатура_Доб), выбранному в соседней колонке той же строки.
В этой статье мы подробно разберем несколько практических способов реализации этой задачи: от простого метода, предложенного разработчиками на форуме, до более сложных, красивых и стандартных механизмов платформы 1С.
Рассмотрим исходную структуру метаданных:
Номенклатура.Номенклатура_Доб.Номенклатура_Доб создана табличная часть (назовем ее ДоступнаяНоменклатура) с реквизитом Номенклатура (ссылка на основной справочник). Здесь задается соответствие «один ко многим».Номенклатура_Доб и Номенклатура.Наша цель — сделать так, чтобы при нажатии на кнопку выбора в колонке Номенклатура пользователь видел не весь огромный массив справочника, а только те позиции, которые прописаны в табличной части выбранного элемента Номенклатура_Доб.
---
Это решение, которое непосредственно обсуждалось на форуме. Суть метода заключается в том, что при начале выбора мы программно считываем данные из табличной части связанного справочника, выгружаем их во временную таблицу значений и выводим эту таблицу пользователю в виде модального окна для выбора строки.
Рассмотрим программную реализацию этого метода по шагам:
НачалоВыбора для колонки «Номенклатура» в нашей табличной части.СтандартнаяОбработка = Ложь.Номенклатура_Доб.ВыбратьСтроку().Посмотрим на пример кода, реализующий этот алгоритм:
Процедура ТабличнаяЧастьНоменклатураНачалоВыбора(Элемент, СтандартнаяОбработка)
// Отменяем стандартное открытие формы выбора справочника
СтандартнаяОбработка = Ложь;
// Получаем текущую строку табличного поля обработки
ТекущаяСтрока = Элемент.ТекущиеДанные;
// Проверяем, заполнено ли ключевое поле связи
Если Не ЗначениеЗаполнено(ТекущаяСтрока.Номенклатура_Доб) Тогда
Предупреждение("Сначала необходимо выбрать значение в колонке 'Номенклатура_Доб'!");
Возврат;
КонецЕсли;
// Создаем временную таблицу значений для вывода пользователю
ТЗДляВыбора = Новый ТаблицаЗначений;
ТЗДляВыбора.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"), "Выберите номенклатуру");
// Заполняем ТЗ данными из подчиненной табличной части справочника Номенклатура_Доб
Для Каждого СтрокаТЧ Из ТекущаяСтрока.Номенклатура_Доб.ДоступнаяНоменклатура Цикл
НоваяСтрокаТЗ = ТЗДляВыбора.Добавить();
НоваяСтрокаТЗ.Номенклатура = СтрокаТЧ.Номенклатура;
КонецЦикла;
// Открываем модальное окно выбора строки из созданной ТЗ
ВыбраннаяСтрокаТЗ = ТЗДляВыбора.ВыбратьСтроку("Доступные варианты для выбора:");
// Если пользователь сделал выбор, записываем значение в ячейку
Если ВыбраннаяСтрокаТЗ <> Неопределено Тогда
ТекущаяСтрока.Номенклатура = ВыбраннаяСтрокаТЗ.Номенклатура;
КонецЕсли;
КонецПроцедуры
Преимущества метода: крайне простая реализация, код пишется буквально за 5 минут, работает безотказно.
Недостатки метода: интерфейс выглядит устаревшим. Открывается полноценное отдельное модальное окно со стандартной таблицей значений, которое перекрывает рабочую область. Как справедливо заметил автор темы на форуме, это выглядит «не кузяво».
---
Более элегантный и визуально привлекательный способ для обычных форм — использование метода ВыбратьИзСписка(). Вместо открытия тяжелого модального окна система выведет компактный выпадающий список прямо под ячейкой, в которой пользователь нажал кнопку выбора.
Проанализируем ситуацию реализации данного подхода:
Процедура ТабличнаяЧастьНоменклатураНачалоВыбора(Элемент, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
ТекущаяСтрока = Элемент.ТекущиеДанные;
Если Не ЗначениеЗаполнено(ТекущаяСтрока.Номенклатура_Доб) Тогда
Предупреждение("Пожалуйста, сначала выберите Номенклатуру_Доб!");
Возврат;
КонецЕсли;
// Формируем аккуратный список значений
СписокВыбора = Новый СписокЗначений;
Для Каждого СтрокаТЧ Из ТекущаяСтрока.Номенклатура_Доб.ДоступнаяНоменклатура Цикл
// Добавляем элемент справочника в список для интерактивного выбора
СписокВыбора.Добавить(СтрокаТЧ.Номенклатура);
КонецЦикла;
// Проверяем, есть ли вообще данные для выбора
Если СписокВыбора.Количество() = 0 Тогда
Предупреждение("Для выбранной Номенклатуры_Доб не сопоставлено ни одного значения Номенклатуры!");
Возврат;
КонецЕсли;
// Показываем компактное контекстное меню выбора у элемента формы (ячейки)
ВыбранныйЭлементСписка = ВыбратьИзСписка(СписокВыбора, Элемент);
Если ВыбранныйЭлементСписка <> Неопределено Тогда
ТекущаяСтрока.Номенклатура = ВыбранныйЭлементСписка.Значение;
КонецЕсли;
КонецПроцедуры
Преимущества метода: современный интерфейс без лишних окон. Идеально подходит, если количество сопоставленных элементов невелико (до 15-20 позиций).
Недостатки метода: в выпадающем списке отсутствует стандартный функционал поиска по строке, иерархия групп и возможность просмотра детальных реквизитов справочника.
---
Если сопоставленных элементов в табличной части справочника Номенклатура_Доб может быть много (десятки или сотни), оператору обязательно понадобится полноценная форма выбора с поиском, группировкой по папкам и сортировкой. В этом случае правильным решением будет открыть стандартную форму выбора справочника Номенклатура, предварительно наложив жесткий программный фильтр.
Разберем этот метод подробно по шагам:
НачалоВыбора и отключаем стандартное открытие формы.СписокЗначений.ПолучитьФормуВыбора(). При этом крайне важно передать текущий элемент управления в качестве владельца формы. Это позволит платформе автоматически вернуть выбранное значение в нужную ячейку табличного поля без написания дополнительного кода в событии ОбработкаВыбора.ВСписке.Посмотрим на пример кода:
Процедура ТабличнаяЧастьНоменклатураНачалоВыбора(Элемент, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
ТекущаяСтрока = Элемент.ТекущиеДанные;
Если Не ЗначениеЗаполнено(ТекущаяСтрока.Номенклатура_Доб) Тогда
Предупреждение("Сначала заполните поле Номенклатура_Доб!");
Возврат;
КонецЕсли;
// Заполняем массив разрешенных элементов
МассивРазрешенных = Новый Массив;
Для Каждого СтрокаТЧ Из ТекущаяСтрока.Номенклатура_Доб.ДоступнаяНоменклатура Цикл
МассивРазрешенных.Добавить(СтрокаТЧ.Номенклатура);
КонецЦикла;
// Получаем стандартную форму выбора справочника.
// Передаем параметр 'Элемент' в качестве владельца формы для автоматического возврата значения.
ФормаВыбора = Справочники.Номенклатура.ПолучитьФормуВыбора("ФормаВыбора", Элемент);
// Настраиваем жесткий отбор по списку ссылок
ОтборСсылка = ФормаВыбора.СправочникСписок.Отбор.Ссылка;
ОтборСсылка.ВидСравнения = ВидСравнения.ВСписке;
ОтборСсылка.Значение = Новый СписокЗначений;
ОтборСсылка.Значение.ЗагрузитьЗначения(МассивРазрешенных);
ОтборСсылка.Использование = Истина;
// Для удобства пользователя можно отключить иерархический просмотр,
// чтобы сразу отображался плоский список доступных позиций
ФормаВыбора.ЭлементыФормы.СправочникСписок.ИерархическийПросмотр = Ложь;
// Открываем подготовленную форму выбора
ФормаВыбора.Открыть();
КонецПроцедуры
Преимущества метода: наиболее профессиональное и стандартное решение для обычных форм. Пользователь работает в привычной среде поиска и выбора справочника, но видит только разрешенные записи.
---
Малоизвестный, но крайне мощный стандартный механизм обычных форм 1С — это использование свойства ячейки табличного поля ДоступныеЗначения. Если у конкретной ячейки установлено это свойство, платформа автоматически ограничивает интерактивный выбор пользователя (как при вводе текста, так и при нажатии на кнопку выбора) данным списком.
При таком подходе логику ограничения следует перенести в событие изменения первой колонки (Номенклатура_Доб):
Процедура ТабличнаяЧастьНоменклатураДобПриИзменении(Элемент)
СтрокаТП = ЭлементыФормы.ТабличнаяЧасть.ТекущаяСтрока;
// Если поле очистили, сбрасываем ограничения
Если Не ЗначениеЗаполнено(СтрокаТП.Номенклатура_Доб) Тогда
ЭлементыФормы.ТабличнаяЧасть.Колонки.Номенклатура.ЭлементУправления.ДоступныеЗначения = Неопределено;
Возврат;
КонецЕсли;
// Заполняем список доступных значений
СписокДоступных = Новый СписокЗначений;
Для Каждого СтрокаТЧ Из СтрокаТП.Номенклатура_Доб.ДоступнаяНоменклатура Цикл
СписокДоступных.Добавить(СтрокаТЧ.Номенклатура);
КонецЦикла;
// Накладываем ограничение непосредственно на элемент управления в текущей строке
// Обратите внимание: для динамического изменения в табличном поле
// лучше использовать событие ПриВыводеСтроки или ПриАктивизацииСтроки
ЭлементыФормы.ТабличнаяЧасть.Колонки.Номенклатура.ЭлементУправления.ДоступныеЗначения = СписокДоступных;
КонецПроцедуры
Преимущества метода: работает на уровне платформы. Не нужно переопределять событие НачалоВыбора и прописывать логику открытия форм вручную.
---
Мы подробно проанализировали четыре способа решения задачи ограничения выбора в табличном поле на обычных формах 1С 8.3: