При программном формировании и пакетной отправке запросов в контролирующие органы (например, запросов на информационное обслуживание налогоплательщика или отчетов) разработчики часто сталкиваются с задачей автоматического закрытия сеанса пользователя после выполнения всех действий. Однако если вызвать метод ЗавершитьРаботуСистемы сразу после строки запуска отправки, программа закрывается мгновенно, а пакет так и не уходит адресату.
Разберем причины такого поведения платформы «1С:Предприятие 8.3» и рассмотрим правильные варианты реализации ожидания завершения отправки.
Проанализируем типовой фрагмент кода, приводящий к ошибке:
СоздатьДокументы();
Сверка = Объект.СсылкаНаСверку;
Организация = Объект.Организация;
Форма = ПолучитьФорму("Документ.ЗапросНаИнформационноеОбслуживаниеНалогоплательщика.Форма.ФормаДокумента", , Сверка);
ОписаниеОповещенияКонтекста = Новый ОписаниеОповещения("ПриОткрытииЗавершение", ЭтотОбъект);
ДокументооборотСКОКлиент.ПолучитьКонтекстЭДО(ОписаниеОповещенияКонтекста);
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеОтправкиЗавершение", Форма);
КонтекстЭДОКлиент.ОтправкаЗапросаНаИнформационноеОбслуживание(Сверка, Организация, ОписаниеОповещения);
ЗавершитьРаботуСистемы(Ложь);
Причина сбоя заключается в особенностях работы асинхронных клиентских методов и криптографических операций:
ПолучитьКонтекстЭДО и ОтправкаЗапросаНаИнформационноеОбслуживание) являются неблокирующими. Они инициируют процесс в фоновом потоке или через системные интерфейсы криптопровайдера и сразу же передают управление следующей строке кода.ПриОткрытииЗавершение), когда контекст ЭДО уже гарантированно сформирован.ЗавершитьРаботуСистемы(Ложь) исполняется синхронно в основном потоке клиента, мгновенно останавливая выполнение всех запланированных асинхронных операций и закрывая окна взаимодействия с криптографией.Наиболее чистое и надежное архитектурное решение — не пытаться искусственно «усыпить» поток в месте вызова, а передать управление процедуре обратного вызова (коллбэку). Нет необходимости изменять типовую форму или типовые общие модули — мы можем указать собственный обработчик оповещения из внешней обработки или формы.
Разберем по шагам правильную организацию цепочки вызовов:
Посмотрим на пример реализации модуля клиентской формы или внешней обработки:
&НаКлиенте
Процедура ЗапуститьПакетнуюОтправку()
СоздатьДокументыНаСервере();
// Формируем оповещение для завершения инициализации контекста ЭДО
ОповещениеКонтекста = Новый ОписаниеОповещения("ПослеПолученияКонтекстаЭДО", ЭтотОбъект);
ДокументооборотСКОКлиент.ПолучитьКонтекстЭДО(ОповещениеКонтекста);
КонецПроцедуры
&НаКлиенте
Процедура ПослеПолученияКонтекстаЭДО(КонтекстЭДО, ДополнительныеПараметры) Экспорт
Если КонтекстЭДО = Неопределено Тогда
// Ошибка инициализации криптографии или контекста
ЗавершитьРаботуСистемы(Ложь);
Возврат;
КонецЕсли;
Сверка = Объект.СсылкаНаСверку;
Организация = Объект.Организация;
// Указываем собственный обработчик завершения процесса отправки
ОповещениеОтправки = Новый ОписаниеОповещения("ПослеЗавершенияОтправки", ЭтотОбъект);
КонтекстЭДО.ОтправкаЗапросаНаИнформационноеОбслуживание(Сверка, Организация, ОповещениеОтправки);
КонецПроцедуры
&НаКлиенте
Процедура ПослеЗавершенияОтправки(Результат, ДополнительныеПараметры) Экспорт
// В этот момент отправка, подписание и передача пакета полностью завершены
// При необходимости можно зафиксировать лог выполнения
ЗавершитьРаботуСистемы(Ложь);
КонецПроцедуры
Если цепочка асинхронных методов по какой-либо причине разрывается открывающимися модальными окнами криптографии или сторонними библиотеками, можно использовать циклический опрос состояния созданного документа через ПодключитьОбработчикОжидания.
Рассмотрим порядок реализации:
ОтключитьОбработчикОжидания и завершаем сеанс.Посмотрим на пример кода:
&НаКлиенте
Процедура ЗапуститьОтправкуСКонтролем()
// Запуск стандартного метода отправки
ВыполнитьОтправкуДокумента();
// Запускаем проверку статуса каждые 3 секунды
ПодключитьОбработчикОжидания("ПроверитьСтатусОтправки", 3);
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьСтатусОтправки()
СостояниеОтправки = ПроверитьСтатусНаСервере(Объект.СсылкаНаСверку);
Если СостояниеОтправки = "Отправлен" Или СостояниеОтправки = "Ошибка" Тогда
ОтключитьОбработчикОжидания("ПроверитьСтатусОтправки");
ЗавершитьРаботуСистемы(Ложь);
КонецЕсли;
КонецПроцедуры
Иногда возникает идея перенести процесс в серверное фоновое задание, чтобы не открывать клиентское приложение вовсе. Однако важно учитывать следующие нюансы:
Серверное подписание возможно, только если:
Ограничение: если для закрытого ключа используется физический USB-токен или требуется интерактивный ввод PIN-кода/пароля пользователем, фоновое серверное задание завершится ошибкой доступа к контейнеру ключа. В этом случае корректно отправлять пакеты можно только через интерактивный клиентский сеанс с использованием цепочки объектов ОписаниеОповещения.