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