В разработке на платформе 1С:Предприятие 8.3 часто возникает необходимость организовывать гибкий ввод данных в табличную часть документа или обработки. Типичный сценарий — когда в одну и ту же колонку табличной части пользователь должен вносить данные разной природы: выбирать из полного справочника, выбирать из отфильтрованного списка (например, сотрудников конкретного контрагента или контактных лиц) либо вводить произвольное наименование текстовой строкой.
Разберем, с какими сложностями можно столкнуться при реализации такого составного ввода и как правильно настроить программное управление формами выбора и типами данных.
Рассмотрим конкретную техническую задачу. В табличной части документа присутствует колонка Участники. Тип реквизита объявлен как составной, включающий ссылки на справочники СправочникСсылка.Сотрудники, СправочникСсылка.КонтактныеЛица и базовый тип Строка.
Пользователю предоставляется выбор режима ввода:
При стандартной реализации разработчики сталкиваются со следующими проблемами:
ОткрытьФорму выбранное значение не переносится в ячейку табличной части.Проанализируем причины этих проблем и рассмотрим пошаговые варианты их решения.
Когда в обработчике события НачалоВыбора отключается стандартная обработка (устанавливается СтандартнаяОбработка = Ложь), платформа больше не управляет открытием формы и подстановкой выбранного значения автоматически. Если просто вызвать программное открытие формы:
&НаКлиенте
Процедура УчастникиНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
ОткрытьФорму("Справочник.Сотрудники.ФормаВыбора");
КонецПроцедуры
В этом случае платформа не знает, какой именно элемент управления является получателем выбранного значения. В результате выбор происходит "в никуда", и данные в табличную часть не переносятся.
Решение: В качестве третьего параметра функции ОткрытьФорму необходимо обязательно передавать элемент управления Элемент, который выступает в роли владельца формы выбора:
&НаКлиенте
Процедура УчастникиНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
ОткрытьФорму("Справочник.Сотрудники.ФормаВыбора", , Элемент);
КонецПроцедуры
При передаче Элемент в качестве владельца платформа автоматически свяжет выбранный элемент справочника с ячейкой табличной части и запишет значение при закрытии формы выбора.
Для более тонкого контроля полученных данных в управляемом режиме рекомендуется использовать асинхронную обработку оповещения о закрытии:
&НаКлиенте
Процедура УчастникиНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеВыбораИзФормы", ЭтотОбъект);
ПараметрыФормы = Новый Структура;
ОткрытьФорму("Справочник.Сотрудники.ФормаВыбора", ПараметрыФормы, Элемент, , , , ОписаниеОповещения);
КонецПроцедуры
&НаКлиенте
Процедура ПослеВыбораИзФормы(РезультатВыбора, ДополнительныеПараметры) Экспорт
Если ЗначениеЗаполнено(РезультатВыбора) Тогда
ТекущаяСтрока = Элементы.ТаблицаУчастники.ТекущиеДанные;
Если ТекущаяСтрока <> Неопределено Тогда
ТекущаяСтрока.Участник = РезультатВыбора;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Когда реквизит имеет составной тип, платформа по умолчанию отображает кнопку диалога выбора типа ([Т]) и кнопку выбора из справочника. Если пользователь выбирает режим ввода произвольного текста, необходимо запретить платформе вызывать диалог типов и скрыть кнопку выбора, дав возможность вводить текст напрямую в ячейку.
Для этого используются два ключевых инструмента:
ОграничениеТипа.КнопкаВыбора.Разберем, как менять состав доступных типов данных динамически на клиенте:
// Включение режима ввода строки:
Элементы.ТаблицаУчастникиУчастник.ОграничениеТипа = Новый ОписаниеТипов("Строка");
Элементы.ТаблицаУчастникиУчастник.КнопкаВыбора = Ложь;
// Включение режима выбора сотрудника:
Элементы.ТаблицаУчастникиУчастник.ОграничениеТипа = Новый ОписаниеТипов("СправочникСсылка.Сотрудники");
Элементы.ТаблицаУчастникиУчастник.КнопкаВыбора = Истина;
Важный нюанс: При смене типа необходимо корректно приводить существующее значение в текущей строке табличной части к новому типу, иначе в ячейке могут остаться невалидные данные:
ТекущаяСтрока = Элементы.ТаблицаУчастники.ТекущиеДанные;
НовыйТип = Новый ОписаниеТипов("Строка");
Элементы.ТаблицаУчастникиУчастник.ОграничениеТипа = НовыйТип;
ТекущаяСтрока.Участник = НовыйТип.ПривестиЗначение(ТекущаяСтрока.Участник);
Часто разработчики пытаются сформировать небольшой список (5–6 человек) запросом на сервере и вручную передать его в ДанныеВыбора. Однако платформенные механизмы позволяют наложить фильтр прямо на стандартную форму выбора с помощью структуры Отбор, что требует существенно меньше кода и работает штатно.
Посмотрим на пример передачи фильтра по контрагенту или массиву сотрудников:
&НаКлиенте
Процедура ВыбратьСотрудниковКлиента(Элемент)
ПараметрыФормы = Новый Структура;
// Формируем структуру отбора
Отбор = Новый Структура;
// Если нужно отфильтровать по владельцу/клиенту:
Отбор.Вставить("ГоловнаяОрганизация", Объект.Организация);
// Или передать готовый массив допустимых ссылок:
// Отбор.Вставить("Ссылка", МассивСотрудниковКлиента);
ПараметрыФормы.Вставить("Отбор", Отбор);
// Передаем Элемент как владельца
ОткрытьФорму("Справочник.Сотрудники.ФормаВыбора", ПараметрыФормы, Элемент);
КонецПроцедуры
Когда вы переопределяете НачалоВыбора и устанавливаете СтандартнаяОбработка = Ложь, ввод по строке может перестать работать. Чтобы вернуть быструю подстановку элементов при вводе текста с клавиатуры, используем событие элемента формы ОбработкаАвтоПодбора:
&НаКлиенте
Процедура ТаблицаУчастникиУчастникОбработкаАвтоПодбора(Элемент, Текст, ДанныеВыбора, Ожидание, СтандартнаяОбработка)
ТекущаяСтрока = Элементы.ТаблицаУчастники.ТекущиеДанные;
Если ТекущаяСтрока = Неопределено Тогда
Возврат;
КонецЕсли;
// Если включен режим произвольной строки — автоподбор не нужен
Если ТекущаяСтрока.ВидУчастника = ПредопределенноеЗначение("Перечисление.ВидыУчастников.Текст") Тогда
Возврат;
КонецЕсли;
// Если нужен поиск по справочнику сотрудников:
СтандартнаяОбработка = Ложь;
ДанныеВыбора = ПолучитьСписокСотрудниковПоТексту(Текст, Объект.Клиент);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьСписокСотрудниковПоТексту(Знач ТекстПоиска, Знач Клиент)
Список = Новый СписокЗначений;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Сотрудники.Ссылка КАК Ссылка,
| Сотрудники.Наименование КАК Наименование
|ИЗ
| Справочник.Сотрудники КАК Сотрудники
|ГДЕ
| Сотрудники.Наименование ПОДОБНО &ТекстПоиска";
Запрос.УстановитьПараметр("ТекстПоиска", "%" + ТекстПоиска + "%");
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Список.Добавить(Выборка.Ссылка, Выборка.Наименование);
КонецЦикла;
Возврат Список;
КонецФункции
Выстроив понятную архитектуру, объединим все рекомендации в единую рабочую схему:
ВидУчастника (например, типа ПеречислениеСсылка.ВидыУчастников со значениями: ВсеСотрудники, СотрудникиКлиента, КонтактныеЛица, Текст).ПриИзменении колонки ВидУчастника настраиваем доступность ввода и ограничиваем допустимые типы.ПриАктивизацииСтроки табличной части обновляем свойства элемента управления под текущую выделенную строку.НачалоВыбора проверяем выбранный вид и открываем форму с нужными отборами.Проанализируем итоговый программный код управляемой формы:
&НаКлиенте
Процедура ТаблицаУчастникиВидУчастникаПриИзменении(Элемент)
ОбновитьОграничениеТипаИВидимость();
КонецПроцедуры
&НаКлиенте
Процедура ТаблицаУчастникиПриАктивизацииСтроки(Элемент)
ОбновитьОграничениеТипаИВидимость();
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьОграничениеТипаИВидимость()
ТекущаяСтрока = Элементы.ТаблицаУчастники.ТекущиеДанные;
Если ТекущаяСтрока = Неопределено Тогда
Возврат;
КонецЕсли;
ЭлементУчастник = Элементы.ТаблицаУчастникиУчастник;
Если ТекущаяСтрока.ВидУчастника = ПредопределенноеЗначение("Перечисление.ВидыУчастников.Текст") Тогда
ЭлементУчастник.ОграничениеТипа = Новый ОписаниеТипов("Строка");
ЭлементУчастник.КнопкаВыбора = Ложь;
ИначеЕсли ТекущаяСтрока.ВидУчастника = ПредопределенноеЗначение("Перечисление.ВидыУчастников.КонтактныеЛица") Тогда
ЭлементУчастник.ОграничениеТипа = Новый ОписаниеТипов("СправочникСсылка.КонтактныеЛица");
ЭлементУчастник.КнопкаВыбора = Истина;
Иначе // Все сотрудники или сотрудники клиента
ЭлементУчастник.ОграничениеТипа = Новый ОписаниеТипов("СправочникСсылка.Сотрудники");
ЭлементУчастник.КнопкаВыбора = Истина;
КонецЕсли;
// Приводим значение к новому ограниченному типу
ТекущаяСтрока.Участник = ЭлементУчастник.ОграничениеТипа.ПривестиЗначение(ТекущаяСтрока.Участник);
КонецПроцедуры
&НаКлиенте
Процедура ТаблицаУчастникиУчастникНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
ТекущаяСтрока = Элементы.ТаблицаУчастники.ТекущиеДанные;
Если ТекущаяСтрока = Неопределено Тогда
Возврат;
КонецЕсли;
// Для текстового режима выбор из форм не требуется
Если ТекущаяСтрока.ВидУчастника = ПредопределенноеЗначение("Перечисление.ВидыУчастников.Текст") Тогда
Возврат;
КонецЕсли;
СтандартнаяОбработка = Ложь;
ПараметрыФормы = Новый Структура;
Отбор = Новый Структура;
Если ТекущаяСтрока.ВидУчастника = ПредопределенноеЗначение("Перечисление.ВидыУчастников.КонтактныеЛица") Тогда
Отбор.Вставить("Владелец", Объект.Клиент);
ПараметрыФормы.Вставить("Отбор", Отбор);
ОткрытьФорму("Справочник.КонтактныеЛица.ФормаВыбора", ПараметрыФормы, Элемент);
ИначеЕсли ТекущаяСтрока.ВидУчастника = ПредопределенноеЗначение("Перечисление.ВидыУчастников.СотрудникиКлиента") Тогда
// Передаем массив ссылок сотрудников, закрепленных за клиентом
МассивСотрудников = ПолучитьСотрудниковКлиента(Объект.Клиент);
Отбор.Вставить("Ссылка", МассивСотрудников);
ПараметрыФормы.Вставить("Отбор", Отбор);
ОткрытьФорму("Справочник.Сотрудники.ФормаВыбора", ПараметрыФормы, Элемент);
Иначе // Все сотрудники
ОткрытьФорму("Справочник.Сотрудники.ФормаВыбора", ПараметрыФормы, Элемент);
КонецЕсли;
КонецПроцедуры
Благодаря указанному подходу гарантируется понятное поведение интерфейса: при переключении режима на текстовый скрывается кнопка выбора и пользователь свободно вводит строку. При выборе ссылочного режима открывается стандартная форма с необходимыми фильтрами, а выбранное значение надежно подставляется в табличную часть.