diff --git a/pytrustnfe/Servidores.py b/pytrustnfe/Servidores.py index 637641b..95f6e17 100644 --- a/pytrustnfe/Servidores.py +++ b/pytrustnfe/Servidores.py @@ -52,9 +52,10 @@ SIGLA_ESTADO = { } -def localizar_url(servico, estado): +def localizar_url(servico, estado, ambiente=2): sigla = SIGLA_ESTADO[estado] - return ESTADO_WS[sigla]['servidor'], ESTADO_WS[sigla][servico] + return ESTADO_WS[sigla][ambiente]['servidor'], + ESTADO_WS[sigla][ambiente][servico] METODO_WS = { diff --git a/pytrustnfe/client.py b/pytrustnfe/client.py index 0867982..dbd58e8 100644 --- a/pytrustnfe/client.py +++ b/pytrustnfe/client.py @@ -43,11 +43,11 @@ class HttpClient(object): def _headers(self, action): return { u'Content-type': u'application/soap+xml; charset=utf-8; action="http://www.portalfiscal.inf.br/nfe/wsdl/%s' % action, - u'Accept': u'application/soap+xml; charset=utf-8' + u'Accept': u'application/soap+xml; charset=utf-8' } def post_soap(self, xml_soap, action): - res = requests.post(self.url, data=xml_soap, + res = requests.post(self.url, data=xml_soap, cert=(self.cert_path, self.key_path), verify=False, headers=self._headers(action)) - return res.text + return res.text diff --git a/pytrustnfe/nfe/__init__.py b/pytrustnfe/nfe/__init__.py index 3857e89..2348d1a 100644 --- a/pytrustnfe/nfe/__init__.py +++ b/pytrustnfe/nfe/__init__.py @@ -10,6 +10,7 @@ from pytrustnfe.xml import render_xml from pytrustnfe.utils import CabecalhoSoap from pytrustnfe.utils import gerar_chave, ChaveNFe from pytrustnfe.Servidores import localizar_url +import re def _build_header(**kwargs): @@ -30,8 +31,10 @@ def _generate_nfe_id(**kwargs): 'tipo': item['infNFe']['ide']['tpEmis'], 'codigo': item['infNFe']['ide']['cNF'], } - chave = ChaveNFe(**vals) - item['infNFe']['Id'] = gerar_chave(chave, 'NFe') + chave_nfe = ChaveNFe(**vals) + chave_nfe = gerar_chave(chave_nfe, 'NFe') + item['infNFe']['Id'] = chave_nfe + item['infNFe']['ide']['cDV'] = chave_nfe[len(chave_nfe) - 1:] def _send(certificado, method, sign, **kwargs): @@ -40,15 +43,27 @@ def _send(certificado, method, sign, **kwargs): xml_send = render_xml(path, '%s.xml' % method, **kwargs) if sign: + xml_send = ']>' + \ + xml_send + xml_send = xml_send.replace('\n', '') pfx_path = certificado.save_pfx() signer = Assinatura(pfx_path, certificado.password) - xml_send = signer.assina_xml( + xml_send = signer.assina_xml_nota( xml_send, kwargs['NFes'][0]['infNFe']['Id']) + xml_send = xml_send.replace( + '\n\n]>\n', '') + print xml_send + xml_send = xml_send.replace('\n', '') url = localizar_url(method, kwargs['estado']) cabecalho = _build_header(**kwargs) - return executar_consulta(certificado, url, cabecalho, xml_send) + response, obj = executar_consulta(certificado, url, cabecalho, xml_send) + return { + 'sent_xml': xml_send, + 'received_xml': response, + 'object': obj + } def autorizar_nfe(certificado, **kwargs): # Assinar diff --git a/pytrustnfe/nfe/assinatura.py b/pytrustnfe/nfe/assinatura.py index 63d5f19..ad531b0 100644 --- a/pytrustnfe/nfe/assinatura.py +++ b/pytrustnfe/nfe/assinatura.py @@ -38,7 +38,6 @@ class Assinatura(object): self._checar_certificado() self._inicializar_cripto() try: - xml = ']>' + xml doc_xml = libxml2.parseMemory( xml, len(xml)) @@ -87,3 +86,54 @@ class Assinatura(object): finally: doc_xml.freeDoc() # self._finalizar_cripto() + + def assina_xml_nota(self, xml, reference): + self._checar_certificado() + self._inicializar_cripto() + try: + doc_xml = libxml2.parseMemory( + xml, len(xml)) + signNode = xmlsec.TmplSignature(doc_xml, + xmlsec.transformInclC14NId(), + xmlsec.transformRsaSha1Id(), None) + doc_xml.getRootElement().get_last().addChild(signNode) + refNode = signNode.addReference(xmlsec.transformSha1Id(), + None, '#' + str(reference), None) + + refNode.addTransform(xmlsec.transformEnvelopedId()) + refNode.addTransform(xmlsec.transformInclC14NId()) + keyInfoNode = signNode.ensureKeyInfo() + keyInfoNode.addX509Data() + + dsig_ctx = xmlsec.DSigCtx() + chave = xmlsec.cryptoAppKeyLoad(filename=str(self.arquivo), + format=xmlsec.KeyDataFormatPkcs12, + pwd=str(self.senha), + pwdCallback=None, + pwdCallbackCtx=None) + + dsig_ctx.signKey = chave + dsig_ctx.sign(signNode) + + status = dsig_ctx.status + dsig_ctx.destroy() + + if status != xmlsec.DSigStatusSucceeded: + raise RuntimeError( + 'Erro ao realizar a assinatura do arquivo; status: "' + + str(status) + + '"') + + xpath = doc_xml.xpathNewContext() + xpath.xpathRegisterNs('sig', NAMESPACE_SIG) + certificados = xpath.xpathEval( + '//sig:X509Data/sig:X509Certificate') + for i in range(len(certificados) - 1): + certificados[i].unlinkNode() + certificados[i].freeNode() + + xml = doc_xml.serialize() + return xml + finally: + doc_xml.freeDoc() + # self._finalizar_cripto() diff --git a/pytrustnfe/nfe/comunicacao.py b/pytrustnfe/nfe/comunicacao.py index 9805ea4..221c6b6 100644 --- a/pytrustnfe/nfe/comunicacao.py +++ b/pytrustnfe/nfe/comunicacao.py @@ -15,14 +15,26 @@ soap_body_path = './soap:Envelope/soap:Body' soap_fault_path = './soap:Envelope/soap:Body/soap:Fault' +def _soap_xml(body): + xml = '' + xml += '' + xml += '' + xml += '433.10' + xml += '' + xml += body + xml += '' + return xml.rstrip('\n') + + def executar_consulta(certificado, url, cabecalho, xmlEnviar): cert, key = extract_cert_and_key_from_pfx(certificado.pfx, certificado.password) cert_path, key_path = save_cert_key(cert, key) url = 'https://nfe-homologacao.sefazrs.rs.gov.br/ws/NfeAutorizacao/NFeAutorizacao.asmx' web_service = 'NfeAutorizacao/nfeAutorizacaoLote' client = HttpClient(url, cert_path, key_path) - xml_retorno = client.post_soap(xmlEnviar, web_service) - + xmlEnviar = xmlEnviar.replace('', '') + xml_enviar = _soap_xml(xmlEnviar) + xml_retorno = client.post_soap(xml_enviar, web_service) return sanitize_response(xml_retorno) @@ -36,16 +48,6 @@ class Comunicacao(object): self.cert = cert self.key = key - def _soap_xml(self, body): - xml = '' - xml += '' - xml += '' - xml += '433.10' - xml += '' - xml += body - xml += '' - return xml.rstrip('\n') - def _preparar_temp_pem(self): cert_path = '/tmp/' + uuid4().hex key_path = '/tmp/' + uuid4().hex diff --git a/pytrustnfe/nfe/templates/NfeAutorizacao.xml b/pytrustnfe/nfe/templates/NfeAutorizacao.xml index 2ca11ad..7844783 100644 --- a/pytrustnfe/nfe/templates/NfeAutorizacao.xml +++ b/pytrustnfe/nfe/templates/NfeAutorizacao.xml @@ -62,7 +62,7 @@ {% endif %} {% if dest.tipo == 'company' -%} {{ dest.cnpj_cpf }} - {% endif %} + {% endif %} {{ dest.xNome }} {{ dest.enderDest.xLgr }} @@ -104,14 +104,24 @@ {% with imposto = det.imposto %} {{ imposto.vTotTrib }} + {% if imposto.ICMS.CST == '00' -%} - {{ imposto.ICMS.ICMS00.orig }} - {{ imposto.ICMS.ICMS00.CST }} - {{ imposto.ICMS.ICMS00.modBC }} - {{ imposto.ICMS.ICMS00.vBC }} - {{ imposto.ICMS.ICMS00.pICMS }} - {{ imposto.ICMS.ICMS00.vICMS }} + {{ imposto.ICMS.orig }} + {{ imposto.ICMS.CST }} + {{ imposto.ICMS.modBC }} + {{ imposto.ICMS.vBC }} + {{ imposto.ICMS.pICMS }} + {{ imposto.ICMS.vICMS }} + {% endif %} + {% if imposto.ICMS.CST == '101' -%} + + {{ imposto.ICMS.orig }} + {{ imposto.ICMS.CST }} + {{ imposto.ICMS.pCredSN }} + {{ imposto.ICMS.vCredICMSSN }} + + {% endif %} {{ imposto.IPI.cEnq }} diff --git a/pytrustnfe/utils.py b/pytrustnfe/utils.py index 7c69009..c84ffb0 100644 --- a/pytrustnfe/utils.py +++ b/pytrustnfe/utils.py @@ -6,7 +6,7 @@ from datetime import date, datetime class CabecalhoSoap(object): - + def __init__(self, **kwargs): self.estado = kwargs.pop('estado', '') self.soap_action = kwargs.pop('soap_action', '') @@ -53,7 +53,7 @@ def gerar_chave(obj_chave, prefix=None): assert isinstance(obj_chave, ChaveNFe), "Objeto deve ser do tipo ChaveNFe" obj_chave.validar() - chave_parcial = "%s%s%s%s%s%09d%d%08d" % (obj_chave.estado, obj_chave.emissao, + chave_parcial = "%s%s%s%s%s%09d%d%s" % (obj_chave.estado, obj_chave.emissao, obj_chave.cnpj, obj_chave.modelo, obj_chave.serie.zfill(3), obj_chave.numero, obj_chave.tipo, obj_chave.codigo)