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