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