Как остановить выполнение кода для взаимодействия с пользователем при открытии новой формы

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

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

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

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

  1. Блокировки СУБД: Пока на экране висит форма или вопрос, транзакция в базе данных остается открытой. Она удерживает блокировки на таблицы. Если пользователь уйдет пить кофе, не закрыв окно, другие пользователи не смогут проводить документы или изменять справочники, что приведет к ошибкам таймаута (Lock Timeout) и дедлокам (Deadlocks).
  2. Разрыв контекста: Программный код в 1С разделен на клиентскую и серверную части. Интерфейсные действия возможны только на клиенте. Серверный вызов, в рамках которого обычно открывается транзакция, не может «заснуть» и ждать ответа от клиента, так как это приведет к зависанию рабочего процесса сервера.

Поэтому правильный путь — это полная переработка логики. Посмотрим на альтернативные методы.

Метод 1: Использование механизмов «Обещаний» (Async/Await)

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

Рассмотрим пример, где нам нужно в цикле опрашивать пользователя. Для этого процедура должна быть объявлена с директивой &НаКлиенте и ключевым словом Асинх. Проанализируем пример кода:


&НаКлиенте
Асинх Процедура ОбработатьДанныеСЗапросомПользователя()
    
    Для Каждого СтрокаТЧ Из Объект.Товары Цикл
        
        // Вместо модального окна используем асинхронный вызов
        ПараметрыФормы = Новый Структура("ТекущееЗначение", СтрокаТЧ.Номенклатура);
        Результат = Ждать ОткрытьФормуАсинх("ОбщаяФорма.УточнениеДанных", ПараметрыФормы);
        
        Если Результат <> Неопределено Тогда
            СтрокаТЧ.РезультатВыбора = Результат;
        КонецЕсли;
        
    КонецЦикла;
    
    // Только ПОСЛЕ завершения цикла и сбора всех данных вызываем серверную транзакцию
    ЗаписатьДанныеНаСервере();
    
КонецПроцедуры

В данном случае выполнение кода в процедуре действительно «замрет» на строке с Ждать, пока пользователь не закроет форму. При этом интерфейс останется отзывчивым, а другие процессы смогут выполняться.

Метод 2: Паттерн «Сбор данных перед обработкой»

Если ваша логика подразумевает сложную транзакцию на сервере, внутри которой вдруг «всплывает» вопрос, необходимо применить стратегию предварительного анализа. Это наиболее надежный и профессиональный подход. Разберем его по шагам:

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

Посмотрим на пример структуры данных для такого подхода:


// На сервере собираем список вопросов
ВопросыДляПользователя = Новый СписокЗначений;
Для Каждого Строка Из Данные Цикл
    Если ТребуетсяУточнение(Строка) Тогда
        ВопросыДляПользователя.Добавить(Строка.Идентификатор, "Уточните данные для " + Строка.Наименование);
    КонецЕсли;
КонецЦикла;

Затем эти данные передаются в ДополнительныеСвойства объекта, как советовали в сообщении 8 форума, чтобы не выполнять расчеты дважды.

Метод 3: Использование ОписанияОповещения (Callback)

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

Рассмотрим, как это реализовать. Нам нужно передать контекст выполнения (индексы, промежуточные итоги) через ДополнительныеПараметры.


&НаКлиенте
Процедура НачатьОбработку()
    Контекст = Новый Структура;
    Контекст.Вставить("Индекс", 0);
    Контекст.Вставить("Данные", ПолучитьСписокДанных());
    
    ВыполнитьШагЦикла(Контекст);
КонецПроцедуры

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

&НаКлиенте
Процедура ПослеЗакрытияФормы(Результат, Контекст) Экспорт
    Если Результат <> Неопределено Тогда
        // Сохраняем результат и переходим к следующему шагу
        Контекст.Данные[Контекст.Индекс].Ответ = Результат;
        Контекст.Индекс = Контекст.Индекс + 1;
        ВыполнитьШагЦикла(Контекст);
    КонецЕсли;
КонецПроцедуры

Проанализируем эту схему: здесь нет классического цикла Для. Вместо него используется рекурсивный вызов процедуры ВыполнитьШагЦикла через оповещение. Это позволяет «разрывать» выполнение кода, отдавая управление пользователю, и возвращаться в нужную точку алгоритма.

Резюме и рекомендации

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

Используйте ВременноеХранилище для передачи больших объемов данных между клиентскими вызовами, чтобы не перегружать параметры оповещений. И всегда старайтесь минимизировать количество всплывающих окон — пользователи оценят, если вы соберете все вопросы в одну удобную форму, а не будете прерывать их работу на каждой итерации цикла.

← На главную