При интеграции систем 1С со сторонними сервисами разработчики часто сталкиваются с ситуацией, когда веб-сервис при получении пакета данных должен программно создать, изменить или провести документ. Однако в процессе проведения возникает критическая ошибка компиляции: «Переменная не определена (РежимДиалогаВопрос)». Ситуация усугубляется, если модуль объекта поставляется без исходных кодов (закрыт разработчиком), из-за чего мы не можем напрямую зайти в код и исправить проблемный участок.
В этой статье мы подробно разберем природу данной ошибки, выясним, почему стандартные механизмы обхода не работают, и рассмотрим три практических способа решения этой проблемы, включая создание виртуальных заглушек и архитектурную перестройку процесса проведения.
---
Для начала проанализируем, почему возникает эта ошибка. Веб-сервисы 1С работают в неинтерактивном режиме на стороне сервера (или в контексте внешнего соединения). В этом контексте полностью отсутствуют клиентские методы и типы данных, связанные с отображением интерфейса. К ним относятся функция Вопрос(), системные перечисления РежимДиалогаВопрос, КодВозвратаДиалога и другие интерактивные элементы.
Если в коде модуля объекта присутствует вызов интерактивного окна, написанный без использования директив препроцессора (таких как #Если Клиент Тогда), то при любой попытке сервера скомпилировать этот модуль произойдет аварийное завершение работы.
Многие разработчики надеются обойти проблему, используя стандартное свойство ОбменДанными.Загрузка = Истина. Рассмотрим, почему этот способ абсолютно бесполезен в данном сценарии:
ОбменДанными.Загрузка анализируется платформой во время выполнения кода в обработчиках событий (например, в ПередЗаписью или ПриПроведении). Оно позволяет пропустить прикладные проверки.РежимДиалогаВопрос, компиляция прерывается с ошибкой. Код выполнения даже не успевает дойти до проверки свойства Загрузка.Поскольку исходный код закрыт, мы не можем просто обернуть ошибочные строки в директивы препроцессора. Ниже приведены детальные методы обхода этой проблемы.
---
Это самый быстрый и элегантный технический лайфхак, который позволяет обмануть компилятор 1С на сервере, вообще не прикасаясь к закрытому модулю объекта. Наша задача — сделать так, чтобы сервер «узнал» неизвестные ему переменные РежимДиалогаВопрос и КодВозвратаДиалога.
Выполним по шагам настройку заглушек в конфигураторе:
Шаг 1. Создание общего модуля «РежимДиалогаВопрос»
Создадим новый общий модуль в конфигурации:
РежимДиалогаВопросВнутри этого модуля объявим экспортные функции, имена которых совпадают со значениями перечисления, используемого в закрытом коде. Чаще всего разработчики используют варианты «ДаНет» или «ОК». Напишем следующий код:
Функция ДаНет() Экспорт
Возврат Неопределено;
КонецФункции
Функция ДаНетОтмена() Экспорт
Возврат Неопределено;
КонецФункции
Функция ОК() Экспорт
Возврат Неопределено;
КонецФункции
Шаг 2. Создание общего модуля «КодВозвратаДиалога»
Точно так же создадим второй общий модуль для эмуляции кодов возврата диалоговых окон:
КодВозвратаДиалогаПропишем в нем функции-заглушки:
Функция Да() Экспорт
Возврат Истина; // Или другое дефолтное значение для прохождения проверок
КонецФункции
Функция Нет() Экспорт
Возврат Ложь;
КонецФункции
Функция ОК() Экспорт
Возврат Истина;
КонецФункции
Шаг 3. Создание глобального обработчика функции «Вопрос»
Если закрытый код вызывает саму функцию Вопрос(), нам потребуется переопределить её в глобальном контексте. Создадим общий модуль:
ГлобальныйСерверныйКонтекстВнутри модуля определим функцию Вопрос(), которая перехватит вызовы на сервере:
Функция Вопрос(ТекстВопроса, Кнопки, СекундыОжидания = 0, КнопкаПоУмолчанию = Неопределено, Заголовок = "", КнопкаТаймаута = Неопределено) Экспорт
// Имитируем автоматический выбор согласия (кнопки "Да" или "ОК")
Возврат КодВозвратаДиалога.Да();
КонецФункции
Как это работает: Когда сервер начнет компилировать закрытый модуль объекта и встретит строку вида РежимДиалогаВопрос.ДаНет, он обратится к метаданным конфигурации. Обнаружив общий модуль с именем РежимДиалогаВопрос и экспортной функцией ДаНет(), компилятор посчитает вызов корректным. Ошибка компиляции исчезнет, и документ будет успешно проведен через веб-сервис.
---
Если использование заглушек по каким-то причинам нежелательно (например, вы боитесь нарушить логику работы других модулей или механизмов платформы), рассмотрим архитектурный паттерн «Очередь задач».
Поскольку проводить документ напрямую в контексте выполнения веб-сервиса нельзя из-за ошибок компиляции, мы разделим процесс на два независимых этапа:
ОчередьПроведенияДокументов).Рассмотрим, как запустить проведение на стороне сервера/клиента, чтобы обойти ошибку:
Если фоновое (регламентное) задание также падает с этой ошибкой (так как фоновые задания тоже выполняются на сервере), то можно настроить запуск специального служебного сеанса «робота» в режиме Толстого клиента. Сделать это можно следующим образом:
1cv8.exe ENTERPRISE /F "ПутьКБазе" /N "РоботПроведения" /P "Пароль" /C "ЗапускОбработкиПроведения"ПриНачалеРаботыСистемы пишем логику:
Процедура ПриНачалеРаботыСистемы()
ПараметрыЗапускаПереданы = ПараметрыЗапуска;
Если ПараметрыЗапускаПереданы = "ЗапускОбработкиПроведения" Тогда
// Вызываем обработку, которая выбирает документы из очереди и проводит их
ОбработатьОчередьПроведения();
ЗавершитьРаботуСистемы(Ложь);
КонецЕсли;
КонецПроцедуры
В режиме толстого клиента переменная РежимДиалогаВопрос определена на уровне платформы, поэтому проведение пройдет абсолютно штатно и без ошибок.
---
Проанализируем третий вариант — физическое исправление кода закрытого модуля. Поставка без исходных текстов означает, что в файле конфигурации присутствует только скомпилированный байт-код модуля. Существуют специализированные утилиты декомпиляции байт-кода 1С (доступные на профильных ресурсах), которые позволяют восстановить исходный текст модуля из бинарного представления.
После восстановления текста модуля мы можем исправить некорректную интеграцию интерактивных методов. Разберем пример правильного написания кода, который предотвратит ошибки на сервере:
#Если Клиент Тогда
Режим = РежимДиалогаВопрос.ДаНет;
Ответ = Вопрос("Вы действительно хотите провести документ?", Режим);
#Иначе
// На сервере, в веб-сервисе или внешнем соединении принимаем решение по умолчанию
Ответ = КодВозвратаДиалога.Да;
#КонецЕсли
Если Ответ = КодВозвратаДиалога.Да Тогда
// Основная логика проведения
КонецЕсли;
После внесения правок модуль компилируется обратно, либо измененная процедура помещается в Расширение конфигурации с аннотацией &Вместо, что позволяет не снимать основную конфигурацию с поддержки.
Важное предупреждение: Перед декомпиляцией программных продуктов сторонних разработчиков обязательно ознакомьтесь с лицензионным соглашением, чтобы не нарушить авторские права поставщика решения.
---
Мы подробно разобрали проблему и изучили несколько путей ее решения. На практике рекомендуем придерживаться следующей последовательности действий:
Вопрос, Предупреждение) категорически запрещено размещать внутри модулей объектов. Они должны быть вынесены в модули форм.