Agora decidindo a próxima etapa.
Por enquanto a opção que vejo é incluir diretamente minha classe na hbnfe.
Dependendo do aplicativo, é só usar a classe e mais nada da hbnfe.
Abaixo a consulta que faço de NFE, CTE e MDFE usando minha classe:
IF Substr( notas->Chave, 21, 2 ) == "55" // nfe
oSefaz := SefazClass():New()
cXmlRet := oSefaz:NfeConsulta( notas->Chave, NomeCertificado( notas->Certif ) )
ELSEIF Substr( notas->Chave, 21, 2 ) == "57" // cte
oSefaz := SefazClass():New()
oSefaz:cProjeto := "cte"
cXmlRet := oSefaz:CteConsulta( notas->Chave, NomeCertificado( notas->Certif ) )
ELSEIF Substr( notas->Chave, 21, 2 ) == "58" // mdfe
oSefaz := SefazClass():New()
oSefaz:cProjeto := "mdfe"
cXmlRet := oSefaz:MDFEConsulta( notas->Chave, NomeCertificado( notas->Certif ) )
ENDIF
Acho que qualquer tentativa de integração com a classe da hbnfe só vai complicar, então será melhor deixar separada.
Pra consultar a nota só precisa da chave e do certificado.
::cProjeto - pra identificar se é nfe, cte, mdfe
::cAmbiente - o default é produção. Só precisa alterar se for diferente
::cScan - idem, o default é normal
o método da consulta nfe:
METHOD NFEConsulta( cChave, cCertificado ) CLASS SefazClass
::cCertificado:= iif( cCertificado == NIL, ::cCertificado, cCertificado )
::cVersao := "2.01"
::cUf := Substr( cChave, 1, 2 )
::cServico := "http://www.portalfiscal.inf.br/nfe/wsdl/NfeConsulta2"
::cSoapAction := "http://www.portalfiscal.inf.br/nfe/wsdl/NfeConsulta2"
::cWebService := ::GetWebService( UfCodigo( ::cUf ), WSNFECONSULTAPROTOCOLO )
::cXmlDados := ""
::cXmlDados += [<consSitNFe versao="] + ::cVersao + [" xmlns="http://www.portalfiscal.inf.br/nfe">]
::cXmlDados += [<tpAmb>] + ::cAmbiente + [</tpAmb>]
::cXmlDados += [<xServ>CONSULTAR</xServ>]
::cXmlDados += [<chNFe>] + cChave + [</chNFe>]
::cXmlDados += [</consSitNFe>]
::XmlSoapPost()
RETURN ::cXmlRetorno
Explicando:
::cCertificado - se não for recebido certificado, usa o default
::cVersao - é a versão da consulta, não da NFE. Isso na hbnfe é confuso
::cUf - isso faz parte da chave da chave de acesso
::cServico - precisa pro SOAP, detalhe sobre como consultar
::cSoapAction - precisa pro SOAP - detalhe sobre como consultar
::cWebService - precisa pro SOAP - endereço do WebService de consulta
::cXmlDados - é o XML no formato do governo, gerado aqui.
A partir daÃ, faz a consulta usando ::cXmlSoapPost() e o retorno fica em ::cXmlRetorno
É igual pra tudo.
METHOD XmlSoapPost() CLASS SefazClass
::XmlSoapEnvelope()
::MicrosoftXmlSoapPost()
RETURN NIL
Como eu disse antes, precisa do envelope ( ::XmlSoapEvenlope() ) pra comunicação SOAP Microsoft ).
Aqui chama a rotina de fazer o envelope, e depois se comunica usando ::MicrosoftXmlSoapPost()
Envelope pra tudo, usando o que foi configurado em NfeConsulta().
Note que o XML da consulta é inserido no meio dele:
METHOD XmlSoapEnvelope() CLASS SefazClass
::cXmlSoap := ""
::cXmlSoap += [<?xml version="1.0" encoding="UTF-8"?>]
::cXmlSoap += [<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ]
::cXmlSoap += [xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">]
::cXmlSoap += [<soap12:Header>]
::cXmlSoap += [<] + ::cProjeto + [CabecMsg xmlns="] + ::cServico + [">]
::cXmlSoap += [<cUF>] + ::cUf + [</cUF>]
::cXmlSoap += [<versaoDados>] + ::cVersao + [</versaoDados>]
::cXmlSoap += [</] + ::cProjeto + [CabecMsg>]
::cXmlSoap += [</soap12:Header>]
::cXmlSoap += [<soap12:Body>]
::cXmlSoap += [<] + ::cProjeto + [DadosMsg xmlns="] + ::cServico + [">]
::cXmlSoap += ::cXmlDados
::cXmlSoap += [</] + ::cProjeto + [DadosMsg>]
::cXmlSoap += [</soap12:Body>]
::cXmlSoap += [</soap12:Envelope>]
RETURN ::cXmlSoap
E finalmente a comunicação com a sefaz pra tudo usando microsoft XML:
METHOD MicrosoftXmlSoapPost() CLASS SefazClass
LOCAL oServer, nCont, cRetorno := "*ERRO*"
BEGIN SEQUENCE WITH { |e| Break(e) }
oServer := win_OleCreateObject( "MSXML2.ServerXMLHTTP")
IF ::cCertificado != NIL
oServer:setOption( 3, "CURRENT_USER\MY\" + ::cCertificado )
ENDIF
oServer:Open( "POST", ::cWebService, .F. )
oServer:SetRequestHeader( "SOAPAction", ::cSoapAction )
oServer:SetRequestHeader( "Content-Type", "application/soap+xml; charset=utf-8" )
oServer:Send( ::cXmlSoap )
oServer:WaitForResponse( 500 )
cRetorno := oServer:ResponseBody
ENDSEQUENCE
IF ValType( cRetorno ) == "C"
::cXmlRetorno := cRetorno
ELSEIF cRetorno == NIL
::cXmlRetorno := "*ERRO*"
ELSE
::cXmlRetorno := ""
FOR nCont = 1 TO Len( cRetorno )
::cXmlRetorno += Chr( cRetorno[ nCont ] )
NEXT
ENDIF
RETURN NIL
Isto confunde muito, precisa ou não precisa no XML?
[<?xml version="1.0" encoding="UTF-8"?>]
Na prática isso não faz parte do XML, mas indica a "codepage" do arquivo.
Para o projeto, ao colocar no inÃcio do envelope já vale pra tudo que tem dentro.
Talvez o problema de muitas assinaturas seja porque o pessoal tem mania de incluir isso no XML mesmo quando não é UTF8.
Então... por enquanto não vejo vantagem em embutir isto dentro da classe hbnfe, porque pode complicar.
Mas dá pra ajustar a hbnfe pra usar a classe.
Se considerar todas as comunicações que tem dentro da hbnfe, no minimo vai eliminar todas as montagens dos XMLs de SOAP.
O método acima de consulta, elimina a montagem do XML de consulta.
E os webservices na classe, eliminam os webservices da hbnfe.
Não comparei com a NFE 3.10, mas acho que não houve alteração na consulta.
Uma opção seria criar a nova como NfeConsulta310() até que fique por definitivo, caso altere.
Ou seja, a classe pra pronta pra os projetos atuais, e para os próximos.
Ao integrar com a hbnfe vou fazer gradativo, pra comparar/acrescentar webservices/métodos que faltam, e pra hbnfe continuar funcionando como sempre foi.
Acredito que, por mais que façam diferente da hbnfe nos aplicativos, pelo menos essa classe vão usar sem alterações.
Aqui uso essa classe, e os temporários deixo no MySql do meu servidor.
O cliente faz lá, e qualquer coisa vejo aqui no meu servidor se deu algo errado.