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