Как правильно обратиться к реквизиту реквизита в 1С 8.3 на управляемых формах?

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

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

На первый взгляд задача кажется простой: достаточно написать условие, в котором мы обратимся к реквизиту через точку от выбранной категории. Однако при работе с управляемым интерфейсом (управляемыми формами) мы мгновенно натыкаемся на особенности клиент-серверной архитектуры и специфику работы базы данных. Давайте подробно разберем, почему стандартные «простые» подходы вызывают ошибки, и как решить эту задачу красиво, быстро и в соответствии со стандартами разработки 1С.

Разбор типичных ошибок начинающих разработчиков

Рассмотрим код, который часто пытаются написать на начальном этапе работы с системой:


&НаКлиенте
Процедура СоставКатегорияБилетаПриИзменении(Элемент)
    ТекДанные = Элементы.Состав.ТекущиеДанные;
    // ОШИБКА 1: Попытка обратиться к реквизиту через точку на клиенте
    Если ТекДанные.КатегорияБилета.ТипБилета = ПредопределенноеЗначение("Перечисление.тит_Типбилета.ВходнойБилет") Тогда 
        // ОШИБКА 2: Поиск элемента по числовому коду без кавычек
        ТекДанные.Экскурсовод = Справочники.тит_Экскурсоводы.НайтиПоКоду(000000026);
    КонецЕсли;
КонецПроцедуры

Проанализируем ключевые ошибки в приведенном примере, чтобы понять, почему этот код не будет работать:

  1. Обращение к данным базы данных на клиенте. На клиенте (в директиве &НаКлиенте) переменная ТекДанные.КатегорияБилета содержит лишь ссылку типа СправочникСсылка.тит_КатегорияБилета. Ссылка — это всего лишь уникальный идентификатор (GUID) объекта в базе данных. Самих данных (таких как реквизит ТипБилета) на клиенте нет. Платформа 1С целенаправленно не кэширует все данные связанных объектов на клиенте для экономии сетевого трафика и оперативной памяти. Любая попытка написать Ссылка.Реквизит на клиенте вызовет ошибку выполнения.
  2. Поиск элемента по жестко заданному коду (Hardcode). Использование конструкции НайтиПоКоду(000000026) — крайне плохая практика по двум причинам. Во-первых, если код текстовый (а в 1С коды справочников по умолчанию текстовые с лидирующими нулями), то число 000000026 преобразуется платформой в обычное число 26, и поиск вернет пустую ссылку. Во-вторых, если в пользовательском режиме кто-то случайно или намеренно изменит код этого экскурсовода, код программы перестанет работать.
  3. Вызов тяжелого серверного контекста. Перенос всей процедуры обработки события на сервер (через директиву &НаСервере) решает проблему чтения реквизитов через точку, но негативно сказывается на производительности формы. При контекстном вызове на сервер передается вся форма целиком со всеми ее реквизитами, данными и оформлением, что создает лишнюю нагрузку на сервер и замедляет отклик интерфейса для пользователя.

Правильная архитектура решения

Для реализации этой логики по стандартам 1С нам необходимо выполнить три основных шага:

  1. Сделать элемент «Без экскурсовода» предопределенным в конфигураторе. Это позволит нам получать на него ссылку без обращения к базе данных через тяжелые функции поиска.
  2. Использовать легкий внеконтекстный серверный вызов (&НаСервереБезКонтекста) для получения реквизита связанного справочника.
  3. Оптимизировать код с помощью функций Библиотеки стандартных подсистем (БСП), если разработка ведется на современной типовой конфигурации.

Выясним подробнее, как реализовать каждый из этих шагов.

Шаг 1. Создание предопределенного элемента

Откроем конфигуратор, найдем наш справочник тит_Экскурсоводы. Перейдем на вкладку «Прочее» и нажмем кнопку «Предопределенные». Создадим там элемент с понятным именем (идентификатором), например, БезЭкскурсовода, и укажем для него понятное пользователю наименование — «Без экскурсовода».

Теперь мы можем получать этот элемент в коде как на сервере, так и на клиенте с помощью быстрой системной функции: ПредопределенноеЗначение("Справочник.тит_Экскурсоводы.БезЭкскурсовода"). Данный метод работает мгновенно и не требует выполнения запросов к СУБД.

Шаг 2. Разработка оптимального серверного запроса

Для того чтобы прочитать значение реквизита ТипБилета у выбранной категории билета, мы должны передать ссылку на эту категорию на сервер. Напишем для этого быструю функцию с директивой &НаСервереБезКонтекста. При таком вызове на сервер передается только один параметр (ссылка на категорию), а не вся форма, что гарантирует максимальное быстродействие.

Рассмотрим по шагам готовый программный код для модуля управляемой формы документа:


&НаКлиенте
Процедура СоставКатегорияБилетаПриИзменении(Элемент)
    
    // Получаем строку табличной части, в которой произошло изменение
    СтрокаТаблицы = Элементы.Состав.ТекущиеДанные;
    
    // Если строка не определена или пустая, прерываем выполнение
    Если СтрокаТаблицы = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Проверяем, заполнена ли категория билета
    Если НЕ ЗначениеЗаполнено(СтрокаТаблицы.КатегорияБилета) Тогда
        Возврат;
    КонецЕсли;
    
    // Вызываем легкую серверную функцию для проверки типа билета
    Если КатегорияЯвляетсяВходнымБилетом(СтрокаТаблицы.КатегорияБилета) Тогда
        
        // Устанавливаем предопределенного экскурсовода прямо на клиенте
        СтрокаТаблицы.Экскурсовод = ПредопределенноеЗначение("Справочник.тит_Экскурсоводы.БезЭкскурсовода");
        
    КонецЕсли;
    
КонецПроцедуры

&НаСервереБезКонтекста
Функция КатегорияЯвляетсяВходнымБилетом(Знач КатегорияСсылка)
    
    // На сервере мы имеем полное право обращаться к реквизитам через точку.
    // Платформа автоматически выполнит легкий SQL-запрос под капотом.
    ТипВходнойБилет = ПредопределенноеЗначение("Перечисление.тит_Типбилета.ВходнойБилет");
    
    Возврат КатегорияСсылка.ТипБилета = ТипВходнойБилет;
    
КонецФункции

Шаг 3. Использование БСП для ускорения работы базы данных

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

Посмотрим на пример использования функций БСП:


&НаСервереБезКонтекста
Функция КатегорияЯвляетсяВходнымБилетомБСП(Знач КатегорияСсылка)
    
    // Чтение значения реквизита "ТипБилета" с использованием кэшированных механизмов БСП
    ТипБилета = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(КатегорияСсылка, "ТипБилета");
    
    Возврат ТипБилета = ПредопределенноеЗначение("Перечисление.тит_Типбилета.ВходнойБилет");
    
КонецФункции

Метод ЗначениеРеквизитаОбъекта работает быстрее стандартного обращения «через точку», так как выполняет специализированный параметризованный запрос к базе данных, выбирая строго одно конкретное поле таблицы, исключая избыточное чтение остальных реквизитов объекта.

Дополнительное юзабилити: Условное оформление формы

Для того чтобы сделать интерфейс программы еще более дружелюбным для конечного пользователя, мы можем запретить ручное редактирование поля «Экскурсовод», если выбран входной билет. Делать это программно в коде не обязательно — для этого идеально подходит механизм Условного оформления управляемой формы.

Проанализируем ситуацию: если билет входной, поле «Экскурсовод» гарантированно должно содержать значение «Без экскурсовода», и пользователю незачем туда кликать. Настроим условное оформление прямо в конфигураторе:

  1. Откроем форму документа и перейдем на вкладку «Условное оформление» (в правом верхнем углу окна свойств формы).
  2. Добавим новое правило оформления.
  3. В колонке «Оформление» установим свойство Доступность в значение Ложь (или ТолькоПросмотр в значение Истина).
  4. В колонке «Условие» укажем: Объект.Состав.КатегорияБилета.ТипБилета Равно Перечисление.тит_Типбилета.ВходнойБилет. Примечание: в условиях оформления на сервере платформа сама умеет обращаться к реквизитам через точку.
  5. В колонке «Оформляемые поля» выберем поле нашей таблицы: СоставЭкскурсовод.

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

Применяя предложенные подходы, мы получаем надежный, быстродействующий и отказоустойчивый код, соответствующий всем современным стандартам разработки на платформе 1С:Предприятие 8.3.

← На главную