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

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

При разработке функционала на управляемых формах в 1С:Предприятие 8.3 разработчики часто сталкиваются с ситуацией, когда серверная процедура или функция, вынесенная в Общий модуль, отказывается работать при вызове с клиента. При этом в модуле формы или модуля объекта код может выполняться без ошибок, а при переносе в общий модуль платформа выдает ошибки компиляции, сообщения вида Переменная не определена, Метод объекта не обнаружен или падает при передаче данных. Разберем по шагам все причины такого поведения и рассмотрим правильные варианты реализации клиент-серверного взаимодействия.

Анализ основных причин ошибок при вызове общего модуля

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

  1. Неправильная настройка свойств (флагов) общего модуля. Для того чтобы экспортная функция общего модуля была доступна напрямую из клиентской процедуры (например, из &НаКлиенте формы), у общего модуля должны быть установлены флаги Сервер и Вызов сервера. Если флаг «Вызов сервера» снят, клиентское приложение не увидит серверные методы модуля.
  2. Установка флага «Глобальный». Если у общего модуля включено свойство Глобальный, его методы встраиваются в общее пространство имен конфигурации. Попытка вызвать метод через имя модуля (например, ОбщийМодульКонтакты.МояФункция()) приведет к ошибке Переменная не определена: ОбщийМодульКонтакты. Для глобальных модулей вызов выполняется напрямую: МояФункция(). В прикладных решениях рекомендуется избегать глобальных модулей и использовать обращение по имени модуля без этого флага.
  3. Ошибочное использование директив компиляции внутри общего модуля. Директивы &НаСервере, &НаКлиенте допустимы в тексте общего модуля только тогда, когда у него одновременно установлены флаги «Клиент» и «Сервер». Если модуль объявлен как чисто серверный (стоит только флаг «Сервер»), указание директивы &НаСервере перед методом вызывает синтаксическую ошибку.
  4. Попытка передачи несериализуемых типов (ТаблицаЗначений) на Тонкий клиент. Тип ТаблицаЗначений доступен исключительно на сервере, в толстом клиенте и во внешнем соединении. В тонком и веб-клиенте такого типа данных не существует. Если серверная функция возвращает ТаблицуЗначений напрямую в клиентский контекст тонкого клиента, платформа генерирует исключение времени выполнения.

Пошаговая настройка свойств общего модуля

Рассмотрим правильную конфигурацию общего модуля для выполнения серверных расчетов и их вызова с формы:

  1. Откроем свойства общего модуля в дереве метаданных.
  2. Установим флаг Сервер.
  3. Установим флаг Вызов сервера (необходим для прямого обращения из &НаКлиенте).
  4. Снимем флаги Клиент (управляемое приложение), Обычное приложение и Глобальный.
  5. Внутри модуля объявляем методы с ключевым словом Экспорт без директив &НаСервере.

Способ 1. Преобразование Таблицы Значений в массив структур

Поскольку вернуть ТаблицуЗначений на клиент в тонком клиенте нельзя, наиболее удобным и стандартным решением является её трансформация в Массив, состоящий из объектов Структура. Каждая структура в таком массиве представляет собой отдельную строку таблицы, где ключами являются имена колонок.

Посмотрим на пример универсальной функции преобразования:


Функция ТаблицаЗначенийВМассив(ТаблицаЗначений) Экспорт
    Массив = Новый Массив;
    СтруктураСтрокой = "";
    НужнаЗапятая = Ложь;
    
    Для Каждого Колонка Из ТаблицаЗначений.Колонки Цикл
        Если НужнаЗапятая Тогда
            СтруктураСтрокой = СтруктураСтрокой + ",";
        КонецЕсли;
        СтруктураСтрокой = СтруктураСтрокой + Колонка.Имя;
        НужнаЗапятая = Истина;
    КонецЦикла;
    
    Для Каждого Строка Из ТаблицаЗначений Цикл
        НоваяСтрока = Новый Структура(СтруктураСтрокой);
        ЗаполнитьЗначенияСвойств(НоваяСтрока, Строка);
        Массив.Добавить(НоваяСтрока);
    КонецЦикла;
    
    Возврат Массив;
КонецФункции

Теперь разберем, как выглядит расчетная функция в общем модуле ОбщийМодульКонтакты, формирующая и возвращающая такой массив:


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

Вызовем подготовленную функцию напрямую из клиентской процедуры в модуле управляемой формы:


&НаКлиенте
Процедура КонтактыКлиента(Команда)
    ПараметрыЗапроса = Новый Структура("Партнеры,Контрагенты", Объект.Партнер, Объект.Контрагент);
    
    // Прямой вызов серверной функции общего модуля
    МассивКонтактов = ОбщийМодульКонтакты.КонтактыКлиентаНаСервереМодульОбщий(ПараметрыЗапроса);
    
    Для Каждого СтрокаКонтакта Из МассивКонтактов Цикл
        Сообщить("Вид: " + СтрокаКонтакта.ВидКонтактов + ", Контакт: " + СтрокаКонтакта.Контакты);
    КонецЦикла;
КонецПроцедуры

Способ 2. Преобразование в двумерный массив

Если структура колонок простая и не требуется обращение по именам свойств через точку, данные можно упаковать в двумерный массив. Это снижает накладные расходы на создание структур для каждой строки.

Посмотрим на реализацию упаковки на сервере:


Функция ПолучитьКонтактыМассивом(ВходнаяСтруктура) Экспорт
    // ... Выполнение запроса и получение ТаблицаРезультат ...
    
    КоличествоСтрок = ТаблицаРезультат.Количество();
    ДвумерныйМассив = Новый Массив(КоличествоСтрок, 2);
    
    Индекс = 0;
    Для Каждого СтрокаТаблицы Из ТаблицаРезультат Цикл
        ДвумерныйМассив[Индекс][0] = СтрокаТаблицы.ВидКонтактов;
        ДвумерныйМассив[Индекс][1] = СтрокаТаблицы.Контакты;
        Индекс = Индекс + 1;
    КонецЦикла;
    
    Возврат ДвумерныйМассив;
КонецФункции

Обработка такого массива на клиенте выполняется с обращением по индексам:


&НаКлиенте
Процедура ОбработатьМассивКлиент(Команда)
    ПараметрыЗапроса = Новый Структура("Партнеры,Контрагенты", Объект.Партнер, Объект.Контрагент);
    ДвумерныйМассив = ОбщийМодульКонтакты.ПолучитьКонтактыМассивом(ПараметрыЗапроса);
    
    Для Индекс = 0 По ДвумерныйМассив.ВГраница() Цикл
        Вид = ДвумерныйМассив[Индекс][0];
        Значение = ДвумерныйМассив[Индекс][1];
        Сообщить("" + Вид + ": " + Значение);
    КонецЦикла;
КонецПроцедуры

Способ 3. Передача через Временное хранилище

Если таблица содержит большой объем данных, который не требуется отображать в интерфейсе тонкого клиента, а необходимо передать в другой серверный контекст или в другую форму, оптимально использовать платформенный механизм временного хранилища. При этом на клиент возвращается только строковый адрес.

Разберем пример реализации:


// В общем модуле (Сервер, Вызов сервера):
Функция ПоместитьТаблицуВХранилище(ВходныеПараметры, УникальныйИдентификаторФормы) Экспорт
    // ... Формирование ТаблицаРезультат ...
    
    АдресХранилища = ПоместитьВоВременноеХранилище(ТаблицаРезультат, УникальныйИдентификаторФормы);
    Возврат АдресХранилища;
КонецФункции

На клиенте получаем адрес и используем его при открытии отчета или другой формы:


&НаКлиенте
Процедура ОткрытьОтчетПоДанным(Команда)
    ПараметрыЗапроса = Новый Структура("Партнеры,Контрагенты", Объект.Партнер, Объект.Контрагент);
    
    АдресВХ = ОбщийМодульКонтакты.ПоместитьТаблицуВХранилище(ПараметрыЗапроса, УникальныйИдентификатор);
    
    ПараметрыОткрытия = Новый Структура("АдресДанных", АдресВХ);
    ОткрытьФорму("Отчет.КонтактыКлиентов.Форма", ПараметрыОткрытия);
КонецПроцедуры

Рекомендации по клиент-серверной разработке в 1С 8.3

  1. Контролируйте типы при пересечении границы Клиент-Сервер. Передавайте только базовые сериализуемые типы (Число, Строка, Дата, Булево, Структура, Массив, ссылки объектов). Избегайте передачи объектных типов (ДокументОбъект, СправочникОбъект) и несериализуемых коллекций (ТаблицаЗначений, ДеревоЗначений).
  2. Разделяйте вызовы с контекстом и без контекста. Если расчет в модуле формы не требует чтения реквизитов формы, используйте директиву &НаСервереБезКонтекста либо прямые вызовы функций общих модулей, чтобы снизить сетевой трафик.
  3. Соблюдайте стандарты архитектуры модулей. Разделяйте общие модули на чисто серверные (без флага «Вызов сервера» для внутренних серверных алгоритмов) и специализированные модули-интерфейсы с постфиксом ВызовСервера для обращения с клиента.
← На главную