Почему не видна серверная функция общего модуля при вызове с клиента и как это исправить?

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

При переходе на разработку под управляемые формы разработчики часто сталкиваются с ошибкой Метод объекта не обнаружен при попытке вызвать функцию из общего модуля. Проанализируем типичную ситуацию: в общем модуле включены флаги Клиент (управляемое приложение), Сервер и Вызов сервера, в коде модуля прописаны директивы &НаКлиенте и &НаСервере, однако вызов серверной функции завершается ошибкой. Выясним причину такого поведения платформы 1С:Предприятие 8.3 и рассмотрим правильные варианты реализации.

Причины возникновения ошибки

Разберем по шагам, какие архитектурные и синтаксические ошибки приводят к неработоспособности кода:

  1. Некорректное использование директив компиляции в общих модулях. Символы & (например, &НаКлиенте, &НаСервере, &НаКлиентеНаСервереБезКонтекста) являются директивами компиляции и предназначены исключительно для контекстных модулей — модулей управляемых форм и команд. В общих модулях платформа 1С их полностью игнорирует. Для управления компиляцией в общих модулях применяются исключительно инструкции препроцессора: #Если Сервер Тогда, #Если Клиент Тогда и #КонецЕсли.
  2. Конфликт флагов общего модуля. Если у общего модуля одновременно установлены флаги Клиент (управляемое приложение) и Сервер, то на стороне тонкого клиента платформа создает локальный клиентский контекст этого модуля. При обращении через точку вида ИмяМодуля.ИмяФункции() тонкий клиент пытается найти метод внутри своего локального клиентского экземпляра модуля. Даже если установлен флаг Вызов сервера, перенаправление вызова на сервер не происходит для методов модуля, скомпилированного на клиенте.
  3. Обращение к реквизитам ссылочных типов на клиенте. Попытка обратиться через точку к реквизиту ссылки (например, Характеристика.УАС_ЦенаЗакупкиСНДС) в контексте тонкого клиента либо вызывает исключение, либо провоцирует неявный тяжелый вызов сервера. Все обращения к данным базы должны выполняться строго на стороне сервера.

Вариант 1. Разделение функционала по специализированным общим модулям (Рекомендуемый подход)

Рассмотрим стандарты разработки 1С. Наиболее правильным архитектурным решением является разделение серверного и клиентского кода по разным общим модулям с четко определенными зонами ответственности:

  1. Модуль с вызовом сервера (например, ЦенообразованиеВызовСервера):

    Устанавливаем флаги: Сервер, Вызов сервера. Флаг Клиент (управляемое приложение) должен быть снят.

    Посмотрим на пример кода серверного модуля, возвращающего данные по ссылке:

    
    Функция ВернутьДанныеПоХарактеристике(Характеристика) Экспорт
        
        СтруктураВозврата = Новый Структура("Ошибка, ЦенаЗакупаСНДС, ЦенаЗакупаБезНДС", Ложь, 0, 0);
        
        Если НЕ ЗначениеЗаполнено(Характеристика) Тогда
            СтруктураВозврата.Ошибка = Истина;
            Возврат СтруктураВозврата;
        КонецЕсли;
        
        СтруктураВозврата.ЦенаЗакупаСНДС   = Характеристика.УАС_ЦенаЗакупкиСНДС;  
        СтруктураВозврата.ЦенаЗакупаБезНДС = Характеристика.УАС_ЦенаЗакупкиБезНДС;
        
        Возврат СтруктураВозврата; 
        
    КонецФункции
    
  2. Клиентский или клиент-серверный модуль (например, ЦенообразованиеКлиентСервер или ЦенообразованиеКлиент):

    Устанавливаем флаги: Клиент (управляемое приложение), Сервер (для клиент-серверного) без флага Вызов сервера.

    Посмотрим, как вызывается серверная функция из клиентского кода:

    
    Процедура ПересчитатьНаценку(ТекущаяСтрока) Экспорт
        
        Характеристика = ТекущаяСтрока.Характеристика;
        Если НЕ ЗначениеЗаполнено(Характеристика) Тогда
            Возврат;
        КонецЕсли;
        
        СтруктураХарактеристики = ЦенообразованиеВызовСервера.ВернутьДанныеПоХарактеристике(Характеристика);
        
        Если СтруктураХарактеристики.Ошибка Тогда
            Сообщить("Ошибка определения наценки по строке: " + ТекущаяСтрока.НомерСтроки);
            Возврат;
        КонецЕсли;
        
        Если СтруктураХарактеристики.ЦенаЗакупаСНДС <> 0 Тогда
            ТекущаяСтрока.НаценкаРубли   = ТекущаяСтрока.Цена - СтруктураХарактеристики.ЦенаЗакупаСНДС;
            ТекущаяСтрока.ПроцентНаценки = (ТекущаяСтрока.Цена * 100) / СтруктураХарактеристики.ЦенаЗакупаСНДС - 100;
        КонецЕсли;
        
    КонецПроцедуры
    

Вариант 2. Вызов через промежуточную серверную процедуру в модуле формы

Рассмотрим случай, когда пересчет выполняется непосредственно в управляемой форме документа или справочника, а логику модуля менять нежелательно. В этом случае вызов сервера инкапсулируется внутри самой формы.

Разберем по шагам реализацию в модуле управляемой формы:

  1. Обработчик изменения данных на клиенте вызывает серверную функцию формы без контекста &НаСервереБезКонтекста.
  2. Серверная процедура формы выполняет обращение к чисто серверному общему модулю или напрямую считывает реквизиты.
  3. Результат расчета возвращается на клиент в виде структуры и подставляется в строку таблицы.

Посмотрим на пример реализации в модуле формы:


&НаКлиенте
Процедура ТабличнаяЧастьХарактеристикаПриИзменении(Элемент)
    
    ТекущаяСтрока = Элементы.ТабличнаяЧасть.ТекущиеДанные;
    Если ТекущаяСтрока = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    СтруктураЦен = ПолучитьДанныеХарактеристикиНаСервере(ТекущаяСтрока.Характеристика);
    
    Если НЕ СтруктураЦен.Ошибка И СтруктураЦен.ЦенаЗакупаСНДС <> 0 Тогда
        ТекущаяСтрока.НаценкаРубли   = ТекущаяСтрока.Цена - СтруктураЦен.ЦенаЗакупаСНДС;
        ТекущаяСтрока.ПроцентНаценки = (ТекущаяСтрока.Цена * 100) / СтруктураЦен.ЦенаЗакупаСНДС - 100;
    КонецЕсли;
    
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьДанныеХарактеристикиНаСервере(Характеристика)
    
    Возврат АфсОбщийМодуль.ВернутьДанныеПоХарактеристике(Характеристика);
    
КонецФункции

Итоги и рекомендации

Для стабильной работы конфигурации соблюдайте ключевые правила:

  1. Никогда не используйте директивы &НаКлиенте и &НаСервере в общих модулях.
  2. Не смешивайте флаги Клиент (управляемое приложение) и Вызов сервера в одном модуле.
  3. Выносите серверные вызовы в отдельные модули с суффиксом ВызовСервера либо разделяйте клиентский и серверный код по разным общим модулям конфигурации.
← На главную