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