Вопрос пользователю перед записью в 1С в управляемой форме задаётся немодально: в ПередЗаписью отменяем запись (Отказ = Истина), показываем вопрос через ПоказатьВопрос или ВопросАсинх, а в обработчике ответа вызываем Записать(ПараметрыЗаписи) повторно. Чтобы повторная запись не задала вопрос снова, используем флаг в переменной модуля формы.

Модальный Вопрос() для этого не годится: модальность в современных конфигурациях отключена, а веб-клиент её не поддерживает. Ниже — два рабочих варианта кода, разбор ошибок и заодно модификатор Знач, о котором часто спрашивают в контексте серверных вызовов.

Два правила клиент-серверного взаимодействия

Всё, что ниже, опирается на два принципа:

  1. Минимум серверных вызовов. Каждый вызов — это сетевой обмен и сериализация контекста.
  2. Минимум передаваемых данных. Передаём на сервер только то, что действительно нужно.

Запись документа из формы устроена именно так: на клиенте выполняется ПередЗаписью формы, затем один серверный вызов, внутри которого идут ПередЗаписьюНаСервере, события модуля объекта, проведение, ПослеЗаписиНаСервере, и уже на клиенте — ПослеЗаписи. Значит, всё взаимодействие с пользователем (вопросы, подтверждения) должно произойти на клиенте до этого вызова. На сервере спросить пользователя уже нельзя.

Вариант 1: ПоказатьВопрос и ОписаниеОповещения

Работает на любой версии платформы 8.3, где нет модальности.

1С
#Область ОбработчикиСобытийФормы

&НаКлиенте
Перем ПодтверждениеПолучено; // Истина - пользователь уже ответил «Да», вопрос не задаём

&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)

	Если ПодтверждениеПолучено = Истина Тогда
		ПодтверждениеПолучено = Ложь; // Сбрасываем, чтобы следующая запись снова спросила
		Возврат;
	КонецЕсли;

	Если Не НужноПодтверждение() Тогда
		Возврат;
	КонецЕсли;

	Отказ = Истина;

	ДополнительныеПараметры = Новый Структура("ПараметрыЗаписи", ПараметрыЗаписи);
	Оповещение = Новый ОписаниеОповещения("ПередЗаписьюЗавершение", ЭтотОбъект, ДополнительныеПараметры);
	ПоказатьВопрос(Оповещение, НСтр("ru = 'Сумма документа превышает лимит. Продолжить запись?'"),
		РежимДиалогаВопрос.ДаНет, , КодВозвратаДиалога.Нет);

КонецПроцедуры

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

&НаКлиенте
Процедура ПередЗаписьюЗавершение(Ответ, ДополнительныеПараметры) Экспорт

	Если Ответ <> КодВозвратаДиалога.Да Тогда
		Возврат; // «Нет» или окно закрыто - запись отменена
	КонецЕсли;

	ПодтверждениеПолучено = Истина;
	Записать(ДополнительныеПараметры.ПараметрыЗаписи);

КонецПроцедуры

&НаКлиенте
Функция НужноПодтверждение()
	Возврат Объект.СуммаДокумента > 1000000;
КонецФункции

#КонецОбласти

Что здесь важно:

  • Обработчик экспортный. Его вызывает платформа, а не код формы, поэтому без Экспорт будет ошибка.
  • ПараметрыЗаписи передаём через дополнительные параметры. В них лежит режим записи: просто запись или проведение. Без них «Провести и закрыть» превратится в обычную запись.
  • Флаг сбрасывается в начале повторного прохода. Если сбрасывать его после Записать(), при ошибке записи флаг может «залипнуть».
  • Кнопка по умолчанию — «Нет». Для опасных действий так безопаснее.

Если после записи нужно ещё и закрыть форму (команда «Провести и закрыть»), учтите, что исходная команда уже отработала с отказом. Проще всего в обработчике проверить, что вызов пришёл из этой команды, и после успешной записи вызвать Закрыть().

Вопрос пользователю перед записью в 1С: первый проход отменяет запись, второй выполняется после подтверждения
Вопрос пользователю перед записью в 1С: первый проход отменяет запись, второй выполняется после подтверждения

Вариант 2: Асинх, Ждать и ВопросАсинх

С платформы 8.3.18 есть асинхронные процедуры. Метод ВопросАсинх() возвращает Обещание, а оператор Ждать внутри процедуры с модификатором Асинх «дожидается» результата без блокировки клиента. Код читается сверху вниз, без отдельного обработчика оповещения.

Ключевая деталь: процедура Асинх при первом Ждать возвращает управление вызывающему. Поэтому Отказ нужно выставить синхронно, до ожидания. Удобнее всего оставить ПередЗаписью обычной процедурой, а вопрос вынести в отдельную асинхронную:

1С
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)

	Если ПодтверждениеПолучено = Истина Тогда
		ПодтверждениеПолучено = Ложь;
		Возврат;
	КонецЕсли;

	Если Не НужноПодтверждение() Тогда
		Возврат;
	КонецЕсли;

	Отказ = Истина;
	ЗапроситьПодтверждениеИЗаписать(ПараметрыЗаписи);

КонецПроцедуры

&НаКлиенте
Асинх Процедура ЗапроситьПодтверждениеИЗаписать(ПараметрыЗаписи)

	Ответ = Ждать ВопросАсинх(НСтр("ru = 'Сумма документа превышает лимит. Продолжить запись?'"),
		РежимДиалогаВопрос.ДаНет, , КодВозвратаДиалога.Нет);

	Если Ответ <> КодВозвратаДиалога.Да Тогда
		Возврат;
	КонецЕсли;

	ПодтверждениеПолучено = Истина;
	Записать(ПараметрыЗаписи);

КонецПроцедуры

Код до Ждать выполняется сразу, код после — когда пользователь ответит. Если вызывающей стороне нужен результат асинхронной функции, она тоже должна быть Асинх и использовать Ждать.

Когда что выбирать

КритерийПоказатьВопрос + оповещениеВопросАсинх + Ждать
Версия платформыЛюбая 8.38.3.18 и новее
ЧитаемостьЛогика разнесена по двум процедурамЛинейный код
Цепочка из нескольких вопросовЦепочка обработчиковНесколько Ждать подряд
Режим совместимости конфигурацииНе важенДолжен допускать асинхронность

В типовых конфигурациях на БСП много кода в стиле оповещений, и внутри них я пишу так же, чтобы не смешивать стили в одном модуле. В новых самописных решениях выбираю Асинх.

Вопрос средствами БСП

В БСП есть обёртка СтандартныеПодсистемыКлиент.ПоказатьВопросПользователю() — тот же немодальный вопрос, но с дополнительными возможностями, например флажком «Больше не задавать этот вопрос». Параметры получаются из СтандартныеПодсистемыКлиент.ПараметрыВопросаПользователю(). Формат результата в обработчике при включённых опциях отличается от простого кода возврата — сверяйтесь с описанием функции в своей версии БСП.

Модификатор Знач в серверных вызовах

Второй частый вопрос в этом же контексте — зачем в серверных процедурах пишут Знач.

В 1С параметры по умолчанию передаются по ссылке: если процедура изменит переданную структуру или присвоит параметру новое значение, это увидит вызывающий код. Пока всё выполняется на клиенте, так и происходит буквально — это один и тот же объект в памяти.

При вызове сервера с клиента одной памяти нет. Платформа сериализует значение и отправляет на сервер. Если параметр объявлен без Знач, после возврата платформа передаёт значение обратно и подменяет переменную на клиенте новым объектом. Отсюда эффект, который удивляет новичков:

1С
&НаКлиенте
Процедура ПроверитьПередачу(Команда)

	Перем1 = Новый Структура("Свойство1");
	Перем2 = Перем1; // Обе переменные указывают на одну структуру

	ИзменитьНаСервере(Перем2);

	// Перем2 теперь - новая структура, пришедшая с сервера, со Свойство2.
	// Перем1 по-прежнему старая структура без Свойство2: связь разорвана.

КонецПроцедуры

&НаСервереБезКонтекста
Процедура ИзменитьНаСервере(Параметр)
	Параметр.Вставить("Свойство2");
КонецПроцедуры

Если объявить Процедура ИзменитьНаСервере(Знач Параметр), значение не возвращается: Перем1 и Перем2 останутся одной и той же неизменённой структурой.

Практические выводы:

  • Знач у параметров серверных методов, вызываемых с клиента, честно сообщает: «результат через параметр не возвращается», и избавляет от обратной передачи.
  • Если метод должен что-то вернуть — возвращайте это функцией, а не через изменение параметра.
  • Не используйте Знач как «магическую оптимизацию» там, где параметр всё равно меняется по смыслу: следующий разработчик запутается.
  • Не передавайте на сервер весь Объект формы ради одного реквизита — это противоречит принципу минимума данных.

Вопрос или проверка: что где делать

Вопрос пользователю и проверка данных — разные вещи, и их часто путают.

ЗадачаГде решатьПочему
Спросить «точно продолжить?»Клиент, ПередЗаписью формыТолько на клиенте можно взаимодействовать с пользователем
Не дать записать некорректные данныеСервер, ОбработкаПроверкиЗаполнения или ПередЗаписью объектаПроверка должна работать при любой записи: из формы, обработки, обмена
Предупредить, но разрешитьКлиент, вопрос + повторная записьРешение принимает пользователь
Заполнить поле по умолчаниюСервер, ОбработкаЗаполнения или ПередЗаписьюНе зависит от интерфейса

Типичная ошибка — переносить в клиентский вопрос то, что на самом деле является запретом. Если документ с превышением лимита записывать нельзя ни при каких условиях, проверка должна быть в модуле объекта с Отказ = Истина и сообщением. Иначе тот же документ спокойно запишется загрузкой из файла или обменом, где никакой формы нет.

Ещё один момент — данные для вопроса. Если условие требует обращения к базе (например, проверить остаток лимита по контрагенту), получите всё нужное одним серверным вызовом до вопроса, а не несколькими. Это и есть принцип минимума серверных вызовов в действии.

Типичные ошибки

  • Неэкспортный обработчик оповещения — ошибка при закрытии вопроса.
  • Потерян ПараметрыЗаписи — документ записывается, но не проводится.
  • Нет флага повторного прохода — бесконечный цикл вопросов.
  • Ждать прямо в ПередЗаписью без Отказ = Истина до него — платформа продолжит запись, не дождавшись ответа.
  • Вопрос на сервере — в ПередЗаписьюНаСервере или модуле объекта взаимодействовать с пользователем нельзя, только отказ и сообщение.
В управляемой форме вопрос — это всегда два прохода записи: первый отменяется ради вопроса, второй выполняется после ответа.

Доработки форм типовых конфигураций по этим правилам делаю в рамках доработки 1С; если нужно разобрать конкретный модуль с вашей командой — подойдёт консультация.

Итог

  • Модальный Вопрос() не используем: отменяем запись, спрашиваем немодально, записываем повторно.
  • Классический вариант — ПоказатьВопрос с экспортным обработчиком ОписаниеОповещения.
  • С 8.3.18 удобнее Асинх + Ждать ВопросАсинх(), но Отказ выставляется до ожидания.
  • Флаг в переменной модуля формы защищает от повторного вопроса.
  • Знач в серверных вызовах отключает обратную передачу параметра; результат возвращайте функцией.