Почему реквизит на управляемой форме 1С имеет значение, а в коде он пустой (0)?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Промышленность, строительство и АПК
← На главную

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

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

Анализ исходной ситуации

Автор столкнулся с проблемой в справочнике "Спецификации". В его форме есть два реквизита:

Задача — в табличной части "Состав" при выборе номенклатуры рассчитать значение для колонки "Количество", перемножив эти два реквизита. Для этого используется следующий код в обработчике события ПриИзменении для поля номенклатуры в табличной части:


&НаКлиенте
Процедура СоставНоменклатураПриИзменении(Элемент)
    СтрокаТабличнойЧасти = Элементы.Состав.ТекущиеДанные;
    
    СтруктураДанные = Новый Структура();
    СтруктураДанные.Вставить("Номенклатура", СтрокаТабличнойЧасти.Номенклатура);
    
    // Получение доп. данных с сервера (Единица измерения, Спецификация и т.д.)
    СтруктураДанные = ПолучитьДанныеНоменклатураПриИзменении(СтруктураДанные);
    
    СтрокаТабличнойЧасти.Характеристика = Неопределено;
    СтрокаТабличнойЧасти.ЕдиницаИзмерения = СтруктураДанные.ЕдиницаИзмерения;
    СтрокаТабличнойЧасти.Спецификация = СтруктураДанные.Спецификация;
    
    // ----- ПРОБЛЕМНАЯ СТРОКА -----
    СтрокаТабличнойЧасти.Количество = Объект.Вес * Объект.ВесК;
    // ----------------------------
    
    СтрокаТабличнойЧасти.КоличествоПродукции = 1;
    СтрокаТабличнойЧасти.ДоляСтоимости = 1;
    
КонецПроцедуры

Проблема в том, что Объект.ВесК (коэффициент) считывается корректно, а Объект.Вес в этой строке всегда равен нулю. Давайте разберемся, почему так происходит.

В чем заключается коренная ошибка?

Ключевая ошибка заключается в несоответствии источника данных. Проанализируем ситуацию по шагам:

  1. Что такое Объект на клиенте? В клиентской процедуре &НаКлиенте переменная Объект представляет собой данные основного объекта формы. В данном случае это данные самого справочника "Спецификации".
  2. Откуда берется значение в коде? Строка Объект.Вес пытается прочитать значение реквизита "Вес" непосредственно из объекта "Спецификация". Судя по тому, что результат равен 0, этот реквизит в самой спецификации не заполнен.
  3. Откуда берется значение на форме? А вот поле на форме, как указал автор, имеет путь к данным Объект.Владелец.Вес. Это означает, что оно показывает значение реквизита "Вес" не самой спецификации, а ее объекта-владельца (например, номенклатуры).

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

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

Правильное и надежное решение: получаем данные с сервера

Самый стабильный и правильный подход в таких случаях — не полагаться на данные объекта формы, которые могут быть неактуальны, а явно запросить все необходимые значения с сервера в момент выбора номенклатуры. Автор уже использует для этого серверную функцию ПолучитьДанныеНоменклатураПриИзменении. Осталось лишь немного ее доработать.

Шаг 1. Модифицируем серверную функцию

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


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

Что мы сделали? Мы добавили в возвращаемую структуру поле Вес, значение которого берется напрямую из реквизита объекта выбранной номенклатуры. Это гарантирует, что мы получим актуальное значение из базы данных.

Шаг 2. Исправляем клиентскую процедуру

Теперь на клиенте нужно использовать значение веса, которое вернула наша серверная функция. Оно будет храниться в переменной СтруктураДанные.


&НаКлиенте
Процедура СоставНоменклатураПриИзменении(Элемент)
    СтрокаТабличнойЧасти = Элементы.Состав.ТекущиеДанные;
    
    СтруктураДанные = Новый Структура();
    СтруктураДанные.Вставить("Номенклатура", СтрокаТабличнойЧасти.Номенклатура);
    
    // Вызываем серверную функцию и получаем все данные, включая вес
    СтруктураДанные = ПолучитьДанныеНоменклатураПриИзменении(СтруктураДанные);
    
    СтрокаТабличнойЧасти.Характеристика = Неопределено;
    СтрокаТабличнойЧасти.ЕдиницаИзмерения = СтруктураДанные.ЕдиницаИзмерения;
    СтрокаТабличнойЧасти.Спецификация = СтруктураДанные.Спецификация;
    
    // ----- ИСПРАВЛЕННАЯ СТРОКА -----
    // Используем вес, полученный с сервера, и коэффициент из объекта формы
    СтрокаТабличнойЧасти.Количество = СтруктураДанные.Вес * Объект.ВесК;
    // -----------------------------
    
    СтрокаТабличнойЧасти.КоличествоПродукции = 1;
    СтрокаТабличнойЧасти.ДоляСтоимости = 1;
    
КонецПроцедуры

Теперь расчет будет работать корректно. СтруктураДанные.Вес содержит актуальное значение веса выбранной номенклатуры, полученное с сервера. А Объект.ВесК можно по-прежнему брать из объекта формы, так как это реквизит самой спецификации, который вводится вручную и всегда актуален на клиенте.

Такой подход не только решает проблему, но и делает код более надежным и предсказуемым, так как он не зависит от случайных факторов, связанных с обновлением данных на форме. Это объясняет, почему код мог работать на тестовой базе (например, из-за другой последовательности действий или настроек), но сломался в рабочей среде.

← На главную