Как настроить прямое подключение и отправку запросов в систему Меркурий (Ветис.API) из 1С 7.7 без внешних шлюзов?

Программист 1С v7.7 Торговля и дистрибуция
← На главную

При автоматизации учета продукции, подконтрольной ветеринарному надзору, разработчики на платформе 1С:Предприятие 7.7 часто сталкиваются с вопросом организации обмена с ФГИС «Меркурий» (Ветис.API). В седьмой версии платформы отсутствует встроенная поддержка веб-сервисов, что заставляет искать пути взаимодействия: использовать платные шлюзы, внешние компоненты на .NET или реализовать прямой обмен средствами COM-объектов операционной системы. Разберем, как построить надежный прямой обмен через стандартные системные библиотеки без использования сторонних платных сервисов.

Архитектура прямого взаимодействия

Для взаимодействия с сервисами Ветис.API из 1С 7.7 оптимально использовать связку из двух стандартных системных COM-объектов Windows:

  1. MSXML2.DOMDocument.6.0 — применяется для генерации и разбора SOAP-конвертов и структурированных XML-документов с поддержкой пространств имен (namespaces).
  2. WinHttp.WinHttpRequest.5.1 — обеспечивает отправку HTTP POST-запросов к веб-сервису по защищенному протоколу HTTPS.

Рассмотрим ключевые технические нюансы, которые необходимо учитывать при реализации протокола:

  1. Протокол безопасности TLS 1.2: Ветис.API не принимает соединения по устаревшим протоколам SSLv3/TLS 1.0. Чтобы WinHttpRequest успешно устанавливал соединение, на рабочей станции должен быть включен TLS 1.2, а в параметрах объекта WinHttp необходимо явно активировать флаг безопасности: HTTP.Option(9) = 2048.
  2. Двухуровневая авторизация: на транспортном уровне HTTP передается заголовок Authorization: Basic ... с логином и паролем пользователя веб-сервиса (в кодировке Base64). Внутри самого XML-тела SOAP-запроса дополнительно передаются параметры apiKey, serviceId, issuerId и логин инициатора.
  3. Обязательные заголовки: при отправке запроса по протоколу SOAP 1.1 необходимо обязательно передавать заголовки Content-Type: text/xml; charset=utf-8 и SOAPAction: "".
  4. Асинхронная модель Ветис.API: операции изменения и регистрации данных выполняются асинхронно. Первичный вызов submitApplicationRequest возвращает идентификатор заявки (applicationId). Далее с интервалом в 3–5 секунд отправляется запрос receiveApplicationResultRequest для проверки статуса готовности и получения итогового GUID созданного объекта.

Пример формирования SOAP-запроса на добавление площадки

Проанализируем практический пример функции на встроенном языке 1С 7.7. В коде формируется SOAP-пакет для метода регистрации новой площадки хозяйствующего субъекта (ModifyEnterpriseOperation) с корректным объявлением пространств имен через объект Msxml2.MXNamespaceManager.6.0.


Функция ДобавитьПлощадку(АдресСервераДляЗапроса)
    
    ТекущаяОперация = "Добавление новой площадки ХС";
    ДобавитьВЛог("" + ТекущееВремя() + " Начало выполнения операции: " + ТекущаяОперация); 
    
    рд_тзРезультат.Очистить();
    рд_тзРезультат.НоваяКолонка("Название");
    рд_тзРезультат.НоваяКолонка("ИНН");
    рд_тзРезультат.НоваяКолонка("Адрес");
    рд_тзРезультат.НоваяКолонка("GUID");
    рд_тзРезультат.НоваяКолонка("GUIDПредприятия", , , "GUID Предприятия");
    
    XML_DOM = CreateObject("MSXML2.DOMDocument.6.0");
    XML_HELPER = CreateObject("Msxml2.MXNamespaceManager.6.0");
    
    XML_HELPER.declarePrefix("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
    XML_HELPER.declarePrefix("base", "http://api.vetrf.ru/schema/cdm/base");
    XML_HELPER.declarePrefix("ws", "http://api.vetrf.ru/schema/cdm/application/ws-definitions");
    XML_HELPER.declarePrefix("app", "http://api.vetrf.ru/schema/cdm/application");
    XML_HELPER.declarePrefix("merc", "http://api.vetrf.ru/schema/cdm/mercury/applications");
    XML_HELPER.declarePrefix("com", "http://api.vetrf.ru/schema/cdm/argus/common");
    XML_HELPER.declarePrefix("ent", "http://api.vetrf.ru/schema/cdm/cerberus/enterprise");
    XML_HELPER.declarePrefix("ikar", "http://api.vetrf.ru/schema/cdm/ikar");
    
    Header = XML_DOM.createProcessingInstruction("xml", "version=""1.0"" encoding=""UTF-8""");
    XML_DOM.appendChild(Header);
    
    XML_ROOT = ДобавитьУзел(XML_DOM, XML_HELPER, XML_DOM, "Envelope", "soapenv");
    ДобавитьАттрибут(XML_DOM, XML_ROOT, "xmlns:soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
    ДобавитьАттрибут(XML_DOM, XML_ROOT, "xmlns:base", "http://api.vetrf.ru/schema/cdm/base");
    ДобавитьАттрибут(XML_DOM, XML_ROOT, "xmlns:ws", "http://api.vetrf.ru/schema/cdm/application/ws-definitions");
    ДобавитьАттрибут(XML_DOM, XML_ROOT, "xmlns:app", "http://api.vetrf.ru/schema/cdm/application");
    ДобавитьАттрибут(XML_DOM, XML_ROOT, "xmlns:merc", "http://api.vetrf.ru/schema/cdm/mercury/applications");
    ДобавитьАттрибут(XML_DOM, XML_ROOT, "xmlns:com", "http://api.vetrf.ru/schema/cdm/argus/common");
    ДобавитьАттрибут(XML_DOM, XML_ROOT, "xmlns:ent", "http://api.vetrf.ru/schema/cdm/cerberus/enterprise");
    ДобавитьАттрибут(XML_DOM, XML_ROOT, "xmlns:ikar", "http://api.vetrf.ru/schema/cdm/ikar");
    
    XML_Header = ДобавитьУзел(XML_DOM, XML_HELPER, XML_ROOT, "Header", "soapenv");      
    XML_Body = ДобавитьУзел(XML_DOM, XML_HELPER, XML_ROOT, "Body", "soapenv");
    XML_getBusinessEntityListRequest = ДобавитьУзел(XML_DOM, XML_HELPER, XML_Body, "submitApplicationRequest", "ws");
    
    XML_apiKey = ДобавитьУзел(XML_DOM, XML_HELPER, XML_getBusinessEntityListRequest, "apiKey", "ws");
    ДобавитьТекст(XML_DOM, XML_apiKey, СокрЛП(рд_APIKey_Тест));
    
    XML_application = ДобавитьУзел(XML_DOM, XML_HELPER, XML_getBusinessEntityListRequest, "application", "app");
    XML_serviceId = ДобавитьУзел(XML_DOM, XML_HELPER, XML_application, "serviceId", "app");
    ДобавитьТекст(XML_DOM, XML_serviceId, СокрЛП(рд_ServiceID_Тест));
    
    XML_issuerId = ДобавитьУзел(XML_DOM, XML_HELPER, XML_application, "issuerId", "app");
    ДобавитьТекст(XML_DOM, XML_issuerId, СокрЛП(рд_IssuerID_Тест));
    
    XML_issueDate = ДобавитьУзел(XML_DOM, XML_HELPER, XML_application, "issueDate", "app");
    СтрДата = Формат(ТекущаяДата(), "ДДДММГГГГ");
    СтрДата = Прав(СтрДата, 4) + "-" + Сред(СтрДата, 4, 2) + "-" + Лев(СтрДата, 2) + "T10:00:00";
    ДобавитьТекст(XML_DOM, XML_issueDate, СокрЛП(СтрДата));
    
    XML_data = ДобавитьУзел(XML_DOM, XML_HELPER, XML_application, "data", "app");
    XML_modifyEnterpriseRequest = ДобавитьУзел(XML_DOM, XML_HELPER, XML_data, "modifyEnterpriseRequest", "merc");
    
    XML_localTransactionId = ДобавитьУзел(XML_DOM, XML_HELPER, XML_modifyEnterpriseRequest, "localTransactionId", "merc");
    ДобавитьТекст(XML_DOM, XML_localTransactionId, "a00001");
    
    XML_initiator = ДобавитьУзел(XML_DOM, XML_HELPER, XML_modifyEnterpriseRequest, "initiator", "merc");
    XML_login = ДобавитьУзел(XML_DOM, XML_HELPER, XML_initiator, "login", "com");
    ДобавитьТекст(XML_DOM, XML_login, СокрЛП(рд_ЛогинМеркурий_Тест));
    
    XML_modificationOperation = ДобавитьУзел(XML_DOM, XML_HELPER, XML_modifyEnterpriseRequest, "modificationOperation", "merc");
    XML_type = ДобавитьУзел(XML_DOM, XML_HELPER, XML_modificationOperation, "type", "ent");
    ДобавитьТекст(XML_DOM, XML_type, "CREATE");
    
    XML_resultingList = ДобавитьУзел(XML_DOM, XML_HELPER, XML_modificationOperation, "resultingList", "ent");
    ДобавитьАттрибут(XML_DOM, XML_resultingList, "count", "1");    
    ДобавитьАттрибут(XML_DOM, XML_resultingList, "total", "1");    
    ДобавитьАттрибут(XML_DOM, XML_resultingList, "offset", "0");    
    
    XML_enterprise = ДобавитьУзел(XML_DOM, XML_HELPER, XML_resultingList, "enterprise", "ent"); 
    XML_name = ДобавитьУзел(XML_DOM, XML_HELPER, XML_enterprise, "name", "ent");                        
    ДобавитьТекст(XML_DOM, XML_name, "ООО Союз магазин Пролетарский");
    
    XML_type = ДобавитьУзел(XML_DOM, XML_HELPER, XML_enterprise, "type", "ent");                        
    ДобавитьТекст(XML_DOM, XML_type, "1");
    
    XML_address = ДобавитьУзел(XML_DOM, XML_HELPER, XML_enterprise, "address", "ent");                        
    XML_country = ДобавитьУзел(XML_DOM, XML_HELPER, XML_address, "country", "ikar");                        
    XML_guid = ДобавитьУзел(XML_DOM, XML_HELPER, XML_country, "guid", "base");                        
    ДобавитьТекст(XML_DOM, XML_guid, СокрЛП(рд_Фильтр3));                             
    
    XML_region = ДобавитьУзел(XML_DOM, XML_HELPER, XML_address, "region", "ikar");                        
    XML_guid = ДобавитьУзел(XML_DOM, XML_HELPER, XML_region, "guid", "base");                        
    ДобавитьТекст(XML_DOM, XML_guid, СокрЛП(рд_Фильтр4));                             
    
    XML_locality = ДобавитьУзел(XML_DOM, XML_HELPER, XML_address, "locality", "ikar");                        
    XML_guid = ДобавитьУзел(XML_DOM, XML_HELPER, XML_locality, "guid", "base");                        
    ДобавитьТекст(XML_DOM, XML_guid, СокрЛП(рд_Фильтр5));                             
    
    XML_Street = ДобавитьУзел(XML_DOM, XML_HELPER, XML_address, "street", "ikar");                        
    XML_guid = ДобавитьУзел(XML_DOM, XML_HELPER, XML_Street, "guid", "base");                        
    ДобавитьТекст(XML_DOM, XML_guid, СокрЛП(рд_Фильтр8));                             
    
    XML_addressView = ДобавитьУзел(XML_DOM, XML_HELPER, XML_address, "addressView", "ikar");                        
    ДобавитьТекст(XML_DOM, XML_addressView, СокрЛП(рд_Фильтр6));                             
    
    XML_activityList = ДобавитьУзел(XML_DOM, XML_HELPER, XML_enterprise, "activityList", "ent");
    ДобавитьАттрибут(XML_DOM, XML_activityList, "count", "1");    
    ДобавитьАттрибут(XML_DOM, XML_activityList, "total", "1");    
    ДобавитьАттрибут(XML_DOM, XML_activityList, "offset", "0");    
    
    XML_activity = ДобавитьУзел(XML_DOM, XML_HELPER, XML_activityList, "activity", "ent");
    XML_name = ДобавитьУзел(XML_DOM, XML_HELPER, XML_activity, "name", "ent");
    ДобавитьТекст(XML_DOM, XML_name, "Реализация пищевых продуктов");    
   
    XML_owner = ДобавитьУзел(XML_DOM, XML_HELPER, XML_enterprise, "owner", "ent");
    XML_guid = ДобавитьУзел(XML_DOM, XML_HELPER, XML_owner, "guid", "base");                        
    ДобавитьТекст(XML_DOM, XML_guid, СокрЛП(рд_Фильтр7));                             
    
    XML_reason = ДобавитьУзел(XML_DOM, XML_HELPER, XML_modificationOperation, "reason", "ent");                          
    ДобавитьТекст(XML_DOM, XML_reason, "Причина добавления тест");                             
    
    спРезультат = ВыгрузитьDOM(XML_DOM, АдресСервераДляЗапроса, ТекущаяОперация);
КонецФункции

Отправка запроса через WinHttp и разбор ответа

Разберем логику вспомогательной функции отправки ВыгрузитьDOM. Чтобы не повредить кодировку UTF-8 при передаче кириллицы из 1С 7.7, в метод Send() следует передавать непосредственно объект XML_DOM (или двоичный поток ADODB.Stream), избегая предварительного преобразования в обычную строку 1С:


Функция ВыгрузитьDOM(XML_DOM, АдресСервера, ОписаниеОперации)
    
    Попытка
        HTTP = CreateObject("WinHttp.WinHttpRequest.5.1");
        HTTP.Option(9, 2048); // Включение TLS 1.2
        HTTP.Open("POST", АдресСервера, 0);
        
        HTTP.SetRequestHeader("Content-Type", "text/xml; charset=utf-8");
        HTTP.SetRequestHeader("SOAPAction", """""");
        
        // Передача заголовка авторизации Basic Auth
        // Заголовок формируется как "Basic " + Base64(ЛогинВебСервиса + ":" + ПарольВебСервиса)
        HTTP.SetRequestHeader("Authorization", "Basic " + СокрЛП(рд_ТокенАвторизации));
        
        HTTP.Send(XML_DOM);
        
        Если HTTP.Status = 200 Тогда
            XML_Ответ = CreateObject("MSXML2.DOMDocument.6.0");
            XML_Ответ.loadXML(HTTP.ResponseText);
            
            // Получаем applicationId для асинхронного опроса результата
            УзелAppId = XML_Ответ.selectSingleNode("//*[local-name()='applicationId']");
            Если ПустоеЗначение(УзелAppId) = 0 Тогда
                Возврат УзелAppId.text;
            Иначе
                Возврат "Ошибка: Не найден applicationId в ответе";
            КонецЕсли;
        Иначе
            Сообщить("Ошибка HTTP: " + HTTP.Status + " " + HTTP.StatusText);
            Сообщить(HTTP.ResponseText);
            Возврат "";
        КонецЕсли;
    Исключение
        Сообщить("Исключение при вызове веб-сервиса: " + ОписаниеОшибки());
        Возврат "";
    КонецПопытки;

КонецФункции

Рекомендации по оптимизации работы

  1. Обработка ошибок парсинга: ошибка сервера вида BEA-382513: OSB Replace action failed... Error parsing XML: expected exactly one item, got 0 items чаще всего свидетельствует об отправке запроса не на тот конечный адрес веб-сервиса (endpoint) или о нарушении обязательной структуры иерархии тегов внутри блока application/data.
  2. Оптимизация памяти при больших ответах: при запросе объемных списков (остатков продукции, справочников ВСД) объект MSXML2.DOMDocument полностью загружает XML-дерево в оперативную память. Для предотвращения переполнения памяти в 32-разрядном процессе 1С 7.7 для чтения тяжелых ответов рекомендуется применять потоковый парсер MSXML2.SAXXMLReader60.
← На главную