Как реализовать показ вопросов в цикле без использования модальности в 1С 8.3

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

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

В этой статье мы подробно разберем два решения этой проблемы: классический рекурсивный обход немодальности, подходящий для любых версий платформы 8.3, и современный элегантный подход с использованием ключевых слов Асинх и Ждать (доступен начиная с платформы 8.3.18). Также мы проанализируем, почему вопросы в цикле считаются плохим тоном в проектировании интерфейсов (UX anti-pattern), и рассмотрим лучшие архитектурные альтернативы.

Причина проблемы: почему обычный цикл не работает с немодальными вызовами

Рассмотрим типичный ошибочный код, который часто пытаются написать начинающие разработчики:


&НаКлиенте
Процедура СоздатьНоменклатуру(Команда)
    Для каждого СтрТЧ Из Объект.ТаблицаEXEL Цикл
        Оповещение = Новый ОписаниеОповещения("ПослеЗакрытияВопроса", ЭтаФорма);
        // Ошибка! Этот метод не останавливает цикл.
        ПоказатьВопрос(Оповещение, "Создать номенклатуру '" + СтрТЧ.Наименование + "'?", РежимДиалогаВопрос.ДаНетОтмена);
    КонецЦикла;
КонецПроцедуры

Выполняя этот код, платформа мгновенно пробежит по всем строкам таблицы ТаблицаEXEL, нагенерирует десятки или сотни вызовов диалоговых окон и только потом (когда управление вернется к интерфейсному потоку) попытается отобразить их пользователю. Понять, к какой строке относится каждый ответ, и прервать цикл при нажатии кнопки «Отмена» в такой архитектуре физически невозможно.

---

Решение 1. Классический рекурсивный обход (через ОписаниеОповещения)

Если ваша конфигурация должна работать на старых релизах платформы 1С (ниже 8.3.18), единственным рабочим решением является замена стандартного цикла Для каждого на пошаговую рекурсивную процедуру. Мы будем передавать индекс обрабатываемой строки в параметрах оповещения и вызывать процедуру обработки следующей строки только после того, как пользователь ответит на текущий вопрос.

Рассмотрим этот алгоритм по шагам:

  1. Инициируем обработку с самого первого элемента (индекс 0).
  2. В процедуре обработки проверяем, не вышли ли мы за границы таблицы.
  3. Если элементы еще есть, формируем ОписаниеОповещения, куда в качестве дополнительных параметров передаем текущий индекс строки.
  4. Показываем вопрос пользователю.
  5. В процедуре-обработчике оповещения анализируем ответ. Если выбрано «Да» — выполняем полезное действие. Если выбрано «Отмена» — завершаем процесс. Если выбрано «Нет» или «Да» — рекурсивно вызываем процедуру обработки для следующего индекса (Индекс + 1).

Посмотрим на практический пример реализации:


&НаКлиенте
Процедура СоздатьНоменклатуру(Команда)
    // Шаг 1: Запускаем пошаговую обработку с первой строки (индекс 0)
    ОбработатьСтрокуТаблицы(0);
КонецПроцедуры

&НаКлиенте
Процедура ОбработатьСтрокуТаблицы(ТекущийИндекс)
    
    // Шаг 2: Проверяем условие окончания "цикла"
    Если ТекущийИндекс >= Объект.ТаблицаEXEL.Количество() Тогда
        ПоказатьПредупреждение(, "Обработка данных успешно завершена!");
        Возврат;
    КонецЕсли;
    
    // Получаем текущую строку для вывода информации в вопросе
    ТекущаяСтрока = Объект.ТаблицаEXEL[ТекущийИндекс];
    
    // Шаг 3: Подготавливаем параметры для передачи в обработчик ответа
    ДополнительныеПараметры = Новый Структура;
    ДополнительныеПараметры.Вставить("Индекс", ТекущийИндекс);
    
    Оповещение = Новый ОписаниеОповещения("ПослеЗакрытияВопроса", ЭтотОбъект, ДополнительныеПараметры);
    
    // Шаг 4: Показываем немодальный вопрос
    ТекстВопроса = "Создать номенклатуру с наименованием '" + ТекущаяСтрока.Наименование + "'?";
    ПоказатьВопрос(Оповещение, ТекстВопроса, РежимДиалогаВопрос.ДаНетОтмена, 0);
    
КонецПроцедуры

&НаКлиенте
Процедура ПослеЗакрытияВопроса(РезультатВопроса, ДополнительныеПараметры) Экспорт
    
    ТекущийИндекс = ДополнительныеПараметры.Индекс;
    ТекущаяСтрока = Объект.ТаблицаEXEL[ТекущийИндекс];
    
    // Шаг 5: Анализируем выбор пользователя
    Если РезультатВопроса = КодВозвратаДиалога.Отмена Тогда
        // Полностью прекращаем обработку
        ПоказатьПредупреждение(, "Выполнение операции прервано пользователем.");
        Возврат;
        
    ИначеЕсли РезультатВопроса = КодВозвратаДиалога.Да Тогда
        // Выполняем создание номенклатуры на сервере
        СоздатьНоменклатуруНаСервере(ТекущаяСтрока.Наименование);
    КонецЕсли;
    
    // Переходим к следующей строке таблицы (рекурсивный вызов)
    ОбработатьСтрокуТаблицы(ТекущийИндекс + 1);
    
КонецПроцедуры

Важный нюанс: Обратите внимание, что процедура ПослеЗакрытияВопроса обязательно должна быть объявлена с ключевым словом Экспорт, иначе платформа не сможет вызвать ее в контексте управляемой формы.

---

Решение 2. Современный подход через Асинх / Ждать (платформа 8.3.18+)

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

Для реализации этого подхода нам потребуются:

Проанализируем, как выглядит современное решение исходной задачи:


&НаКлиенте
Асинх Процедура СоздатьНоменклатуру(Команда)
    
    Для каждого СтрТЧ Из Объект.ТаблицаEXEL Цикл
        
        ТекстВопроса = "Создать номенклатуру с наименованием '" + СтрТЧ.Наименование + "'?";
        
        // Платформа приостановит выполнение цикла на этой строке, 
        // вернет управление интерфейсу, а после нажатия кнопки продолжит выполнение.
        Ответ = Ждать ВопросАсинх(ТекстВопроса, РежимДиалогаВопрос.ДаНетОтмена, 0);
        
        Если Ответ = КодВозвратаДиалога.Отмена Тогда
            // Прерываем цикл мгновенно и лаконично, как в обычных синхронных процедурах
            Прервать; 
        ИначеЕсли Ответ = КодВозвратаДиалога.Да Тогда
            СоздатьНоменклатуруНаСервере(СтрТЧ.Наименование);
        КонецЕсли;
        
    КонецЦикла;
    
    ПоказатьПредупреждение(, "Обработка завершена!");
    
КонецПроцедуры

Этот код выглядит невероятно просто, читается сверху вниз и не требует создания сложных вспомогательных структур данных. Настоятельно рекомендуем использовать именно этот метод при работе на современных конфигурациях.

---

Технические «подводные камни» работы асинхронного кода

При использовании асинхронных вызовов (как через ОписаниеОповещения, так и через Ждать) важно понимать физику процессов в платформе 1С:

---

Архитектурные лучшие практики (UX и производительность)

С технической точки зрения мы решили задачу. Однако давайте посмотрим на нее глазами конечного пользователя. Представьте, что в файле Excel содержится 500 строк номенклатуры. Если система на каждую строчку будет выводить диалоговое окно, пользователю придется совершить 500 кликов мышкой! Это превращает работу в рутину и повышает риск механической ошибки (пользователь случайно нажмет «Да» там, где нужно было нажать «Нет», или промахнется по кнопке «Отмена»).

Проанализируем более качественные варианты проектирования интерфейса:

  1. Паттерн «Таблица конфликтов» (интерактивный список):

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

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

  2. Использование фоновых заданий:

    Обработка больших файлов Excel на клиенте и постоянные вызовы сервера для создания каждой позиции номенклатуры по отдельности (в цикле) создают колоссальную нагрузку на базу данных и сетевой трафик. Правильным архитектурным решением будет передача файла Excel на сервер и запуск Фонового задания. Клиентская часть при этом будет лишь отображать красивый прогресс-бар, а все неоднозначные ситуации или ошибки запишутся в лог-журнал, который пользователь сможет изучить по завершении процесса.

← На главную