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.