Как передать документ между базами 1С через Web-сервис из расширения?

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

Передача данных между различными конфигурациями 1С — одна из классических задач интеграции. Использование Web- и HTTP-сервисов позволяет настроить оперативный обмен в режиме реального времени, например, отправлять документ из базы-источника в базу-приемник непосредственно в момент его проведения. Однако при реализации этого механизма через расширения конфигурации разработчики часто сталкиваются с целым рядом трудностей: от проблем с видимостью опубликованных сервисов до ошибок сериализации XDTO при попытке передать сложные типы данных (ссылки или таблицы значений). Регулярный обмен по расписанию без изменения самих конфигураций автоматизирует программа для обмена данными между базами 1С на основе готовых XML-правил.

В этой статье мы подробно разберем, как решить все эти проблемы, настроить правильную передачу данных между базами (например, между 1С:УНФ и 1С:КА 2), корректно упаковать документ на стороне источника, разобрать его на стороне приемника и настроить отладку веб-сервиса. Если вместо частичной интеграции планируется полноценный переход в новую систему, стоит изучить раздел с решениями для переноса данных в 1С:Комплексную автоматизацию из различных конфигураций.

---

Шаг 1. Публикация Web-сервисов, созданных в расширении конфигурации

Начиная с современных версий платформы «1С:Предприятие 8.3», разработчики могут создавать Web- и HTTP-сервисы непосредственно внутри расширений. Это очень удобно, так как позволяет не снимать основную конфигурацию с поддержки. Однако при публикации на веб-сервере (IIS или Apache) созданные операции могут быть не видны «снаружи».

Для того чтобы веб-сервисы из расширений успешно публиковались, необходимо выполнить следующие настройки:

  1. Откройте окно публикации на веб-сервере (Администрирование — Публикация на веб-сервере).
  2. Перейдите на вкладку «Дополнительно» (или воспользуйтесь свойствами публикации в конфигураторе) и обязательно установите флаг «Публиковать Web-сервисы расширений по умолчанию».
  3. В файле настроек публикации default.vrd, который находится в каталоге публикации на веб-сервере, за эту настройку отвечает параметр publishExtensionsByDefault="true". Если флаг не устанавливается через интерфейс, вы можете отредактировать этот файл вручную, добавив атрибут в тег <point>.

Адрес для подключения к SOAP-сервису, созданному в расширении, будет выглядеть следующим образом:

http://<ИмяСервера>/<ИмяПубликации>/ws/<ИмяСервиса>?wsdl

---

Шаг 2. Проблема передачи сложных типов и архитектура обмена

Попытка передать напрямую через веб-сервис такие типы данных, как ТаблицаЗначений, СправочникСсылка или ДокументСсылка, всегда будет приводить к ошибке несовместимости типов. Протокол SOAP строго типизирован, а платформа 1С не умеет передавать свои внутренние ссылочные типы во внешние системы без предварительного преобразования. Чтобы избежать ручной настройки контрактов SOAP, можно использовать конструктор API в расширении для обмена данными в формате JSON.

Кроме того, если ваши базы имеют разные конфигурации (например, источником служит «1С:Управление нашей фирмой», а приемником — «1С:Комплексная автоматизация»), стандартная сериализация XDTO через метод СериализаторXDTO.ЗаписатьXML() работать не будет. Причина в том, что по умолчанию данный метод использует пространство имен текущей конфигурации http://v8.1c.ru/8.1/data/enterprise/current-config. Структуры метаданных, состав реквизитов и типы данных в УНФ и КА 2 принципиально отличаются, поэтому база-приемник просто не сможет распознать («десериализовать») пришедший XML-пакет.

Для решения этой проблемы мы рассмотрим два наиболее надежных и профессиональных способа обмена:

---

Шаг 3. Подготовка и отправка данных на стороне источника (База 1)

Рассмотрим вариант с выгрузкой документа в XML-формат и его отправкой через SOAP Web-сервис. Нам нужно собрать данные документа, записать их в XML-файл, получить из него двоичные данные и передать в параметре веб-сервиса.

Обратите внимание на важный нюанс: в настройках самого Web-сервиса в базе-приемнике тип принимаемого параметра (например, Customer или ДанныеДокумента) должен иметь тип base64Binary (из пространства имен http://www.w3.org/2001/XMLSchema) либо тип string, если вы передаете XML в виде строки.

Ниже приведен пример процедуры, выполняемой при проведении документа в базе-источнике:


Процедура ОтправитьДокументНаВебСервис(ДокументСсылка) Экспорт

    // 1. Формируем имя временного файла для XML
    ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xml");
    
    // 2. Записываем данные документа в XML-формат вручную
    ЗаписьXML = Новый ЗаписьXML;
    ЗаписьXML.ОткрытьФайл(ИмяВременногоФайла, "UTF-8");
    ЗаписьXML.ЗаписатьОбъявлениеXML();
    
    ЗаписьXML.ЗаписатьНачалоЭлемента("Document");
    
    // Записываем шапку документа
    ЗаписьXML.ЗаписатьНачалоЭлемента("Header");
    ЗаписьXML.ЗаписатьСодержимоеЭлемента("Number", ДокументСсылка.Номер);
    ЗаписьXML.ЗаписатьСодержимоеЭлемента("Date", Формат(ДокументСсылка.Дата, "ДФ=гггг-ММ-ддТЧЧ:мм:сс"));
    ЗаписьXML.ЗаписатьСодержимоеЭлемента("PartnerINN", ДокументСсылка.Контрагент.ИНН);
    ЗаписьXML.ЗаписатьКонецЭлемента(); // Header
    
    // Записываем табличную часть "Товары"
    ЗаписьXML.ЗаписатьНачалоЭлемента("TabularSection");
    Для Каждого СтрокаТЧ Из ДокументСсылка.Запасы Цикл
        ЗаписьXML.ЗаписатьНачалоЭлемента("Row");
        ЗаписьXML.ЗаписатьСодержимоеЭлемента("SKU", СтрокаТЧ.Номенклатура.Артикул);
        ЗаписьXML.ЗаписатьСодержимоеЭлемента("Quantity", СтрокаТЧ.Количество);
        ЗаписьXML.ЗаписатьСодержимоеЭлемента("Price", СтрокаТЧ.Цена);
        ЗаписьXML.ЗаписатьКонецЭлемента(); // Row
    КонецЦикла;
    ЗаписьXML.ЗаписатьКонецЭлемента(); // TabularSection
    
    ЗаписьXML.ЗаписатьКонецЭлемента(); // Document
    ЗаписьXML.Закрыть();
    
    // 3. Упаковываем файл в Двоичные Данные
    ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ИмяВременногоФайла);
    
    // 4. Подключаемся к Web-сервису базы-приемника
    // Адрес подключения берем из настроек публикации
    Попытка
        Определение = Новый WSОпределения("http://server2/base2/ws/exchange.1cws?wsdl", "Администратор", "Пароль");
        Прокси = Новый WSПрокси(Определение, "http://www.mycompany.ru/exchange", "ExchangeService", "ExchangeServiceSoap");
        
        // Отправляем двоичные данные в функцию веб-сервиса
        // Параметр "DataDocument" на веб-сервисе должен иметь тип base64Binary
        Результат = Прокси.LoadDocument(ДвоичныеДанныеФайла);
        
        ЗаписьЖурналаРегистрации("Обмен через Web-сервис", УровеньЖурналаРегистрации.Информация, , , 
            "Документ успешно отправлен. Ответ сервера: " + Результат);
            
    Исключение
        ЗаписьЖурналаРегистрации("Обмен через Web-сервис", УровеньЖурналаРегистрации.Ошибка, , , 
            "Ошибка при отправке документа: " + ОписаниеОшибки());
    КонецПопытки;
    
    // Удаляем временный файл
    Попытка
        УдалитьФайлы(ИмяВременногоФайла);
    Исключение
    КонецПопытки;

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

---

Шаг 4. Прием и обработка данных на стороне приемника (База 2)

Теперь настроим логику на стороне базы-приемника. Наш веб-сервис принимает параметр с типом base64Binary. Платформа 1С автоматически преобразует этот параметр в объект типа ДвоичныеДанные при вызове функции.

Нам необходимо получить текст XML из двоичных данных, прочитать его структуру и программно создать (или обновить) документ в базе-приемнике. Разберем код функции веб-сервиса: Для контроля точности созданных в приемнике документов удобно автоматически сверить проводки между базами 1С после выполнения обмена.


Функция LoadDocument(DataDocument) Export

    // DataDocument приходит как тип ДвоичныеДанные
    Если ТипЗнч(DataDocument) <> Тип("ДвоичныеДанные") Тогда
        Возврат "Ошибка: Неверный формат входных данных.";
    КонецЕсли;

    // 1. Сохраняем двоичные данные во временный XML-файл
    ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xml");
    DataDocument.Записать(ИмяВременногоФайла);
    
    // 2. Инициализируем чтение XML
    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.ОткрытьФайл(ИмяВременногоФайла);
    
    // Структура для хранения прочитанных параметров
    ДанныеДокумента = Новый Структура;
    ДанныеДокумента.Вставить("Товары", Новый ТаблицаЗначений);
    ДанныеДокумента.Товары.Колонки.Добавить("Артикул");
    ДанныеДокумента.Товары.Колонки.Добавить("Количество");
    ДанныеДокумента.Товары.Колонки.Добавить("Цена");
    
    // 3. Парсинг XML-структуры
    Пока ЧтениеXML.Прочитать() Цикл
        
        Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
            ИмяУзла = ЧтениеXML.Имя;
            
            Если ИмяУзла = "Number" Тогда
                ЧтениеXML.Прочитать();
                ДанныеДокумента.Вставить("Номер", ЧтениеXML.Значение);
            ИначеЕсли ИмяУзла = "Date" Тогда
                ЧтениеXML.Прочитать();
                // Преобразуем строку в дату 1С
                ДанныеДокумента.Вставить("Дата", XMLЗначение(Тип("Дата"), ЧтениеXML.Значение));
            ИначеЕсли ИмяУзла = "PartnerINN" Тогда
                ЧтениеXML.Прочитать();
                ДанныеДокумента.Вставить("ИНН", ЧтениеXML.Значение);
            ИначеЕсли ИмяУзла = "Row" Тогда
                НоваяСтрока = ДанныеДокумента.Товары.Добавить();
                Пока ЧтениеXML.Прочитать() Цикл
                    Если ЧтениеXML.ТипУзла = ТипУзлаXML.КонецЭлемента И ЧтениеXML.Имя = "Row" Тогда
                        Прервать;
                    КонецЕсли;
                    
                    Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
                        ТекущийРеквизитТЧ = ЧтениеXML.Имя;
                        ЧтениеXML.Прочитать();
                        ЗначениеРеквизита = ЧтениеXML.Значение;
                        
                        Если ТекущийРеквизитТЧ = "SKU" Тогда
                            НоваяСтрока.Артикул = ЗначениеРеквизита;
                        ИначеЕсли ТекущийРеквизитТЧ = "Quantity" Тогда
                            НоваяСтрока.Количество = Число(ЗначениеРеквизита);
                        ИначеЕсли ТекущийРеквизитТЧ = "Price" Тогда
                            НоваяСтрока.Цена = Число(ЗначениеРеквизита);
                        КонецЕсли;
                    КонецЕсли;
                КонецЦикла;
            КонецЕсли;
        КонецЕсли;
        
    КонецЦикла;
    
    ЧтениеXML.Закрыть();
    
    // Удаляем временный файл после прочтения
    Попытка
        УдалитьФайлы(ИмяВременногоФайла);
    Исключение
    КонецПопытки;
    
    // 4. Программное создание документа в Базе 2 (Комплексная автоматизация)
    Попытка
        Возврат СоздатьДокументВПриемнике(ДанныеДокумента);
    Исключение
        Возврат "Ошибка при создании документа: " + ОписаниеОшибки();
    КонецПопытки;

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

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

---

Шаг 5. Как правильно настроить отладку Web-сервиса

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

  1. Включите отладку на сервере 1С:

    Если база-приемник работает в клиент-серверном режиме, на сервере 1С по умолчанию отключена отладка. Необходимо остановить службу сервера 1C:Enterprise 8.3 Server Agent и в параметрах ее запуска в реестре Windows (ветка HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\1C:Enterprise 8.3 Server Agent...) добавить ключ -debug. После этого запустите службу заново.

  2. Разрешите отладку в публикации веб-сервиса:

    При публикации базы на веб-сервере в диалоговом окне перейдите на вкладку «Прочие». В разделе «Отладка» установите переключатель в положение «Разрешена (протокол TCP/IP)» (или HTTP, в зависимости от версии платформы). Веб-сервер (IIS или Apache) после изменения настроек обязательно нужно перезапустить.

  3. Настройте подключение предметов отладки в Конфигураторе:

    Откройте Конфигуратор базы-приемника. Перейдите в меню Отладка — Подключение... В открывшемся окне нажмите кнопку «Автоматическое подключение» и установите флаги напротив пунктов «Web-сервисы», «HTTP-сервисы» и «Фоновые задания». Нажмите ОК.

Теперь вы можете смело ставить точку останова прямо на первой строчке функции LoadDocument в модуле вашего веб-сервиса. При отправке запроса из базы-источника выполнение кода автоматически прервется в этой точке, и вы сможете детально проанализировать значения переменных и процесс парсинга XML.

← На главную