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