Реализация подборов в документах

Доброго времени суток, уважаемые читатели! В этой заметке мы будем говорить о реализации механизма подборов в системе 1С:Предприятие. Подборы в основном используются в документах, но могут быть использованы в справочниках и любых других объектах. В чем заключается работа этого механизма? Давайте посмотрим.

  1. Предназначение механизма
  2. Простой вариант подбора
  3. Подборы с множественным выбором
  4. Реализация формы подбора
  5. Передача результата в вызывающую форму
  6. Передача данных табличной части в вызываемую форму

Предназначение механизма

Как обычно пользователь вводит данные в документы? Например, пользователь создает документ Реализация. Затем, чтобы добавить строку в табличную часть документа, нажимает на кнопку «Добавить». В строке табличной части выбирает элемент из справочника, либо пользуется функцией ввода по строке, вводит название и выбирает соответствующую позицию.

Такой ввод является относительно удобным, но если в документе требуется ввести сто и больше строк, то такой метод одиночного выбора является достаточно долгим. Более эффективно было бы сделать следующим образом: открыть форму справочника, затем нажать на соответствующий элемент, который автоматически добавляется в табличную часть, но форма не закрывается. В этом случае можно ввести документ с большим количеством строк. Это и называется механизмом подбора. По умолчанию в платформе его нет. Его надо будет реализовывать самостоятельно программными средствами. Но ничего сложного в этом нет.

Хочется отметить отличия в подборе версии платформы 8.2 от 8.1. В подборе версии 8.1 открывалось специальное окно, которое обычно являлось прикрепляемым, оно прикреплялось к правой части экрана главного окна приложения. Пользователь мог одновременно видеть, и документ, и подбор, и мог между ними произвольным образом переключаться.

В платформе 8.2 подобная реализация невозможна из-за изменения принципа оконного интерфейса. Здесь нет одного главного окна, в которое входят все подчиненные окна. Реализация является независимой и отображается как отдельный элемент панели задач.

Здесь применяется принцип под названием «Блокировать окно владельца». Когда будет открываться форма подбора, система не должна позволять переключаться на другие элементы. Если открыть форму справочника, то невозможно переключиться в документ, окно будет заблокировано. Но в приложение можно спокойно переключиться. Такой принцип позволяет избежать ошибочного клика на документ, который сделает окно подбора неактивным. Если такое произойдет, то в этом случае можно легко запутаться. Реализуем подобный механизм, чтобы окно владельца блокировалось.

Простой вариант подбора

Начинаем реализацию подборов и начнем от простого к сложному. Для начала реализуем простейший вариант подбора. Этот вариант будет такой. Первое, нужно, чтобы форма выбора не закрывалась. Для этого можно использовать специальный параметр управляемой формы, который называется «ЗакрыватьПриВыборе». Обратимся к Синтаксис-помощнику и найдем этот параметр управляемой формы.

Интерфейс (управляемый) -> Управляемая форма -> Параметры формы -> ЗакрыватьПриВыборе
Параметр управляемой формы ЗакрыватьПриВыборе
Описание параметра управляемой формы ЗакрыватьПриВыборе в Синтаксис-помощнике

Итак, в параметрах указывается флаг «ЗакрыватьПриВыборе, то есть мы можем при открытии формы выбора передать значение этого параметра равную ЛОЖЬ. В этом случае, в момент двойного клика, либо при нажатии ENTER система не будет закрывать форму выбора. Именно такое поведение нам нужно.

Дальше мы открываем форму с передачей этого параметра и обязательно указываем, что эта форма подчинена определенному владельцу. Она должна открыватся в таком режиме. Для чего нужен владелец? Дело в том, что нужно поймать событие двойного клика или нажатие на ENTER в исходной форме владельца и в этой форме реализовать обработчик, который называется «ОбработкаВыбора».  Давайте посмотрим, как это работает и всё станет понятно.

Итак, в форме документа создадим команду Подбор. Отразим эту команду на форме в командной панели. Здесь важно расположить кнопку в командной панели таблицы формы.

Кнопка Подбор на панели задач
Размещение команды Подбор на панели задач табличной части

Итак, в обработчике действия открываем форму и передаем в неё параметр. Конечно, можно создать структуру с названием «Параметры», но это было бы критической ошибкой. «Параметры» – это название свойства текущей управляемой формы, поэтому название изменим на «ПараметрыПодбора».

&НаКлиенте
Процедура Подбор(Команда)
	ПараметрыПодбора = Новый Структура("ЗакрыватьПриВыборе", Ложь);
	ОткрытьФорму("Справочник.Номенклатура.ФормаВыбора", ПараметрыПодбора, Элементы.Товары);	
КонецПроцедуры

Для того, чтобы открыть форму используем метод глобального контекста «ОткрытьФорму», где в первом параметре указано, что конкретно мы хоти открыть.

Функция глобального контекста ОткрытьФорму
Описание функции ОткрытьФорму в Синтаксис-помощники

Если нужно указать конкретную форму, то следует написать так:

Справочник.Номенклатура.Форма.ИмяФормы

Где «ИмяФормы» это название формы, которую нужно открыть. Если нужно открыть основную форму, то нужно указать имя основной формы так:

Справочник.Номенклатура.ФормаВыбора

И вторым параметром передаем параметры подбора. Третий параметр для этого метода – это Владелец, то есть какому владельцу будет подчинена данная форма.

При этом в качестве владельца может выступать форма либо какой-либо элемент формы. Разница в том, что события обработки выбора надо настраивать либо для элемента формы, либо для самой формы.

Обработчик события формы ОбработкаВыбора
Обработка выбора или подбора на форме

Этот обработчик, который называется «ОбработкаВыбора» срабатывает, когда пользователь делает двойной клик, нажимает ENTER или кнопку «Выбрать» в подчиненной форме. Более логично подчинить форму непосредственно таблице формы «Товары». Когда пользователь будет осуществлять выбор будет срабатывать обработчик.

&НаКлиенте
Процедура ТоварыОбработкаВыбора(Элемент, ВыбранноеЗначение, СтандартнаяОбработка)
	Строка = Объект.Товары.Добавить();
	Строка.Номенклатура = ВыбранноеЗначение;
	Строка.Количество = 1;
	ТоварыНоменклатураПриИзменении(Элемент);
КонецПроцедуры

Добавляем новую строку в табличную часть. Для этого обращаемся к объекту табличная часть «Товары», используя метод «Добавить». Можно заполнить и цену выбранного товара. Есть обработчик «ТоварыНоменклатураПриИзменении», который может это сделать и может возникнуть соблазн вызвать этот обработчик. Зачем что-то писать если всё уже написано, но это обернется проблемами.

Если пробовать это сделать в старом документе с заполненной табличной частью, то все будет работать. Но могут быть проблемы с новыми документами. Если создать новый документ и попытаться подобрать номенклатуру, то возникает ошибка, связанная с методом «ТоварыНоменклатураПриИзменении».

Ошибка при попытке указать цену
{Документ.РеализацияТоваров.Форма.ФормаДокумента.Форма(54)}: Значение не является значением объектного типа (Цена)
ТД.Цена = ПолучитьЦену(ТД.Номенклатура, Объект.Дата);

Система не знает такого выражения как «Цена». Если сделать еще раз выбор и посмотреть в отладчике, что содержится в текущих данных, то можно увидеть, что значение еще не определено. То есть на самой форме нет еще ни одной строки. Мы добавили строку лишь в основной реквизит формы. Еще не произошла синхронизация, поэтому вызывать данное событие нельзя. Если нужно присвоить цену, то это нужно сделать без обработчика «ТоварыНоменклатураПриИзменении».

&НаКлиенте
Процедура ТоварыОбработкаВыбора(Элемент, ВыбранноеЗначение, СтандартнаяОбработка)
	Строка = Объект.Товары.Добавить();
	Строка.Номенклатура = ВыбранноеЗначение;
	Строка.Количество = 1;
	Строка.Цена = ПолучитьЦену(ВыбранноеЗначение, Объект.Дата);
КонецПроцедуры

Подборы с множественным выбором

Сейчас немного усложним задачу. Ведь подбор работает только на одиночные выборы. То есть, если выделить несколько строк в форме подбора справочника и нажать на кнопку «Выбрать», то добавиться последняя выбранная строка. Каким образом можно реализовать множественный подбор? Если открыть форму выбора справочника и открыть в реквизитах свойства списка, то можно увидеть флаг «МножественныйВыбор». Именно это флаг нам и нужен.

Свойство таблицы МножественныйВыбор
Флаг для указания режима множественного выбора из списка

Здесь же, в свойствах списка есть еще такое свойство как «РежимВыделения». По умолчанию оно стоит взначении «Множественный» и пользователь может выделять много строк, но множественного выбора не происходит. Нужно установить флаг «МножественныйВыбор». Лучше это делать программно, обращаясь к элементу управления формы и к его флагу «МножественныйВыбор». Но есть одна проблема, которая связана с названием элемента управления формы. Это название нужно знать точно. Если элемент будет называться по-другому, то это вызовет ошибку. Этого можно избежать.

Если открыть Синтаксис-помощник, то можно увидеть параметры формы, если в качестве основного реквизита выбран динамический список.

Интерфейс (управляемый) -> Управляемая форма -> Расширение динамического списка -> Параметры формы

Как видно, здесь тоже есть параметр «МножественныйВыбор». Если выставить в основном реквизите данной формы этот параметр, то не нужно знать имя элемента управления формы. Поэтому при открытии формы нужно просто передать параметр «МножественныйВыбор».

&НаКлиенте
Процедура Подбор(Команда)
	ПараметрыПодбора = Новый Структура("ЗакрыватьПриВыборе, МножественныйВыбор", Ложь, Истина);
	ОткрытьФорму("Справочник.Номенклатура.ФормаВыбора", ПараметрыПодбора, Элементы.Товары);	
КонецПроцедуры

Теперь при выборе значения будет возвращаться несколько значений. Это будет тип значений «Массив». Изменим программный код в соответствии с этим пониманием:

&НаКлиенте
Процедура ДобавитьСтрокуТовары(Товар)
	Строка = Объект.Товары.Добавить();
	Строка.Номенклатура = Товар;
	Строка.Количество = 1;
	Строка.Цена = ПолучитьЦену(Товар, Объект.Дата);
КонецПроцедуры
&НаКлиенте
Процедура ТоварыОбработкаВыбора(Элемент, ВыбранноеЗначение, СтандартнаяОбработка)
	Если ТипЗнч(ВыбранноеЗначение) = Тип("СправочникСсылка.Номенклатура") Тогда
		ДобавитьСтрокуТовары(ВыбранноеЗначение);	
	Иначе
		Для каждого Товар Из ВыбранноеЗначение Цикл
			ДобавитьСтрокуТовары(Товар);
		КонецЦикла;
	КонецЕсли;	
КонецПроцедуры

Сейчас выбираются все строки, которые выделены, желаемый результат получен. Но еще нет возможности выбирать количество подбираемого товара. Давайте это сделаем. Для этого нужно немного изменить процедуру «ДобавитьСтрокуТовары», добавив функцию глобального контекста «ВвестиЧисло».

&НаКлиенте
Процедура ДобавитьСтрокуТовары(Товар)
	Строка = Объект.Товары.Добавить();
	Строка.Номенклатура = Товар;
	Строка.Количество = 1;
	ВвестиЧисло(Строка.Количество, "Введите количество");
	Строка.Цена = ПолучитьЦену(Товар, Объект.Дата);
КонецПроцедуры

Теперь для каждой позиции будет запрашиваться количество. Вполне возможно, что это имеет смысл.

Реализация формы подбора

Был создан очень простой способ подбора, ведь в реальных прикладных решениях используется несколько иной механизм. Механизм должен решать следующие задачи:

  • Просмотр выбранных значений в текущем сеансе подбора, то есть ему нужно просмотреть что он уже сделал и произвести необходимые изменения. Может быть пользователь кликнул дважды случайно и хочет отказаться от сделанного выбора. То есть нужно предоставить возможность визуализации выбранных значений.
  • Корректировка выбранных значений, например, пользователь ввел одну строчку и потом решает, что вместо одной штуки товара нужно указать четыреста штук. У пользователя должна быть возможность корректировки строки, не закрывая подбор.

Как реализовать такой механизм подбора? Во-первых, нужно использовать специальную форму для документа. В этом случае удобно ориентироваться на месте расположения подбора. Кроме того, в этой форме мы будем размещать не только данные справочника, но еще и таблицу, которая будет отображать, сделанный пользователем выбор.

Создадим для документа «РеализацияТоваров» новую форму, которая будет называться «ФормаПодбора». Создадим произвольную форму, так как она не будет отображать данные документа. На форме создадим реквизит «Таблица» с типом данных «ТаблицаЗначений». Сделанный пользователем выбор будет размещен в этом реквизите.

В таблицу добавим несколько колонок. Колонка «Номенклатура» тип данных справочник «Номенклатура». И колонка «Количество» тип данных число длина 15, точность 3.

Создадим еще один реквизит «Товары», в котором будет размещен список товаров с типом данных «ДинамическийСписок». В качестве основной таблицы динамического списка укажем справочник «Номенклатура». Здесь важно отметить, что обычно в подборе, кроме данных по номенклатуре, еще указывается и текущий остаток товара, который можно получить с помощью произвольного запроса. Чтобы работали механизмы множественного выбора нужно выставить флаг «Основной реквизит».

После установки этого флага система будет наследовать соответствующие параметры от динамического списка для этой формы. К сожалению, после установки этого флага на форме появилось три командных панели. И две из них абсолютно одинаковые. Чтобы отключить лишние панели нужно убрать флаг «Автозаполнение» у элемента формы «Командная панель». Для номенклатуры оставим командную панель, а вот для «Таблицы» отключим, поскольку пользователи добавляет данные в таблицу двойным кликом по верхней таблицы формы.

Флаг Автозаполнение
Флаг Автозаполнение элемента формы «Командная панель»

Итак, форму настроили, теперь нужно реализовать правильное поведение формы. Для начала в списке свойств формы найдем свойство «РежимОткрытияОкна» и из выпадающего списка выберем значение «Блокировать окно владельца». Это необходимо, чтобы пользователь не потерял окно подбора, когда нечаянно кликнет за пределами формы. В этом режиме можно перейти в главное приложение, а окно владельца блокируется, то есть то окно, из которого происходил вызов. Если сравнить с модальным режимом, то в этом режиме блокируются все окна приложения.

Когда пользователь делает выбор на одном из строк таблицы «Товары», то происходит добавление этой строки в нижнюю таблицу. Посмотрим какие есть события для таблицы формы «Товары». Событие «Выбор» как раз подойдет для реализации наших планов. Оно срабатывает при двойном щелчке мыши или нажатии на ENTER. По умолчанию система при выполнении этого действия откроет элемент справочника на редактирование, но нашем случае нужно изменить стандартное поведение.

&НаКлиенте
Процедура ТоварыВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
	СтандартнаяОбработка = Ложь;
	Строка = Таблица.Добавить();
	Строка.Номенклатура = ВыбраннаяСтрока;
	Строка.Количество = 1;
КонецПроцедуры

Чтобы проверить работу формы, нужно открыть ее из основной формы документа. Изменим немного код, который мы использовали ранее.

&НаКлиенте
Процедура Подбор(Команда)
	ПараметрыПодбора = Новый Структура("ЗакрыватьПриВыборе, МножественныйВыбор", Ложь, Истина);
	ОткрытьФорму("Документ.РеализацияТоваров.Форма.ФормаПодбора", ПараметрыПодбора, Элементы.Товары);	
КонецПроцедуры

Если сделать отладку, то обнаружится, что номенклатура не заполняется, так как переменная «ВыбраннаяСтрока» является массивом. Нужно сделать обход элементов этого массива. На каждом витке нужно добавить новую строку и установить количество.

&НаКлиенте
Процедура ТоварыВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
	СтандартнаяОбработка = Ложь;
	Для каждого Товар Из ВыбраннаяСтрока Цикл
		Строка = Таблица.Добавить();
		Строка.Номенклатура = Товар;
		Строка.Количество = 1;
	КонецЦикла;
КонецПроцедуры

Теперь нужно, чтобы при добавлении товара, который уже есть в нижней таблице не было дублирования строк, а вместо этого увеличивалось количество. Но хотелось бы вначале поговорить о том, какой тип данных у реквизита «Таблица». Если говорить о модуле формы подбора, то на клиенте переменная «Таблица» имеет другой тип данных. Если посмотреть в отладчике, то он покажет, что это тип данных «ДанныеФормыКоллекция». Синтаксис-помощник показывает, что у этого объекта есть метод «НайтиСтроки». Можно использовать этот метод, передавая параметры поиска. Перед тем, как добавить новую строку, будет осуществлен поиск.

&НаКлиенте
Процедура ТоварыВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
	СтандартнаяОбработка = Ложь;
	Для каждого Товар Из ВыбраннаяСтрока Цикл
		Поиск = Новый Структура("Номенклатура", Товар);  
		Массив = Таблица.НайтиСтроки(Поиск);
		Если Массив.Количество() > 0 Тогда
			Строка = Массив[0];		 		 
		Иначе
			Строка = Таблица.Добавить();
			Строка.Номенклатура = Товар;
		КонецЕсли;
		Строка.Количество = Строка.Количество + 1;
	КонецЦикла;
КонецПроцедуры

Передача результата в вызывающую форму

Сейчас нужно сделать передачу выбранных значений пользователя в форму владельца. Для этого воспользуемся методом «ОповеститьОВыборе». Этот метод управляемой формы нужно вызывать в форме подбора. Нужно создать команду формы, назовём ее «Перенести» и разместим ее на форме в виде кнопке и установим флаг «КнопкаПоУмолчанию», чтобы при нажатии клавиш Ctrl+ENTER происходило нажатие на эту кнопку.

Форма подбора номенклатуры
Форма подбора с возможностью перенести данные в табличную часть документа

Обратите внимание, что в метод «ОповеститьОВыборе» будет передаваться не тип данных таблица значений, ведь этого нельзя сделать на клиенте, а тип «ДанныеФормыКоллекция».

&НаКлиенте
Процедура Перенести(Команда)
	ОповеститьОВыборе(Таблица);
	Закрыть();
КонецПроцедуры

На уровне владельца нужно обработать сделанный выбор в обработчике событий «ТоварыОбработкаВыбора» и в процедуре «ДобавитьСтрокуТовары».

&НаКлиенте
Процедура ДобавитьСтрокуТовары(Товар, Количество)
	Строка = Объект.Товары.Добавить();
	Строка.Номенклатура = Товар;
	Строка.Количество = Количество;
	Строка.Цена = ПолучитьЦену(Товар, Объект.Дата);
КонецПроцедуры

&НаКлиенте
Процедура ТоварыОбработкаВыбора(Элемент, ВыбранноеЗначение, СтандартнаяОбработка)
	Если ТипЗнч(ВыбранноеЗначение) = Тип("СправочникСсылка.Номенклатура") Тогда
		ДобавитьСтрокуТовары(ВыбранноеЗначение, 1);	
	Иначе
		Для каждого Строка Из ВыбранноеЗначение Цикл
			ДобавитьСтрокуТовары(Строка.Номенклатура, Строка.Количество);	
		КонецЦикла;
	КонецЕсли;	
КонецПроцедуры

Передача данных табличной части в вызываемую форму

Сделаем так, чтобы при открытии подбора данные из табличной части документа переносились в таблицу подбора и пользователь мог видеть, что уже выбрано в текущем документе. Для этого изменим процедуру «Подбор» в форме документа.

&НаКлиенте
Процедура Подбор(Команда)
	ПараметрыПодбора = Новый Структура("ЗакрыватьПриВыборе, МножественныйВыбор, Таблица", Ложь, Истина, Объект.Товары);
	ОткрытьФорму("Документ.РеализацияТоваров.Форма.ФормаПодбора", ПараметрыПодбора, Элементы.Товары);	
КонецПроцедуры

Кроме того, в форме подбора напишем обработчик события «ПриСозданииНаСервере».

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
	Таблица.Загрузить(Параметры.Таблица.Выгрузить());
КонецПроцедуры

Еще чуточку изменим обработчик событий «ТоварыОбработкаВыбора» и процедуру «ДобавитьСтрокуТовары».

&НаКлиенте
Процедура ДобавитьСтрокуТовары(Товар, Количество)
	Строка = Объект.Товары.Добавить();
	Строка.Номенклатура = Товар;
	Строка.Количество = Количество;
	Строка.Цена = ПолучитьЦену(Товар, Объект.Дата);
	Строка.Сумма = Строка.Цена * Строка.Количество;
КонецПроцедуры

&НаКлиенте
Процедура ТоварыОбработкаВыбора(Элемент, ВыбранноеЗначение, СтандартнаяОбработка)
	Если ТипЗнч(ВыбранноеЗначение) = Тип("СправочникСсылка.Номенклатура") Тогда
		ДобавитьСтрокуТовары(ВыбранноеЗначение, 1);	
	Иначе
		Объект.Товары.Очистить();
		Для каждого Строка Из ВыбранноеЗначение Цикл
			ДобавитьСтрокуТовары(Строка.Номенклатура, Строка.Количество);	
		КонецЦикла;
	КонецЕсли;	
КонецПроцедуры

Теперь все корректно работает. Может вы увидите, что можно еще улучшить для себя в этом подборе. А результат можно посмотреть в коротком видео (33 секунды):

ПОДПИСКА