Почему не работают обработчики событий при изменении количества и номенклатуры в 1С

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

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

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

Анализ исходного кода и выявление логических ошибок

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


&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
    Стр = Элементы.Товары.ТекущиеДанные;
    Стр.Сумма = Стр.Количество * Стр.Цена;
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьЦенуНоменклатуры(Номенклатура)
    Возврат Номенклатура.ЦенаПокупки;
КонецФункции

&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
    Стр = ПолучитьЦенуНоменклатуры(Стр.Номенклатура);
    ТоварыКоличествоПриИзменении(Элемент);
КонецПроцедуры

Проанализируем первую грубую ошибку в процедуре ТоварыНоменклатураПриИзменении. Обратите внимание на строку Стр = ПолучитьЦенуНоменклатуры(Стр.Номенклатура);. Здесь переменная Стр используется для передачи параметра функции, но сама эта переменная в контексте процедуры еще не объявлена и не инициализирована! Платформа 1С просто не знает, откуда взять объект Стр, и возвратит ошибку времени выполнения.

Более того, даже если бы переменная была определена, результатом выполнения функции ПолучитьЦенуНоменклатуры является число (цена), которое перезаписывает саму переменную Стр. Из-за этого последующий вызов пересчета суммы приводит к сбою.

Правильное присвоение значений в этой процедуре должно выглядеть так, как указано ниже:


&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
    Стр = Элементы.Товары.ТекущиеДанные;
    Стр.Цена = ПолучитьЦенуНоменклатуры(Стр.Номенклатура);
    ТоварыКоличествоПриИзменении(Элемент);
КонецПроцедуры

В этом исправленном варианте мы сначала получаем текущую строку табличной части через свойство Элементы.Товары.ТекущиеДанные, затем записываем полученную с сервера цену в соответствующий реквизит строки Стр.Цена, и только после этого вызываем процедуру расчета суммы.

Главная причина: Отсутствие привязки обработчика к элементу формы

Очень часто исправление кода в текстовом редакторе модуля не решает проблему. Почему так происходит? Дело в том, что в платформе «1С:Предприятие 8» наличие процедуры с правильным именем в модуле формы не означает, что она автоматически привяжется к событию элемента интерфейса.

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

  1. Откройте конфигуратор вашей базы данных и найдите нужный документ (например, ПоступлениеТоваров).
  2. Перейдите на вкладку «Формы» и откройте форму документа (обычно это ФормаДокумента).
  3. В дереве элементов формы (в левом верхнем углу редактора) найдите вашу табличную часть Товары и разверните её список колонок.
  4. Выберите нужный элемент управления, для которого написан обработчик. Например, поле ввода ТоварыНоменклатура или ТоварыКоличество.
  5. Нажмите на выбранный элемент правой кнопкой мыши и откройте «Свойства» (или нажмите комбинацию клавиш Alt+Enter).
  6. В открывшейся палитре свойств прокрутите список в самый низ до раздела «События».
  7. Найдите событие ПриИзменении. Обратите внимание на поле ввода рядом с ним. Если оно пустое — обработчик не привязан к элементу, даже если процедура написана в модуле!
  8. Нажмите на кнопку с лупой или выберите имя вашей процедуры (например, ТоварыНоменклатураПриИзменении) из выпадающего списка. Платформа визуально свяжет событие интерфейса с кодом в модуле.

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

Проблема обновления конфигурации базы данных

Еще одна классическая ошибка начинающих разработчиков — путаница между сохранением текста модуля и обновлением базы данных. Рассмотрим эту ситуацию подробнее.

Когда вы вносите изменения в код модуля формы в конфигураторе и нажимаете кнопку «Сохранить» (или сочетание клавиш Ctrl+S), вы сохраняете изменения только в разрабатываемой конфигурации. Если в этот момент у вас запущен сеанс «1С:Предприятие», в нем продолжает работать старая версия программы.

Чтобы изменения вступили в силу, необходимо выполнить следующие действия:

  1. Убедитесь, что все синтаксические ошибки исправлены.
  2. Нажмите клавишу F7 на клавиатуре или синюю кнопку с изображением бочки на панели инструментов конфигуратора (команда «Обновить конфигурацию базы данных»).
  3. Если система выдает предупреждение о наличии изменений, примите их, нажав кнопку «Да».
  4. Перезапустите или обновите клиентский сеанс «1С:Предприятие».

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

Ошибки в метаданных и обращении к реквизитам

Посмотрим внимательно на серверную функцию, которая должна возвращать цену номенклатуры:


&НаСервереБезКонтекста
Функция ПолучитьЦенуНоменклатуры(Номенклатура)
    Возврат Номенклатура.ЦенаПокупки;
КонецФункции

Эта функция будет корректно работать только при выполнении двух условий:

  1. В конфигурации существует справочник Номенклатура, и у него создан реквизит с точным именем ЦенаПокупки (с учетом регистра символов). Если реквизит называется «Цена» или «ЦенаЗакупки», вы получите ошибку отсутствия поля объекта.
  2. Реквизит ЦенаПокупки имеет числовой тип данных. Если тип реквизита не определен или заполнен некорректно, функция вернет пустое значение, что приведет к ошибкам при математическом расчете суммы на клиенте.

В реальных (типовых) конфигурациях 1С цены номенклатуры практически никогда не хранятся непосредственно в справочнике. Они хранятся в периодических регистрах сведений (например, ЦеныНоменклатуры). Если вы пытаетесь применить данный учебный код в реальной рабочей базе, прямое обращение через точку Номенклатура.ЦенаПокупки вернет ошибку или неопределенное значение. Для получения актуальных цен в реальных задачах необходимо использовать запросы к срезу последних регистра сведений.

Повышение надежности кода: Защита от пустых значений

Проанализируем потенциальные уязвимости в коде, которые могут привести к критическим ошибкам («падениям» программы) в процессе работы пользователя.

Если пользователь случайно очистит строку табличной части или удалит товар, событие изменения все равно сработает. При этом метод Элементы.Товары.ТекущиеДанные вернет значение Неопределено. Попытка обратиться к свойствам пустого объекта (например, Стр.Номенклатура) вызовет аварийное завершение работы формы.

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

Итоговый корректный и безопасный вариант кода

С учетом всех разобранных ошибок и лучших практик разработки под управляемые формы 1С, напишем полностью работоспособный, отказоустойчивый код для модуля формы документа:


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

&НаСервереБезКонтекста
Функция ПолучитьЦенуНоменклатуры(НоменклатураСсылка)
    
    // Проверяем, заполнена ли ссылка на номенклатуру
    Если Не ЗначениеЗаполнено(НоменклатураСсылка) Тогда
        Возврат 0;
    КонецЕсли;
    
    // Возвращаем цену из справочника (предполагается, что реквизит ЦенаПокупки существует)
    Возврат НоменклатураСсылка.ЦенаПокупки;
    
КонецФункции

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

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

← На главную