При разработке в системе «1С:Предприятие 8» программисты часто сталкиваются с ситуацией, когда код, написанный в одном месте, отказывается работать в другом. Типичный пример: вызов интерактивного диалога через функцию Вопрос(). Вроде бы код написан правильно, синтаксическая проверка не выдает ошибок, но при запуске системы программа «вылетает» с системной ошибкой: «Процедура или функция с указанным именем не определена (Вопрос)». Особенно часто это происходит при попытке вынести логику проверки во внешний общий модуль, вызываемый из процедуры ПриНачалеРаботыСистемы.
В данной статье мы подробно разберем, почему возникает эта проблема, как работает механизм компиляции в 1С и какими способами можно обеспечить корректную работу интерактивных функций в общих модулях.
Главная причина возникновения ошибки — разделение контекстов исполнения кода. Платформа 1С четко разделяет код, который выполняется на стороне клиента (там, где есть графический интерфейс, монитор и мышь пользователя), и код, который выполняется на стороне сервера (где нет интерфейса, а есть только база данных и вычислительные мощности).
Функция Вопрос() является интерактивной. Она требует взаимодействия с пользователем: отрисовки окна, ожидания клика по кнопке. Следовательно, эта функция физически существует только в клиентском приложении. На сервере этой функции нет, сервер «не знает», как рисовать окна и ждать ответа человека. Если платформа пытается скомпилировать или выполнить код с функцией Вопрос() в контексте сервера, она сообщает, что такая функция не определена.
Давайте проанализируем настройки вашего общего модуля. В дереве конфигурации найдите модуль, в котором находится ваша процедура (например, ОбщегоНазначения), нажмите правую кнопку мыши и выберите «Свойства».
Обратите внимание на следующие флажки в палитре свойств:
Если у вас одновременно стоят галочки «Клиент» и «Сервер», платформа пытается скомпилировать этот модуль для обоих контекстов. В момент компиляции «для сервера» интерпретатор натыкается на слово Вопрос() и выдает ошибку, так как для серверной части это «незнакомое слово».
Если ваш общий модуль должен быть универсальным (то есть вызываться и с клиента, и с сервера), мы не можем просто убрать галочку «Сервер». В этом случае нам на помощь приходят директивы препроцессора. Они указывают платформе, какие части кода нужно компилировать для конкретного случая, а какие — игнорировать.
Рассмотрим, как правильно оформить вызов диалога в общем модуле, чтобы избежать ошибки:
#Если Клиент Тогда
// Этот блок кода будет виден только на стороне клиента
Режим = РежимДиалогаВопрос.ДаНет;
ТекстВопроса = "Вы уверены, что хотите продолжить проверку ХУ?";
Ответ = Вопрос(ТекстВопроса, Режим);
Если Ответ = КодВозвратаДиалога.Нет Тогда
Возврат;
КонецЕсли;
#КонецЕсли
Используя конструкцию #Если Клиент Тогда ... #КонецЕсли, мы гарантируем, что серверная часть платформы просто «пропустит» эти строки при компиляции, и ошибка «Процедура или функция не определена» исчезнет.
Проанализируем ситуацию, описанную в теме: вызов идет из модуля обычного приложения из события ПриНачалеРаботыСистемы(). Само это событие всегда выполняется на клиенте. Однако, если процедура ПроверкаХУ() расположена в модуле с установленным флагом «Сервер», ошибка все равно может возникнуть в момент инициализации модуля.
Важный совет: Старайтесь разделять логику. Если процедура предполагает интерактивное общение с пользователем (вопросы, предупреждения), лучше размещать её в модулях, предназначенных только для клиента, либо жестко ограничивать интерфейсные вызовы директивами препроцессора. Размещение Вопрос() в серверном модуле без директив считается грубым нарушением стандартов разработки фирмы «1С».
Выясним еще одну важную деталь. Если ваш модуль доступен для «Внешнего соединения» (флажок в свойствах), использование любых интерактивных методов там запрещено категорически. Внешнее соединение — это программный доступ к базе, где нет интерфейса в принципе. Если ваш код сработает при подключении через COM, система не просто выдаст ошибку, а может аварийно завершить сеанс связи.
Аналогично обстоит дело с фоновыми заданиями. Они всегда выполняются на сервере. Если ваша процедура ПроверкаХУ() будет вызвана из регламентного задания ночью, когда никакого пользователя за компьютером нет, функция Вопрос() приведет к немедленной остановке выполнения задания с ошибкой.
Если вы планируете в будущем переходить на управляемые формы или обновлять платформу до актуальных версий (8.3.3 и выше), следует помнить о свойстве конфигурации «Режим использования модальности». В режиме «Не использовать» классическая функция Вопрос() перестает работать даже на клиенте!
Вместо неё рекомендуется использовать асинхронный метод ПоказатьВопрос(). Рассмотрим пример:
// Пример для управляемого приложения или режима без модальности
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеЗакрытияВопроса", ЭтотОбъект);
ПоказатьВопрос(ОписаниеОповещения, "Продолжить выполнение операции?", РежимДиалогаВопрос.ДаНет);
// Отдельная процедура для обработки ответа
&НаКлиенте
Процедура ПослеЗакрытияВопроса(Результат, ДополнительныеПараметры) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
// Продолжаем логику
КонецЕсли;
КонецПроцедуры
Хотя в обычном приложении (о котором шла речь в исходном вопросе) модальность чаще всего разрешена, использование правильных контекстных проверок и понимание того, где «живет» Вопрос(), поможет вам писать стабильный и качественный код.
Для решения проблемы выполните следующие действия:
Вопрос(), Предупреждение() или Сообщить() (в некоторых случаях) корректно обрамлены препроцессором.