Ошибка обращения к общему модулю в функции обработки на сервере: переменная не определена

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке в среде 1С:Предприятие 8, особенно при переходе от обычных форм к управляемым или при написании сложных обработок обмена данными, часто возникает ошибка "Переменная не определена" при попытке обратиться к Общему модулю. Чаще всего эта проблема проявляется, когда мы пытаемся вызвать клиентский функционал (например, работу с торговым оборудованием или диалоговые окна) из серверной процедуры.

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

Суть проблемы: невидимость модулей

Рассмотрим исходную ситуацию. У нас есть функция ПолучитьНоменклатуру, которая выполняется под директивой &НаСервере. Внутри этой функции происходит создание элемента справочника, после чего разработчик пытается сгенерировать штрихкод:


&НаСервере 
Функция ПолучитьНоменклатуру(...)
    // ... Код создания номенклатуры ...
    Номен.Записать();
    
    // Попытка вызова общего модуля
    ШтрихКод = РаботаСТорговымОборудованием.СформироватьШтрихКод(); 
КонецФункции

Система выдает ошибку: Переменная не определена (РаботаСТорговымОборудованием).

Почему это происходит?
В свойствах общего модуля РаботаСТорговымОборудованием обычно установлен флажок "Клиент (обычное приложение)" или "Клиент (управляемое приложение)", но снят флажок "Сервер". Это означает, что данный модуль физически существует и компилируется только на стороне клиента. Когда код выполняется на сервере (внутри функции &НаСервере), контекст сервера ничего не знает о существовании этого модуля.

Попытка решения "в лоб" и новые ошибки

Логичным первым шагом кажется зайти в свойства общего модуля и поставить галочку "Сервер". Однако, как показала практика в обсуждении, это приводит к новым ошибкам компиляции:


{ОбщийМодуль.РаботаСТорговымОборудованием.Модуль(858,18)}: Тип не определен (ДиалогВыбораФайла)
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.ВыборКаталога);

Причина ошибки:
Сервер 1С — это фоновый процесс, который может работать на другом компьютере без монитора и пользователя. Любые интерактивные действия (диалоги, вопросы, предупреждения, работа с буфером обмена или UI) на сервере строго запрещены. Так как в модуле РаботаСТорговымОборудованием содержатся функции, использующие интерфейс (например, выбор файла драйвера), модуль не может быть скомпилирован на сервере целиком.

Правильные способы решения

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

Способ 1: Использование инструкций препроцессора (Быстрое решение)

Если вам жизненно необходимо сделать модуль доступным на сервере, и функция СформироватьШтрихКод не содержит интерактивных действий (чистая математика), мы можем "скрыть" интерактивный код от сервера.

Для этого в общем модуле необходимо установить галочку "Сервер", а все участки кода, вызывающие ошибку (работа с диалогами, формами), обернуть в инструкции препроцессора:


#Если Клиент Тогда
    Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.ВыборКаталога);
    // ... остальной клиентский код ...
#КонецЕсли

В этом случае при компиляции на сервере 1С просто проигнорирует код внутри блоков #Если Клиент, и модуль станет доступен. Однако, если сама функция штрихкодирования использует внешние компоненты (DLL), которые установлены только на клиенте, этот способ не сработает.

Способ 2: Разделение логики Клиент-Сервер (Рекомендуемое решение)

Наиболее грамотный подход — разделить выполнение задачи на этапы. Сервер должен заниматься данными (создание номенклатуры), а клиент — работой с оборудованием и генерацией штрихкодов.

Алгоритм действий должен быть следующим:

  1. Клиент: Инициирует процесс.
  2. Сервер: Создает и записывает номенклатуру, возвращает Ссылку на клиента.
  3. Клиент: Получает ссылку, вызывает функцию генерации штрихкода (модуль доступен на клиенте).
  4. Сервер: Получает ссылку и штрихкод, записывает данные в регистр сведений.

Разберем пример кода, реализующий этот подход.

Шаг 1. Клиентская процедура управления


&НаКлиенте
Процедура ОбработкаИмпорта()
    
    // 1. Вызываем сервер для создания номенклатуры
    // Передаем параметры, необходимые для создания
    СсылкаНаНоменклатуру = СоздатьНоменклатуруНаСервере(ДанныеСтроки);
    
    Если ЗначениеЗаполнено(СсылкаНаНоменклатуру) Тогда
        
        // 2. Генерируем штрихкод (Модуль доступен, т.к. мы на Клиенте)
        // Предполагаем, что функция возвращает строку штрихкода
        ШтрихКодСтрока = РаботаСТорговымОборудованием.СформироватьШтрихКод();
        
        Если ЗначениеЗаполнено(ШтрихКодСтрока) Тогда
            // 3. Записываем штрихкод в регистр на сервере
            ЗаписатьШтрихкодНаСервере(СсылкаНаНоменклатуру, ШтрихКодСтрока);
        КонецЕсли;
        
    КонецЕсли;

КонецПроцедуры

Шаг 2. Функция создания номенклатуры


&НаСервере
Функция СоздатьНоменклатуруНаСервере(Данные)
    
    // ... Логика поиска или создания номенклатуры ...
    НоваяНоменклатура = Справочники.Номенклатура.СоздатьЭлемент();
    НоваяНоменклатура.Наименование = Данные.Наименование;
    // ... Заполнение остальных полей ...
    НоваяНоменклатура.Записать();
    
    // Возвращаем ССЫЛКУ на созданный объект
    Возврат НоваяНоменклатура.Ссылка;

КонецФункции

Шаг 3. Процедура записи штрихкода

Обратите внимание, для записи в регистр сведений лучше использовать МенеджерЗаписи.


&НаСервере
Процедура ЗаписатьШтрихкодНаСервере(ВладелецСсылка, ШтрихКод)
    
    // Создаем запись в независимом регистре сведений
    РегЗапись = РегистрыСведений.Штрихкоды.СоздатьМенеджерЗаписи();
    
    // Заполняем измерения (ключевые поля)
    РегЗапись.Владелец = ВладелецСсылка;
    РегЗапись.ТипШтрихкода = ПланыВидовХарактеристик.ТипыШтрихкодов.EAN13; 
    
    // Заполняем ресурсы
    РегЗапись.Штрихкод = ШтрихКод;
    РегЗапись.Качество = Справочники.Качество.Новый; // Или нужное значение
    
    // Попытка записи
    Попытка
        РегЗапись.Записать();
    Исключение
        Сообщить("Не удалось записать штрихкод: " + ОписаниеОшибки());
    КонецПопытки;

КонецПроцедуры

Важные замечания по коду

В процессе обсуждения были выявлены типичные ошибки при написании подобного кода:

  1. Ссылка от ссылки. В исходном коде встречалась конструкция Рег.Владелец = СсылкаНаОбъект.Ссылка;. Это избыточно и считается плохим тоном. Если переменная СсылкаНаОбъект уже содержит тип СправочникСсылка, обращаться к свойству .Ссылка не нужно.
  2. Поиск по коду. Конструкции вида Справочники.ВидыНоменклатуры.НайтиПоКоду("000000003") являются ненадежными. Если в другой базе коды будут отличаться, обработка перестанет работать. Рекомендуется использовать предопределенные элементы или искать по уникальным реквизитам/наименованию, либо выносить такие настройки в константы.
  3. Контекст модуля. Если алгоритм формирования штрихкода представляет собой чистую математику (расчет контрольной суммы EAN-13) и не требует драйверов, имеет смысл вынести эту функцию в отдельный Серверный общий модуль (например, ШтрихкодированиеСервер). Это позволит вызывать его напрямую из процедуры создания номенклатуры без лишних переходов Клиент-Сервер.

Резюме

Ошибка "Переменная не определена" при вызове общего модуля на сервере означает, что модуль скомпилирован только для клиента. Не пытайтесь просто включить галочку "Сервер", если модуль содержит интерактивные команды. Лучшее решение — разделить логику: создавайте объекты на сервере, возвращайте ссылки на клиент, там выполняйте работу с оборудованием или клиентскими модулями, и затем сохраняйте результаты снова на сервере.

← На главную