В практике разработки на платформе 1С:Предприятие 8 часто возникает ситуация, когда программист пытается вызвать процедуру с директивой &НаСервере из клиентского контекста, находясь при этом в одном и том же общем модуле. Несмотря на установленные флаги «Сервер» и «Вызов сервера», система выдает ошибку, сообщая, что метод не обнаружен. Проанализируем, почему так происходит, и разберем архитектурные принципы, которые помогут избежать подобных ошибок.
Рассмотрим ситуацию, с которой столкнулся автор темы. В общем модуле установлены три флага: Клиент, Сервер и Вызов сервера. Казалось бы, такая комбинация должна обеспечивать максимальную универсальность, но именно здесь кроется ловушка.
Проанализируем механизм работы платформы. Когда у общего модуля одновременно включены флаги «Клиент» и «Сервер», система при запуске приложения фактически создает два разных экземпляра (слепка) этого модуля:
Выясним причину ошибки: когда программный код исполняется на клиенте и обращается к процедуре этого же модуля, 1С ищет её в «клиентском» экземпляре. Если процедура помечена как серверная или обернута в инструкции препроцессора #Если Сервер Тогда, она просто не попадает в клиентскую версию скомпилированного кода. Платформа видит, что модуль доступен локально (на клиенте), и не инициирует удаленный вызов (RPC) к серверу, считая, что процедура должна быть здесь. Не найдя её, система выдает ошибку.
Разберем подробнее роль флага Вызов сервера. Этот флаг предназначен исключительно для того, чтобы разрешить клиентскому коду «перешагнуть» границу контекста и обратиться к процедурам модуля, который компилируется только на сервере.
Если же в настройках модуля стоит галочка «Клиент», то для клиентской части приложения этот модуль становится «своим», родным. Попытка вызвать из него серверный метод воспринимается системой как попытка обратиться к несуществующей локальной процедуре. Флаг «Вызов сервера» в данном случае игнорируется, так как механизм удаленного вызова не задействуется для модулей, имеющих клиентскую область видимости.
Посмотрим на пример кода, который часто приводит к ошибке:
// В модуле установлены флаги: Клиент, Сервер, Вызов сервера
&НаКлиенте
Процедура КомандаВыполнить(Команда) Экспорт
// Платформа ищет этот метод в клиентской копии этого же модуля
Результат = МойСерверныйМетод();
КонецПроцедуры
&НаСервере
Функция МойСерверныйМетод() Экспорт
// Этот код существует только в серверной копии модуля
Возврат "Данные с сервера";
КонецФункции
В данном примере вызов МойСерверныйМетод() приведет к ошибке «Процедура или функция с указанным именем не определена», так как компилятор на клиенте «отрезал» все, что помечено &НаСервере. Для автоматического поиска подобных проблем в коде можно использовать инструмент для анализа конфигураций на ошибки вызова процедур общих модулей.
Проанализируем рекомендации стандартов разработки 1С. Для того чтобы взаимодействие между клиентом и сервером было стабильным и понятным, необходимо разделять логику на разные общие модули. Рассмотрим классическую суффиксальную схему именования и настройки модулей:
Выясним, как это выглядит в коде. Предположим, нам нужно получить остаток товара из общего модуля:
Шаг 1. Создаем модуль СкладСервер (Флаг: Сервер)
Функция ПолучитьОстатокНаСкладе(Товар) Экспорт
Запрос = Новый Запрос;
// ... текст запроса ...
Возврат РезультатЗапроса;
КонецФункции
Шаг 2. Создаем модуль СкладВызовСервера (Флаги: Сервер, Вызов сервера)
Функция ОстатокТовара(Товар) Экспорт
// Вызываем логику из чисто серверного модуля
Возврат СкладСервер.ПолучитьОстатокНаСкладе(Товар);
КонецФункции
Шаг 3. Вызываем из клиента (из любого места)
&НаКлиенте
Процедура ПроверитьОстаток()
// Теперь вызов отработает корректно, так как 1С знает,
// что СкладВызовСервера доступен через RPC (вызов сервера)
ТекОстаток = СкладВызовСервера.ОстатокТовара(ВыбранныйТовар);
КонецПроцедуры
Важно понимать, что объединение флагов «Клиент» и «Сервер» в одном огромном модуле негативно сказывается на производительности. Проанализируем ситуацию: если ваш модуль содержит 10 000 строк кода, большая часть из которых — это работа с запросами и транзакциями (серверная часть), то при включенном флаге «Клиент» весь этот объем текста будет передаваться на клиентское место при инициализации системы.
Хотя серверные процедуры не будут исполняться на клиенте, они все равно занимают оперативную память в клиентском приложении и увеличивают время первого обращения к модулю. Разделение на специализированные модули позволяет клиенту загружать только тот код, который ему действительно необходим для работы интерфейса. Для ускорения соблюдения этих правил можно применить шаблоны для внедрения стандартов разработки 1С.
Рассмотрим использование конструкций вида #Если Сервер Тогда. В модулях со смешанными флагами разработчики часто пытаются «спрятать» серверный код от клиента таким образом. Однако это лишь подтверждает проблему: для клиента этот кусок кода просто перестает существовать в момент компиляции. Если вы вызываете процедуру, которая обернута в такой препроцессор, из клиентского контекста того же модуля, вы гарантированно получите ошибку отсутствия метода.
Подведем итог: для корректной работы серверных вызовов процедура должна находиться в модуле, где флаг Клиент выключен, а флаги Сервер и Вызов сервера включены. Это единственный надежный способ заставить платформу совершить контекстный переход с клиента на сервер.