Форум OlegON > Программы и оборудование для автоматизации торговли > ЕГАИС в опте и рознице

Поделитесь опытом парсинга XML из УТМ : ЕГАИС в опте и рознице

28.03.2024 22:46


27.08.2019 19:20
tarakan
 
Доброго времени суток.
Ребята поделитесь опытом парсинга XML : в частности интересует несколько моментов.
Например, на накладную есть квитанция и извещение

Первый
Код:
<?xml version="1.0" encoding="utf-8"?>
<ns:Documents xmlns:tc="http://fsrar.ru/WEGAIS/Ticket" xmlns:oref="http://fsrar.ru/WEGAIS/ClientRef" xmlns:ns="http://fsrar.ru/WEGAIS/WB_DOC_SINGLE_01" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="1.0">
	<ns:Owner>
		<ns:FSRAR_ID>3463047</ns:FSRAR_ID>
	</ns:Owner>
	<ns:Document>
		<ns:Ticket>
			<tc:TicketDate>2018-06-21T13:26:32.8394096</tc:TicketDate>
			<tc:Identity>deba12d2-fd62-4489-a509-13d5cf15bef1</tc:Identity>
			<tc:DocId>50C7B406-53CD-4186-ADCC-D8874F9067D4</tc:DocId>
			<tc:TransportId>2c6a11ec-efbb-43b4-8c63-8d3e650040e9</tc:TransportId>
			<tc:RegID>TTN-0208764811</tc:RegID>
			<tc:DocHash/>
			<tc:DocType>WayBillAct_v3</tc:DocType>
			<tc:OperationResult>
				<tc:OperationName>Confirm</tc:OperationName>
				<tc:OperationResult>Accepted</tc:OperationResult>
				<tc:OperationDate>2018-06-21T13:26:33.693</tc:OperationDate>
				<tc:OperationComment>Накладная №00000001056 от 20.06.2018 00:00:00 подтверждена</tc:OperationComment>
			</tc:OperationResult>
		</ns:Ticket>
	</ns:Document>
</ns:Documents>
второй
Код:
<?xml version="1.0" encoding="utf-8"?>
<ns:Documents xmlns:tc="http://fsrar.ru/WEGAIS/Ticket" xmlns:oref="http://fsrar.ru/WEGAIS/ClientRef" xmlns:ns="http://fsrar.ru/WEGAIS/WB_DOC_SINGLE_01" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="1.0">
	<ns:Owner>
		<ns:FSRAR_ID>3463047</ns:FSRAR_ID>
	</ns:Owner>
	<ns:Document>
		<ns:Ticket>
			<tc:TicketDate>2018-06-27T11:04:12.4946591</tc:TicketDate>
			<tc:Identity/>
			<tc:DocId>84D77A9D-0CBA-4BA8-BB5D-432DFD729923</tc:DocId>
			<tc:TransportId>5551fdef-9584-4b4a-99be-67f2bf2afba7</tc:TransportId>
			<tc:RegID>TTN-0203877908</tc:RegID>
			<tc:DocHash/>
			<tc:DocType>WayBillAct_v3</tc:DocType>
			<tc:Result>
				<tc:Conclusion>Accepted</tc:Conclusion>
				<tc:ConclusionDate>2018-06-27T11:04:12.4946591</tc:ConclusionDate>
				<tc:Comments>Документ успешно принят.</tc:Comments>
			</tc:Result>
		</ns:Ticket>
	</ns:Document>
</ns:Documents>
Разница только в выделенных частях. Кто как в своих софтах распределяет по типам Извещение или Квитанция.
28.08.2019 08:57
AndreyZh
 
Механизмы и используемые инструменты парсинга в разных языках программирования различаются. Кроме этого "парсинг" является малой частью процесса обработки входящего документа (тикета) ЕГАИС. Просто, как пример дам исходник модуля из "УС Лэнд:ЕГАИС", где логика довольно подробно документирована

Исходник анализа проведения акта списания в ЕГАИС в "УСЕга":
Код:
* ------------------------------------------------------------------------------
*   Контроль прохождения актов списания - обнаружение и анализ двух ticket
*   Один - ответ о принятии запроса, другой с подтверждением или отказом. Схема:
/*  
*   <?xml version="1.0" encoding="utf-8"?>
*   <ns:Documents xmlns:tc="http://fsrar.ru/WEGAIS/Ticket" xmlns:oref="http://fsrar.ru/WEGAIS/ClientRef" xmlns:ns="http://fsrar.ru/WEGAIS/WB_DOC_SINGLE_01" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="1.0">
*   <ns:Owner> <ns:FSRAR_ID>3463047</ns:FSRAR_ID> </ns:Owner>
*   <ns:Document>
*       <ns:Ticket>
*           <tc:TicketDate>2016-02-23T11:20:15.8764045</tc:TicketDate>
*           <tc:Identity>1_23.02.16_11:19:45</tc:Identity>
*           <tc:DocId>38AB13B1-8ADF-4239-A405-7C0C8BE3FA7E</tc:DocId>
*           <tc:TransportId>b9069248-0160-4322-8c91-3831a18dbaa4</tc:TransportId>
*           <tc:RegID>TEST-WOF-0000003221</tc:RegID>
*           <tc:DocHash />
*           <tc:DocType>ActWriteOffShop_v2</tc:DocType>
*           <tc:Result>
*               <tc:Conclusion>Accepted</tc:Conclusion>
*               <tc:ConclusionDate>2016-02-23T11:20:15.8764045</tc:ConclusionDate>
*               <tc:Comments>Документ успешно принят.</tc:Comments>
*           </tc:Result>
*       </ns:Ticket>
*   </ns:Document>
*   </ns:Documents>
*   2.
*   <?xml version="1.0" encoding="utf-8"?>
*   <ns:Documents xmlns:tc="http://fsrar.ru/WEGAIS/Ticket" xmlns:oref="http://fsrar.ru/WEGAIS/ClientRef" xmlns:ns="http://fsrar.ru/WEGAIS/WB_DOC_SINGLE_01" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="1.0">
*   <ns:Owner> <ns:FSRAR_ID>3463047</ns:FSRAR_ID> </ns:Owner>
*   <ns:Document>
*       <ns:Ticket>
*           <tc:TicketDate>2016-02-23T11:20:16.750006</tc:TicketDate>
*           <tc:Identity>1_23.02.16_11:19:45</tc:Identity>
*           <tc:DocId>38AB13B1-8ADF-4239-A405-7C0C8BE3FA7E</tc:DocId>
*           <tc:TransportId>b9069248-0160-4322-8c91-3831a18dbaa4</tc:TransportId>
*           <tc:RegID>TEST-WOF-0000003221</tc:RegID>
*           <tc:DocHash />
*           <tc:DocType>ActWriteOffShop_v2</tc:DocType>
*           <tc:OperationResult>
*               <tc:OperationName>Confirm</tc:OperationName>
*               <tc:OperationResult>Accepted</tc:OperationResult>
*               <tc:OperationDate>2016-02-23T11:19:28.8</tc:OperationDate>
*               <tc:OperationComment>Акт списания успешно проведен</tc:OperationComment>
*           </tc:OperationResult>
*       </ns:Ticket>
*   </ns:Document>
*   </ns:Documents>
*/
*** Проверяю состояние по текущему акту, а записи меняю по группе записей в таблице
*   Параметр lList TRUE - пакетный контроль - ограниченная диагностика
*   05.12.2017  В случае проведения изменяю статус акцизной марки в таблице пула минусованием
STAT PROC pCheckEgais( lList )
    LOCA aHost:={}, aTick:={}, nI:=0, nJ:=0, nLen:=0, nRec:=RecNo(), cDoc:=Alltrim(codDoc)
    LOCA lPrin:=FALSE, cPrDat:="", cPrComm:="", lOper:=FALSE, cOpDat:="", cOpComm:="", cRegId:=""
    LOCA dDat:=dat, cNumb:=number
    LOCA nb:=0, nL:=0, nRep:=78, cTxt:="", cXml:="", cZ:="", cZap:="", oUrl, oDoc, oNode
    DEFAULT lList TO FALSE

    //  Анализирую только отправленные акты списания - статус > 0
    IF active == 0
        IF !lList THEN ErrMess("Запрашиваем реакцию ЕГАИС только по отправленным в ЕГАИС актам на списание!",cError)
        RETU
    ENDI

    //  Запрашиваем у УТМ все входящие запросы при индивидуальном контроле прохождения
    IF !lList
        pZapOutUTM( cdData+"org_lout.xml" )
        IF !File( cdData+"org_lout.xml" ) THEN ErrMess("Вообще нет ответов от ЕГАИС! Проверьте работу УТМ!",cError); RETU
        IF Val( Left(Dtos(Date())+Time(),10) ) - Val( Left( Dtos(FileDate(cdData+"org_lout.xml")) + FileTime(cdData+"org_lout.xml"),10) )  > 1
            ErrMess("Файл ответов от ЕГАИС очень старый! Проверьте пожалуйста работу УТМ!",cError); RETU
        ENDI
    ENDI

    IF active > 1               //  Отправили и уже уяснили реакцию ЕГАИС. Далее в зависимости от ответа.
        IF Empty(codDoc)
            IF !lList THEN ErrMess("Акт на списание полностью проведен и сопутствующие запросы удалены их системы! Все работы по нему завершены, остатки понижены...",cHelpc)
        ELSE                    //  Предлагаю дополнительные работы
            IF active = 2       //  Полное принятие - предлагаю каскадное удаление
                IF !lList
                    IF Fyn("Акт подтвержден! Желаете удалить каскад запросов?",cHelpc) THEN pKaskad(lList)
                ENDI
            ELSE
                IF Fyn("Отказ по акту "+Left(number,5)+" от "+Dtoc(dat)+"! Отменить отправку и удалить каскад?",cError)
                    pKaskad(lList)   //  Первый по причине перепроверки статуса
                    FilLock(-1)
                    REPL FOR Alltrim(codDoc) == cDoc active WITH 0, codDoc WITH "", codReg WITH ""
                    UN_COMM
                    DbGoTo(nRec)
                ENDI
            ENDI
        ENDI
        RETU
    ENDI 

    //***   Производим анализ ответов от ЕГАИС и анализ пришедших тикетов, при их наличии
    
    //  Выборка и анализ наличия входящих запросов по идентификатору запроса акта
    aHost   := aHostReplyId( Alltrim(cDoc), cdData+"org_lout.xml" )
    IF Len( aHost ) <= 0
        IF !lList THEN ErrMess("Пока вообще нет ответов от ЕГАИС по отправленному акту на списание!",cError)
        RETU
    ENDI

    //  Определяю комплект ответов - тикетов от ЕГАИС
    FOR nI := 1 TO Len(aHost)
        IF At("TICKET",Upper(aHost[nI])) > 0 THEN aadd(aTick,nI)
    NEXT nI
    DO CASE
    CASE Len(aTick) <= 0
        IF !lList THEN ErrMess("Нет Ticket от ЕГАИС! Может быть его уже удалили или пока еще не поступил ответ от ЕГАИС?",cError)
        RETU
    CASE Len(aTick) = 1
        IF lgRejTick( aHost[aTick[1]] )     //  enewfunc
            IF !lList THEN ErrMess("Пришел Ticket что ЕГАИС отверг наш акт по причине неверного оформления или других проблем! Анулируйте акт, измените его и отправьте заново!",cError)
        ELSE                                //  Выхожу из обработки
            IF !lList THEN ErrMess("Пришел единственный Ticket, что ЕГАИС получил наш акт, но пока ответа о принятии его нет! Перепроверьте состояние прохождения акта через пару минут!",cError)
            RETU
        ENDI
    ENDC

    /*  Делаю бесконечный запросы на получение файлов тикета и их парсинг для информации для отчета
        Ticket приходят разных форматов, с разными тегами, но суть их единообразна:
        Делаю двойную проверку и парсинг */
    FOR nI := 1 TO Len(aTick)
        cXml    := cdData+"TicketOnClearActShop.XML"
        cZ      := cgpCurl+[curl -X GET ]+aHost[ aTick[nI] ]+[ > ]+cXml
        DO WHIL TRUE
            RUN (cZ)
            IF File( cXml) THEN EXIT
            Inkey(0.5)
        ENDD
        Inkey(1)

        //  Парсинг конкретного тикета и получение информации от ЕГАИС
        oDoc    := TXmlDocument():new()
        oDoc:read( HB_UTF8TOSTR(MemoRead(cXml)) )

        //  Обработка тикета о приеме акта системой ЕГАИС
        oUrl	:= oDoc:findFirst( "tc:Conclusion" )            //  Принято или нет системой ЕГАИС
        IF oUrl <> NIL THEN lPrin := (Upper(Alltrim(oUrl:cData))=="ACCEPTED")

        oUrl	:= oDoc:findFirst( "tc:Comments" )              //  Текст сообщения от ЕГАИС
        IF oUrl <> NIL THEN cPrComm := Alltrim(oUrl:cData)

        oUrl	:= oDoc:findFirst( "tc:ConclusionDate" )        //  Дата и время обработки
        IF oUrl <> NIL THEN cPrDat := Alltrim(oUrl:cData)

        oUrl	:= oDoc:findFirst( "tc:RegID" )                 //  Для информации - код регистрации акта в ЕГАИС
        IF oUrl <> NIL
            IF ValType(oUrl:cData) <> "U"              //  Может приходить неопределенный тэг
                cRegId  := Alltrim(oUrl:cData)
            ENDI
        ENDI

        //  Обработка второго тикета о проведении операции по акту
        oUrl	:= oDoc:findFirst( "tc:OperationResult" )       //  Это же и контейнер... Делаю следующий поиск
        IF oUrl <> NIL
            oUrl	:= oDoc:findNext( "tc:OperationResult" )
            IF oUrl <> NIL THEN lOper := ( Upper(Alltrim(oUrl:cData))=="ACCEPTED" )
        ENDI

        oUrl	:= oDoc:findFirst( "tc:OperationDate" )        //  Дата и время обработки
        IF oUrl <> NIL THEN cOpDat := Alltrim(oUrl:cData)

        oUrl	:= oDoc:findFirst( "tc:OperationComment" )      //  Комментарий
        IF oUrl <> NIL THEN cOpComm := Alltrim(oUrl:cData)
    NEXT nI

    IF !lList
    
        //  Вывожу отчет в текстовый файл - просто информация общего характера
        cTxt	:= zTemp(cdTemp,"TXT",32)

        O____A
        ? _ZDT+" Акт на списание  "+IF(lPrin .AND. lOper,"ПРИНЯТ","ОТКАЗАН")+" системой ЕГАИС"
        ? "Код регистрации: "+cRegId+CRLF

        nb  := 1
        ? "Дата ответа "+cPrDat
        nL  := Int( Len(cPrComm)/78 ) + 1
        FOR nI := 1 TO nL DO ? fexcl_str(cPrComm,@nb,nRep)
        ? CRLF+CRLF

        nb  := 1
        ? "Дата ответа "+cOpDat
        nL  := Int( Len(cOpComm)/78 ) + 1
        FOR nI := 1 TO nL DO ? fexcl_str(cOpComm,@nb,nRep)
        ? Chr(12)
        C____A
        pFileOutEga( cTxt )
        fErase( cTxt )
    ENDI

    //  Изменяю статус прохождения акта через ЕГАИС
    FilLock(-1)
    REPL FOR Alltrim(codDoc) == cDoc active WITH IF(lOper,2,3), codReg WITH IF(!Empty(cRegId),cRegId,codReg)
    UN_COMM
    
    //  Меняю статус акцизных марок, если они привязаны к списанию
    IF lOper THEN pgWrClPull(dDat,cNumb,TRUE)
    DbGoTo(nRec)
    RETU
28.08.2019 10:41
tarakan
 
Спасибо за ответ.
Судя по коду начиная с этих строк
Цитата:
// Парсинг конкретного тикета и получение информации от ЕГАИС
oDoc := TXmlDocument():new()
Вы также грузите XML и разбираете по его содержимому.
Просто хотелось как-то упорядочить у себя в БД все XML-ки.
Типа
1 - Waybill (накладная)
2 - Ticket (квитанция)
3 - Ticket_ляляля (извещение)
....
Думал, что может я чего-то недопонимаю.
28.08.2019 13:36
MWWRuza
 
Я у себя не разбираю, парсю все тикеты относящиеся к текущему документу, потом специальной функцией собираю все тикеты по документу в одну таблицу, и по итогам колонок(колонки - разные поля тикетов, у разных тикетов могут быть данные как в одних, так и в разных колонках, так, как состав полей может быть разным, в зависимости от того, что это за тикет), смотрю статус документа.
Может немного коряво объяснил, но это работает.
28.08.2019 13:51
tarakan
 
Цитата:
MWWRuza Я у себя не разбираю, парсю все тикеты относящиеся к текущему документу, потом специальной функцией собираю все тикеты по документу в одну таблицу, и по итогам колонок(колонки - разные поля тикетов, у разных тикетов могут быть данные как в одних, так и в разных колонках, так, как состав полей может быть разным, в зависимости от того, что это за тикет), смотрю статус документа.
Может немного коряво объяснил, но это работает.
Да вроде все понятно - тоже интересная идея.
Часовой пояс GMT +3, время: 22:46.

Форум на базе vBulletin®
Copyright © Jelsoft Enterprises Ltd.
В случае заимствования информации гипертекстовая индексируемая ссылка на Форум обязательна.