При разработке в среде 1С:Предприятие 8.3 программисты часто сталкиваются с ситуацией, когда при попытке вызвать процедуру или функцию из вновь созданного общего модуля в расширении система выдает ошибку компиляции: «Переменная не определена (ИмяОбщегоМодуля)». Эта проблема особенно коварна, поскольку визуально модуль существует, имя написано без ошибок, а ключевое слово Экспорт присутствует на своем месте. Давайте подробно разберем, почему возникает эта ошибка, проанализируем механизмы работы платформы с расширениями и выясним, как правильно настроить систему для корректной работы.
Самым частым и неочевидным фактором, из-за которого платформа отказывается видеть общий модуль расширения, является свойство самого расширения под названием «Назначение». Рассмотрим подробнее, как этот параметр влияет на компиляцию кода.
В платформе 1С существует три основных типа назначения расширений:
Для решения проблемы нам необходимо проверить и изменить назначение нашего расширения. Разберем этот процесс по шагам:
Другой распространенной ошибкой является путаница между модулями форм и общими модулями в части использования директив компиляции. Проанализируем типичную ошибку начинающих разработчиков: попытку объявить директиву &НаКлиенте или &НаСервере непосредственно перед процедурой в общем модуле.
Важнейшее правило разработки в 1С: В общих модулях использовать директивы компиляции (начинающиеся с символа амперсанда «&») строго запрещено. Эти директивы предназначены исключительно для модулей форм и модулей команд. Если мы напишем их в общем модуле, компилятор выдаст синтаксическую ошибку либо проигнорирует контекст, что приведет к неработоспособности кода.
Если нам требуется разделить выполнение кода внутри одного общего модуля на клиентскую и серверную части, мы должны использовать инструкции препроцессора (начинающиеся с символа решетки «#»). Посмотрим на правильный пример реализации кода внутри общего модуля расширения:
#Если Клиент Тогда
Функция db_Сообщить() Экспорт
Сообщить("Вызов процедуры общего модуля на стороне клиента");
КонецФункции
#КонецЕсли
В данном примере препроцессор при компиляции клиентского приложения оставит эту функцию доступной, а при компиляции на сервере — проигнорирует её, что исключит возникновение конфликтов сред выполнения.
Для того чтобы общий модуль был виден в том контексте, из которого мы пытаемся его вызвать, свойства самого общего модуля в дереве метаданных расширения должны быть строго согласованы с местом вызова.
Выясним причину, почему вызов клиентского метода формы заваливается на обращении к модулю. Допустим, у нас есть процедура на форме документа:
&НаКлиенте
Процедура db_ПослеЗаписиПосле(ПараметрыЗаписи)
db_ПроцедурыИФункции.db_Сообщить();
КонецПроцедуры
Чтобы этот код успешно отработал, платформа должна понимать, что общий модуль db_ПроцедурыИФункции доступен на клиенте. Выполним правильную настройку свойств общего модуля:
&НаКлиенте (как в нашем примере), в свойствах модуля обязательно должен стоять флаг «Клиент (управляемое приложение)».Экспорт, иначе метод останется локальным для модуля.При добавлении нового расширения в информационную базу платформа 1С по умолчанию часто запускает его в так называемом безопасном режиме (Safe Mode). Безопасный режим накладывает серьезные ограничения на работу кода: блокируется работа с файловой системой, внешними компонентами, COM-объектами, а также могут накладываться ограничения на вызовы некоторых серверных методов общих модулей с клиента.
В некоторых случаях ограничения безопасного режима могут маскироваться платформой под ошибку отсутствия видимости модуля. Чтобы исключить влияние этого фактора при разработке и отладке, выполним следующие действия:
В сложных архитектурных решениях, когда необходимо обойти жесткие ограничения компилятора на этапе проверки синтаксиса (например, когда модуль может временно отсутствовать в базе или его доступность зависит от функциональных опций), можно прибегнуть к возможностям Библиотеки стандартных подсистем. В БСП существуют специальные методы для безопасного и динамического обращения к общим модулям.
Рассмотрим пример получения общего модуля динамическим путем на стороне клиента:
&НаКлиенте
Процедура db_ПослеЗаписиПосле(ПараметрыЗаписи)
// Динамическое получение клиентского общего модуля через БСП
МодульКлиент = ОбщегоНазначенияКлиент.ОбщийМодуль("db_ПроцедурыИФункции");
МодульКлиент.db_Сообщить();
КонецПроцедуры
Если же вызов планируется на стороне сервера, мы используем серверный аналог функции:
&НаСервереБезКонтекста
Процедура ВыполнитьНаСервере()
// Динамическое получение серверного общего модуля через БСП
МодульСервер = ОбщегоНазначения.ОбщийМодуль("db_ПроцедурыИФункции");
МодульСервер.ВыполнитьСервернуюОперацию();
КонецПроцедуры
Такой подход позволяет избежать жестких ошибок компиляции «Переменная не определена» на этапе сохранения конфигурации, перенося проверку существования модуля на этап выполнения (Runtime). Однако использовать его следует осознанно, отдавая предпочтение штатной статической типизации и правильной настройке свойств расширения, описанной в первых разделах нашей статьи.