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