Вопрос в 1С 8.3 — отказываемся от модальности в управляемых формах с помощью асинхронных вызовов
Когда появилась возможность публиковать 1С в интернете или устанавливать платформу 1С 8.3 на мобильные приложения, возникла проблема с модальными окнами.
Дело в том, что в мобильных приложениях (web интерфейсах) они не открываются вообще, а на настольных компьютерах в браузере они воспринимаются как всплывающие окна, и чаще всего заблокированы пользователем.
Какое-то время фирма 1С боролась с таким положением вещей и даже разработала сложный и ненадежный механизм для модальных окон. Но, например, на iPhone он до сих пор не работает (из источников на сайте 1С).
В связи с этим, фирма 1С решила вообще отказаться от модальных окон. Точнее, пока оставили такую возможность, но и появилась настройка, запрещающая полностью использовать модальность. В 1С 8.3 появилась возможность производить асинхронный вызов модальных окон.
Эту настройку можно найти в свойствах конфигурации.
Заходим в Конфигуратор, нажимаем правой кнопкой мышки на самую первую строчку и выбираем «Свойства». У кого все свойства не уместились на экране, прокручиваем до самого низа:
На данный момент существует три режима применения модальных окон:
Теперь разберемся, как оформить новый функционал программно.
Задаем вопрос в 1С Предприятие 8.3
Начну с того, что нужно будет делать две процедуры. Первая, где задается сам вопрос, вторая, где он обрабатывается.
Я сразу напишу пример, а в комментариях будут пояснения. Так проще и наглядней:
На этом все, приятного программирования!
К сожалению, мы физически не можем проконсультировать бесплатно всех желающих, но наша команда будет рада оказать услуги по внедрению и обслуживанию 1С. Более подробно о наших услугах можно узнать на странице Услуги 1С или просто позвоните по телефону +7 (499) 350 29 00. Мы работаем в Москве и области.
Как в общем модуле вызвать диалог Вопроса «Да.Нет»
Подскажите плиз.
В общем модуле сделал процедуру :
Процедура ДЗР(Отказ,ДокументОбъект,ТекПользователь,ЭтаФорма) Экспорт
Если ДокументОбъект.Дата >РежимДиалогаВопрос.ДаНет;
<ОбщийМодуль.ОбщегоНазначения.Модуль(1760,17)>: Переменная не определена (КодВозвратаДиалога)
в модуле есть функция :
Функция ВопросДаНет(ТекстВопроса) Экспорт
Возврат (КодВозврата = КодВозвратаДиалога.Да);
я в процедуре пишу так :
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
Режим = РежимДиалогаВопрос.ДаНет;
Ответ = Вопрос(«Закрыть без сохранения?», Режим, 0);
Если Ответ = КодВозвратаДиалога.Да Тогда
Отказ=Истина;
ЭтаФорма.Модифицированность=Ложь;
ЭтаФорма.Закрыть();
Иначе
//а тут ни фига
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
Режим = РежимДиалогаВопрос.ДаНет;
Ответ = Вопрос(«Закрыть без сохранения?», Режим, 0);
Если Ответ = КодВозвратаДиалога.Да Тогда
Отказ=Истина;
ЭтаФорма.Модифицированность=Ложь;
ЭтаФорма.Закрыть();
Иначе
//а тут ни фига
КонецЕсли;
КонецПроцедуры
Этот код не канает, мне надо код разместить в общем модуле.
Все оказалось намного проще :
1. В общем модуле
Функция ВопросДаНет(ТекстВопроса) Экспорт
Возврат (КодВозврата = КодВозвратаДиалога.Да);
Процедура ДЗР(Отказ,ДокументОбъект,ТекПользователь,ЭтаФорма) Экспорт
Разработка конфигураций в режиме без использования модальности
1С:Предприятие использует модальные окна в тех ситуациях, когда от пользователя требуется ввод информации, без которой невозможно дальнейшее выполнение алгоритма. При этом полностью блокируется весь остальной интерфейс программы, а исполнение программного кода останавливается до тех пор, пока пользователь не закроет модальное окно. С появлением веб-клиента и с переходом 1С:Предприятия на мобильные платформы, модальные окна оказались источником большого количества неудобств и проблем, зачастую неразрешимых.
Как правило «десктопные» браузеры лучше или хуже, но поддерживают модальные окна и открывают их в новом окне браузера как «всплывающее окно». Но из-за того, что технология всплывающих окон массово используется в Интернете для размещения рекламы, практически во всех браузерах стандартно отключен показ всплывающих окон. В подавляющем большинстве случаев без изменения этой настройки браузера работа веб-клиента становится невозможной.
Другая проблема заключается в том, что мобильные браузеры вообще не поддерживают модальные окна.
В такой ситуации вариант работы интерфейса 1С:Предприятия без использования модальных окон снимает все перечисленные выше проблемы. При этом не происходит отказа от функциональности, которая обеспечивалась ранее модальными окнами. Но эта функциональность реализуется другими технологиями, которые не препятствуют работе 1С:Предприятия в браузерах. В новом режиме работы интерфейса окно, которое раньше было бы модальным, рисуется в пределах родительского окна, и точно так же блокирует весь остальной интерфейс веб-клиента. В результате:
Когда надо отказываться от модальности
Не нужно «бросать всё» и начинать отказываться от модальности. Есть несколько случаев, когда можно продолжать работать в модальном режиме.
Во-первых, не любая версия технологической платформы поддерживает режим работы интерфейса без использования модальности. Этот режим работы существует, начиная с версии 8.3.3.721 (06.09.2013). Поэтому, если вы работаете на младших версиях платформы, можете пока не озадачиваться отказом от модальности.
Во-вторых, не все прикладные решения в обязательном порядке должны использовать этот режим. Критичными являются приложения, работа с которыми будет вестись:
Если ваше прикладное решение заведомо не будет использоваться в этих режимах, вы можете пока не отказываться от модальности.
Однако все типовые решения 1С разрабатывает исходя из того, что они могут использоваться любым из доступных способов. Поэтому в них основным режимом работы интерфейса является режим без использования модальности. В этом режиме будут работать новые прикладные решения. На этот режим будут переводиться старые прикладные решения. А значит, этот режим работы вам лучше начать осваивать уже сейчас. Даже если ваше приложение пока может его не использовать, мы рекомендуем, по возможности, начать перевод уже сейчас. Потому что в будущем этот режим работы будет единственным.
Сравнение модального и немодального режимов работы
Существенное количество сценариев работы может потребовать не просто замены одних методов встроенного языка другими, а изменения самого подхода к проектированию юзабилити интерфейса прикладного решения. Проще говоря, прежде чем пытаться переделать модальный диалог, стоит подумать, а можно ли здесь вообще обойтись без него?
Подробнее об этом мы поговорим позже, а для начала разберемся, в чём же заключаются принципиальные отличия этих двух режимов работы интерфейса. Понять разницу модального и немодального режимов работы проще всего на небольшом примере. Старая процедура, открывавшая модальное окно и обрабатывавшая полученные им данные, могла выглядеть так:
Метод Вопрос() открывает модальное окно. В момент исполнения этого метода дальнейшее исполнение программного кода останавливается, модальное окно блокирует весь интерфейс и ожидает реакции пользователя.
В этом примере выводятся два тестовых сообщения и на картинке видно, что оба сообщения появляются на экране только после того, как модальное окно исчезнет.
Таким образом можно сказать, что старый режим работы обеспечивает модальность как для разработчика (останавливается исполнение кода), так и для пользователя (блокируется интерфейс, доступно только модальное окно).
Новый режим работы интерфейса обеспечивает модальность лишь для пользователя. Для разработчика, в момент отображения блокирующего окна, исполнение программного кода не останавливается. Чтобы увидеть это рассмотрим не совсем корректный пример:
В этом примере мы специально допустили ошибку. Но благодаря ей мы можем увидеть обе важные особенности.
Во-первых, алгоритм, который раньше являлся одним целым, теперь нужно разделять на две части. Одна часть открывает блокирующее окно. Вторая часть обрабатывает реакцию пользователя в блокирующем окне. Чтобы система знала, с какого места продолжать исполнение программного кода, блокирующему окну передаётся имя процедуры, которая должна быть выполнена тогда, когда пользователь закроет это окно.
Во-вторых, после оператора, открывающего блокирующее окно, не должно содержаться исполняемого кода. Иначе этот код будет выполнен, не дожидаясь реакции пользователя.
Поэтому правильным решением является перенос всего кода в процедуру, исполняемую после закрытия блокирующего окна:
Тут нужно оговориться, и мы это видели на примере, что нет запрета на то, чтобы после оператора, открывающего блокирующее окно, содержался какой-либо исполняемый код. Но вы должны понимать, что платформа выполнит этот код без остановки. Независимо от того, что введёт пользователь в блокирующем окне, или когда он это окно закроет. Если такое поведение не противоречит логике вашего прикладного решения, то пожалуйста, можно использовать этот вариант.
Механизмы платформы для отказа от модальности
Этот раздел носит, скорее, «энциклопедический» характер. Он будет полезен тем, кто хочет в полной мере познакомиться со всеми механизмами платформы, которые появились для обеспечения немодального режима работы.
Если же вам не терпится приступить к «активным действиям», то вы можете перейти сразу к одному из следующих разделов:
Итак, возвращаясь к механизмам платформы нужно сказать, что по большому счёту они делятся на две большие группы. Механизмы, которые обеспечивают работу в немодальном режиме и механизмы, помогающие перейти в этот режим. Основная часть этих механизмов появилась в платформе начиная с версии 8.3.3.721 (06.09.2013), а в версии 8.3.5 в неё были добавлены инструменты рефакторинга, облегчающие преобразование имеющихся конфигураций. Перечислим все эти механизмы по-порядку.
Новый режим интерфейса
Новый режим открытия окна формы
Немодальные аналоги модальных методов
Кроме документации метод-дублёр можно посмотреть и в синтакс-помощнике. Для каждого старого метода они указаны.
Пример использования этих методов есть, например, в разделе Вопрос в модуле команды, формы или в общем модуле.
Новый синтаксис метода ОткрытьФорму()
Они позволяют сразу передать форме нужное описание оповещения и указать новый режим открытия окна. Пример можно посмотреть в разделе Как поступить, если форму нужно открывать то в обычном, то в блокирующем режиме.
Объект встроенного языка ОписаниеОповещения
Иногда возникает необходимость разместить обработчик оповещения не в том модуле, в котором создаётся описание оповещения, а в другом, например, в общем модуле. В таком случае вместо ЭтотОбъект можно указать имя общего модуля. Пример можно посмотреть в разделе Вопрос в модуле управляемого приложения.
Кроме этих параметров описание оповещения может содержать структуру с произвольными данными. Передача этих данных в обработчик оповещения может понадобиться для того, чтобы алгоритм, выполняемый после реакции пользователя, мог использовать значения, вычисленные перед открытием блокирующего окна. Такой пример можно посмотреть в разделе Как передать дополнительную информацию в форму, открываемую в блокирующем режиме.
Свойство формы ОписаниеОповещенияОЗакрытии
Метод ВыполнитьОбработкуОповещения()
Этот метод используется для того, чтобы выполнить процедуру обработчика оповещения. Чтобы указать на этот обработчик, в метод передаётся описание оповещения.
Этот метод требуется в сложных «многоуровневых» алгоритмах асинхронных вызовов, т.к. описание оповещения может быть сконструировано в начале алгоритма, а реальное использование этого обработчика может понадобиться через несколько вложенных процедур. Такой пример можно посмотреть в разделе Вопрос во вложенной процедуре, после которой есть код.
Поиск модальных методов в режиме расширенной проверки конфигурации
Инструменты для полуавтоматического перевода конфигураций в режим без использования модальности
Как писать новые конфигурации
Общие замечания
Если вы создаёте новую конфигурацию и хотите, чтобы она не использовала модальный режим, нужно придерживаться нескольких простых советов.
Как мы уже говорили ранее, не все сценарии работы могут быть легко и просто переведены на использование блокирующих диалогов. Если нельзя отказаться от использования блокирующих диалогов, то в новых конфигурациях имеет смысл заранее продумывать разделение кода, требующего действий пользователя, и кода, реализующего прикладную логику. И стараться их не смешивать. Например, сначала выполнить все интерактивные действия, «пообщаться» с пользователем, а после этого выполнить прикладной алгоритм «без вопросов».
В-третьих, если вы всё же решили использовать блокирующие диалоги, то вместо старых, модальных методов, следует использовать их новые немодальные аналоги. Обычно (но не всегда) немодальные аналоги начинаются со слова «Показать». В синтакс-помощнике для каждого старого модального метода указан его блокирующий аналог. А полный список соответствия всех методов можно посмотреть в документации.
В-пятых, если форма всегда будет открываться только в блокирующем режиме, в конфигураторе нужно установить её свойство РежимОткрытияОкна в значение «Блокировать весь интерфейс «.
Здесь надо сделать следующее замечание. Используя » Блокировать весь интерфейс » мы полностью моделируем для пользователя старое, модальное поведение. Но дело в том, что модальные окна, являясь элементами синхронной (блокирующей) модели, не очень хорошо сочетаются с природой веб-приложений, которые в большинстве своём построены на асинхронной модели. То есть веб-приложения не блокируют действия пользователя, если на это нет совсем крайней необходимости.
В некоторых сценариях это может быть полезно. Например для того, чтобы выполнить действия, препятствующие дальнейшей работе длительной обработки. Через 30 минут её работы выяснилось, что не все нужные документы проведены. Тогда пользователь может провести документы, а потом вернуться к прерванной обработке. Не запуская её с самого начала.
В некоторых ситуациях это может быть вредно. Потому что пользователь может «потерять фокус», начать «гулять» по интерфейсу, зайти далеко и потом долго искать ту форму, на которой был задан вопрос.
В результате платформа создаст конструктор и процедуру, обрабатывающую оповещение:
Типичные примеры
Далее мы разберём несколько типичных примеров использования немодальных вызовов. Эти примеры помогут вам разобраться в новых подходах к написанию кода и построению алгоритмов.
Вопрос в модуле команды, формы или в общем модуле
Суть этого примера заключается в том, что мы используем немодальный метод-дублёр, в который передаём описание оповещения. А всю логику, которая должна обрабатывать ответ пользователя, помещаем в отдельную экспортируемую процедуру в этом же модуле. Именно на эту процедуру и указывает описание оповещения.
Вопрос в модуле управляемого приложения
Этот же приём нужно использовать, если вы находитесь в глобальном общем модуле.
Например, в конфигурации есть форма, перед закрытием которой также задаётся вопрос пользователю в немодальном диалоге. Допустим, пользователь модифицировал данные в этой форме и, не сохраняя их, пытается закрыть всё приложение. В результате он ответит что-то в диалоге перед закрытием формы, форма закроется, но приложение останется открытым. Так происходит потому, что в немодальном диалоге перед закрытием формы сначала отменяется стандартная последовательность действий системы (см. пример). А это, в том числе, отменяет и закрытие самого приложения.
Как открыть произвольную форму в блокирующем режиме
Как поступить, если форму нужно открывать то в обычном, то в блокирующем режиме
Как передать дополнительную информацию в форму, открываемую в блокирующем режиме
Может так случиться, что до вызова блокирующего окна у вас накопились какие-то значения, которые должны быть использованы после того, как пользователь ответит на вопрос или закроет блокирующую форму. Как передать их в процедуру, обрабатывающую оповещение?
Для этого у объекта ОписаниеОповещения существует третий параметр в конструкторе, в который можно передать структуру, содержащую значения, необходимые в дальнейшем:
Вопрос во вложенной процедуре, после которой нет кода
Рассмотрим ситуацию, когда блокирующий вызов хочется написать во вложенной процедуре. Например, так:
В этом случае достаточно будет вместо одной вложенной процедуры написать две процедуры, использующие блокирующий метод ПоказатьВопрос() :
Вопрос во вложенной процедуре, после которой есть код
Рассмотрим более сложный случай, когда после вызова вложенной функции выполнятся некоторый код. Например, так:
Казалось бы, следуя рекомендациям, надо просто заменить вложенную процедуру двумя процедурами:
Однако в этом случае алгоритм, следующий за вызовом вложенной процедуры, будет выполнен ещё до того, как пользователь ответит на вопрос. Ведь, как мы знаем, исполнение кода не останавливается после выполнения блокирующего метода (Сравнение модального и немодального режимов работы):
Поэтому в такой ситуации нужно не только использовать блокирующий метод с вызовом оповещения, но ещё и выполнять асинхронный вызов самой вложенной процедуры, также используя оповещение:
Здесь во вложенную процедуру мы сразу передаём описание оповещения, в котором содержится тот код, который должен быть выполнен после вложенной процедуры (последняя процедура). Вызывая блокирующий метод, мы передаём ему «его» описание оповещения ( ВложеннаяПроцедураЗавершение ), а также, через дополнительные параметры, то описание оповещения, которое нужно будет выполнить после того, как будут обработаны интерактивные действия пользователя ( ОбработкаКомандыЗавершение ).
Если сразу осознать такую конструкцию сложно, можно писать «по старому», а затем использовать функции рефакторинга. Они доступны начиная с версии 8.3.5. Как это делается можно посмотреть в примере Необходимо преобразование в асинхронную процедуру.
Вопрос в обработчике формы ПередЗакрытием
В данном случае сложность заключается в том, что ответ пользователя мы узнаем уже после того, как выйдем из контекста этого обработчика. В процедуре, обрабатывающей оповещение. А параметр Отказ нужно установить именно в этом обработчике.
Поэтому мы действуем в два приёма:
Проблема заключается в том, что обработчик ПередЗакрытием будет выполнен два раза. И чтобы отличить первое его выполнение от второго (когда ответ пользователя уже известен) мы используем клиентскую переменную ВыполняетсяЗакрытие в качестве флага.
Вопрос в обработчике формы ПередЗаписью
В обработчике события формы ПередЗаписью также может возникнуть потребность задать вопрос. Как и в предыдущем примере. Однако здесь вопрос так просто не решается. Отличие заключается в следующем.
Поэтому тут можно предложить три варианта, но все они, к сожалению, обладают недостатками:
Как переделывать старые конфигурации
Общие замечания
Переделка имеющихся конфигураций для работы в режиме без использования модальности сводится к трём этапам:
Далее мы рассмотрим все три этапа по-порядку. Какие инструменты можно использовать, какие рекомендации можно дать.
Поиск модальных методов
Существует несколько инструментов, которые позволяют найти модальные вызовы конфигурации. Некоторые из этих инструментов позволяют также оценить возможность автоматического преобразования этих вызовов.
В режиме 1С:Предприятие
Проверка конфигурации в режиме Конфигуратор
Результат проверки будет выдан в окно служебных сообщений, из которого можно сразу же перейти в модуль, в котором находится данный метод:
Нужно отметить, что эта проверка обнаруживает не все возможные модальные вызовы, а только те, которые можно определить «наверняка». То есть это модальные методы глобального контекста и модальные методы тех объектов, тип которых однозначно определён в анализируемом контексте.
Поэтому, например, в приведённом примере проверка не обнаруживает модальное открытие диалога выбора цвета во вложенной процедуре. В контексте этой процедуры тип переменной «Диалог» неизвестен.
Проверка одного модуля
В этом случае будут найдены все строки кода, которые приводят или могут привести к открытию модальных окон. В том числе и те строки, где открытие модального окна возможно, если объект имеет определённый тип:
Например, во вложенной процедуре в данном примере действительно будет открыто модальное окно для выбора цвета.
Если бы во вложенную процедуру в переменной » Диалог » передавалась бы выборка справочника, то, естественно, никакого модального вызова не было бы. Однако проверка всё равно отметила бы эту строку как «потенциально опасную».
Поэтому окончательное решение в таких случаях нужно принимать на основе визуального анализа алгоритма и типов используемых объектов.
Поиск с анализом возможности автоматического преобразования
Можно не только найти все модальные вызовы, но и оценить возможность их автоматического преобразования (начиная с версии 8.3.5). Оговоримся, что автоматическое преобразование выполняется не во всех случаях, подробнее об этом можно прочитать в разделе Автоматическое преобразование модальных методов.
Перед выполнением анализа можно задать параметры. Они позволяют уточнить анализ и одновременно с анализом выполнить преобразование:
Со стандартными значениями (если ничего не менять) буду найдены только те вызовы модальных методов, которые удалось определить однозначно:
В любом случае (с теми или иными параметрами анализа) результат будет содержать подробную информацию по каждому вызову с комментарием:
Автоматическое преобразование модальных методов
Эти инструменты доступны начиная с версии 8.3.5.
Автоматически преобразуются только тривиальные вызовы. Это такие вызовы, преобразование которых затрагивает одну процедуру и не требует операций помимо выделения объекта.
Остальные вызовы требуют вашего вмешательства. Часть из них можно также преобразовать автоматически, но с помощью последовательного выполнения других команд рефакторинга. Другая часть потребует предварительного изменения алгоритма программы. Некоторые такие типичные ситуации рассматриваются в примерах следующего раздела.
Ручное преобразование конструкций, которые невозможно преобразовать автоматически
В этом разделе мы разберём некоторые типичные случаи, когда автоматическое преобразование невозможно и требуется вмешательство разработчика.
Мы рекомендуем отталкиваться от того соображения, что асинхронная модель работы, по сути, требует другого подхода к проектированию юзабилити интерфейса. То есть, «по хорошему», «по большому счёту» вопрос не в том, чтобы заменить одни операторы встроенного языка другими. Вопрос в том, чтобы изменить сценарий работы прикладного решения таким образом, когда использование блокирующего диалога станет ненужным.
Необходимо преобразование в асинхронную процедуру
При анализе модулей могут встретиться конструкции, автоматическое преобразование которых затруднительно. Например, на модальный вызов во вложенной процедуре система отреагирует следующим образом:
Это не значит, что автоматическое преобразование невозможно. Это значит, что его результат может оказаться непростым. И по этой причине желательно, чтобы разработчик выполнил его отдельно и самостоятельно.
Перед выполнением платформа известит вас, что кроме самого модального вызова необходимо преобразовать вызов процедуры к асинхронному виду:
И после этого будут выполнены оба преобразования:
Присвоение cсылочного параметра: СтандартнаяОбработка
Это простой, но типичный пример. Такое сообщение, например, будет выдано при анализе следующего модуля:
Здесь проблема заключается в том, что присвоение значения переменной СтандартнаяОбработка происходит уже после выполнения модального метода, а значит должно выполняться в процедуре обработчика оповещения. Но там это выполнить невозможно.
На самом деле видно, что в этом фрагменте строку СтандартнаяОбработка = Ложь; можно разместить и до вызова модального метода. От этого логика работы не меняется:
И если мы поступим таким образом, то сразу же облегчим задачу преобразования. Теперь оно может быть выполнено автоматически:
Присвоение cсылочного параметра: Отказ
Этот пример по своему «диагностическому сообщению» очень похож на предыдущий, но решение его уже не такое простое. Например, исходный код выглядит так:
Дополнительная сложность заключается в том, что просто переставить местами строки кода, как в предыдущем примере, тоже нельзя. Отказ выполняется только при одном из условий.
Поэтому тут нужно применять решение, которое описано в разделе Вопрос в обработчике формы ПередЗакрытием. Выполнять закрытие формы «в два прохода». Первый раз безусловно отказываться от действия и только задавать вопрос. А второй раз уже закрывать форму. А чтобы не зациклиться, использовать переменную модуля для того, чтобы отличить первый проход от второго.
Невозможно выделить фрагмент-окончание: фрагмент содержит незавершенный блок условия. Пример 1
Подобные сообщения свидетельствуют о том, что код довольно сложен и алгоритм автоматического преобразования не может его обработать. В таких случаях можно попытаться изменить или упростить алгоритм для того, чтобы автоматическое преобразование стало возможным. Условия, при которых возможно автоматическое преобразование, подробно описаны в документации.
Безусловно, ситуации могут быть самыми разными и в общем случае они требуют творческого подхода. Здесь мы покажем пару типичных приёмов. Это избавление от цикла и выделение фрагментов в отдельную процедуру.
Итак, к сообщению, вынесенному в заголовок, может привести, например, следующий фрагмент:
Проблема этого фрагмента в том, что модальный вызов выполняется внутри цикла. Для того чтобы появилась возможность преобразовать этот фрагмент в немодальный, нужно изменить логику алгоритма. Вместо того чтобы задавать вопрос каждый раз, как попадается «подозрительная» строка таблицы, нужно сначала найти все подозрительные строки, а затем один раз задать вопрос по поводу этих строк:
После такого изменения становится доступным автоматическое преобразование:
Невозможно выделить фрагмент-окончание: фрагмент содержит незавершенный блок условия. Пример 2
Другой фрагмент, который может привести к такому сообщению, может выглядеть так:
Проблема этого фрагмента заключается в строках, отмеченных стрелками. Каждая из этих строк может быть выполнена как в результате прохода по ветке с модальным вызовом, так и в результате прохода по «безмодальным» веткам алгоритма. Если бы в коде присутствовала только одна из этих строк, автоматическое преобразование было бы возможно. Но так, как их две, ситуация становится довольно сложной.
Что можно сделать? Путём выделения фрагмента кода в отдельную процедуру можно попытаться уменьшить «вариативность» алгоритма.
Посмотрим на первую «проблемную» строку. Перейти к ней мы можем из двух веток алгоритма:
Причём эти переходы должны будут принципиально отличаться, ведь из «немодальной» ветки мы можем перейти в эту же процедуру, а из «модальной» мы должны будем перейти в обработчик оповещения.
Можно упростить эту ситуацию, выделив весь оператор » Если» в отдельную процедуру: