Постановка задачи
Требуется выполнить несколько проверок одну за другой. Если некоторые из проверок не проходят, то необходимо вывести пользователю диалог, для принятия решения.
В зависимости от выбранного пользователем варианта, выполняются некоторые действия, а также может выполняться следующая проверка, либо сценарий может прерваться.
Использование модальности в конфигурации отключено.
Вложенная схема выполнения асинхронных вызовов (Классический способ)
Стандартный подход к решению данной задачи можно прочитать тут и тут
Схематично его можно изобразить так
Данный подход очень неудобен.
Во-первых, непонятно количество проверок. Чтобы его узнать, необходимо в коде переходить из одной процедуры в другую.
Во-вторых, приходится создавать по две процедуры на каждую проверку.
В-третьих, Проверка2 не имеет отношения к Проверка1 с точки зрения логики. Однако в Проверка1Завершение явно прописан ее вызов. Это нехорошо.
Линейная схема выполнения синхронных вызовов
Если бы по условию задачи можно было бы использовать модальные вызовы, то схема выглядела бы так.
Этот способ удобнее для использования. Все проверки вызываются из общей процедуры. Никакая проверка "ничего не знает" о других проверках. Мы можем менять их местами, отключать, ветвить и т.д.
При этом риск потери работоспособности кода - значительно ниже.
Линейная схема выполнения асинхронных методов
Оказывается, возможно организовать вызов асинхронных действий по аналогии с синхронной цепочкой.
Для этого можно использовать метод, который умеет, образно говоря, выполняться с того места, на котором остановился в предыдущий раз.
Сценарий
&НаКлиенте
Процедура ВыполнитьСценарийСДиалогами(Результат = Неопределено, ПараметрыСценария = Неопределено) Экспорт
НачатьОтсчетШагов(ПараметрыСценария);
Оповещение = Новый ОписаниеОповещения("ВыполнитьСценарийСДиалогами", ЭтаФорма, ПараметрыСценария);
// проверка 1
Если ЭтотШагЕщеНеВыполнялся(ПараметрыСценария) Тогда
ТекстВопроса = "Это вопрос 1. Продолжить?";
ПоказатьВопрос(Оповещение, ТекстВопроса, РежимДиалогаВопрос.ДаНет);
Возврат;
ИначеЕсли ЭтоРезультатВыполненногоШага(ПараметрыСценария) И Результат <> КодВозвратаДиалога.Да Тогда
// сюда попадаем если пользователь не ответил Да на первый вопрос
// выход из процедуры без открытия асинхронного метода - прервет выполнение сценария
Возврат;
КонецЕсли;
// проверка 2
Если ЭтотШагЕщеНеВыполнялся(ПараметрыСценария) Тогда
Если ЧастьТоваровОтсутствуетНаСкладе() Тогда
ТекстВопроса = "Это вопрос 2. Хотите заменить отсутствующие товары?";
ПоказатьВопрос(Оповещение, ТекстВопроса, РежимДиалогаВопрос.ДаНетОтмена);
Возврат;
КонецЕсли;
ИначеЕсли ЭтоРезультатВыполненногоШага(ПараметрыСценария) Тогда
Если Результат = КодВозвратаДиалога.Отмена Тогда
Возврат;
ИначеЕсли Результат = КодВозвратаДиалога.Нет Тогда
// переходим к следующему шагу сценария
ИначеЕсли Результат = КодВозвратаДиалога.Да Тогда
ОткрытьФормуПодбораАналогов = Истина;
КонецЕсли;
КонецЕсли;
// продолжение проверки 2. Открываем форму подбора аналогов асинхронно и обрабатываем результат
Если ЭтотШагЕщеНеВыполнялся(ПараметрыСценария) Тогда
Если ОткрытьФормуПодбораАналогов = Истина Тогда
ОткрытьФорму("Обработка.ПодборАналоговНоменклатуры.Форма",
ПараметрыСценария.ДополнительныеПараметры,
ЭтаФорма, , , ,
Оповещение,
РежимОткрытияОкнаФормы.БлокироватьОкноВладельца;)
);
Возврат;
КонецЕсли;
ИначеЕсли ЭтоРезультатВыполненногоШага(ПараметрыСценария) Тогда
ВыполнитьДействияСАналогами(Результат);
КонецЕсли;
// проверка 3. вопрос в цикле
Если ЭтотШагЕщеНеВыполнялся(ПараметрыСценария)
ИЛИ (ЭтоРезультатВыполненногоШага(ПараметрыСценария)
И Результат = КодВозвратаДиалога.Да) // повторяем вопрос, пока пользователь отвечает Да
Тогда
ТекстВопроса = "Это вопрос 3. Задать его еще раз?";
ПоказатьВопрос(Оповещение, ТекстВопроса, РежимДиалогаВопрос.ДаНет);
Возврат;
КонецЕсли;
// действия после проверок
ВыполнитьОсновныеДействия();
КонецПроцедуры
Служебные методы
&НаКлиенте
Процедура НачатьОтсчетШагов(ПараметрыСценария)
Если ПараметрыСценария = Неопределено Тогда
ПараметрыСценария = Новый Структура;
КонецЕсли;
Если НЕ ПараметрыСценария.Свойство("ПредыдущийШаг") Тогда
// при первом запуске основной процедуры это свойство отстуствует
ПараметрыСценария.Вставить("ПредыдущийШаг", 0);
КонецЕсли;
ПараметрыСценария.Вставить("ТекущийШаг", 0);
КонецПроцедуры
&НаКлиенте
Функция ЭтотШагЕщеНеВыполнялся(ПараметрыСценария)
ПараметрыСценария.ТекущийШаг = ПараметрыСценария.ТекущийШаг + 1;
Если ПараметрыСценария.ПредыдущийШаг >= ПараметрыСценария.ТекущийШаг Тогда
Возврат Ложь;
КонецЕсли;
ПараметрыСценария.ПредыдущийШаг = ПараметрыСценария.ТекущийШаг;
Возврат Истина;
КонецФункции
&НаКлиенте
Функция ЭтоРезультатВыполненногоШага(ПараметрыСценария)
Возврат ПараметрыСценария.ПредыдущийШаг = ПараметрыСценария.ТекущийШаг;
КонецФункции
Данный подход очень похож на синхронную цепочку и лишен описанных выше недостатков классического способа.
Файлы
Для иллюстрации прикладываю микроконфигурацию с единственным документом, который выполняет действие Провести и закрыть с несколькими проверками. Это переделанная конфигурация из этой статьи.
Также в качестве примера прикладываю обработку, которая задает два вопроса и одно предупреждение по описанной методике, для тех кому лень разворачивать конфигурацию. Она открывается в управляемом режиме в любой конфигурации на платформе 8.3
Послесловие
Данная схема была разработана при решении производственной задачи. Источником идеи послужило вот это видео