Как заменить модальное открытие форм на последовательное немодальное в 1С?

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

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

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

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


&НаСервере
Процедура СоздатьДокументыНаСервереФормы(ДанныеФормы, НомерСтроки)
    Документ = Документы.ПоступлениеТоваровУслуг.СоздатьДокумент();
    Обработка = РеквизитФормыВЗначение("Объект");
    Обработка.СоздатьПоступлениеТоваров(Документ, НомерСтроки);
    ЗначениеВРеквизитФормы(Обработка,"Объект");
    ЗначениеВДанныеФормы(Документ,ДанныеФормы);
КонецПроцедуры

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

Проблема этого кода в использовании метода ОткрытьМодально(), который полностью останавливает выполнение кода до тех пор, пока пользователь не закроет форму. Если просто заменить его на Открыть(), то система мгновенно откроет столько окон, сколько отмечено строк, что неприемлемо.

Решение через ОписаниеОповещенияОЗакрытии

Правильный подход заключается в организации "рекурсивного" вызова открытия следующей формы из обработчика события закрытия предыдущей. Это достигается с помощью механизма оповещений.

Разберем по шагам, как это реализовать.

  1. Подготовка данных.
    Вместо того чтобы обрабатывать строки прямо в цикле, сначала соберем все отмеченные строки в отдельный массив. Это позволит нам управлять процессом и передавать контекст между вызовами.
  2. Организация последовательного вызова.
    Создадим две клиентские процедуры: одну для запуска процесса, вторую — для открытия формы конкретного документа по его индексу в массиве.
  3. Использование ОписаниеОповещенияОЗакрытии.
    Это ключевой объект. Перед открытием формы мы создаем экземпляр ОписаниеОповещения, в котором указываем имя процедуры-обработчика, которая должна выполниться после закрытия формы. Важно также передать в оповещение параметры, необходимые для продолжения работы, например, массив данных и текущий индекс.
  4. Открытие нового, незаписанного документа.
    Важный нюанс: мы работаем с еще не записанными в базу документами. Стандартный метод ОткрытьФорму() с параметром Ключ ожидает ссылку на существующий объект. Попытка передать туда сам объект документа приведет к открытию пустой формы.
    Чтобы открыть заполненную, но не записанную форму, нужно:
    • Получить объект формы через ПолучитьФорму().
    • На сервере создать и заполнить объект документа.
    • Вернуть данные на клиент и скопировать их в объект формы с помощью КопироватьДанныеФормы().
    • Открыть полученную и заполненную форму методом Форма.Открыть().

Посмотрим на пример кода, который реализует описанный алгоритм.


&НаКлиенте
Перем мМассивСтрокДляОбработки; // Переменная модуля формы для хранения массива

// 1. Процедура-инициатор
&НаКлиенте
Процедура СоздатьДокументы(Команда)
    // Собираем все отмеченные строки в массив
    мМассивСтрокДляОбработки = Новый Массив;
    Для Каждого Строка Из Объект.Накладные Цикл
        Если Строка.Пометка Тогда
            мМассивСтрокДляОбработки.Добавить(Строка);
        КонецЕсли;
    КонецЦикла;

    // Если есть что обрабатывать, запускаем процесс с первого элемента (индекс 0)
    Если мМассивСтрокДляОбработки.Количество() > 0 Тогда
        ОткрытьФормуДокументаПоИндексу(0);
    КонецЕсли;
КонецПроцедуры

// 2. Процедура открытия одной формы
&НаКлиенте
Процедура ОткрытьФормуДокументаПоИндексу(Индекс)
    // Получаем текущую строку из массива
    ТекущаяСтрока = мМассивСтрокДляОбработки[Индекс];

    // Получаем объект формы для нового документа
    ФормаДокумента = ПолучитьФорму("Документ.ПоступлениеТоваровУслуг.Форма.ФормаДокументаТовары");
    
    // Передаем объект данных формы на сервер для заполнения
    ДанныеФормыДляЗаполнения = ФормаДокумента.Объект;
    СоздатьДокументыНаСервереФормы(ДанныеФормыДляЗаполнения, ТекущаяСтрока.НомерСтроки);
    
    // Копируем заполненные на сервере данные обратно в объект формы
    КопироватьДанныеФормы(ДанныеФормыДляЗаполнения, ФормаДокумента.Объект);

    // 3. Создаем оповещение о закрытии
    // В параметры передаем индекс обработанной строки и саму форму, чтобы получить из нее ссылку
    ДополнительныеПараметры = Новый Структура;
    ДополнительныеПараметры.Вставить("Индекс", Индекс);
    ДополнительныеПараметры.Вставить("Форма", ФормаДокумента); // Передаем саму форму

    ОповещениеОЗакрытии = Новый ОписаниеОповещения("ПослеЗакрытияФормыДокумента", ЭтотОбъект, ДополнительныеПараметры);
    
    // Привязываем оповещение к форме
    ФормаДокумента.ОписаниеОповещенияОЗакрытии = ОповещениеОЗакрытии;
    
    // Открываем форму немодально
    ФормаДокумента.Открыть();
КонецПроцедуры

// 4. Процедура-обработчик закрытия формы
&НаКлиенте
Процедура ПослеЗакрытияФормыДокумента(РезультатЗакрытия, ДополнительныеПараметры) Экспорт
    // Получаем данные из параметров оповещения
    ОбработанныйИндекс = ДополнительныеПараметры.Индекс;
    ТекущаяСтрока = мМассивСтрокДляОбработки[ОбработанныйИндекс];
    
    // Если пользователь записал документ, в объекте формы будет ссылка
    // Ее можно сохранить в табличной части обработки
    СсылкаНаДокумент = ДополнительныеПараметры.Форма.Объект.Ссылка;
    Если ЗначениеЗаполнено(СсылкаНаДокумент) Тогда
        ТекущаяСтрока.Ссылка = СсылкаНаДокумент;
    КонецЕсли;

    // Вычисляем индекс для следующего элемента
    СледующийИндекс = ОбработанныйИндекс + 1;
    
    // Если в массиве еще остались элементы, вызываем открытие следующей формы
    Если СледующийИндекс < мМассивСтрокДляОбработки.Количество() Тогда
        ОткрытьФормуДокументаПоИндексу(СледующийИндекс);
    КонецЕсли;
КонецПроцедуры

// Серверная процедура остается без изменений
&НаСервере
Процедура СоздатьДокументыНаСервереФормы(ДанныеФормы, НомерСтроки)
    // ... логика создания и заполнения объекта документа ...
    Документ = Документы.ПоступлениеТоваровУслуг.СоздатьДокумент();
    Обработка = РеквизитФормыВЗначение("Объект");
    Обработка.СоздатьПоступлениеТоваров(Документ, НомерСтроки);
    ЗначениеВРеквизитФормы(Обработка,"Объект");
    ЗначениеВДанныеФормы(Документ,ДанныеФормы);
КонецПроцедуры

Альтернативный подход: обработка по одной строке

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

Логика будет такой:

  1. Процедура-инициатор находит первую строку с флагом "Пометка" и отсутствующим флагом "Обработано".
  2. Открывает для нее форму, как в предыдущем примере, с оповещением о закрытии.
  3. В обработчике закрытия она устанавливает для этой строки флаг "Обработано".
  4. Затем она снова вызывает процедуру-инициатор, которая повторит поиск следующей строки для обработки.
  5. Если строки для обработки не найдены, цикл завершается.

Этот метод может быть удобен, если нет возможности или желания хранить большой массив на клиенте. Однако он приводит к большему количеству итераций по табличной части и может быть менее производительным на больших объемах данных.

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

← На главную