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