No artigo de hoje, vamos demonstrar em como acessar a API do Espião NFE e baixar os XMLs das Notas usando AdvPL / TLPP.
Para quem já precisou baixar informações de notas fiscais, e utiliza o serviço do Espião NFE, saiba que eles tem uma API, onde é possível fazermos o consumo direto via código fonte usando recursos das linguagens AdvPL ou TLPP.
Basicamente a lógica é a seguinte:
- Contratar um plano de serviços do Espião NFE utilizando um CNPJ (na customização, vamos usar o parâmetro MV_X_BXCNP)
- Pegar as informações com eles de Cloud Token e de User Token (na customização, vamos acionar através dos parâmetros MV_X_BXCLO e MV_X_BXUSR respectivamente)
- É passado as informações dos Tokens no Header da Requisição
- Na URL da requisição é passado o CNPJ e a data em questão, algo similar a https://api.espiaonfe.com.br/v1-cloud/consulta/periodo/xmls?dataInicial=2024-11-13&dataFinal=2024-11-13&modelo=55&cnpjCpf=[XXXXXXXXXXXXXX]&tipoPeriodo=E
- É efetuado o consumo da API, e se houver dados, faz um For percorrendo os XMLs
- Captura a String, usa a Decode64() para decodificar, em seguida usa a GzStrDecomp para descompatar do GZIP
- Baixa o XML em questão dentro de uma pasta da Protheus Data
- Se teve paginação, repete os passos 5 e 6
- Ai depois, basta usarmos os XMLs que foram baixados
Um ponto de atenção que percebemos, não sei dizer se foi algo atípico do cliente em que atendemos, mas a cada caractere do XML, vinha um caractere inválido (char 0 da tabela ASCII), e mesmo tentando tratar com StrTran, FWNoAccent, RemCharEsp, OemToAnsi / AnsiToOem, entre outras… O resultado permanecia o mesmo (com caracteres invisíveis entre os outros). Então foi feito um For … Next, e a cada posição par, irá desconsiderar o caractere. Abaixo um print do resultado do XML antes de tratar (através do codebeautify.org):
E abaixo o código fonte desenvolvido conforme a lógica descrita acima:
//Bibliotecas #Include "Totvs.ch" /*/{Protheus.doc} User Function zBxEspiao Integração com o XML Espião @author Atilio @since 14/11/2024 @version 1.0 @type function @see https://api.espiaonfe.com.br/swagger/index.html /*/ User Function zBxEspiao() Local aArea := FWGetArea() Local cResultado := '' Local cErro := '' Local jListaDados := Nil Local nRegAtu := 0 Local jXMLAtual := Nil //Arquivo XML Local cPastaTemp := "\x_xmls\" Local cArquiXML := "" Local cXMLDecomp := "" Local cXMLConteu := "" //Data do Filtro Local dData := Date() Local cData := dToS(dData) Local cDataFormat := Left(cData, 4) + "-" + StrZero(Month(dData), 2) + "-" + StrZero(Day(dData), 2) //Informações usadas na integração Local cProxPagina := "" Local cPathRequi := "" Local aHeader := {} Local cEspCloud := Alltrim(SuperGetMV("MV_X_BXCLO", .F., "")) Local cUsrToken := Alltrim(SuperGetMV("MV_X_BXUSR", .F., "")) Local cCnpjCpf := Alltrim(SuperGetMV("MV_X_BXCNP", .F., "")) Local oRestClient := FWRest():New('https://api.espiaonfe.com.br/v1-cloud/') Local cInitDate := cDataFormat //"2024-11-13" Local cFinalDate := cDataFormat //"2024-11-13" Local cModelo := "55" // 55 (NF-e), 65 (NFC-e), 57 (CT-e), 67 (CT-e OS), 59 (Sat), 41 (NFS-e Nacional) Local cTpPer := "E" // E (período de emissão do XML) ou I (período de inclusão no Espião Cloud) //Se a pasta não existir, cria ela If ! ExistDir(cPastaTemp) MakeDir(cPastaTemp) EndIf //Monta o cabeçalho da Requisição aAdd(aHeader, 'User-Agent: Mozilla/4.0 (compatible; Protheus 12.1.x)') aAdd(aHeader, 'Content-Type: application/json; charset=utf-8') aAdd(aHeader, 'esp-cloud-token: ' + cEspCloud) aAdd(aHeader, 'user-token: ' + cUsrToken) //Faz um laço percorrendo todas as NFs do dia While .T. //Monta a url da path cPathRequi := 'consulta/periodo/xmls?dataInicial=' + cInitDate + '&dataFinal=' + cFinalDate + '&modelo=' + cModelo + '&cnpjCpf=' + cCnpjCpf + '&tipoPeriodo=' + cTpPer If ! Empty(cProxPagina) .And. cProxPagina != "-1" cPathRequi += '&codigoProximaPagina=' + cProxPagina EndIf cProxPagina := "" //Define a path e Executa a Requisição oRestClient:setPath(cPathRequi) If oRestClient:Get(aHeader) //Captura o resultado da requisição e converte para Json cResultado := oRestClient:GetResult() jListaDados := JsonObject():New() jListaDados:FromJson(cResultado) //Busca a próxima página cProxPagina := jListaDados:GetJsonObject("codigoProximaPagina") //Busca os XMLs aXMLs := jListaDados:GetJsonObject("dados") For nRegAtu := 1 To Len(aXMLs) jXMLAtual := aXMLs[nRegAtu] //Se tiver autorizada If Upper(jXMLAtual:GetJsonObject("situacao")) == "AUTORIZADA" //Define o nome do arquivo cArquiXML := cPastaTemp + "arquivo_" + cData + "_seq_" + StrZero(nRegAtu, 5) + ".xml" //Se o arquivo já existir, apaga If File(cArquiXML) FErase(cArquiXML) EndIf //Pega o conteúdo vindo do WS e aplica a Base64 cXMLDecomp := "" cXMLConteu := jXMLAtual:GetJsonObject("xml") cXMLConteu := Decode64(cXMLConteu) //Aplica para descomprimir usando GZip GzStrDecomp(cXMLConteu, Len(cXMLConteu), @cXMLDecomp) //Como vem com caracteres 'irreconhecíveis', vamos tratar esse cenário, exemplo: // '< n f e P r o c v e r s a o = " 4 . 0 0 " x m l n s = ' ===> '<nfeProc versao="4.00" xmlns=' //Teve que ser feito com SubStr, pois as posições vazias estavam vindo com caractere irreconhecível (Chr(0)) /* cXMLDecomp := StrTran(cXMLDecomp, " ", "|||-|||") cXMLDecomp := StrTran(cXMLDecomp, " ", "") cXMLDecomp := StrTran(cXMLDecomp, "|||-|||", " ") */ cXMLDecomp := fFormataStr(cXMLDecomp) //Grava dentro da Protheus Data MemoWrite(cArquiXML, cXMLDecomp) EndIf Next QOut('Sucesso na integração, resultado é: ' + CRLF + cResultado) Else cErro := oRestClient:GetLastError() QOut('Houve um erro na integração: ' + CRLF + cErro) EndIf //Se não tiver a próxima página If Empty(cProxPagina) .Or. cProxPagina == "-1" Exit EndIf EndDo FWRestArea(aArea) Return Nil Static Function fFormataStr(cString) Local nAtual := 0 Local lPar := .F. Local cNovaStr := "" //Percorre a string For nAtual := 1 To Len(cString) //Encontra se é par lPar := nAtual % 2 == 0 //Se não for par, incrementa a string If ! lPar cNovaStr += SubStr(cString, nAtual, 1) EndIf Next Return cNovaStr
Referências:
- Site do Espião NFE: https://www.espiaonfe.com.br/
- Swagger com a documentação das APIs do Espião NFE: https://api.espiaonfe.com.br/swagger/index.html
- Site para testes de converter a string para gerar o XML: https://codebeautify.org/gzip-decompress-online
Bom pessoal, por hoje é só.
Abraços e até a próxima.