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