Подключение к Web-сервису ECOD для обмена документами в формате EDI (провайдер Корус)

Подробное описание подключения из 1С 8.2 к Web-сервису ECOD для одностороннего обмена электронными документами в формате EDI (провайдер Корус).

Была поставлена задача – принимать заказы от крупной сети в электронном виде. Сеть работает следующим образом – рассылает всем заказы (ORDERS) через провайдера Корус в формате EDI, а подтверждений обратно не принимает. Соответственно, обмен идет только в одну сторону. Ну, ещё желательно было сэкономить на модуле сопряжения, который Корус предлагает купить, и коннектиться к ним непосредственно из 1С.

После заполнения всех необходимых бумаг и получения GLN своей организации, наконец–то мне выдали описание формата обмена и способа подключения к Web–сервису.

Уже в процессе работы созрели 2 варианта технической реализации обмена – тот, который рекомендует Корус (по протоколу SOAP) и который позволяет платформа 8.2 (через XDTO). Опишу ОБА способа реализации.

Первый способ (рекомендованный Корусом).

Для обмена через протокол SOAP, необходимо установить Soap Toolkit 3 с сайта Майкрософт. Он может там быть доступен для скачивания в разобранном виде (файлы с расширением .msm), тогда лучше поискать дистрибутив в виде инсталлятора. Также желательно установить финишное обновление SOAPDSK3.0–KB827257–x86.exe, которое лежит на том же Майкрософте, но уже в виде инсталлятора. Для 64-битных систем всё должно тоже штатно установиться. Протокол слегка устаревший, но Корус пока другого не предлагает….

Следующий шаг – добавление сертификатов в файл Cacert.pem из каталога bin программы. На всякий случай, добавил и корневые сертификаты, выданные Thawte, и сертификаты сайта, на котором живёт Web–сервис. Способ добавления сертификатов подробно описан в литературе, не буду на нём заострять внимание.

Пример реально работающего кода:

// Необходимо установить Soap Toolkit 3 и обновление к нему
// В папку bin программы 1С 8.2 добавить отредактированный файл cacert.pem с сертификатами Web-сервиса
//
Процедура ПолучитьЗаказыПокупателяMSSOAP(ДатаНачала, ДатаКонца, Login=«Ваш логин», Password=«Ваш пароль», Partner_ID=«GLN Партнера») Экспорт

    ИмяСервиса = «https://www.ecod.pl/webserv2/EDIservice.asmx?WSDL»;

    Попытка
        oSOAPClient = Новый COMОбъект(«MSSOAP.SoapClient30»);
    Исключение
       
#Если Клиент Тогда
       
Сообщить(ОписаниеОшибки());
       
#КонецЕсли
       
Возврат;
    КонецПопытки;

    Попытка
        Инициализация = oSOAPClient.mssoapinit(ИмяСервиса, «», «», «»);
    Исключение
       
#Если Клиент Тогда
       
Сообщить(ОписаниеОшибки());
       
#КонецЕсли
       
Возврат;
    КонецПопытки;

    Попытка
        Подключение = oSOAPClient.Relationships(Login, Password, «5000»);
       
СтрокаХML = Подключение.item(1).text;
       
КодОшибкиПодключения = Подключение.item(0).text;
    Исключение
       
#Если Клиент Тогда
       
Сообщить(ОписаниеОшибки());
       
#КонецЕсли
       
Возврат;
    КонецПопытки;

    Если КодОшибкиПодключения <> «00000000» Тогда
       
#Если Клиент Тогда
       
Сообщить(«Код ошибки подключения = » + КодОшибкиПодключения);
       
#КонецЕсли
       
Возврат;
    КонецЕсли;

    ДатаНачалаECOD = ПривестиДатуКВидуECOD(ДатаНачала);
   
ДатаКонцаECOD = ПривестиДатуКВидуECOD(ДатаКонца);

    // Возвращает массив tracking-id документов, доставленных пользователю ECOD
   
Попытка
       
Доставка = oSOAPClient.ListMBEx(Login, Password, Partner_ID, «ORDER», «RU1», «XML», «P»,
                       
ДатаНачалаECOD, ДатаКонцаECOD, «», «», «N», «5000»);
       
СтрокаДоставки = Доставка.item(1).text;
       
КодОшибкиДоставки = Доставка.item(0).text;
    Исключение
       
#Если Клиент Тогда
       
Сообщить(ОписаниеОшибки());
       
#КонецЕсли
       
Возврат;
    КонецПопытки;

    Если КодОшибкиДоставки <> «00000000» Тогда
       
#Если Клиент Тогда
       
Сообщить(«Код ошибки идентификации документов = » + КодОшибкиДоставки);
       
#КонецЕсли
       
Возврат;
    КонецЕсли;

    // Теперь соберём массив идентификаторов документов tracking-id
   
МассивИдентификаторов = ПолучитьМассивИдентификаторовДокументовECOD(СтрокаДоставки);

    // Теперь для каждого идентификатора получим документ
   
Для Каждого Элемент Из МассивИдентификаторов Цикл
        Попытка
           
Документ = oSOAPClient.Receive(Login, Password, Partner_ID, «ORDER», Элемент, «XML», «R», «5000»);
           
СтрокаДокументаXML = Документ.item(1).text;
           
КодОшибкиДокументаXML = Документ.item(0).text;
            Если
КодОшибкиДокументаXML <> «00000000» Тогда
               
#Если Клиент Тогда
               
Сообщить(«Код ошибки доставки документа » + Элемент + » = » + КодОшибкиДокументаXML);
               
#КонецЕсли
               
Продолжить;
            КонецЕсли;
           
СформироватьЗаказПокупателяECODИзСтрокиXML(СтрокаДокументаXML);
        Исключение
           
#Если Клиент Тогда
           
Сообщить(ОписаниеОшибки());
           
#КонецЕсли
           
Продолжить;
        КонецПопытки;
    КонецЦикла;

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

Второй способ (использование возможностей платформы 8.2).

Известно, что 1С 8.2 тоже умеет работать с неродственными ей объектами, для этого нужно использовать объект Фабрика XDTO. В тексте процедуры показан динамический способ создания описания объектов Web–сервиса, подключения к нему и управления входящими документами.

Добавление сертификатов также обязательно и в этом случае.

 

Полный текст кода:

 

Процедура ПолучитьЗаказыПокупателяXDTO(ДатаНачала, ДатаКонца, Login=«Ваш логин», Password=«Ваш пароль», Partner_ID=«GLN Партнера») Экспорт

    Определение = Новый WSОпределения(«https://www.ecod.pl/webserv2/EDIservice.asmx?WSDL»);
   
Прокси = Новый WSПрокси(Определение, «http://www.comarch.com/», «EDIWebService», «EDIWebServiceSoap12»);

    Фабрика = Прокси.ФабрикаXDTO;

    Запрос = Фабрика.Создать(Фабрика.Тип(«http://www.comarch.com/», «Relationships»));
   
Запрос.Name = Login;
   
Запрос.Password = Password;
   
Запрос.Timeout = «5000»;

    РезультатRelationships = Прокси.Relationships(Запрос.Name, Запрос.Password, Запрос.Timeout);
   
// Возвращает документ XML, если операция выполнена успешно

    КодОшибкиПодключения = РезультатRelationships.Res;
    Если
КодОшибкиПодключения <> «00000000» Тогда
       
#Если Клиент Тогда
       
Сообщить(«Код ошибки подключения = » + КодОшибкиПодключения);
       
#КонецЕсли
       
Возврат;
    КонецЕсли;

    Запрос = Фабрика.Создать(Фабрика.Тип(«http://www.comarch.com/», «ListMBEx»));
   
Запрос.Name = Login;
   
Запрос.Password = Password;
   
Запрос.PartnerIln = Partner_ID;
   
Запрос.DocumentType = «ORDER»;
   
Запрос.DocumentVersion = «RU1»;
   
Запрос.DocumentStandard = «XML»;
   
Запрос.DocumentTest = «P»;
   
Запрос.DateFrom = ПривестиДатуКВидуECOD(ДатаНачала);
   
Запрос.DateTo = ПривестиДатуКВидуECOD(ДатаКонца);
   
Запрос.ItemFrom = «»;
   
Запрос.ItemTo = «»;
   
Запрос.DocumentStatus = «N»;   // Читаем только новые документы
   
Запрос.Timeout = «5000»;

    РезультатListMBEx = Прокси.ListMBEx(Запрос.Name, Запрос.Password, Запрос.PartnerIln, Запрос.DocumentType, Запрос.DocumentVersion,
               
Запрос.DocumentStandard, Запрос.DocumentTest, Запрос.DateFrom, Запрос.DateTo, Запрос.ItemFrom, Запрос.ItemTo,
               
Запрос.DocumentStatus, Запрос.Timeout);

    КодОшибкиДоставки = РезультатListMBEx.Res;
    Если
КодОшибкиДоставки <> «00000000» Тогда
       
#Если Клиент Тогда
       
Сообщить(«Код ошибки идентификации документов = » + КодОшибкиДоставки);
       
#КонецЕсли
       
Возврат;
    КонецЕсли;

    СтрокаДоставки = РезультатListMBEx.Cnt;
   
// Теперь соберём массив идентификаторов документов tracking-id
   
МассивИдентификаторов = ПолучитьМассивИдентификаторовДокументовECOD(СтрокаДоставки);

    // Разберем полученные идентификаторы документов
   
Для Каждого Элемент Из МассивИдентификаторов Цикл
       
// А теперь получим файлы заказов из системы
       
Запрос = Фабрика.Создать(Фабрика.Тип(«http://www.comarch.com/», «Receive»));
       
Запрос.Name = Login;
       
Запрос.Password = Password;
       
Запрос.PartnerIln = Partner_ID;
       
Запрос.DocumentType = «ORDER»;
       
Запрос.TrackingId = Элемент;
       
Запрос.DocumentStandard = «XML»;
       
Запрос.ChangeDocumentStatus = «R»;   // Меняем статус на прочтённый
       
Запрос.Timeout = «5000»;

        РезультатReceive = Прокси.Receive(Запрос.Name, Запрос.Password, Запрос.PartnerIln, Запрос.DocumentType,
                   
Запрос.TrackingId, Запрос.DocumentStandard, , Запрос.Timeout);

        КодОшибкиДокументаXML = РезультатReceive.Res;
       
СтрокаДокументаXML = РезультатReceive.Cnt;
        Если
КодОшибкиДокументаXML <> «00000000» Тогда
           
#Если Клиент Тогда
           
Сообщить(«Код ошибки доставки документа » + Элемент + » = » + КодОшибкиДокументаXML);
           
#КонецЕсли
           
Продолжить;
        КонецЕсли;
       
СформироватьЗаказПокупателяECODИзСтрокиXML(СтрокаДокументаXML);

        Запрос = Фабрика.Создать(Фабрика.Тип(«http://www.comarch.com/», «ChangeDocumentStatus»));
       
Запрос.Name = Login;
       
Запрос.Password = Password;
       
Запрос.TrackingId = Элемент;
       
Запрос.Status = «R»;   // Меняем статус на прочтённый

        РезультатStatus = Прокси.ChangeDocumentStatus(Запрос.Name, Запрос.Password, Запрос.TrackingId, Запрос.Status);
       
КодОшибкиStatus = РезультатStatus.Res;
        Если
КодОшибкиStatus <> «00000000» Тогда
           
#Если Клиент Тогда
           
Сообщить(«Код ошибки изменения статуса документа » + Элемент + » = » + КодОшибкиStatus);
           
#КонецЕсли
       
КонецЕсли;
    КонецЦикла;

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

Результат работы обеих процедур абсолютно одинаков — возвращаются документы ORDERS из Web-сервиса. Теперь задача существенно упрощается — необходимо правильно распарсить входной документ в формате XML и создать документы Заказ Покупателя. Для облегчения вашего читательского труда привожу код сервисных процедур чтения строки XML и парсинга. Ну а создать нужный документ в собственной базе из полученной структуры не составит особого труда.

 

// Читает полученную строку XML в формате ECOD (из Web-сервиса)
Процедура СформироватьЗаказПокупателяECODИзСтрокиXML(СтрокаXML, СсылкаНаОбъект = Неопределено) Экспорт

    Чтение = Новый ЧтениеXML;
   
ДеревоЗначений = Новый ДеревоЗначений();
    Попытка
       
Чтение.УстановитьСтроку(СтрокаXML);
       
Чтение.Прочитать();
    Исключение
       
#Если Клиент Тогда
       
Сообщить(«Ошибка при чтении строки XML! » + Символы.ПС + ОписаниеОшибки(), СтатусСообщения.Внимание);
       
#КонецЕсли
       
Чтение.Закрыть();
    КонецПопытки;

    ДеревоЗначений.Колонки.Добавить(«Элемент»);
   
ДеревоЗначений.Колонки.Добавить(«Текст»);
   
Корень         = ДеревоЗначений.Строки.Добавить();
   
Корень.Элемент = Чтение.Имя;
   
Рекурсия(Корень, Чтение);
   
Дерево = ДеревоЗначений;

    Чтение.Закрыть();

    СтруктураДокумента = ПолучитьСтруктуруДокумента_ORDER_ECOD(Дерево);
    Если
ТипЗнч(СтруктураДокумента) = Тип(«Структура») Тогда
       
ЗаказСсылка = ЗагрузитьСтруктуруВДокументЗаказ(СтруктураДокумента, СсылкаНаОбъект);
        Если
ЗаказСсылка <> Неопределено Тогда
           
СделатьЗаписьВСправочникеЗаказыПокупателяEDI(СтруктураДокумента, ЗаказСсылка);
        КонецЕсли;
    КонецЕсли;

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

Процедура Рекурсия(СтрокаДерева, Чтение)

    АтрибутыСоответствие = Новый Соответствие();
    Пока
Чтение.ПрочитатьАтрибут() Цикл
       
АтрибутыСоответствие.Вставить(Чтение.Имя, Чтение.Значение);
    КонецЦикла;
    Если
АтрибутыСоответствие.Количество() > 0 Тогда
       
СтрокаДерева.Атрибуты = АтрибутыСоответствие;
    Иначе
       
АтрибутыСоответствие = 0;
    КонецЕсли;
    Пока
Чтение.Прочитать() Цикл
        Если
Чтение.ТипУзла = ТипУзлаXML.КонецЭлемента Тогда
            Прервать;
        ИначеЕсли
Чтение.ТипУзла = ТипУзлаXML.Текст Тогда
           
СтрокаДерева.Текст = Чтение.Значение;
        ИначеЕсли
Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
           
Дочерний         = СтрокаДерева.Строки.Добавить();
           
Дочерний.Элемент = Чтение.Имя;
           
Рекурсия(Дочерний, Чтение);
        КонецЕсли;
    КонецЦикла;

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

// Получить структуру Заказа Покупателя
Функция ПолучитьСтруктуруДокумента_ORDER_ECOD(Дерево)

    СтруктураДокумента = Новый Структура;

    Узел = ПолучитьУзелДерева(Дерево.Строки, «Строка», «Document-Order»);
    Если НЕ
Узел = Неопределено Тогда
       
СтрокаКорня = Узел.Строки;
    Иначе
       
#Если Клиент Тогда
       
Сообщить(«Файл не является Заказом покупателя!», СтатусСообщения.Важное);
       
#КонецЕсли
       
Возврат Ложь;
    КонецЕсли;

    // I. Order-Header (из корня)

    Узел = ПолучитьУзелДерева(СтрокаКорня, «Строка», «Order-Header»);
    Если НЕ
Узел = Неопределено Тогда
       
СтрокаДерева = Узел.Строки;
    Иначе
       
#Если Клиент Тогда
       
Сообщить(«Не найдена шапка документа!», СтатусСообщения.Важное);
       
#КонецЕсли
       
Возврат Ложь;
    КонецЕсли;

    // Номер документа
   
xmlНомерДокумента = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «OrderNumber», Истина);
    Если НЕ
xmlНомерДокумента = Неопределено Тогда
       
СтруктураДокумента.Вставить(«НомерДокумента», СокрЛП(xmlНомерДокумента));
    Иначе
        Возврат Ложь;
    КонецЕсли;

    //  Дата Документа
   
xmlДатаДокумента = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «OrderDate», Истина);
    Если НЕ
xmlДатаДокумента = Неопределено Тогда
       
СтруктураДокумента.Вставить(«ДатаДокумента», Дата(Прав(СокрЛП(xmlДатаДокумента),2)+«.»+Сред(СокрЛП(xmlДатаДокумента),6,2)+«.»+Лев(СокрЛП(xmlДатаДокумента),4)+» 0:00:00″));
    Иначе
        Возврат Ложь;
    КонецЕсли;

    // Дата Поставки(обязательное поле)
   
xmlДатаПоставки = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «ExpectedDeliveryDate», Истина);
    Если НЕ
xmlДатапоставки = Неопределено Тогда
       
ДатаПоставки = Дата(Прав(СокрЛП(xmlДатаПоставки),2)+«.»+Сред(СокрЛП(xmlДатаПоставки),6,2)+«.»+Лев(СокрЛП(xmlДатаПоставки),4)+» 0:00:00″);
       
СтруктураДокумента.Вставить(«ДатаПоставки», ДатаПоставки);
    Иначе
        Возврат Ложь;
    КонецЕсли;

    // Время Поставки(опциональное поле)
   
xmlВремяПоставки = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «ExpectedDeliveryTime», Ложь);
    Если НЕ
xmlВремяПоставки = Неопределено Тогда
       
СтруктураДокумента.Вставить(«ВремяПоставки», xmlВремяПоставки);
    Иначе
       
СтруктураДокумента.Вставить(«ВремяПоставки», «00:00»);
    КонецЕсли;

    // Валюта(UAH,RUB,USD,EUR,MDL,BYR)(опциональное поле)
   
xmlВалюта = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «Currency», Ложь);
    Если НЕ
xmlВалюта = Неопределено Тогда
       
СтруктураДокумента.Вставить(«Валюта», ПолучитьВалюту(xmlВалюта));
    КонецЕсли;

    // Обозначение Документа(опциональное поле)
   
xmlОбозначениеДокумента = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «DocumentFunctionCode», Ложь);
    Если НЕ
xmlОбозначениеДокумента = Неопределено Тогда
       
СтруктураДокумента.Вставить(«ОбозначениеДокумента», СокрЛП(xmlОбозначениеДокумента));
    КонецЕсли;

    // Свободный текст(опциональное поле)
   
xmlСвободныйТекст = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «Remarks», Ложь);
    Если НЕ
xmlСвободныйТекст = Неопределено Тогда
       
СтруктураДокумента.Вставить(«СвободныйТекст», СокрЛП(xmlСвободныйТекст));
    КонецЕсли;

    // II. Order-Summary (из корня)
   
Узел = ПолучитьУзелДерева(СтрокаКорня, «Строка», «Order-Summary»);
    Если НЕ
Узел = Неопределено Тогда
       
СтрокаДерева = Узел.Строки;
    Иначе
       
#Если Клиент Тогда
       
Сообщить(«Не найдены реквизиты табличной части!», СтатусСообщения.Важное);
       
#КонецЕсли
       
Возврат Ложь;
    КонецЕсли;

    // Количество Строк(опциональное поле)
   
xmlКоличествоСтрок = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «TotalLines», Ложь);
    Если НЕ
xmlКоличествоСтрок = Неопределено Тогда
       
СтруктураДокумента.Вставить(«КоличествоСтрок», СокрЛП(xmlКоличествоСтрок));
    КонецЕсли;

    // Общая Сумма(опциональное поле)
   
xmlОбщаяСумма = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «TotalOrderedAmount», Ложь);
    Если НЕ
xmlОбщаяСумма = Неопределено Тогда
       
СтруктураДокумента.Вставить(«ОбщаяСумма», СокрЛП(xmlОбщаяСумма));
    КонецЕсли;

    // III. Order-Parties (из корня)
   
Узел = ПолучитьУзелДерева(СтрокаКорня, «Строка», «Order-Parties»);
    Если НЕ
Узел = Неопределено Тогда
       
СтрокаУровня = Узел.Строки;
    Иначе
       
#Если Клиент Тогда
       
Сообщить(«Не найдены реквизиты покупателя!», СтатусСообщения.Важное);
       
#КонецЕсли
       
Возврат Ложь;
    КонецЕсли;

    Узел = ПолучитьУзелДерева(СтрокаУровня, «Строка», «Buyer»);
    Если НЕ
Узел = Неопределено Тогда
       
СтрокаДерева = Узел.Строки;
    Иначе
       
#Если Клиент Тогда
       
Сообщить(«Не найдены реквизиты покупателя!», СтатусСообщения.Важное);
       
#КонецЕсли
       
Возврат Ложь;
    КонецЕсли;

    // Поставщик(Организация)(обязательное поле)
   
xmlПоставщикGLN = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «ILN», Истина);
    Если НЕ
xmlПоставщикGLN = Неопределено Тогда
       
СтруктураДокумента.Вставить(«ПоставщикGLN», СокрЛП(xmlПоставщикGLN));
    КонецЕсли;

    Узел = ПолучитьУзелДерева(СтрокаУровня, «Строка», «Seller»);
    Если НЕ
Узел = Неопределено Тогда
       
СтрокаДерева = Узел.Строки;
    Иначе
       
#Если Клиент Тогда
       
Сообщить(«Не найдены реквизиты продавца!», СтатусСообщения.Важное);
       
#КонецЕсли
       
Возврат Ложь;
    КонецЕсли;

    // Покупатель(контрагент)(обязательное поле)
   
xmlКонтрагентGLN = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «ILN», Истина);
    Если НЕ
xmlКонтрагентGLN = Неопределено Тогда
       
СтруктураДокумента.Вставить(«КонтрагентGLN», СокрЛП(xmlКонтрагентGLN));
    КонецЕсли;

    xmlКонтрагентКод = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «CodeByBuyer», Истина);
    Если НЕ
xmlКонтрагентКод = Неопределено Тогда
       
СтруктураДокумента.Вставить(«КонтрагентКод», СокрЛП(xmlКонтрагентКод));
    КонецЕсли;

    Узел = ПолучитьУзелДерева(СтрокаУровня, «Строка», «DeliveryPoint»);
    Если НЕ
Узел = Неопределено Тогда
       
СтрокаДерева = Узел.Строки;
    Иначе
       
#Если Клиент Тогда
       
Сообщить(«Не найдены реквизиты точки доставки!», СтатусСообщения.Важное);
       
#КонецЕсли
       
Возврат Ложь;
    КонецЕсли;

    // Точка доставки (ДоговорКонтрагента)(опциональное поле)
   
xmlТочкаДоставкиGLN = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «ILN», Ложь);
    Если НЕ
xmlТочкаДоставкиGLN = Неопределено Тогда
       
СтруктураДокумента.Вставить(«ТочкаДоставкиGLN», СокрЛП(xmlТочкаДоставкиGLN));
    КонецЕсли;

    // IV. Табличная часть
    // Order-Lines (из корня)
   
Узел = ПолучитьУзелДерева(СтрокаКорня, «Строка», «Order-Lines»);
    Если НЕ
Узел = Неопределено Тогда
       
СтрокаУровня = Узел.Строки;
    Иначе
       
#Если Клиент Тогда
       
Сообщить(«Не найдена табличная часть!», СтатусСообщения.Важное);
       
#КонецЕсли
       
Возврат Ложь;
    КонецЕсли;

    // Создаем таблицуЗначений, в которую выгружаем табличную часть документа
   
ДокументТЧ = Новый ТаблицаЗначений;
   
ДокументТЧ.Колонки.Добавить(«НомерСтрокиТЧ»);
   
ДокументТЧ.Колонки.Добавить(«ШтрихКод»);
   
ДокументТЧ.Колонки.Добавить(«КодПокупателя»);
   
ДокументТЧ.Колонки.Добавить(«НоменклатураНаименование»);
   
ДокументТЧ.Колонки.Добавить(«Количество»);
   
ДокументТЧ.Колонки.Добавить(«Цена»);
   
ДокументТЧ.Колонки.Добавить(«Сумма»);
   
ДокументТЧ.Колонки.Добавить(«Коэффициент»);

    ТабличнаяЧасть = ПолучитьУзелДерева(СтрокаУровня, «Массив», «Line»);

    Для Каждого Узел ИЗ ТабличнаяЧасть Цикл

        Если НЕ Узел = Неопределено Тогда
           
СтрокаДерева = Узел.Строки;
        Иначе
           
#Если Клиент Тогда
           
Сообщить(«Не найдены строки табличной части!», СтатусСообщения.Важное);
           
#КонецЕсли
           
Возврат Ложь;
        КонецЕсли;

        Узел = ПолучитьУзелДерева(СтрокаДерева, «Строка», «Line-Item»);

        Если НЕ Узел = Неопределено Тогда
           
СтрокаДерева = Узел.Строки;
        Иначе
           
#Если Клиент Тогда
           
Сообщить(«Не найдена строка табличной части!», СтатусСообщения.Важное);
           
#КонецЕсли
           
Продолжить;
        КонецЕсли;

        НоваяСтрокаТЧ = ДокументТЧ.Добавить();

        //Номер строки (этот параметр при создании заказа не используется, может быть проверочным)
       
xmlНомерСтрокиТЧ = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «LineNumber», Истина);
        Если НЕ
xmlНомерСтрокиТЧ = Неопределено Тогда
           
НоваяСтрокаТЧ.НомерСтрокиТЧ = xmlНомерСтрокиТЧ;
        КонецЕсли;

        // Определяем атрибуты номенклатуры:
        // Штрих — код номенклатуры
       
xmlШтрихКодНоменклатуры = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «EAN», Истина);
        Если НЕ
xmlШтрихКодНоменклатуры = Неопределено Тогда
           
НоваяСтрокаТЧ.ШтрихКод = СокрЛП(xmlШтрихКодНоменклатуры);
        КонецЕсли;

        // Код поставщика(поле поиска)
       
xmlКодПокупателя = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «BuyerItemCode», Ложь);
        Если НЕ
xmlКодПокупателя = Неопределено Тогда
           
НоваяСтрокаТЧ.КодПокупателя = СокрЛП(xmlКодПокупателя);
        КонецЕсли;

        // Наименование номенклатуры(опциональное поле)
       
xmlНоменклатураНаименование = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «ItemDescription», Ложь);
        Если НЕ
xmlНоменклатураНаименование = Неопределено Тогда
           
НоваяСтрокаТЧ.НоменклатураНаименование = СокрЛП(xmlНоменклатураНаименование);
        КонецЕсли;

        xmlКоличество = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «OrderedQuantity», Истина);
        Если НЕ
xmlКоличество = Неопределено Тогда
            Попытка
               
НоваяСтрокаТЧ.Количество = Число(xmlКоличество);
            Исключение
               
#Если Клиент Тогда
               
Сообщить(«Не определил количество!», СтатусСообщения.Важное);
               
#КонецЕсли
               
НоваяСтрокаТЧ.Количество = 0;
            КонецПопытки;
        КонецЕсли;

        // Единицы измерения количества(опциональное поле)
       
xmlЦена = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «OrderedUnitGrossPrice», Ложь);
        Если НЕ
xmlЦена = Неопределено Тогда
           
НоваяСтрокаТЧ.Цена = Число(xmlЦена);
        КонецЕсли;

        // Сумма
       
xmlСумма = ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, «GrossAmount», Ложь);
        Если НЕ
xmlЦена = Неопределено Тогда
           
НоваяСтрокаТЧ.Сумма = Число(xmlСумма);
        КонецЕсли;

        НоваяСтрокаТЧ.Коэффициент = 1;

    КонецЦикла;

    СтруктураДокумента.Вставить(«ДокументТЧ», ДокументТЧ);

    Возврат СтруктураДокумента;

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

Функция ПолучитьУзелДерева(СтрокаДерева, ТипУзла, ИмяПоля)

    Если ТипУзла = «Строка» Тогда
       
xmlУзел = СтрокаДерева.Найти(ИмяПоля, «Элемент», Ложь);
    ИначеЕсли
ТипУзла = «Массив» Тогда
       
xmlУзел = СтрокаДерева.НайтиСтроки(Новый Структура(«Элемент», ИмяПоля), Ложь);
    Иначе
       
xmlУзел = Неопределено;
    КонецЕсли;

    Возврат xmlУзел;

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

Функция ПолучитьЗначениеВСтрокеДерева(СтрокаДерева, ИмяПоля, Обязательное)

    xmlЗначение = СтрокаДерева.Найти(ИмяПоля, «Элемент», Ложь);

    Если xmlЗначение = Неопределено Тогда

        Если Обязательное Тогда
           
#Если Клиент Тогда
           
Сообщить(«В файле не найдено обязательное поле «»» + ИмяПоля + «»»», СтатусСообщения.ОченьВажное);
           
#КонецЕсли
       
КонецЕсли;

        Возврат Неопределено;

    Иначе

        ТекстовоеЗначение = xmlЗначение.Текст;

        Если Не ЗначениеЗаполнено(ТекстовоеЗначение) Тогда

            Если Обязательное Тогда
               
#Если Клиент Тогда
               
Сообщить(«Не заполнено обязательное поле «»» + ИмяПоля + «»»», СтатусСообщения.Важное);
               
#КонецЕсли
           
КонецЕсли;

            Возврат Неопределено;

        Иначе

            Возврат ТекстовоеЗначение;

        КонецЕсли;

    КонецЕсли;

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

Функция ПолучитьВалюту(xmlВалюта)

    Если xmlВалюта = «UAH» Тогда
        Возврат
Справочники.Валюты.НайтиПоКоду(980);
    ИначеЕсли
xmlВалюта = «RUB» или xmlВалюта = «0» Тогда
        Возврат
Справочники.Валюты.НайтиПоКоду(643);
    ИначеЕсли
xmlВалюта = «USD» Тогда
        Возврат
Справочники.Валюты.НайтиПоКоду(840);
    ИначеЕсли
xmlВалюта = «EUR» Тогда
        Возврат
Справочники.Валюты.НайтиПоКоду(978);
    ИначеЕсли
xmlВалюта = «MDL» Тогда
        Возврат
Справочники.Валюты.НайтиПоКоду(498);
    ИначеЕсли
xmlВалюта = «BYR» Тогда
        Возврат
Справочники.Валюты.НайтиПоКоду(974);
    ИначеЕсли
xmlВалюта = «KZT» Тогда
        Возврат
Справочники.Валюты.НайтиПоКоду(398);
    Иначе
        Возврат
Справочники.Валюты.ПустаяСсылка();
    КонецЕсли;

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

Функция ПривестиДатуКВидуECOD(ВходящаяДата) Экспорт

    Возврат Формат(ВходящаяДата, «ДФ=»»гггг-ММ-дд»»»);

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

Функция ПолучитьМассивИдентификаторовДокументовECOD(СтрокаХML) Экспорт

    МассивИдентификаторов = Новый Массив;

    Чтение = Новый ЧтениеXML;
   
Чтение.УстановитьСтроку(СтрокаХML);
    Пока
Чтение.Прочитать() Цикл
       
ИмяУзла = Чтение.Имя;
        Если
ИмяУзла <> «tracking-id» Тогда  // Пропускаем все узлы, кроме tracking-id
           
Продолжить;
        КонецЕсли;
        Если
ИмяУзла = «tracking-id» Тогда
           
Чтение.Прочитать();
            Если
Чтение.ТипУзла = ТипУзлаXML.Текст Тогда
               
МассивИдентификаторов.Добавить(Чтение.Значение);
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
    Возврат
МассивИдентификаторов;

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

Хочу заметить, что хотя формат EDI и является стандартом электронного документооборота, однако форматы одинаковых документов у разных провайдеров могут существенно отличаться (казалось бы должны быть одинаковы — ан нет, разные у Коруса и Эксайта например). Так что этот формат обмена тоже не универсален……

Вот, собственно, и всё…

Если буду делать двусторонний обмен с Web-сервисами, обязательно выложу работающий код.

Удачи вам и не повторяйте чужих ошибок.

 

26 Comments

  1. Alex_Japanese_Student

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

    Reply
  2. aspirator23

    К сожалению сколько провайдеров столько форматов. Метро работает по другому, Гиперглобус по третьему.

    Уж лучше 1с формат для обмена документами.

    Reply
  3. miroha

    Нас тоже крупный покупатель обязал перейти на EDI рекомендовал корус. Сначала был шок, зачем платить за каждый отправленный документ? ведь полно способов сделать это бесплатно. Для нас это сумма сотни тысяч рублей, причем сервера корус не всегда доступны и являлись узким местом в нашем обмене.

    Купили у них пакет с внедрением, в итоге внедряли все сами и полностью переписали их программу для хранения данные в базе, а не файловой помойке, которая в их конфигурации тормозит. Техническая поддержка ужасная, специалисты плохо разбираться в конфигурации и все сводиться к пустой болтовне в почте между менеджерами, руководителями и программистами. Когда разобрались в принципах обмена перешли на другого более доступного по ценам провайдера. Дешевле всего стать самому провайдером или найти другой способ обмена.

    Reply
  4. MICK77

    (3) miroha, Если не великая тайна и не в качестве рекламы на кого перешли? А то тоже выбираем сейчас из 4-х.

    Reply
  5. 10ra

    этот кусок кода лишний т.к. уже поменяли статус при получении XML (Receive…Запрос.ChangeDocumentStatus = «R»;)

    Запрос = Фабрика.Создать(Фабрика.Тип(«http://www.comarch.com/», «ChangeDocumentStatus»));
    Запрос.Name = Login;
    Запрос.Password = Password;
    Запрос.TrackingId = Элемент;
    Запрос.Status = «R»;   // Меняем статус на прочтённый
    
    РезультатStatus = Прокси.ChangeDocumentStatus(Запрос.Name, Запрос.Password, Запрос.TrackingId, Запрос.Status);
    КодОшибкиStatus = РезультатStatus.Res;
    Если КодОшибкиStatus <> «00000000» Тогда
    #Если Клиент Тогда
    Сообщить(«Код ошибки изменения статуса документа » + Элемент + » = » + КодОшибкиStatus);
    #КонецЕсли
    КонецЕсли;
    

    Показать

    + почитать Техническая спецификация EDIservice

    Reply
  6. vbelyaev

    Товарищи, меня чё-т в гугле забанили походу. Не могу найти как добавить сертификаты в cacert.pem.

    Пните в нужном направлении пожалуйста)))

    Reply
  7. Ulus

    Хорошая статья.

    Грамотно!

    Reply
  8. Ulus

    А мы взяли Адаптер 1С от коруса.

    начали внедрять.

    Но оказалось, что в серийном Адаптере нету документа «Приложение к уведомлению об отгрузки».

    Это новый документ, для поставщиков алкогольной продукции.

    И все … Корус нас завтраками кормит. В службу поддержки, вставайте в очереь и.т.п.

    Ужасный сервис у них.

    Сейчас пробую стрести с них формат файла этого электронного документа.

    Сделаем сами.

    P.s. кому интересно рекомендую Е-Ком. Четко, быстро, качественно. Не реклама. Просто работаем с несколькими провайдерами.

    Reply
  9. Pim

    Никто не делал обратную выгрузку (выгрузку подтверждений) на сайт?

    Reply
  10. Antonio

    (8) Ulus, позвольте спросить как организовывали обмен с несколькими провайдерами? Сейчас тоже оптимизируем затраты на провайдеров, и возникла необходимость использовать несколько провайдеров.

    Reply
  11. itcmsch71

    Спасибо Вам большое — статья очень помогла

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

    Если кому интересно — могу отдать…….

    Да, еще — установка сертификатов не понадобилась……

    Так работает

    Reply
  12. gfoods

    (11) itcmsch71,

    Было бы очень любопытно посмотреть Вашу работу. Сам готовлюсь уйти от *%#k8SjZc9Dxk#нутого Коруса, сил нет больше их поделками пользоваться.

    Если готовы показать, то пришлите ссылку в личку.

    Спасибо.

    Reply
  13. binex

    (11) itcmsch71, поясните пожалуйста, что значит «теперь работаем без участия операторов»?

    Какие сообщения внедрили?

    Reply
  14. АЛьФ

    На попытке сменить статус документа отваливается с ошибкой:

    Результат = ПолучитьСОАП().ChangeDocumentStatus(
    {W:…EDI.ERT(358)}: Client: Client:Incorrect number of parameters supplied for SOAP request HRESULT=0x80070057: Параметр задан неверно.
    — Client:Unspecified client error. HRESULT=0x80070057: Параметр задан неверно.
    

    Кто-нибудь сталкивался? Я чего-то недопонял или проблема на их стороне?

    Ой, чуть не забыл.

    Вызов такой:

     Результат = ПолучитьСОАП().ChangeDocumentStatus(
    Идентификатор,
    Пароль,
    тзДокументы.идДокумента,
    НовыйСтатусДокумента);
    
    Reply
  15. Pipapalamm

    (11) itcmsch71, Очень хотелось бы посмотреть на ваш обмен. Сам обдумываю уход от Корусовских «Адаптер+Коннектор».

    Буду очень благодарен за конфигурацию.

    Заранее спасибо!

    Reply
  16. Oleg_Anat

    (11) itcmsch71, очень заинтересовал сделанный вами обмен. Мы сейчас только начинаем работать с этой системой, вот обдумываю варианты передачи данных. Вариант корусовских адаптера и коннектора — теперь не рассматриваю — после прочтения отзывов в данной статье об их техподдержке и сервисе. Если можете поделится вашей разработкой — буду очень благодарен, заранее огромное спасибо!

    Reply
  17. serg1785

    (11) itcmsch71, заинтересовала ваша разработка — если есть возможность — поделитесь. Спасибо!

    Reply
  18. katanovich

    Добрый день!

    Выдает во втором варианте:

    КодОшибкиПодключения = 000000001.

    Логин/Пароль верные. Где можно посмотреть расшифровку ошибки?

    Reply
  19. asrgroup

    Неправильный пароль или логин. Логин надо брать из настроек ТТКонектора (обычно GLN + «ЕС»)

    Reply
  20. bsv69

    (11) itcmsch71, Добрый день. Если можно, вышлите обработку обмена. Спасибо.

    Reply
  21. bobo93

    (6) vbelyaev,

    Здравствуйте.

    Вы не розобрались с вопросом?

    «Товарищи, меня чё-т в гугле забанили походу. Не могу найти как добавить сертификаты в cacert.pem.

    Пните в нужном направлении пожалуйста)))»

    Reply
  22. кольщик

    А для 1с 7.7 есть какое-нибудь похожее описание??

    Reply
  23. Shooroopik111

    Ребят

    (11)

    Поделитесь?

    Reply
  24. taasha25

    (11) Здравствуйте! Буду благодарна, если поделитесь разработкой. Спасибо!

    Reply
  25. taasha25

    (11) Здравствуйте! Можете поделиться обработкой выгрузки Приложения к уведомлению об отгрузке? Заранее спасибо!

    Reply
  26. darkmessiahan

    Что за НайтиОбъектПоЗначениюСвойства?

    Reply

Leave a Comment

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