Как корректно закрыть сеанс 1С после завершения асинхронной обработки данных

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

При автоматизации обмена данными (например, с кассами ККМ Offline в конфигурации «1С:Розница») внешнюю обработку часто запускают по расписанию через планировщик задач Windows. Типовая проблема, с которой сталкиваются разработчики: если вызвать метод ЗавершитьРаботуСистемы() в обработчике формы ПриОткрытии, клиентское приложение закрывается мгновенно, прерывая выполнение фоновых процедур и запись документов.

Выясним причину такого поведения и разберем правильные способы организации ожидания и корректного завершения работы программы.

Почему сеанс закрывается до создания документов

Методы подсистемы подключаемого оборудования (БПО), такие как ПодключаемоеОборудованиеOfflineКлиент.НачатьЗагрузкуОтчетовОРозничныхПродажах и ПодключаемоеОборудованиеOfflineКлиент.НачатьВыгрузкуТоваровВККМOffline, являются асинхронными. Они инициализируют процесс обработки и сразу же возвращают управление клиентскому коду, передавая реальную работу серверным процедурам и фоновым заданиям.

Когда в коде вызывается метод ЗавершитьРаботуСистемы(), платформа 1С принудительно завершает клиентский сеанс. Связанные с ним серверные задания аварийно прерываются, транзакции откатываются, и создаваемые документы «Отчет о розничных продажах» не сохраняются в базе данных.

Решение 1. Построение цепочки асинхронных вызовов через ОписаниеОповещения

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

Разберем по шагам реализацию этого механизма в модуле формы обработки:

  1. В обработчике ПриСозданииНаСервере получаем список оборудования без лишних клиент-серверных вызовов.
  2. В процедуре ПриОткрытии подключаем однократный обработчик ожидания для запуска загрузки отчетов.
  3. По завершении загрузки передаем управление в процедуру выгрузки товаров через callback-оповещение.
  4. После окончания выгрузки вызываем метод ЗавершитьРаботуСистемы(Ложь).

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


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    ЗаполнитьТЗКассыККМ();
КонецПроцедуры

&НаСервере
Процедура ЗаполнитьТЗКассыККМ()
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ПодключаемоеОборудование.Ссылка,
        |   ПодключаемоеОборудование.Наименование КАК НазваниеККМ
        |ИЗ
        |   Справочник.ПодключаемоеОборудование КАК ПодключаемоеОборудование
        |ГДЕ
        |   ПодключаемоеОборудование.ТипОборудования = &ТипОборудования";
    Запрос.УстановитьПараметр("ТипОборудования", Перечисления.ТипыПодключаемогоОборудования.ККМOffline);
    
    Объект.ТЗКассыККМ.Загрузить(Запрос.Выполнить().Выгрузить());
КонецПроцедуры

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

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

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

&НаКлиенте
Процедура ПослеВыгрузкиТоваров(Результат, Параметры) Экспорт
    // Закрываем клиентский сеанс без вывода диалоговых окон подтверждения
    ЗавершитьРаботуСистемы(Ложь);
КонецПроцедуры

Решение 2. Дополнительная задержка для завершения тяжелых транзакций

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

Проанализируем пример завершающего шага:


&НаКлиенте
Процедура ПослеВыгрузкиТоваров(Результат, Параметры) Экспорт
    // Задаем гарантированную паузу в 5-10 секунд для фиксации всех транзакций
    ПодключитьОбработчикОжидания("ФинальноеЗавершениеРаботы", 5, Истина);
КонецПроцедуры

&НаКлиенте
Процедура ФинальноеЗавершениеРаботы() Экспорт
    ЗавершитьРаботуСистемы(Ложь);
КонецПроцедуры

Важно: Не используйте пустые циклы вида Пока А < 300000 Цикл для создания пауз. Такие циклы на 100% загружают процессорное ядро и блокируют обработку системных сообщений интерфейса 1С. Все задержки должны реализовываться исключительно через ПодключитьОбработчикОжидания.

Решение 3. Синхронное выполнение через внешнее соединение (VBScript / COM)

Если нет необходимости открывать интерактивный интерфейс тонкого клиента, запуск регламентной обработки можно реализовать через скрипт VBScript или COM-соединение V83.COMConnector.

При COM-соединении код выполняется полностью на стороне сервера в синхронном режиме. Скрипт не перейдет к следующей строке и не закроет соединение до тех пор, пока серверная процедура полностью не завершит запись всех документов.

Посмотрим на пример вызова внешней обработки через VBScript:


Set v8 = CreateObject("V83.Application")
' Подключаемся к информационной базе
res = v8.Connect("Srvr=Server1C;Ref=BaseRetail;Usr=AutoExchange;Pwd=SecretPassword;")

' Создаем объект внешней обработки
Set obr = v8.ExternalDataProcessors.Create("C:\Exchange\OfflineKKM.epf")

' Вызываем экспортную процедуру модуля обработки
obr.ВыполнитьОбменСКассами()

' Завершаем сеанс
v8.Exit()

Рекомендации по надежности обмена

  1. Используйте типовые регламентные задания: В большинстве конфигураций линейки «1С:Предприятие» функционал обмена с ККМ Offline уже встроен в типовой механизм фоновых заданий. Использование регламентного задания внутри базы избавляет от необходимости поднимать клиентские сеансы через планировщик Windows.
  2. Проверяйте модули на серверные аналоги: При создании собственных фоновых сценариев проверяйте наличие экспортных синхронных процедур в общих модулях с постфиксом Сервер (например, ПодключаемоеОборудованиеOfflineСервер). Прямой вызов серверной логики гарантирует завершение всех транзакций до выхода из процедуры.
← На главную