Ошибка «Использование модальных окон в данном режиме запрещено!» появляется, когда конфигурация запрещает модальные окна, а код вызывает метод, который останавливает выполнение до закрытия окна: Вопрос(), Предупреждение(), ОткрытьФормуМодально(), ВвестиЧисло(), выбор файла через ДиалогВыбораФайла.Выбрать(). Путей два. Быстрый — разрешить модальность в свойствах конфигурации. Правильный — переписать вызовы на ОписаниеОповещения или на Асинх/Ждать. Ниже — где смотреть настройку, таблица замен и код «было/стало» для самых частых методов.
Вопрос() или ОткрытьФормуМодально().ВвестиЧисло(), ВвестиСтроку() и ВвестиДату() синтакс-помощник прямо пишет: в веб-клиенте генерируется исключение. То же касается работы в сервисе вроде 1cfresh.com.Типовые решения 1С разрабатывает с расчётом на немодальный режим, а новая пустая конфигурация сразу создаётся с «Не использовать». В тексте ошибки платформа указывает модуль и номер строки — с них и начинайте.
Режим задаётся в свойствах корня конфигурации:
| Значение | Что происходит при вызове модального метода |
|---|---|
| Использовать | окно открывается как раньше; веб-клиент на платформах без модальных окон полноценно работать не будет |
| Использовать с предупреждениями | окно открывается, а в окно сообщений выводится предупреждение — удобно, чтобы собрать список модальных вызовов на время переделки |
| Не использовать | ошибка «Использование модальных окон в данном режиме запрещено!» |
У свойства синхронных вызовов те же три значения. При «Не использовать» синхронные методы пропадают из контекстной подсказки, а в режиме 1С:Предприятие их вызов заканчивается исключением.
Без конфигуратора режимы можно посмотреть из внешней обработки — заодно виден режим совместимости, от которого зависит переход на Асинх:
// Внешняя обработка: показать режимы, от которых зависит ошибка.
// Метаданные доступны только на сервере, поэтому чтение вынесено в серверную функцию.
&НаСервереБезКонтекста
Функция РежимыКонфигурации()
Возврат СтрШаблон("Режим использования модальности: %1
|Синхронные вызовы расширений и компонент: %2
|Режим совместимости: %3",
Метаданные.РежимИспользованияМодальности,
Метаданные.РежимИспользованияСинхронныхВызововРасширенийПлатформыИВнешнихКомпонент,
Метаданные.РежимСовместимости);
КонецФункции
&НаКлиенте
Процедура ПоказатьРежимы(Команда)
ПоказатьПредупреждение(, РежимыКонфигурации());
КонецПроцедуры
Почему в типовой конфигурации так делать не стоит:
Быстрый путь оправдан для своей конфигурации без веб-клиента или как временная мера, пока обработку переписывают.
Модальный метод останавливает код, пока пользователь не закроет окно. Немодальный аналог показывает окно и сразу выполняет следующую строку. Поэтому всё, что стояло после Вопрос(), переезжает:
ОписаниеОповещения после закрытия окна (начиная с 8.3.3);Ждать внутри процедуры с модификатором Асинх (8.3.18 и новее).Как устроен объект оповещения, разобрано в статье про ОписаниеОповещения. Замены по синтакс-помощнику 8.3.27:
| Было (модально) | С ОписаниеОповещения | Асинх-вариант (8.3.18+) |
|---|---|---|
| Вопрос() | ПоказатьВопрос() | ВопросАсинх() |
| Предупреждение() | ПоказатьПредупреждение() | ПредупреждениеАсинх() |
| ОткрытьФормуМодально(), Форма.ОткрытьМодально() | ОткрытьФорму() с ОписаниеОповещенияОЗакрытии и РежимОткрытияОкна | нет |
| ВвестиЧисло() | ПоказатьВводЧисла() | ВвестиЧислоАсинх() |
| ВвестиСтроку() | ПоказатьВводСтроки() | ВвестиСтрокуАсинх() |
| ВвестиДату() | ПоказатьВводДаты() | ВвестиДатуАсинх() |
| ВвестиЗначение() | ПоказатьВводЗначения() | ВвестиЗначениеАсинх() |
| ОткрытьЗначение() | ПоказатьЗначение() | ОткрытьЗначениеАсинх() |
| ВыбратьИзСписка(), ВыбратьИзМеню() — методы формы | ПоказатьВыборИзСписка(), ПоказатьВыборИзМеню() | ВыбратьИзСпискаАсинх(), ВыбратьИзМенюАсинх() |
| СписокЗначений.ВыбратьЭлемент(), ОтметитьЭлементы() | ПоказатьВыборЭлемента(), ПоказатьОтметкуЭлементов() | ВыбратьЭлементАсинх(), ОтметитьЭлементыАсинх() |
| ДиалогВыбораФайла.Выбрать() | ДиалогВыбораФайла.Показать() | ДиалогВыбораФайла.ВыбратьАсинх() |
| ПоместитьФайл() с диалогом | НачатьПомещениеФайлаНаСервер() (8.3.13+), НачатьПомещениеФайла() | ПоместитьФайлНаСерверАсинх() |
Немодальные аналоги обычно начинаются с «Показать». Асинх-версии платформа 8.3.18 добавила методам, которые начинаются с «Показать» и «Начать», поэтому у ОткрытьФорму() её нет: форму по-прежнему открывают с оповещением о закрытии.
Команда «Перезаполнить» в форме старой обработки: спросить, очистить табличную часть, сообщить результат.
// Было: модальный код из старой обработки (команда «Перезаполнить»)
Если Объект.Товары.Количество() > 0 Тогда
Ответ = Вопрос("Табличная часть будет очищена. Продолжить?",
РежимДиалогаВопрос.ДаНет, 0, КодВозвратаДиалога.Нет);
Если Ответ <> КодВозвратаДиалога.Да Тогда
Возврат;
КонецЕсли;
КонецЕсли;
Загружено = ПерезаполнитьНаСервере();
Предупреждение("Загружено строк: " + Загружено);
Вариант с ОписаниеОповещения:
// Стало: ПоказатьВопрос + ОписаниеОповещения (любая версия 8.3, начиная с 8.3.3).
// Форма своей обработки, табличная часть Объект.Товары.
&НаКлиенте
Процедура Перезаполнить(Команда)
Если Объект.Товары.Количество() = 0 Тогда
// Спрашивать не о чем: сразу выполняем ветку «Да»
ПерезаполнитьЗавершение(КодВозвратаДиалога.Да, Неопределено);
Возврат;
КонецЕсли;
Оповещение = Новый ОписаниеОповещения("ПерезаполнитьЗавершение", ЭтотОбъект);
ПоказатьВопрос(Оповещение, "Табличная часть будет очищена. Продолжить?",
РежимДиалогаВопрос.ДаНет, 0, КодВозвратаДиалога.Нет);
// Здесь больше ничего: код после ПоказатьВопрос выполнится, не дожидаясь ответа
КонецПроцедуры
&НаКлиенте
Процедура ПерезаполнитьЗавершение(Ответ, ДополнительныеПараметры) Экспорт
Если Ответ <> КодВозвратаДиалога.Да Тогда
Возврат;
КонецЕсли;
Загружено = ПерезаполнитьНаСервере();
ПоказатьПредупреждение(, СтрШаблон("Загружено строк: %1", Загружено));
КонецПроцедуры
&НаСервере
Функция ПерезаполнитьНаСервере()
Объект.Товары.Очистить();
// ... заполнение табличной части из файла или запроса ...
Возврат Объект.Товары.Количество();
КонецФункции
ПоказатьВопрос() в процедуре ничего нет: любая строка там выполнится сразу, пока окно открыто.Экспорт и лежит в модуле формы, на который указывает ЭтотОбъект.КодВозвратаДиалога.Да.ПоказатьПредупреждение(, Текст) вызван без оповещения: после него кода нет, ждать нечего.Тот же сценарий на Асинх/Ждать:
// Стало: тот же сценарий на Асинх/Ждать (платформа 8.3.18 и новее)
&НаКлиенте
Асинх Процедура Перезаполнить(Команда)
Если Объект.Товары.Количество() > 0 Тогда
Ответ = Ждать ВопросАсинх("Табличная часть будет очищена. Продолжить?",
РежимДиалогаВопрос.ДаНет, 0, КодВозвратаДиалога.Нет);
Если Ответ <> КодВозвратаДиалога.Да Тогда
Возврат;
КонецЕсли;
КонецЕсли;
Загружено = ПерезаполнитьНаСервере();
ПоказатьПредупреждение(, СтрШаблон("Загружено строк: %1", Загружено));
КонецПроцедуры
&НаСервере
Функция ПерезаполнитьНаСервере()
Объект.Товары.Очистить();
// ... заполнение табличной части ...
Возврат Объект.Товары.Количество();
КонецФункции
Ждать кладёт ответ прямо в переменную, структура совпадает со старым модальным кодом — переделка сводится к замене строк. Кнопки, таймаут и свои подписи разобраны в статье про ПоказатьВопрос и ВопросАсинх.
Модальный код, который запрашивает новую дату и причину переноса:
// Было: два модальных диалога подряд
НоваяДата = Объект.ДатаОтгрузки;
Если Не ВвестиДату(НоваяДата, "Новая дата отгрузки", ЧастиДаты.Дата) Тогда
Возврат;
КонецЕсли;
Причина = "";
Если Не ВвестиСтроку(Причина, "Причина переноса", 200, Истина) Тогда
Возврат;
КонецЕсли;
ПеренестиОтгрузкуНаСервере(НоваяДата, Причина);
С оповещениями здесь получилась бы цепочка из трёх процедур с передачей даты через дополнительные параметры. На Асинх — одна процедура:
// Стало: команда «Перенести отгрузку» в форме своего документа
// (реквизиты ДатаОтгрузки и Комментарий)
&НаКлиенте
Асинх Процедура ПеренестиОтгрузку(Команда)
НоваяДата = Ждать ВвестиДатуАсинх(Объект.ДатаОтгрузки, "Новая дата отгрузки", ЧастиДаты.Дата);
Если НоваяДата = Неопределено Тогда
Возврат; // нажали «Отмена»
КонецЕсли;
Причина = Ждать ВвестиСтрокуАсинх("", "Причина переноса", 200, Истина);
Если Причина = Неопределено Или ПустаяСтрока(Причина) Тогда
ПоказатьПредупреждение(, "Без причины дату не переносим.");
Возврат;
КонецЕсли;
ПеренестиОтгрузкуНаСервере(НоваяДата, Причина);
КонецПроцедуры
&НаСервере
Процедура ПеренестиОтгрузкуНаСервере(НоваяДата, Причина)
Объект.ДатаОтгрузки = НоваяДата;
Объект.Комментарий = СокрЛП(Объект.Комментарий + Символы.ПС
+ СтрШаблон("Отгрузка перенесена на %1: %2", Формат(НоваяДата, "ДЛФ=D"), Причина));
Модифицированность = Истина;
КонецПроцедуры
ВвестиДатуАсинх() и ВвестиСтрокуАсинх() возвращают не Булево, как старые методы, а само значение или Неопределено, если пользователь нажал «Отмена».ЧастиДаты; значение ЧастиДаты.Дата убирает из диалога время.Ждать — обычный: асинхронна только клиентская часть.Раньше результат формы возвращался прямо в переменную:
// Было: код ждал закрытия формы на этой же строке
Результат = ОткрытьФормуМодально("Обработка.ЗагрузкаПрайса.Форма.НастройкаКолонок", ПараметрыФормы);
Если ТипЗнч(Результат) = Тип("Структура") Тогда
ЗаполнитьЗначенияСвойств(Объект, Результат);
КонецЕсли;
Теперь он приходит в обработчик оповещения:
// Стало: форма блокирует только окно владельца, результат приходит в обработчик.
// Для внешней обработки имя формы начинается с «ВнешняяОбработка.».
&НаКлиенте
Процедура НастроитьКолонки(Команда)
ПараметрыФормы = Новый Структура("КолонкаАртикула, КолонкаЦены",
Объект.КолонкаАртикула, Объект.КолонкаЦены);
Оповещение = Новый ОписаниеОповещения("НастроитьКолонкиЗавершение", ЭтотОбъект);
ОткрытьФорму("Обработка.ЗагрузкаПрайса.Форма.НастройкаКолонок", ПараметрыФормы, ЭтотОбъект, , , ,
Оповещение, РежимОткрытияОкнаФормы.БлокироватьОкноВладельца);
КонецПроцедуры
&НаКлиенте
Процедура НастроитьКолонкиЗавершение(Результат, ДополнительныеПараметры) Экспорт
// Результат - значение из Закрыть(...) открытой формы; Неопределено - окно закрыли крестиком
Если ТипЗнч(Результат) <> Тип("Структура") Тогда
Возврат;
КонецЕсли;
ЗаполнитьЗначенияСвойств(Объект, Результат);
Модифицированность = Истина;
КонецПроцедуры
Открытая форма отдаёт результат через Закрыть(Результат), например Закрыть(Новый Структура("КолонкаАртикула, КолонкаЦены", КолонкаАртикула, КолонкаЦены)). Если окно закрыли крестиком, в обработчик придёт Неопределено. Режим БлокироватьОкноВладельца закрывает доступ только к вызвавшей форме. БлокироватьВесьИнтерфейс точнее повторяет старое поведение, но методичка 1С советует первый вариант. Остальные параметры ОткрытьФорму() — в статье про открытие формы программно.
// Было: модальный диалог выбора файла внутри ПоместитьФайл
Адрес = "";
ВыбранноеИмя = "";
Если Не ПоместитьФайл(Адрес, "", ВыбранноеИмя, Истина, УникальныйИдентификатор) Тогда
Возврат;
КонецЕсли;
ЧислоСтрок = ПрочитатьПрайсНаСервере(Адрес);
// Стало: диалог и передача файла на сервер одной асинхронной функцией (8.3.18+)
&НаКлиенте
Асинх Процедура ЗагрузитьПрайс(Команда)
ПараметрыДиалога = Новый ПараметрыДиалогаПомещенияФайлов("Выберите прайс-лист", Ложь,
"Прайс-лист CSV (*.csv)|*.csv");
ОписаниеФайла = Ждать ПоместитьФайлНаСерверАсинх(, , , ПараметрыДиалога, УникальныйИдентификатор);
Если ОписаниеФайла = Неопределено Или ОписаниеФайла.ПомещениеФайлаОтменено Тогда
Возврат; // пользователь закрыл диалог
КонецЕсли;
ЧислоСтрок = ПрочитатьПрайсНаСервере(ОписаниеФайла.Адрес);
ПоказатьПредупреждение(, СтрШаблон("Файл %1: строк %2", ОписаниеФайла.СсылкаНаФайл.Имя, ЧислоСтрок));
КонецПроцедуры
&НаСервере
Функция ПрочитатьПрайсНаСервере(Адрес)
Данные = ПолучитьИзВременногоХранилища(Адрес);
УдалитьИзВременногоХранилища(Адрес);
Текст = ПолучитьСтрокуИзДвоичныхДанных(Данные, КодировкаТекста.UTF8);
// ... разбор строк и заполнение Объект.Товары ...
Возврат СтрЧислоСтрок(Текст);
КонецФункции
ПараметрыДиалогаПомещенияФайлов заменяет настройку ДиалогВыбораФайла: заголовок, множественный выбор, фильтр.ОписаниеПомещенногоФайла: адрес во временном хранилище в свойстве Адрес, имя файла — в СсылкаНаФайл.Имя. При отмене придёт Неопределено.В конфигурациях на БСП то же делает ФайловаяСистемаКлиент.ЗагрузитьФайл() с оповещением (сверено по БСП в УТ 11.4). Разбор файла на сервере — в статье про загрузку из Excel.
Самый неудобный случай — вопрос в обработчике с параметром Отказ. Ответ приходит уже после выхода из обработчика, и вернуть Отказ некуда.
// Было: модальный вопрос прямо в обработчике ПередЗаписью
Если Вопрос("Сумма документа нулевая. Всё равно провести?", РежимДиалогаВопрос.ДаНет) = КодВозвратаДиалога.Нет Тогда
Отказ = Истина;
КонецЕсли;
Методичка 1С предлагает два прохода: в первый раз безусловно отказаться и задать вопрос, после ответа «Да» повторить действие, а флаг в переменной модуля не даст спросить второй раз.
// Стало: форма документа, вопрос перед проведением с нулевой суммой.
// Два прохода: сначала Отказ = Истина и вопрос, после «Да» - повторная запись.
&НаКлиенте
Перем ПроведениеПодтверждено; // Истина - пользователь уже ответил «Да»
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
Если ПроведениеПодтверждено = Истина Тогда
ПроведениеПодтверждено = Ложь; // второй проход: ответ уже получен
Возврат;
КонецЕсли;
РежимЗаписи = Неопределено;
ПараметрыЗаписи.Свойство("РежимЗаписи", РежимЗаписи);
Если РежимЗаписи <> РежимЗаписиДокумента.Проведение
Или Объект.Товары.Итог("Сумма") <> 0 Тогда
Возврат; // спрашивать не о чем
КонецЕсли;
Отказ = Истина; // первый проход: запись отменяем и задаём вопрос
Оповещение = Новый ОписаниеОповещения("ПередЗаписьюЗавершение", ЭтотОбъект, ПараметрыЗаписи);
ПоказатьВопрос(Оповещение, "Сумма документа нулевая. Всё равно провести?",
РежимДиалогаВопрос.ДаНет, 0, КодВозвратаДиалога.Нет);
КонецПроцедуры
&НаКлиенте
Процедура ПередЗаписьюЗавершение(Ответ, ПараметрыЗаписи) Экспорт
Если Ответ <> КодВозвратаДиалога.Да Тогда
Возврат;
КонецЕсли;
ПроведениеПодтверждено = Истина;
Записать(ПараметрыЗаписи); // снова вызовет ПередЗаписью - флаг пропустит вопрос
КонецПроцедуры
Ограничение схемы: после «Провести и закрыть» документ проведётся, но форма останется открытой — из ПередЗаписью не узнать, какой командой начата запись. Если это критично, спрашивайте в своей команде до записи. В ПередЗакрытием схема та же, только в обработчике ответа вызывают Закрыть(), а при ЗавершениеРаботы = Истина окна открывать нельзя — заполните ТекстПредупреждения. В БСП для этого есть ОбщегоНазначенияКлиент.ПоказатьПодтверждениеЗакрытияФормы().
Простые вызовы конфигуратор переписывает сам. В методичке 1С времён 8.3.5 это контекстное меню модуля «Рефакторинг» → «Модальные вызовы». В 8.3.27 команда называется «Рефакторинг» → «Нерекомендуемые синхронные вызовы» → «Преобразовать вызовы модуля», а модальные вызовы она берёт при флажке «Преобразовывать модальные вызовы» в параметрах. Вызов в цикле или с присвоением Отказ после него придётся разбирать руками, как выше.
ПодключитьРасширениеРаботыСФайлами(), НайтиФайлы(), КопироватьФайл(). Замените их на версии «Начать…» или «…Асинх», например ПодключитьРасширениеРаботыСФайламиАсинх() и НайтиФайлыАсинх().ПоказатьВопрос(), и он отработал сразу. Перенесите его в обработчик оповещения или перейдите на Ждать.Экспорт или лежит не в том модуле, который передан в ОписаниеОповещения.Асинх перед Процедура. Второй подвох: асинхронная процедура отдаёт управление вызывающему коду на первом Ждать, и строки после её вызова выполнятся сразу. Чтобы дождаться вложенной логики, сделайте её Асинх Функция и вызывайте через Ждать.Отказ = Истина до платформы не дойдёт, и запись пройдёт без ответа. Здесь нужна схема с двумя проходами.Записать() или Закрыть(), а флаг не выставили.ЭтотОбъект: обработчик кладут в неглобальный клиентский общий модуль и передают в ОписаниеОповещения его имя.Если платформа 8.3.18 или новее и режим совместимости конфигурации не ниже 8.3.18, удобнее Асинх: код остаётся линейным, несколько диалогов подряд не превращаются в цепочку процедур. Для старых режимов совместимости, обработчиков с Отказ и открытия форм остаётся ОписаниеОповещения. В одном модуле оба способа уживаются.
«Конфигурация» → «Проверка конфигурации» → флажки «Расширенная проверка» и «Поиск использования модальности», рядом есть «Поиск использования синхронных вызовов». Проверка находит только вызовы, где тип объекта известен однозначно. Внешнюю обработку проще проверить поиском по модулю: Вопрос(, Предупреждение(, Модально, Ввести, .Выбрать(.
Нет. Режим задаётся для конфигурации целиком: либо переписать обработку, либо менять свойство всей базы.
Нет: метода ОткрытьФормуАсинх в синтакс-помощнике 8.3.27 нет. Форму открывают через ОткрытьФорму() с оповещением о закрытии, как в примере выше.
Если модальных вызовов десятки, они разбросаны по старым внешним обработкам и печатным формам, а конфигурация на поддержке, переделку проще отдать исполнителю: доработка отчётов и обработок, остальные работы — в разделе услуг. Перечислите, какие обработки падают с ошибкой, и разместите задачу — оценка бесплатная, ответ в течение рабочего дня.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.