Как реализовать немодальный ввод данных в цикле в 1С 8.3 при запрете модальных окон?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При переходе на современные версии платформы 1С:Предприятие 8.3 многие разработчики сталкиваются с ошибкой: «Использование модальных окон в данном режиме запрещено». Эта проблема особенно остро проявляется в обработках загрузки данных, когда алгоритм в цикле обходит строки файла и при обнаружении новых сущностей запрашивает параметры у пользователя через метод ВвестиЗначение().

Попытка заменить синхронный метод на его асинхронный аналог ПоказатьВводЗначения() внутри стандартного цикла Для приводит к тому, что код не ждет ответа от пользователя. Программа мгновенно пробегает по всем итерациям цикла, открывая каскад диалоговых окон, а последующие строки кода выполняются с пустыми значениями. Проанализируем причины такого поведения и разберем правильные варианты реализации задачи.

Выясним причину проблемы и поведение асинхронных методов

Запрет модальности обусловлен архитектурой современных интерфейсов: веб-клиент и мобильные платформы не поддерживают синхронную остановку потока выполнения JavaScript в браузере. Поэтому в свойствах конфигурации устанавливается параметр Режим использования модальности = Не использовать.

Метод ПоказатьВводЗначения() лишь создает немодальное окно и не приостанавливает исполнение кода. Управление сразу передается следующей инструкции текущей процедуры. Кроме того, вызов диалогов и серверных процедур по отдельности внутри каждой итерации цикла порождает огромное количество клиент-серверных вызовов (round-trips), что критически замедляет работу системы.

Рассмотрим три способа решения этой проблемы: от рекомендуемого архитектурного разделения до реализации последовательного ввода и асинхронных конструкций.

Решение 1. Архитектурное разделение процесса на этапы (рекомендуемый подход)

Наиболее правильный путь с точки зрения производительности и удобства пользователя — полностью исключить интерактивные запросы из цикла обработки строк. Разберем по шагам, как построить этот процесс:

  1. Чтение и предварительный анализ. Считываем файл на сервере, определяем список всех уникальных видов номенклатуры и проверяем их наличие в информационной базе одним пакетным запросом.
  2. Сбор недостающих данных. Если найдены новые виды номенклатуры, для которых не задана ценовая группа, формируем их список в отдельную таблицу.
  3. Единая форма ввода. Открываем пользователю отдельное немодальное окно (или форму сопоставления), где в таблице представлены сразу все новые группы. Пользователь заполняет ценовые группы для всех позиций за один раз.
  4. Пакетное создание и запись. После подтверждения формы данные передаются на сервер, где в рамках единой транзакции создаются все виды номенклатуры и загружаются зависимые элементы справочников.

Такой подход избавляет пользователя от необходимости отвечать на десятки всплывающих окон подряд и сводит обмен с сервером к минимуму.

Решение 2. Пошаговая очередь через рекурсивные оповещения

Если бизнес-логика требует задавать вопросы строго последовательно по очереди для каждой позиции, стандартный цикл Для использовать нельзя. В этом случае организуем очередь обработки с помощью рекурсивного вызова процедур через ОписаниеОповещения.

Посмотрим на пример организации пошагового ввода:


&НаКлиенте
Процедура НачатьОбработкуОчереди(Команда)
    // Массив структур с элементами, требующими интерактивного ввода
    ОчередьЭлементов = ПолучитьСписокНовыхВидов(); 
    
    ПараметрыПроцесса = Новый Структура;
    ПараметрыПроцесса.Вставить("Очередь", ОчередьЭлементов);
    ПараметрыПроцесса.Вставить("ТекущийИндекс", 0);
    
    ОбработатьСледующийЭлемент(ПараметрыПроцесса);
КонецПроцедуры

&НаКлиенте
Процедура ОбработатьСледующийЭлемент(ПараметрыПроцесса)
    Если ПараметрыПроцесса.ТекущийИндекс >= ПараметрыПроцесса.Очередь.Количество() Тогда
        // Все данные введены, переходим к финальной записи
        ЗавершитьСозданиеЭлементовНаСервере(ПараметрыПроцесса.Очередь);
        ПоказатьОповещениеПользователя("Загрузка успешно завершена");
        Возврат;
    КонецЕсли;

    ТекущаяСтрока = ПараметрыПроцесса.Очередь[ПараметрыПроцесса.ТекущийИндекс];
    
    Оповещение = Новый ОписаниеОповещения("ПослеВводаЦеновойГруппы", ЭтотОбъект, ПараметрыПроцесса);
    ПоказатьВводЗначения(Оповещение, Неопределено, "Выберите ценовую группу для: " + ТекущаяСтрока.Наименование, Тип("СправочникСсылка.ЦеновыеГруппы"));
КонецПроцедуры

&НаКлиенте
Процедура ПослеВводаЦеновойГруппы(ВыбранноеЗначение, ПараметрыПроцесса) Экспорт
    Если ВыбранноеЗначение = Неопределено Тогда
        ПоказатьПредупреждение(Неопределено, "Ввод отменен пользователем. Процесс прерван.");
        Возврат;
    КонецЕсли;

    // Сохраняем результат в структуру текущего шага
    ПараметрыПроцесса.Очередь[ПараметрыПроцесса.ТекущийИндекс].ЦеноваяГруппа = ВыбранноеЗначение;
    
    // Переходим к следующему шагу
    ПараметрыПроцесса.ТекущийИндекс = ПараметрыПроцесса.ТекущийИндекс + 1;
    ОбработатьСледующийЭлемент(ПараметрыПроцесса);
КонецПроцедуры

В этом примере поток выполнения не блокируется: процедура завершает свою работу, а продолжение алгоритма происходит только после того, как пользователь сделает выбор в диалоге и сработает процедура-обработчик оповещения.

Решение 3. Использование асинхронных конструкций Асинх и Ждать (начиная с платформы 8.3.18)

Начиная с версии платформы 8.3.18, в платформе 1С появился полноценный асинхронный механизм на базе ключевых слов Асинх и Ждать. Это позволяет сохранить привычную структуру цикла Для каждого, не блокируя при этом клиентское приложение.

Разберем, как выглядит реализация асинхронного цикла:


&НаКлиенте
Асинх Процедура ЗагрузитьДанныеАсинхронно(Команда)
    МассивНовыхВидов = ПолучитьСписокНовыхВидов(); // Получаем данные с сервера
    
    Для Каждого СтрВид Из МассивНовыхВидов Цикл
        Подсказка = "Выберите ценовую группу для: " + СтрВид.Наименование;
        
        // Оператор Ждать приостанавливает выполнение текущей процедуры до ответа пользователя,
        // не блокируя при этом интерфейс платформы
        ВыбраннаяГруппа = Ждать ВвестиЗначениеАсинх(Неопределено, Подсказка, Тип("СправочникСсылка.ЦеновыеГруппы"));
        
        Если ВыбраннаяГруппа = Неопределено Тогда
            ПоказатьПредупреждение(Неопределено, "Загрузка прервана пользователем.");
            Возврат;
        КонецЕсли;
        
        СтрВид.ЦеноваяГруппа = ВыбраннаяГруппа;
    КонецЦикла;
    
    // После завершения цикла отправляем всю заполненную коллекцию на сервер
    СоздатьЭлементыНаСервере(МассивНовыхВидов);
КонецПроцедуры

Здесь функция ВвестиЗначениеАсинх() возвращает объект Обещание, а оператор Ждать заставляет процедуру дождаться разрешения этого обещания перед переходом к следующей итерации цикла.

Временное решение: изменение режима без снятия конфигурации с поддержки

Если код необходимо запустить срочно без переписывания архитектуры, снимать конфигурацию с поддержки полностью не требуется. Достаточно включить возможность изменения корневого объекта:

  1. Открываем меню Конфигурация — Поддержка — Настройка поддержки.
  2. Включаем возможность изменения и устанавливаем для корня конфигурации статус «Редактируется с сохранением поддержки».
  3. В свойствах корня конфигурации находим параметр Режим использования модальности и переключаем его в значение Использовать (или Использовать с предупреждениями).

Такая настройка сохранит возможность типового обновления через режим сравнения и объединения, однако ее следует рассматривать исключительно как временную меру до рефакторинга кода.

← На главную