Как проверить реквизит связанного объекта, такого как "ЭтоПокупатель" контрагента, в управляемой форме 1С:Предприятия?

Программист 1С v8.3 (Управляемые формы)
← На главную

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

Проблема: Отсутствие доступа к реквизитам связанного объекта на клиенте

Представьте ситуацию: у нас есть документ, в котором присутствует реквизит Контрагент (ссылка на элемент справочника "Контрагенты"). Мы хотим в обработчике события КонтрагентПриИзменении, который выполняется на клиенте, проверить значение булевого реквизита ЭтоПокупатель у выбранного контрагента. Первая мысль, которая может прийти в голову: обратиться к реквизиту напрямую через точку, например, Объект.Контрагент.ЭтоПокупатель.

Однако при попытке выполнить такой код, мы получим ошибку: "Поле объекта не обнаружено". Почему это происходит? Разберем по шагам:

  1. Разделение клиент-сервер: Платформа 1С:Предприятие 8.3 работает по клиент-серверной архитектуре. Код может выполняться либо на клиенте (на компьютере пользователя), либо на сервере (на сервере 1С:Предприятия).
  2. Ограниченный контекст на клиенте: В клиентском контексте, когда мы работаем с реквизитами формы, мы получаем лишь *ссылки* на объекты базы данных (например, СправочникСсылка.Контрагенты). Эти ссылки содержат только уникальный идентификатор объекта и его представление.
  3. Отсутствие полных данных: Полные данные объекта, включая все его реквизиты, хранятся на сервере в базе данных. Клиент не загружает автоматически все реквизиты связанного объекта по его ссылке для экономии трафика и ресурсов. Поэтому, когда мы пытаемся обратиться к Объект.Контрагент.ЭтоПокупатель на клиенте, платформа не находит поле ЭтоПокупатель непосредственно в клиентском представлении ссылки на контрагента.

Именно поэтому, для получения значений реквизитов связанных объектов, нам необходимо обращаться к серверу.

Решение: Использование серверных функций для доступа к реквизитам

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

Шаг 1: Создаем серверную функцию

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

Рассмотрим два основных способа получения значения реквизита на сервере:

  1. Прямое обращение через ПолучитьОбъект():

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

    
    &НаСервере
    Функция КонтрагентЯвляетсяПокупателемНаСервере(СсылкаНаКонтрагента)
        Если ЗначениеЗаполнено(СсылкаНаКонтрагента) Тогда
            Попытка
                // Получаем объект контрагента по ссылке
                СправочникОбъектКонтрагент = СсылкаНаКонтрагента.ПолучитьОбъект(); 
                // Теперь мы можем обратиться к его реквизитам
                Если СправочникОбъектКонтрагент.ЭтоПокупатель Тогда
                    Возврат Истина;
                КонецЕсли;
            Исключение
                // Обработка ошибок, если объект не найден или недоступен
                Возврат Ложь;
            КонецПопытки;
        КонецЕсли;
        Возврат Ложь;
    КонецФункции
    

    Недостаток этого метода: вызов ПолучитьОбъект() приводит к загрузке всех реквизитов объекта в память на сервере, что может быть избыточно, если нам нужен только один реквизит. Это может негативно сказаться на производительности при большом количестве обращений.

  2. Использование функции ОбщегоНазначения.ЗначениеРеквизитаОбъекта из БСП:

    Библиотека Стандартных Подсистем (БСП) предоставляет удобную и оптимизированную функцию ЗначениеРеквизитаОбъекта, которая позволяет получить значение конкретного реквизита объекта по его ссылке, не загружая при этом весь объект целиком. Этот метод является предпочтительным для чтения отдельных реквизитов.

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

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

Шаг 2: Вызов серверной функции из клиентского обработчика

Теперь, когда у нас есть серверная функция, мы можем вызвать ее из клиентского обработчика события ПриИзменении поля Контрагент.


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

Таким образом, мы сначала проверяем, заполнен ли реквизит Объект.Контрагент, а затем передаем его ссылку в нашу серверную функцию. Серверная функция возвращает булево значение, на основе которого мы принимаем решение на клиенте.

Важный нюанс: Проверка флагов "ЭтоПокупатель" и "ЭтоПоставщик"

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

Если ваша логика требует, чтобы контрагент был строго поставщиком (и, возможно, не был покупателем), то необходимо проверять оба флага:

  1. Проверка на "не поставщик": Если цель – убедиться, что контрагент *не является* поставщиком, то следует проверять флаг ЭтоПоставщик.
  2. Проверка на "только поставщик": Если контрагент должен быть *только* поставщиком, тогда проверка может выглядеть так:
    
    &НаСервере
    Функция КонтрагентНеЯвляетсяПоставщикомНаСервере(СсылкаНаКонтрагента)
        Если ЗначениеЗаполнено(СсылкаНаКонтрагента) Тогда
            Перем ЭтоПоставщик;
            Перем ЭтоПокупатель;
    
            // Получаем оба реквизита
            ЭтоПоставщик = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаКонтрагента, "ЭтоПоставщик");
            ЭтоПокупатель = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаКонтрагента, "ЭтоПокупатель");
    
            // Если контрагент не является поставщиком
            Если Не ЭтоПоставщик Тогда
                Возврат Истина;
            КонецЕсли;
    
            // Или если он является покупателем, но не поставщиком (если такая логика требуется)
            // Если ЭтоПокупатель И Не ЭтоПоставщик Тогда
            //    Возврат Истина;
            // КонецЕсли;
        КонецЕсли;
        Возврат Ложь;
    КонецФункции
    

    И вызов на клиенте будет:

    
    Процедура КонтрагентПриИзменении(Элемент)
        Если ЗначениеЗаполнено(Объект.Контрагент) Тогда
            Если КонтрагентНеЯвляетсяПоставщикомНаСервере(Объект.Контрагент) Тогда
                Сообщить("Контрагент не является поставщиком или не соответствует требованиям!");
                Отказ = Истина;
            КонецЕсли;
        КонецЕсли;
    КонецПроцедуры
    

Всегда уточняйте точную бизнес-логику, которая должна быть реализована. Если сообщение "Контрагент не поставщик!" должно выдаваться, когда у него установлен флаг ЭтоПокупатель, это означает, что мы исключаем контрагентов, имеющих статус покупателя, из выбора, независимо от статуса поставщика. Если же имеется в виду, что должен быть именно снят флаг ЭтоПоставщик, то проверять следует именно его.

Заключение

Мы выяснили, что прямой доступ к реквизитам связанных объектов из клиентского кода в 1С:Предприятии приводит к ошибке из-за особенностей клиент-серверной архитектуры. Эффективным и правильным решением является использование серверных функций. Мы рассмотрели два подхода к реализации серверной функции: получение объекта целиком (менее производительный для одиночных реквизитов) и использование оптимизированной функции ОбщегоНазначения.ЗначениеРеквизитаОбъекта из БСП. Применяя эти методы, мы сможем надежно и производительно проверять любые реквизиты связанных объектов, обеспечивая корректную логику работы наших управляемых форм.

← На главную