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