При разработке в среде 1С:Предприятие 8.3 на управляемых формах начинающие разработчики часто сталкиваются с ситуацией, когда попытка получить данные «через точку» от ссылки в табличной части приводит к ошибке Поле объекта не обнаружено. В этой статье мы подробно разберем, почему это происходит, как правильно организовать обращение к данным справочника из документа и какие существуют «подводные камни» у различных методов.
Рассмотрим ситуацию: у нас есть документ Производство, в табличной части которого выбрана Номенклатура. У номенклатуры есть числовой реквизит Fe (содержание железа). Мы хотим использовать это число в расчетах прямо в строке документа. Проблема заключается в том, что на клиенте объект ссылки представляет собой лишь идентификатор и строковое представление. Физически данных реквизита Fe в оперативной памяти клиентского приложения нет.
Проанализируем типичный ошибочный код на клиенте:
&НаКлиенте
Процедура ТабличнаяЧастьКоличествоПриИзменении(Элемент)
СтрокаТаблицы = Элементы.ТабличнаяЧасть.ТекущиеДанные;
// ОШИБКА: Поле объекта не обнаружено (Fe)
Результат = СтрокаТаблицы.Номенклатура.Fe / 100;
КонецПроцедуры
Чтобы получить доступ к значению Fe, нам необходимо обратиться к базе данных, а это действие возможно только на сервере. Разберем основные способы решения этой задачи.
Если нам не нужно показывать результат расчета пользователю мгновенно, а достаточно зафиксировать его при сохранении документа, мы можем использовать обработчик ПередЗаписью. В этом случае мы работаем с объектом документа, который полностью доступен на сервере.
Разберем по шагам реализацию этого метода:
Пример кода в модуле документа:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Для Каждого СтрокаДокумента Из Состав Цикл
// Проверяем, выбрана ли номенклатура
Если ЗначениеЗаполнено(СтрокаДокумента.Номенклатура) Тогда
// На сервере обращение через точку работает напрямую
СтрокаДокумента.РезультатРасчета = (СтрокаДокумента.Номенклатура.Fe / 100) * СтрокаДокумента.Количество;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Часто требуется, чтобы цифры обновлялись сразу, как только пользователь выбрал товар. Для этого нам нужно «сходить» на сервер из клиентского события ПриИзменении. Рассмотрим, как это сделать правильно, чтобы интерфейс оставался отзывчивым.
Проанализируем пример реализации:
&НаКлиенте
Процедура СоставНоменклатураПриИзменении(Элемент)
СтрокаТаблицы = Элементы.Состав.ТекущиеДанные;
Если СтрокаТаблицы <> Неопределено Тогда
// Вызываем серверную функцию для получения данных
СтрокаТаблицы.СодержаниеЖелеза = ПолучитьЗначениеFeНаСервере(СтрокаТаблицы.Номенклатура);
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьЗначениеFeНаСервере(СсылкаНаНоменклатуру)
Если ЗначениеЗаполнено(СсылкаНаНоменклатуру) Тогда
// Возвращаем только нужное число
Возврат СсылкаНаНоменклатуру.Fe;
Иначе
Возврат 0;
КонецЕсли;
КонецФункции
Обратите внимание на использование директивы &НаСервереБезКонтекста. Это критически важно для производительности: в этом режиме на сервер не передается вся форма, а только конкретный параметр (ссылка), что значительно ускоряет работу программы.
Хотя обращение СтрокаДокумента.Номенклатура.Fe на сервере выглядит простым, оно имеет скрытую проблему. Каждый такой вызов — это отдельный неявный запрос к базе данных (SQL-запрос). Если в вашей табличной части 200 строк, система выполнит 200 маленьких запросов, что может замедлить работу сервера.
Рекомендация: Если строк много, лучше использовать объект Запрос. Посмотрим на пример оптимизированного кода, который получает данные для всей таблицы одним махом:
&НаСервере
Процедура РассчитатьВсеСтрокиТаблицы()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Fe КАК Железо,
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В (&МассивСсылок)";
МассивНоменклатуры = Объект.Состав.ВыгрузитьКолонку("Номенклатура");
Запрос.УстановитьПараметр("МассивСсылок", МассивНоменклатуры);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
Для Каждого СтрокаТЧ Из Объект.Состав Цикл
СтрокаДанных = РезультатЗапроса.Найти(СтрокаТЧ.Номенклатура, "Ссылка");
Если СтрокаДанных <> Неопределено Тогда
СтрокаТЧ.СодержаниеЖелеза = СтрокаДанных.Железо;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
В задачах учета производства (как в примере автора темы) крайне важно помнить о сохранности исторических данных. Если вы просто рассчитываете значения на лету, обращаясь к справочнику, то при изменении процента железа в справочнике завтра — старые документы при пересчете или перепроведении изменят свои итоговые показатели. Это недопустимо в бухгалтерском и управленческом учете.
Правильное решение: Создайте в табличной части документа реквизит ПроцентЖелеза. При выборе номенклатуры один раз скопируйте значение из справочника в этот реквизит документа. Это «заморозит» состояние данных на момент совершения операции. Все дальнейшие расчеты делайте только на основании реквизитов самого документа.
Существует еще один способ увидеть данные из справочника на форме без написания кода. В свойствах колонки таблицы на форме можно указать ПутьКДанным как Объект.Состав.Номенклатура.Fe. В этом случае система сама отобразит число в колонке.
Однако помните:
ТекущиеДанные все равно не получится без предварительных манипуляций.Подведем итоги нашего анализа. Чтобы успешно работать с данными ссылочных типов в табличных частях, придерживайтесь следующих правил:
&НаСервереБезКонтекста.ЗначениеЗаполнено() перед получением свойств.Запрос вместо обращения через точку в цикле.