Как правильно обработать событие ПриИзменении для реквизита составного типа данных в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке документов и справочников в 1С часто возникает необходимость использовать реквизиты составного типа. Например, поле табличной части может содержать как ссылку на элемент справочника Номенклатура, так и на справочник Материалы. Однако при интерактивной работе с таким полем разработчики сталкиваются с неожиданным поведением: процедура события ПриИзменении срабатывает еще до того, как пользователь фактически выбрал конкретную позицию из списка, что приводит к ошибке «Значение не является значением объектного типа». Разберем механику этой проблемы и рассмотрим надежные способы её решения.

Выясним причину такого поведения платформы 1С. Когда реквизит имеет составной ссылочный тип, интерактивный выбор в поле ввода происходит в два этапа:

  1. Платформа открывает стандартное системное окно выбора типа данных, где пользователь указывает нужный справочник (например, Номенклатура).
  2. Сразу после подтверждения типа платформа присваивает реквизиту пустую ссылку выбранного типа (например, СправочникСсылка.Номенклатура.Пустая()). С точки зрения платформы значение реквизита уже изменилось (было Неопределено или пустое значение другого типа, а стала пустая ссылка нового типа). В этот момент немедленно генерируется событие ПриИзменении.
  3. Только после этого открывается форма списка или форма выбора самого справочника. Если в коде процедуры ПриИзменении сразу идет обращение к реквизитам объекта (например, считывание ставки НДС, единицы измерения или артикула), обращение к свойствам пустой ссылки вызывает исключение.

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

Способ 1. Проверка заполненности значения через функцию ЗначениеЗаполнено

Рассмотрим самый быстрый и безопасный способ исправления ошибки непосредственно в процедуре ПриИзменении. Нам необходимо прерывать выполнение алгоритма, если в реквизите находится пустое значение (включая пустую ссылку выбранного справочника) или значение не определено.

Обратим внимание на частую ошибку: прямое использование конструкции вида НЕ Реквизит.Пустая() может вызвать падение платформы, если в реквизите окажется значение простого типа, Неопределено или Null, у которых метод Пустая() отсутствует. Поэтому согласно стандартам разработки 1С следует использовать универсальную системную функцию ЗначениеЗаполнено().

Посмотрим на пример правильной организации процедуры в модуле формы:


&НаКлиенте
Процедура ТабличнаяЧастьНаименованиеПриИзменении(Элемент)
    
    СтрокаТабличнойЧасти = Элементы.ТабличнаяЧасть.ТекущиеДанные;
    Если СтрокаТабличнойЧасти = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Если пользователь только выбрал тип (значение - пустая ссылка)
    // или очистил поле, прерываем выполнение алгоритма заполнения
    Если НЕ ЗначениеЗаполнено(СтрокаТабличнойЧасти.Наименование) Тогда
        ОчиститьЗависимыеРеквизиты(СтрокаТабличнойЧасти);
        Возврат;
    КонецЕсли;
    
    // Выполняем дальнейшую обработку заполненного значения
    ОбработатьВыборЭлемента(СтрокаТабличнойЧасти);
    
КонецПроцедуры

Функция ЗначениеЗаполнено() корректно вернет Ложь как для Неопределено, так и для пустой ссылки любого из допустимых справочников. Таким образом, промежуточный шаг выбора типа данных будет проигнорирован, а полезный код отработает только тогда, когда пользователь выберет конкретный элемент.

Способ 2. Проверка конкретного типа значения через ТипЗнч

Разберем ситуацию, когда алгоритм подтягивания данных зависит от того, какой именно объект был выбран — материал или номенклатура. В этом случае мы объединяем проверку на заполненность с ветвлением логики по типу полученного значения с помощью функции ТипЗнч() и системной функции Тип().

Посмотрим на пример реализации такого условия:


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

Важно помнить, что при сравнении типов справочников мы проверяем совпадение именно с типом ссылки: Тип("СправочникСсылка.ИмяСправочника"), а не с объектом или менеджером.

Способ 3. Перенос логики заполнения в событие ОбработкаВыбора

Проанализируем альтернативный подход на уровне событий формы. У любого поля ввода есть событие ОбработкаВыбора. В отличие от события ПриИзменении, данное событие вызывается только в тот момент, когда пользователь действительно завершил интерактивный выбор в открывшейся форме списка или в выпадающем меню.

В параметр события ВыбранноеЗначение передается уже выбранная ссылка на объект, минуя промежуточную установку пустой ссылки. Рассмотрим пример кода:


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

Обратите внимание: событие ОбработкаВыбора не срабатывает при ручной очистке поля ввода (например, при нажатии клавиши Delete). Поэтому, если логика документа требует очистки зависимых полей (цены, единицы измерения, ставки НДС) при сбросе значения, в процедуре ПриИзменении следует оставить минимальный блок очистки.

Способ 4. Программное управление выбором через событие НачалоВыбора

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

Рассмотрим, как отключить стандартное поведение и реализовать последовательный выбор:


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

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

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

Способ 5. Настройка свойства элемента формы ВыбиратьТип и ограничение типов

Посмотрим на доступные свойства самого элемента формы:

  1. Свойство элемента формы «ВыбиратьТип»: по умолчанию оно установлено в значение Истина. Если установить его в Ложь, платформа не будет повторно вызывать диалог выбора типа, если в поле уже определен какой-либо тип (даже пустая ссылка). Это ускоряет работу пользователей, когда реквизит изменяется повторно.
  2. Использование свойства «ОграничениеТипа»: если в строке табличной части есть дополнительный реквизит, указывающий тип строки (например, переключатель «Товар» / «Материал»), разработчик может динамически передать в свойство элемента формы ОграничениеТипа объект ОписаниеТипов, содержащий только один конкретный справочник. В этом случае диалог выбора типа будет подавлен платформой автоматически.

Подведем итог: наиболее простым, универсальным и устойчивым решением является добавление проверки ЗначениеЗаполнено() в начало процедуры ПриИзменении с последующим анализом через ТипЗнч(). Это полностью предотвращает ошибки выполнения и делает код безопасным независимо от действий пользователя.

← На главную