Как вызвать обработку без открытия формы и вернуть данные в вызывающий код

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

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

Нередко разработчики пытаются решить эту задачу «в лоб»: вызывают метод ОткрытьФорму() с параметром автозапуска, а внутри обработчика события ПриОткрытии пытаются выполнить логику и закрыть форму с помощью метода Закрыть(Параметры) или установки флага Отказ = Истина. Однако при таком подходе форма либо зависает, либо вызывающая процедура оповещения получает Неопределено вместо реальных данных. Разберем по шагам, почему это происходит и как правильно решить задачу.

Выясним причину: почему Закрыть() и Отказ не работают в ПриОткрытии

Проанализируем поведение управляемой формы на этапе её инициализации:

  1. Установка флага Отказ = Истина: если в обработчике события ПриОткрытии мы присваиваем параметру Отказ значение Истина, платформа прерывает процесс создания окна. Поскольку форма фактически так и не была открыта, событие её закрытия не наступает. Обработчик оповещения о закрытии (описание оповещения, переданное в параметрах метода ОткрытьФорму) либо не вызывается вовсе, либо возвращает Неопределено.
  2. Вызов метода Закрыть(Параметр): в момент выполнения кода ПриОткрытии контекст визуального окна еще формируется, и форма формально не имеет статуса открытой. Попытка вызвать метод Закрыть() на этом этапе игнорируется платформой управляемого приложения. В результате окно формы все равно выводится на экран, а параметры не возвращаются.

Вариант 1. Быстрый обход проблемы через обработчик ожидания

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

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

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


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

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

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

Вариант 2. Архитектурно правильное решение: вынос логики в модуль менеджера или модуль объекта

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

Разберем эталонную архитектуру разделения обязанностей:

  1. Модуль формы: отвечает исключительно за визуализацию данных, управление доступностью кнопок, интерактивный выбор файлов или ввод параметров администратором.
  2. Модуль менеджера или модуль объекта: содержит чистую бизнес-логику получения данных, обработки запросов, создания документов и проверки соответствий.
  3. Точка вызова: перед запуском проверяет условия и права пользователя. Если требуется вмешательство человека — открывается форма; если операция автоматическая — вызывается экспортный метод модуля менеджера без создания формы.

Рассмотрим, как вынести бизнес-логику в модуль менеджера обработки. В качестве примера возьмем загрузку данных из внешнего источника через COM-соединение ADODB:


// В модуле менеджера обработки
Функция ЗагрузитьТЗИзПП(СменаИлиПериодВыборки, ТаблицаРезультата, СтрокаОшибки) Экспорт
    
    СтрокаПодключения = "ваша_строка_подключения";
    Connection = Новый COMОбъект("ADODB.Connection");
    Connection.Open(СтрокаПодключения);
    
    RS = Новый COMОбъект("ADODB.Recordset");
    RS.ActiveConnection = Connection;
    
    QueryText = "SELECT Shift.DateTimeOfBegin, ClientName.Name, Product.name, TRS.price " +
                "FROM gas.dbo.CrdTransActRegister as TRS " +
                "LEFT JOIN gas.dbo.CrdClients ClientName ON TRS.kClient = ClientName.kClient " +
                "LEFT JOIN gas.dbo.products Product ON TRS.kPr = Product.kprCard";
                
    RS.CursorType = 3;
    RS.Open(QueryText);
    
    Пока RS.EOF = 0 Цикл
        НоваяСтрока = ТаблицаРезультата.Добавить();
        Для Каждого Колонка Из ТаблицаРезультата.Колонки Цикл
            НоваяСтрока[Колонка.Имя] = RS.Fields(Колонка.Имя).Value;
        КонецЦикла;
        RS.MoveNext();
    КонецЦикла;
    
    RS.Close();
    Connection.Close();
    
    // Анализируем соответствия в регистрах сведений
    ВсеОк = Истина;
    Для Каждого СтрТЗ Из ТаблицаРезультата Цикл
        Отбор = Новый Структура("КодПП", СтрТЗ.КодКлиент);
        СтрТЗ.КлиентСсылка = РегистрыСведений.ах_СоответствияКлиенты.Получить(Отбор).Контрагент;
        
        Если Не ЗначениеЗаполнено(СтрТЗ.КлиентСсылка) Тогда
            ВсеОк = Ложь;
            СтрокаОшибки = СтрокаОшибки + Символы.ПС + "Не найдено соответствие для: " + СтрТЗ.Клиент;
        КонецЕсли;
    КонецЦикла;
    
    Возврат ВсеОк;
    
КонецФункции

Как вызывать обработку в разных режимах

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


&НаСервере
Процедура ВыполнитьОперациюНаСервере(РежимАдминистратора)

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

КонецПроцедуры

Если же открытие формы всё-таки требуется администратору, на клиенте вызывается стандартный метод ОткрытьФорму, а в модуле самой формы заполнение таблицы части выполняется простой загрузкой полученной таблицы значений через метод ТабличнаяЧасть.Загрузить(ТаблицаДанных).

Важные ограничения и особенности

При реализации фоновых и клиент-серверных механизмов обратим внимание на следующие технические детали:

  1. Работа с COMОбъект: технология COM поддерживается только в операционной системе Windows. При работе на сервере под управлением Linux или в веб-клиенте создание COMОбъект вызовет исключение. Также следует учитывать разрядность: 64-разрядный сервер 1С требует наличия 64-разрядного OLE DB провайдера базы данных.
  2. Модуль объекта на тонком клиенте: метод Обработки.ИмяОбработки.Создать() недоступен на тонком клиенте. Создание экземпляра объекта и вызов методов модуля объекта всегда должны выполняться на стороне сервера.
  3. Оповещение через события приложения: если требуется вернуть данные на клиенте без жесткой связки с формой, можно воспользоваться методом Оповестить(ИмяСобытия, Параметры), который доставит результат в форму-приемник независимо от нюансов закрытия дочернего окна.
← На главную