Página 1 de 4

Remodelar hbnfe pra aceitar NFE, CTE, MDFE, e tudo mais

Enviado: 08 Jun 2013 18:13
por JoséQuintas
Volto a sugerir a remodelagem de toda hbnfe.
Eu não sei como criar um projeto no SF ou GIT, mas mesmo que soubesse, não sei se teria paciência com usuários que nem sequer olham manuais.
Pela minha pouca experiência, vi que daria pra fazer um bom trabalho mexendo nas funções base de tudo.
Infelizmente, até hoje, meus clientes continuam usando o emissor do governo.
Até cheguei a fazer minhas próprias rotinas, rodei durante um dia inteiro pra tudo, e tudo funcionou perfeitamente.
Como o principal/maior cliente não insistiu nisso, eu também não insisti, mas devo voltar a fazê-lo.

Inicialmente tinha feito funções pra cada coisa, mas depois transformei em uma classe.
Não cheguei a fazer classe separada pra CTE e outros, porque ficaram tão simples, que pareceu exagero separar, e também porque só cheguei a fazer completo pra NFE.

Como contribuição, vou postar o que tenho pronto, mas não testado depois que virou classe.
Mas antes, tentar explicar como tudo funciona.

Remodelar hbnfe pra aceitar NFE, CTE, MDFE, e tudo mais

Enviado: 08 Jun 2013 18:17
por JoséQuintas
Arquivo XML:
É um arquivo txt, portanto gerado igual txt.
Acho mais interessante gerar em uma variável, e SE PRECISAR, salvar em disco usando hb_MemoWrit()

XmlDocumento := o XML do documento em si
É o documento em si, em formato XML.
Isso tem no manual de usuário de cada documento, seria NFE, CTE, MDFE, NFSe
Como é o sistema que gera o XML, alterou layout altera no sistema.

Assinatura
O manual de usuário indica o que precisa ou não de assinatura.
A CAPICOM é a API para assinatura do Windows.
O XML 5.0 é o único que permite trabalhar com assinatura, e é o único que não faz parte do Windows, apenas do Office.
A assinatura não altera nunca. Funciona do mesmo jeito desde que começou a estória da NFE e vai ser igual sempre.

XmlEnvio := TextoInicial + XmlDocumento + TextoFinal
É o pacote que envolve o documento pra envio, que resulta num XML com tudo.
Por exemplo, o lote de notas fiscais.
Também tem no manual de usuário.
Cada tipo de operação tem seu XMLEnvio.
Isto é praticamente automático: Acessando o webservice é apresentado um manual resumido com exemplo.

XmlEnvelope := TextoInicial + XmlEnvio + TextoFinal
É um conjunto padrão de comunicação com SOAP, que resulta num XML com tudo.
Isto é praticamente automático: acessando o webservice é apresentado um manual resumido com exemplo.
O XMLEnvelope sempre foi o mesmo em cada projeto. Tem pro projeto NFE, CTE, MDFE

SOAP: Envio e retorno
É a comunicação. O SOAP é um componente instalável no Windows, mas precisa do XML 5.0 e CAPICOM pra quando usa certificado.
Ele simplesmente envia o XMLEnvelope e recebe uma resposta.

Validação de XML e schemmas:
Se o XML já for gerado de acordo com o manual, não precisa mexer com validação e schemmas.
Mas dá pra validar com o pacote XML da Microsoft..

Todas estas etapas não precisam de arquivo.
É até estranho o manual da Fazenda indicar nomes padrão pra arquivos, se em nenhum momento é utilizado nome de arquivo.

Entendido isso, tudo fica mais fácil.
Qualquer mudança de layout, é só alterar XmlDocumento, necessário seja qual for a biblioteca/componente utilizado.
Em webservices novos, só alterar XmlEnvio
Em projetos novos, como o MDFe, teremos um novo XMLEnvelope, novos XMLEnvio, novos XML, e ajuste em rotina de assinatura.
A mesma coisa pra NFSe.

Importante:
No meu caso me baseei em Microsfoft CAPICOM, Microsoft XML 5.0, e Harbour 3.2
Não faço idéia o que seria diferente com libcurl e xHarbour

Remodelar hbnfe pra aceitar NFE, CTE, MDFE, e tudo mais

Enviado: 08 Jun 2013 18:27
por JoséQuintas
Identifiquei esta parte como variável, que coloquei na classe.
A variável NfeCteMdfe era só NfeCte. Talvez seja hora de pensar em outro nome, antes que ela fique com um nome exgerado.

Código: Selecionar todos

CREATE CLASS Sefaz
   DATA Ambiente     INIT "1"
   DATA Scan         INIT "N"
   DATA UF           INIT "SP"
   DATA Versao       INIT ""
   DATA Servico      INIT ""
   DATA SoapAction   INIT ""
   DATA UrlWs        INIT ""
   DATA Certificado  INIT ""
   DATA XmlDados     INIT ""
   DATA XmlSoap      INIT ""
   DATA XmlRetorno   INIT "*ERRO*"
   DATA NfeCteMdfe   INIT "nfe"

Remodelar hbnfe pra aceitar NFE, CTE, MDFE, e tudo mais

Enviado: 08 Jun 2013 18:28
por JoséQuintas
O método de comunicação SOAP.
É o mesmo pra tudo.

Código: Selecionar todos

METHOD MicrosoftXmlSoapPost() CLASS Sefaz
   LOCAL oServer, nCont, cRetorno := "*ERRO*"
   BEGIN SEQUENCE WITH { |e| Break(e) }
      oServer := win_OleCreateObject( "MSXML2.ServerXMLHTTP")
      IF ::Certificado != NIL
         oServer:setOption( 3, "CURRENT_USER\MY\" + ::Certificado )
      ENDIF   
      oServer:Open( "POST", ::UrlWS, .F. )
      oServer:SetRequestHeader( "SOAPAction", ::SoapAction )
      oServer:SetRequestHeader( "Content-Type", "application/soap+xml; charset=utf-8" )
      oServer:Send( ::XmlSoap )
      DO WHILE oServer:ReadyState <> 4
         MilliSec(500)
      ENDDO
      cRetorno := oServer:ResponseBody
   ENDSEQUENCE
   IF ValType( cRetorno ) == "C"
      ::XmlRetorno := cRetorno
   ELSEIF cRetorno == NIL
      ::XmlRetorno := "*ERRO*"
   ELSE   
      ::XmlRetorno := ""
      FOR nCont = 1 TO Len( cRetorno )
         ::XmlRetorno += Chr( cRetorno[ nCont ] )
      NEXT
   ENDIF   
   // IF .NOT. "<cStat>" $ cRetorno
   //   cRetorno := "<cStat>ERRO NO RETORNO</cStat>" + cRetorno
   // ENDIF
   RETURN NIL

Remodelar hbnfe pra aceitar NFE, CTE, MDFE, e tudo mais

Enviado: 08 Jun 2013 18:31
por JoséQuintas
O método do envelope.
Só altera uma parte mínima em cada projeto.
Tenho usado apenas a parte de consultar NFE, CTE e cadastro, portanto, dois projetos diferentes: nfe e cte
Só acrescentei o mdfe depois de ver um post aqui.

Código: Selecionar todos

METHOD XmlSoapPost() CLASS Sefaz
   ::XmlSoap := ""
   ::XmlSoap += [<?xml version="1.0" encoding="UTF-8"?>]
   ::XmlSoap += [<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ] + ;
                [xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">]
   ::XmlSoap +=    [<soap12:Header>]
   ::XmlSoap +=       [<] + ::NfeCteMdfe + [CabecMsg xmlns="] + ::Servico + [">]
   ::XmlSoap +=          [<cUF>] + ::Uf + [</cUF>]
   ::XmlSoap +=          [<versaoDados>] + ::Versao + [</versaoDados>]
   ::XmlSoap +=       [</] + ::NfeCteMdfe + [CabecMsg>]
   ::XmlSoap +=    [</soap12:Header>]
   ::XmlSoap +=    [<soap12:Body>]
   ::XmlSoap +=       [<] + ::NfeCteMdfe + [DadosMsg xmlns="] + ::Servico + [">]
   ::XmlSoap += ::XmlDados
   ::XmlSoap +=    [</] + ::NfeCteMdfe + [DadosMsg>]
   ::XmlSoap +=    [</soap12:Body>]
   ::XmlSoap += [</soap12:Envelope>]
   ::MicrosoftXmlSoapPost()
   RETURN NIL

Remodelar hbnfe pra aceitar NFE, CTE, MDFE, e tudo mais

Enviado: 08 Jun 2013 18:41
por JoséQuintas
Com base nessas duas funções/métodos, já daria pra dar uma simplificada na hbnfe, pois tem isso repetido em tudo que é processo.
E essas duas funções/métodos atendem os projetos NFE, CTE e MDFE, o que facilita futuras expansões.

Lembrando:
Se não fosse a hbnfe eu não saberia como fazer assinatura, e sem assinatura todo resto não serve pra nada.
Pode ser que simplificando acabe tendo mais usuários ajudando, seja com novos webservices, novos eventos, ou até mesmo um novo projeto, como CTE, MDFE e NFSE.

Essas funções não utilizam arquivo temporário, mas nada impede que existam outras rotinas pra ler/gravar XML, ou converter TXT.

Remodelar hbnfe pra aceitar NFE, CTE, MDFE, e tudo mais

Enviado: 08 Jun 2013 18:43
por JoséQuintas
Um dos métodos mais simples, que não precisa assinatura: consultar nfe

Código: Selecionar todos

METHOD NfeConsulta( cChave, cCertificado ) CLASS Sefaz
   ::Certificado:= cCertificado
   ::Versao     := "2.01"
   ::Uf         := Substr( cChave, 1, 2 )
   ::Servico    := "http://www.portalfiscal.inf.br/nfe/wsdl/NfeConsulta2"
   ::SoapAction := "http://www.portalfiscal.inf.br/nfe/wsdl/NfeConsulta2"
   ::UrlWs      := ::GetUrlWs( UfCodigo( ::Uf ), "nfeconsultaprotocolo" )
   ::XmlDados   := ""
   ::XmlDados   += [<consSitNFe versao="] + ::Versao + [" xmlns="http://www.portalfiscal.inf.br/nfe">]
   ::XmlDados   += [<tpAmb>] + ::Ambiente + [</tpAmb>] 
   ::XmlDados   += [<xServ>CONSULTAR</xServ>]
   ::XmlDados   += [<chNFe>] + cChave + [</chNFe>]
   ::XmlDados   += [</consSitNFe>]
   ::XmlSoapPost()
   RETURN ::XmlRetorno


Remodelar hbnfe pra aceitar NFE, CTE, MDFE, e tudo mais

Enviado: 08 Jun 2013 18:45
por JoséQuintas
Outro método simples, de consultar CTE (aqui já é o outro projeto, mas a mesma classe)
Nessas horas que parece ser exagero criar classe separada pra CTE.

Código: Selecionar todos

METHOD CteConsulta( cChave, cCertificado ) CLASS Sefaz
   ::Certificado:= cCertificado
   ::Versao     := "1.04"
   ::Uf         := Substr( cChave, 1, 2 )
   ::Servico    := "http://www.portalfiscal.inf.br/cte/wsdl/CteConsulta"
   ::SoapAction := "http://www.portalfiscal.inf.br/cte/wsdl/CteConsulta/cteConsultaCT"
   ::UrlWs      := ::GetUrlWs( UfCodigo( ::Uf ), "cteconsultaprotocolo" )
   ::XmlDados   := ""
   ::XmlDados += [<consSitCTe versao="] + ::Versao + [" xmlns="http://www.portalfiscal.inf.br/cte">]
   ::XmlDados += [<tpAmb>] + ::Ambiente + [</tpAmb>] 
   ::XmlDados += [<xServ>CONSULTAR</xServ>]
   ::XmlDados += [<chCTe>] + cChave + [</chCTe>]
   ::XmlDados += [</consSitCTe>]
   ::XmlSoapPost()
   RETURN ::XmlRetorno   

Remodelar hbnfe pra aceitar NFE, CTE, MDFE, e tudo mais

Enviado: 08 Jun 2013 19:12
por JoséQuintas
Outro método, consultar cadastro:

Código: Selecionar todos

METHOD NfeCadastro( cUf, cCnpj, cCertificado ) CLASS Sefaz
   ::Certificado := cCertificado
   ::Versao      := "2.00"
   ::Uf          := UfCodigo( cUf )
   ::Servico     := "http://www.portalfiscal.inf.br/nfe/wsdl/CadConsultaCadastro2"
   ::SoapAction  := "http://www.portalfiscal.inf.br/nfe/wsdl/CadConsultaCadastro2"
   ::UrlWs       := ::GetUrlWs( cUf, "nfeconsultacadastro" )
   ::XmlDados    := ""
   ::XmlDados    += [<ConsCad versao="2.00" xmlns="http://www.portalfiscal.inf.br/nfe">]
   ::XmlDados    += [<infCons>]
   ::XmlDados    += [<xServ>CONS-CAD</xServ>]
   ::XmlDados    += [<UF>] + cUf + [</UF>]
   ::XmlDados    += [<CNPJ>] + cCNPJ + [</CNPJ>]
   ::XmlDados    += [</infCons>]
   ::XmlDados    += [</ConsCad>]
   IF .NOT. cUf $ "AM,RS" // UFs que dependem de estar cadastrado, ou sem webservice de consulta
      IF Empty( ::UrlWs )
         ::XmlRetorno := ""
      ELSE
         ::XmlSoapPost()
      ENDIF   
   ENDIF
   RETURN ::XmlRetorno   

Remodelar hbnfe pra aceitar NFE, CTE, MDFE, e tudo mais

Enviado: 08 Jun 2013 19:18
por JoséQuintas
Uma observação:
Até pensei em postar em contribuições ao invés daqui, mas se puder ser incorporado a hbnfe, melhor.

Ainda não sei se ficaria melhor classe separada pra nfe/cte/mdfe, uma apenas herdando a outra.
Talvez com tudo pronto ajude a definir.
Na dúvida, classes vazias herdando a principal deixaria compatível com separação futura.

Remodelar hbnfe pra aceitar NFE, CTE, MDFE, e tudo mais

Enviado: 08 Jun 2013 19:20
por JoséQuintas
Cancelamento de nota.
O cancelamento de nota nada mais é do que enviar o XML de cancelamento.

Código: Selecionar todos

METHOD NfeCancela( cUf, cXml, cCertificado ) CLASS Sefaz
   ::Certificado := cCertificado
   ::Versao      := "2.00"
   ::Uf          := UfCodigo( cUf )
   ::Servico     := "http://www.portalfiscal.inf.br/nfe/wsdl/NfeCancelamento2"
   ::SoapAction  := "http://www.portalfiscal.inf.br/nfe/wsdl/NfeCancelamento2/nfeCancelamentoNF2"
   ::UrlWs       := ::GetUrlWs( cUf, "nfecancelamento" )
   ::XmlDados    := cXml
   ::XmlSoapPost()
   RETURN ::XmlRetorno

Remodelar hbnfe pra aceitar NFE, CTE, MDFE, e tudo mais

Enviado: 08 Jun 2013 19:26
por JoséQuintas
Status do serviço de NFE:

Código: Selecionar todos

METHOD NfeStatus( cUf, cCertificado ) CLASS Sefaz
   ::Certificado := cCertificado
   ::Versao      := "2.00"
   ::Uf          := UfCodigo( cUf )
   ::Servico     := "http://www.portalfiscal.inf.br/nfe/wsdl/NfeStatusServico2"
   ::SoapAction  := "http://www.portalfiscal.inf.br/nfe/wsdl/NfeStatusServico2/nfeStatusServicoNF2"
   ::UrlWs       := ::GetUrlWs( cUf, "nfestatusservico" )
   ::XmlDados    := ""
   ::XmlDados := [<consStatServ versao="] + ::Versao + [" xmlns="http://www.portalfiscal.inf.br/nfe" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">]
   ::XmlDados +=    [<tpAmb>] + ::Ambiente + [</tpAmb>]
   ::XmlDados +=    [<cUF>] + ::Uf + [</cUF>]
   ::XmlDados +=    [<xServ>STATUS</xServ>]
   ::XmlDados += [</consStatServ>]
   ::XmlPost()
   RETURN ::XmlRetorno   

Remodelar hbnfe pra aceitar NFE, CTE, MDFE, e tudo mais

Enviado: 08 Jun 2013 19:28
por JoséQuintas
Status do serviço de CTE;

Código: Selecionar todos

METHOD CteStatus( cUf, cCertificado ) CLASS Sefaz
   ::Certificado := cCertificado
   ::Versao      := "1.04"
   ::Uf          := UfCodigo( cUf )
   ::Servico     := "http://www.portalfiscal.inf.br/cte/wsdl/CteStatusServico"
   ::SoapAction  := "http://www.portalfiscal.inf.br/cte/wsdl/CteStatusServico/cteStatusServicoCT"
   ::UrlWs       := ::GetUrlWs( cUf, "ctestatusservico" )
   ::XmlDados    := ""
   ::XmlDados := [<consStatServCte versao="]  + ::Versao + [" xmlns="http://www.portalfiscal.inf.br/cte" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >]
   ::XmlDados +=    [<tpAmb>] + ::Ambiente + [</tpAmb>]
   ::XmlDados +=    [<xServ>STATUS</xServ>]
   ::XmlDados += [</consStatServCte>]
   ::XmlPost()
   RETURN ::XmlRetorno   

Remodelar hbnfe pra aceitar NFE, CTE, MDFE, e tudo mais

Enviado: 08 Jun 2013 19:42
por JoséQuintas
A idéia é que tudo isso seja implementado nos fontes da hbnfe.
Se interessar, para o projeto hbnfe, posto o restante, que é só mais um pouco.
A função que retorna o nome do webservice é a maior de todas, porque contém toda a lista de webservices.
Mas criei como se fosse um arquivo texto, preparado pra caso se use um arquivo externo (xml).
A função é simples, a lista que é grande.
Um pequeno trecho:

Código: Selecionar todos

STATIC FUNCTION XmlUrls()
   LOCAL cXml := ""

   cXml += "<webservices>"
   cXml += "<AM>"
   cXml +=    "<1>"
   cXml +=       "<nferecepcao>https://nfe.sefaz.am.gov.br/services2/services/NfeRecepcao2</nferecepcao>"
   cXml +=       "<nferetrecepcao>https://nfe.sefaz.am.gov.br/services2/services/NfeRetRecepcao2</nferetrecepcao>"
   cXml +=       "<nfecancelamento>https://nfe.sefaz.am.gov.br/services2/services/NfeCancelamento2</nfecancelamento>"
...
<1> identificação de homologação/produção
<AM> UF do webservice ou SCAN ou SVAN
<nferecepcao> endereço

e a função que pega o endereço, conforme a Operação/UF/Serviço:

Código: Selecionar todos

METHOD GetUrlWs( cUf, cServico ) CLASS Sefaz
   LOCAL cTexto
   // SVAN: ES,MA,PA,PI,RN
   // SVRS: AC,AL,AM,AP,DF,MS,PB,RJ,RO,RR,SC,SE,TO
   // Autorizadores: AM,BA,CE,GO,MG,MS,MT,PE,PR,RN,RS,SP,SVAN,SVRS,SCAN
   IF ::Scan == "S"
      cTexto := XmlNode( XmlUrls(), "SCAN" )
   ELSE
      cTexto := XmlNode( XmlUrls(), cUf )
   ENDIF
   IF Empty(cTexto)
      IF cUf $ "AC,AL,AM,AP,DF,MS,PB,RJ,RO,RR,SC,SE,TO" // Sefaz Virtual RS
         cTexto := XmlNode( XmlUrls(), "SVRS" )
      ELSEIF cUf $ "ES,MA,PA,PI,RN" // Sefaz Virtual
         cTexto := XmlNode( XmlUrls(), "SVAN" )
      ENDIF   
   ENDIF
   cTexto := XmlNode( cTexto, ::Ambiente )
   cTexto := XmlNode( cTexto, cServico )
   RETURN cTexto
Talvez esta seja uma parte "não padrão", já que precisei identificar cada tipo de serviço.
Mas serviços novos, basta adicionar nessa lista.

Remodelar hbnfe pra aceitar NFE, CTE, MDFE, e tudo mais

Enviado: 09 Jun 2013 14:38
por sygecom
Olá José,
Toda ajuda ao projeto é bem vinda, se desejar podemos dar acesso ao projeto e você mesmo fazer as melhorias nos fontes, porém seria interessante manter a compatibilidade de uso por parte dos desenvolvedores que já utilizam o projeto ou, criar um exemplo demonstrando como deve usar com essas mudanças.