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