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