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.
