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