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