From ed322847eddbd938db117052875b3f005ad19dc8 Mon Sep 17 00:00:00 2001 From: Felipe Date: Thu, 29 Mar 2018 12:05:24 -0300 Subject: [PATCH 01/13] decode no sent_xml nfse imperial --- pytrustnfe/nfse/imperial/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pytrustnfe/nfse/imperial/__init__.py b/pytrustnfe/nfse/imperial/__init__.py index e57091c..c21b085 100644 --- a/pytrustnfe/nfse/imperial/__init__.py +++ b/pytrustnfe/nfse/imperial/__init__.py @@ -27,7 +27,7 @@ def _send(certificado, method, **kwargs): response = client.post_soap(soap, 'NFeaction/AWS_NFE.%s' % method) response, obj = sanitize_response(response.encode('utf-8')) return { - 'sent_xml': xml_send, + 'sent_xml': xml_send.decode(), 'received_xml': response.decode(), 'object': obj } diff --git a/setup.py b/setup.py index c7e626b..9437020 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages -VERSION = "0.9.12" +VERSION = "0.9.13" setup( From 92a2505726335fe83c5044b0de89dda715d3e07f Mon Sep 17 00:00:00 2001 From: pal0schi <31492998+pal0schi@users.noreply.github.com> Date: Mon, 2 Apr 2018 13:19:30 -0300 Subject: [PATCH 02/13] =?UTF-8?q?Correcao=20NFS-e=20Floripa=20-=20Descri?= =?UTF-8?q?=C3=A7=C3=A3o=20dos=20servi=C3=A7os?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * decode no sent_xml nfse imperial * --- pytrustnfe/nfse/floripa/templates/processar_nota.xml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pytrustnfe/nfse/floripa/templates/processar_nota.xml b/pytrustnfe/nfse/floripa/templates/processar_nota.xml index 9fa0aba..6aaeaf3 100644 --- a/pytrustnfe/nfse/floripa/templates/processar_nota.xml +++ b/pytrustnfe/nfse/floripa/templates/processar_nota.xml @@ -18,7 +18,7 @@ {{ item.aliquota }} {{ item.cst_servico }} - {{ item.descricao|normalize|escape }} + {{ item.name|normalize|escape }} {{ item.cnae }} {{ item.quantidade }} {{ item.valor_total }} diff --git a/setup.py b/setup.py index 9437020..b258e76 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages -VERSION = "0.9.13" +VERSION = "0.9.14" setup( From 1f5645cf8ef54786b31d104281af27d1d6d50e69 Mon Sep 17 00:00:00 2001 From: carcaroff Date: Thu, 29 Mar 2018 18:24:59 -0300 Subject: [PATCH 03/13] [FEAT][10.0]Cancelado no DANFE # Conflicts: # setup.py --- pytrustnfe/nfe/danfe.py | 12 ++++++++++++ pytrustnfe/utils.py | 13 +++++++++++++ 2 files changed, 25 insertions(+) diff --git a/pytrustnfe/nfe/danfe.py b/pytrustnfe/nfe/danfe.py index 001810a..73a0355 100644 --- a/pytrustnfe/nfe/danfe.py +++ b/pytrustnfe/nfe/danfe.py @@ -191,6 +191,8 @@ class danfe(object): ".//{http://www.portalfiscal.inf.br/nfe}protNFe") elem_emit = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}emit") elem_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide") + elem_evento = oXML.find( + ".//{http://www.portalfiscal.inf.br/nfe}infEvento") cChave = elem_infNFe.attrib.get('Id')[3:] barcode128 = code128.Code128( @@ -318,6 +320,16 @@ class danfe(object): self.string(self.nLeft + 65, 449, 'SEM VALOR FISCAL') self.canvas.restoreState() + # Cancelado + if tagtext(oNode=elem_evento, cTag='xEvento') == \ + 'Cancelamento registrado': + self.canvas.saveState() + self.canvas.rotate(45) + self.canvas.setFont('NimbusSanL-Bold', 60) + self.canvas.setFillColorRGB(1, 0.2, 0.2) + self.string(self.nLeft + 80, 275, 'CANCELADO') + self.canvas.restoreState() + self.nlin += 48 def destinatario(self, oXML=None): diff --git a/pytrustnfe/utils.py b/pytrustnfe/utils.py index 4eff0a0..17c1a45 100644 --- a/pytrustnfe/utils.py +++ b/pytrustnfe/utils.py @@ -96,3 +96,16 @@ def gerar_nfeproc(envio, recibo): root.append(nfe) root.append(protocolo) return ET.tostring(root) + + +def gerar_nfeproc_cancel(nfe_proc, cancelamento): + import ipdb + ipdb.set_trace() + docEnvio = ET.fromstring(nfe_proc) + docCancel = ET.fromstring(cancelamento) + + ev_cancelamento = _find_node(docCancel, "retEvento") + if ev_cancelamento is None: + return '' + docEnvio.append(ev_cancelamento) + return ET.tostring(docEnvio) From 2aea7bae5d11666efb09b0aade9a11b3d8cbbafd Mon Sep 17 00:00:00 2001 From: Johny Chen Jy <31947361+carcaroff@users.noreply.github.com> Date: Thu, 29 Mar 2018 18:36:11 -0300 Subject: [PATCH 04/13] Update utils.py --- pytrustnfe/utils.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pytrustnfe/utils.py b/pytrustnfe/utils.py index 17c1a45..c494496 100644 --- a/pytrustnfe/utils.py +++ b/pytrustnfe/utils.py @@ -99,8 +99,6 @@ def gerar_nfeproc(envio, recibo): def gerar_nfeproc_cancel(nfe_proc, cancelamento): - import ipdb - ipdb.set_trace() docEnvio = ET.fromstring(nfe_proc) docCancel = ET.fromstring(cancelamento) From 1b2e206fcc339e3c8ae5a7975e13bb83a69bc597 Mon Sep 17 00:00:00 2001 From: Danimar Ribeiro Date: Tue, 3 Apr 2018 17:12:19 -0300 Subject: [PATCH 05/13] =?UTF-8?q?Vers=C3=A3o=200.9.15?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b258e76..039b040 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages -VERSION = "0.9.14" +VERSION = "0.9.15" setup( From 7a3f6275b892fa3c02b4b711c9011529f60d0d23 Mon Sep 17 00:00:00 2001 From: Danimar Ribeiro Date: Mon, 9 Apr 2018 20:41:00 -0300 Subject: [PATCH 06/13] =?UTF-8?q?Incrementando=20vers=C3=A3o=20do=20pyopen?= =?UTF-8?q?ssl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 039b040..b7e7049 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages -VERSION = "0.9.15" +VERSION = "0.9.17" setup( @@ -45,6 +45,7 @@ later (LGPLv2+)', long_description=open('README.md', 'r').read(), install_requires=[ 'Jinja2 >= 2.8', + 'pyOpenSSL >= 16.0.0, < 18', 'signxml >= 2.4.0', 'lxml >= 3.5.0, < 5', 'suds-jurko >= 0.6', From eb1db219b8fc242f5243ce7743392e56c5c86fe7 Mon Sep 17 00:00:00 2001 From: Danimar Ribeiro Date: Wed, 11 Apr 2018 20:13:30 -0300 Subject: [PATCH 07/13] =?UTF-8?q?Nova=20tag=20de=20base=20de=20c=C3=A1lcul?= =?UTF-8?q?o=20para=20nfse-floripa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/nfse/floripa/templates/processar_nota.xml | 1 + setup.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pytrustnfe/nfse/floripa/templates/processar_nota.xml b/pytrustnfe/nfse/floripa/templates/processar_nota.xml index 6aaeaf3..4fb97e8 100644 --- a/pytrustnfe/nfse/floripa/templates/processar_nota.xml +++ b/pytrustnfe/nfse/floripa/templates/processar_nota.xml @@ -17,6 +17,7 @@ {% for item in rps.itens_servico -%} {{ item.aliquota }} + {{ item.base_calculo }} {{ item.cst_servico }} {{ item.name|normalize|escape }} {{ item.cnae }} diff --git a/setup.py b/setup.py index b7e7049..4f68dcf 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages -VERSION = "0.9.17" +VERSION = "0.9.18" setup( From 38874665b9df092697d378f43a67f520101d8c2c Mon Sep 17 00:00:00 2001 From: Danimar Ribeiro Date: Fri, 13 Apr 2018 21:21:43 -0300 Subject: [PATCH 08/13] =?UTF-8?q?WIP=20-=20Implementa=C3=A7=C3=A3o=20da=20?= =?UTF-8?q?nota=20de=20BH,=20tentativa=20de=20usar=20o=20zeep?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/nfse/bh/__init__.py | 78 +++++++++++++++++++++++ pytrustnfe/nfse/bh/templates/CancelarNfse.xml | 13 ++++ pytrustnfe/nfse/bh/templates/GerarNfse.xml | 11 ++++ pytrustnfe/nfse/bh/templates/Rps.xml | 91 +++++++++++++++++++++++++++ 4 files changed, 193 insertions(+) create mode 100644 pytrustnfe/nfse/bh/__init__.py create mode 100644 pytrustnfe/nfse/bh/templates/CancelarNfse.xml create mode 100644 pytrustnfe/nfse/bh/templates/GerarNfse.xml create mode 100644 pytrustnfe/nfse/bh/templates/Rps.xml diff --git a/pytrustnfe/nfse/bh/__init__.py b/pytrustnfe/nfse/bh/__init__.py new file mode 100644 index 0000000..4c9fd3f --- /dev/null +++ b/pytrustnfe/nfse/bh/__init__.py @@ -0,0 +1,78 @@ +# © 2018 Danimar Ribeiro, Trustcode +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import os + +from requests import Session +from zeep import Client +from zeep.transports import Transport + +from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key +from pytrustnfe.xml import render_xml, sanitize_response +from pytrustnfe.nfe.assinatura import Assinatura + + +def _render(certificado, method, **kwargs): + path = os.path.join(os.path.dirname(__file__), 'templates') + xml_send = render_xml(path, '%s.xml' % method, True, **kwargs) + + reference = '' + if method == 'GerarNfse': + reference = 'r%s' % kwargs['rps']['numero'] + elif method == 'CancelarNfse': + reference = 'Cancelamento_NF%s' % kwargs['cancelamento']['numero_nfse'] + + signer = Assinatura(certificado.pfx, certificado.password) + xml_send = signer.assina_xml(xml_send, reference) + return xml_send.encode('utf-8') + + +def _send(certificado, method, **kwargs): + base_url = '' + if kwargs['ambiente'] == 'producao': + base_url = 'https://bhissdigital.pbh.gov.br/bhiss-ws/nfse?wsdl' + else: + base_url = 'https://bhisshomologa.pbh.gov.br/bhiss-ws/nfse?wsdl' + + xml_send = kwargs["xml"].decode('utf-8') + xml_cabecalho = '' + + cert, key = extract_cert_and_key_from_pfx( + certificado.pfx, certificado.password) + cert, key = save_cert_key(cert, key) + + session = Session() + session.cert = (cert, key) + session.verify = False + transport = Transport(session=session) + + client = Client(base_url, transport=transport) + + response = client.service[method](xml_cabecalho, xml_send) + + response, obj = sanitize_response(response) + return { + 'sent_xml': str(xml_send), + 'received_xml': str(response), + 'object': obj + } + + +def xml_gerar_nfse(certificado, **kwargs): + return _render(certificado, 'GerarNfse', **kwargs) + + +def gerar_nfse(certificado, **kwargs): + if "xml" not in kwargs: + kwargs['xml'] = xml_gerar_nfse(certificado, **kwargs) + return _send(certificado, 'GerarNfse', **kwargs) + + +def xml_cancelar_nfse(certificado, **kwargs): + return _render(certificado, 'CancelarNfse', **kwargs) + + +def cancelar_nfse(certificado, **kwargs): + if "xml" not in kwargs: + kwargs['xml'] = xml_cancelar_nfse(certificado, **kwargs) + return _send(certificado, 'CancelarNfse', **kwargs) diff --git a/pytrustnfe/nfse/bh/templates/CancelarNfse.xml b/pytrustnfe/nfse/bh/templates/CancelarNfse.xml new file mode 100644 index 0000000..137897e --- /dev/null +++ b/pytrustnfe/nfse/bh/templates/CancelarNfse.xml @@ -0,0 +1,13 @@ + + + + + {{ cancelamento.numero_nfse }} + {{ cancelamento.cnpj_prestador }} + {{ cancelamento.inscricao_municipal }} + {{ cancelamento.cidade }} + + 1 + + + diff --git a/pytrustnfe/nfse/bh/templates/GerarNfse.xml b/pytrustnfe/nfse/bh/templates/GerarNfse.xml new file mode 100644 index 0000000..c6ead33 --- /dev/null +++ b/pytrustnfe/nfse/bh/templates/GerarNfse.xml @@ -0,0 +1,11 @@ + + + {{ nfse.numero_lote }} + {{ nfse.cnpj_prestador }} + {{ nfse.inscricao_municipal }} + 1 + + {% include 'Rps.xml' %} + + + diff --git a/pytrustnfe/nfse/bh/templates/Rps.xml b/pytrustnfe/nfse/bh/templates/Rps.xml new file mode 100644 index 0000000..7b883af --- /dev/null +++ b/pytrustnfe/nfse/bh/templates/Rps.xml @@ -0,0 +1,91 @@ + + + + {{ rps.numero }} + {{ rps.serie }} + {{ rps.tipo_rps }} + + {{ rps.data_emissao }} + {{ rps.natureza_operacao }} + {{ rps.regime_tributacao }} + {{ rps.optante_simples }} + {{ rps.incentivador_cultural }} + {{ rps.status }} + + {{ rps.numero_substituido }} + {{ rps.serie_substituido }} + {{ rps.tipo_substituido }} + + + + {{ rps.valor_servico }} + {{ rps.valor_deducao }} + {{ rps.valor_pis }} + {{ rps.valor_cofins }} + {{ rps.valor_inss }} + {{ rps.valor_ir }} + {{ rps.valor_csll }} + {{ rps.iss_retido }} + {{ rps.valor_iss }} + {{ rps.valor_iss_retido }} + {{ rps.outras_retencoes }} + {{ rps.base_calculo }} + {{ rps.aliquota_issqn }} + {{ rps.valor_liquido_nfse }} + {{ rps.desconto_incondicionado }} + {{ rps.desconto_condicionado }} + + {{ rps.codigo_servico }} + {{ rps.cnae_servico }} + {{ rps.codigo_tributacao_municipio }} + {{ rps.descricao }} + {{ rps.codigo_municipio }} + + + {{ rps.prestador.cnpj }} + {{ rps.prestador.inscricao_municipal }} + + + + + {% if rps.tomador.cnpj_cpf|length == 14 %} + {{ rps.tomador.cnpj_cpf }} + {% endif %} + {% if rps.tomador.cnpj_cpf|length == 11 %} + {{ rps.tomador.cnpj_cpf }} + {% endif %} + + {{ rps.tomador.inscricao_municipal }} + + {{ rps.tomador.razao_social }} + + {{ rps.tomador.logradouro }} + {{ rps.tomador.numero }} + {{ rps.tomador.complemento }} + {{ rps.tomador.bairro }} + {{ rps.tomador.cidade }} + {{ rps.tomador.uf }} + {{ rps.tomador.cep }} + + + {{ rps.tomador.telefone }} + {{ rps.tomador.email }} + + + {% if rps.intermediario is defined -%} + + {{ rps.intermediario.razao_social }} + + {{ rps.intermediario.cnpj }} + + {{ rps.intermediario.inscricao_municipal }} + + {% endif %} + {% if rps.construcao_civil is defined -%} + + {{ rps.construcao_civil.codigo_obra }} + {{ rps.construcao_civil.art }} + + {% endif %} + + From 9a1f406c623d19a112c75c7fe12d0cad54164bda Mon Sep 17 00:00:00 2001 From: Danimar Ribeiro Date: Sat, 14 Apr 2018 12:28:18 -0300 Subject: [PATCH 09/13] =?UTF-8?q?Corre=C3=A7=C3=A3o=20no=20envio=20de=20NF?= =?UTF-8?q?e=20para=20o=20estado=20do=20Cear=C3=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/Servidores.py | 8 ++++---- pytrustnfe/client.py | 9 +++++---- pytrustnfe/nfe/__init__.py | 26 ++++++++++++++------------ pytrustnfe/nfe/comunicacao.py | 4 ++-- pytrustnfe/utils.py | 1 + setup.py | 2 +- 6 files changed, 27 insertions(+), 23 deletions(-) diff --git a/pytrustnfe/Servidores.py b/pytrustnfe/Servidores.py index 0b3e28d..8a340fe 100644 --- a/pytrustnfe/Servidores.py +++ b/pytrustnfe/Servidores.py @@ -352,8 +352,8 @@ UFBA = { UFCE = { NFE_AMBIENTE_PRODUCAO: { 'servidor': 'nfe.sefaz.ce.gov.br', - WS_NFE_AUTORIZACAO: 'nfe2/services/NfeRecepcao2', - WS_NFE_RET_AUTORIZACAO: 'nfe2/services/NfeRetRecepcao2', + WS_NFE_AUTORIZACAO: 'nfe2/services/NfeAutorizacao', + WS_NFE_RET_AUTORIZACAO: 'nfe2/services/NfeRetAutorizacao', WS_NFE_INUTILIZACAO: 'nfe2/services/NfeInutilizacao2', WS_NFE_CONSULTA: 'nfe2/services/NfeConsulta2', WS_NFE_SITUACAO: 'nfe2/services/NfeStatusServico2', @@ -363,8 +363,8 @@ UFCE = { }, NFE_AMBIENTE_HOMOLOGACAO: { 'servidor': 'nfeh.sefaz.ce.gov.br', - WS_NFE_AUTORIZACAO: 'nfe2/services/NfeRecepcao2', - WS_NFE_RET_AUTORIZACAO: 'nfe2/services/NfeRetRecepcao2', + WS_NFE_AUTORIZACAO: 'nfe2/services/NfeAutorizacao', + WS_NFE_RET_AUTORIZACAO: 'nfe2/services/NfeRetAutorizacao', WS_NFE_INUTILIZACAO: 'nfe2/services/NfeInutilizacao2', WS_NFE_CONSULTA: 'nfe2/services/NfeConsulta2', WS_NFE_SITUACAO: 'nfe2/services/NfeStatusServico2', diff --git a/pytrustnfe/client.py b/pytrustnfe/client.py index 03adbd2..e6e67e3 100644 --- a/pytrustnfe/client.py +++ b/pytrustnfe/client.py @@ -45,19 +45,20 @@ class HttpClient(object): def _headers(self, action, send_raw): if send_raw: return { - 'Content-type': 'text/xml; charset=utf-8; action="http://www.portalfiscal.inf.br/nfe/wsdl/%s"' % action, + 'Content-type': 'text/xml; charset=utf-8;', + 'SOAPAction': "http://www.portalfiscal.inf.br/nfe/wsdl/%s" % action, 'Accept': 'application/soap+xml; charset=utf-8', } return { - 'Content-type': 'application/soap+xml; charset=utf-8; action="http://www.portalfiscal.inf.br/nfe/wsdl/%s"' % action, - 'Accept': 'application/soap+xml; charset=utf-8', + 'Content-type': 'application/soap+xml; charset=utf-8;', + 'SOAPAction': 'http://www.portalfiscal.inf.br/nfe/wsdl/%s' % action, } def post_soap(self, xml_soap, cabecalho, send_raw): header = self._headers(cabecalho.soap_action, send_raw) urllib3.disable_warnings(category=InsecureRequestWarning) - res = requests.post(self.url, data=xml_soap, + res = requests.post(self.url, data=xml_soap.encode('utf-8'), cert=(self.cert_path, self.key_path), verify=False, headers=header) return res.text diff --git a/pytrustnfe/nfe/__init__.py b/pytrustnfe/nfe/__init__.py index bf87674..c903014 100644 --- a/pytrustnfe/nfe/__init__.py +++ b/pytrustnfe/nfe/__init__.py @@ -19,19 +19,21 @@ from pytrustnfe.exceptions import NFeValidationException def _build_header(method, **kwargs): action = { - 'NfeAutorizacao': ('NfeAutorizacao', '3.10'), - 'NfeRetAutorizacao': ('NfeRetAutorizacao', '3.10'), - 'NfeConsultaCadastro': ('CadConsultaCadastro2', '2.00'), - 'NfeInutilizacao': ('NfeInutilizacao2', '3.10'), - 'RecepcaoEventoCancelamento': ('RecepcaoEvento', '1.00'), - 'RecepcaoEventoCarta': ('RecepcaoEvento', '1.00'), - 'NFeDistribuicaoDFe': ('NFeDistribuicaoDFe/nfeDistDFeInteresse', - '1.00'), - 'RecepcaoEventoManifesto': ('RecepcaoEvento', '1.00'), + 'NfeAutorizacao': ('NfeAutorizacao', '3.10', 'NfeAutorizacao/nfeAutorizacaoLote'), + 'NfeRetAutorizacao': ('NfeRetAutorizacao', '3.10', 'NfeRetAutorizacao/nfeRetAutorizacaoLote'), + 'NfeConsultaCadastro': ('CadConsultaCadastro2', '2.00', 'CadConsultaCadastro2/consultaCadastro2'), + 'NfeInutilizacao': ('NfeInutilizacao2', '3.10', 'NfeInutilizacao2/nfeInutilizacaoNF2'), + 'RecepcaoEventoCancelamento': ('RecepcaoEvento', '1.00', 'RecepcaoEvento/nfeRecepcaoEvento'), + 'RecepcaoEventoCarta': ('RecepcaoEvento', '1.00', 'RecepcaoEvento/nfeRecepcaoEvento'), + 'NFeDistribuicaoDFe': ('NFeDistribuicaoDFe/nfeDistDFeInteresse', '1.00', 'NFeDistribuicaoDFe/nfeDistDFeInteresse'), + 'RecepcaoEventoManifesto': ('RecepcaoEvento', '1.00', 'RecepcaoEvento/nfeRecepcaoEvento'), + } + vals = { + 'estado': kwargs['estado'], + 'method': action[method][0], + 'soap_action': action[method][2], + 'versao': action[method][1] } - vals = {'estado': kwargs['estado'], - 'soap_action': action[method][0], - 'versao': action[method][1]} return CabecalhoSoap(**vals) diff --git a/pytrustnfe/nfe/comunicacao.py b/pytrustnfe/nfe/comunicacao.py index df6ba53..34c550e 100644 --- a/pytrustnfe/nfe/comunicacao.py +++ b/pytrustnfe/nfe/comunicacao.py @@ -12,9 +12,9 @@ from ..xml import sanitize_response def _soap_xml(body, cabecalho): xml = '' xml += '' - xml += '' + xml += '' xml += '' + cabecalho.estado + '' + cabecalho.versao + '' - xml += '' + xml += '' xml += body xml += '' return xml.rstrip('\n') diff --git a/pytrustnfe/utils.py b/pytrustnfe/utils.py index c494496..b06fb6e 100644 --- a/pytrustnfe/utils.py +++ b/pytrustnfe/utils.py @@ -13,6 +13,7 @@ class CabecalhoSoap(object): def __init__(self, **kwargs): self.versao = kwargs.pop('versao', '') self.estado = kwargs.pop('estado', '') + self.method = kwargs.pop('method', '') self.soap_action = kwargs.pop('soap_action', '') diff --git a/setup.py b/setup.py index 4f68dcf..8b6eabe 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages -VERSION = "0.9.18" +VERSION = "0.9.19" setup( From 4f423e28f5661e9e0cefcc5b3dc63c063560dd9c Mon Sep 17 00:00:00 2001 From: Johny Chen Jy <31947361+carcaroff@users.noreply.github.com> Date: Mon, 16 Apr 2018 20:29:00 -0300 Subject: [PATCH 10/13] Implementa timezone ao imprimir a danfe --- pytrustnfe/nfe/danfe.py | 94 +++++++++++++++++++++++++++++++++++-------------- requirements.txt | 1 + setup.py | 3 +- 3 files changed, 71 insertions(+), 27 deletions(-) diff --git a/pytrustnfe/nfe/danfe.py b/pytrustnfe/nfe/danfe.py index 73a0355..3790574 100644 --- a/pytrustnfe/nfe/danfe.py +++ b/pytrustnfe/nfe/danfe.py @@ -20,6 +20,9 @@ from reportlab.lib.styles import ParagraphStyle from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont +import pytz +from datetime import datetime, timedelta + def chunks(cString, nLen): for start in range(0, len(cString), nLen): @@ -36,10 +39,43 @@ def format_cnpj_cpf(value): return cValue -def getdateUTC(cDateUTC): - cDt = cDateUTC[0:10].split('-') +def getdateByTimezone(cDateUTC, timezone=None): + + ''' + Esse método trata a data recebida de acordo com o timezone do + usuário. O seu retorno é dividido em duas partes: + 1) A data em si; + 2) As horas; + :param cDateUTC: string contendo as informações da data + :param timezone: timezone do usuário do sistema + :return: data e hora convertidos para a timezone do usuário + ''' + + # Aqui cortamos a informação do timezone da string (+03:00) + dt = cDateUTC[0:19] + + # Verificamos se a string está completa (data + hora + timezone) + if timezone and len(cDateUTC) == 25: + + # tz irá conter informações da timezone contida em cDateUTC + tz = cDateUTC[19:25] + tz = int(tz.split(':')[0]) + + dt = datetime.strptime(dt, '%Y-%m-%dT%H:%M:%S') + + # dt agora será convertido para o horario em UTC + dt = dt - timedelta(hours=tz) + + # tzinfo passará a apontar para + dt = pytz.utc.localize(dt) + + # valor de dt é convertido para a timezone do usuário + dt = timezone.normalize(dt) + dt = dt.strftime('%Y-%m-%dT%H:%M:%S') + + cDt = dt[0:10].split('-') cDt.reverse() - return '/'.join(cDt), cDateUTC[11:16] + return '/'.join(cDt), dt[11:16] def format_number(cNumber): @@ -74,7 +110,8 @@ def get_image(path, width=1 * cm): class danfe(object): def __init__(self, sizepage=A4, list_xml=None, recibo=True, - orientation='portrait', logo=None, cce_xml=None): + orientation='portrait', logo=None, cce_xml=None, + timezone=None): path = os.path.join(os.path.dirname(__file__), 'fonts') pdfmetrics.registerFont( @@ -114,8 +151,8 @@ class danfe(object): self.Page = 1 # Calculando total linhas usadas para descrições dos itens - # Com bloco fatura, apenas 29 linhas para itens na primeira folha - nNr_Lin_Pg_1 = 34 if oXML_cobr is None else 30 + # Com bloco fatura, apenas 25 linhas para itens na primeira folha + nNr_Lin_Pg_1 = 30 if oXML_cobr is None else 26 # [ rec_ini , rec_fim , lines , limit_lines ] oPaginator = [[0, 0, 0, nNr_Lin_Pg_1]] el_det = oXML.findall(".//{http://www.portalfiscal.inf.br/nfe}det") @@ -154,13 +191,13 @@ class danfe(object): self.NrPages = len(oPaginator) # Calculando nr. páginas if recibo: - self.recibo_entrega(oXML=oXML) + self.recibo_entrega(oXML=oXML, timezone=timezone) - self.ide_emit(oXML=oXML) - self.destinatario(oXML=oXML) + self.ide_emit(oXML=oXML, timezone=timezone) + self.destinatario(oXML=oXML, timezone=timezone) if oXML_cobr is not None: - self.faturas(oXML=oXML_cobr) + self.faturas(oXML=oXML_cobr, timezone=timezone) self.impostos(oXML=oXML) self.transportes(oXML=oXML) @@ -172,7 +209,7 @@ class danfe(object): # Gera o restante das páginas do XML for oPag in oPaginator[1:]: self.newpage() - self.ide_emit(oXML=oXML) + self.ide_emit(oXML=oXML, timezone=timezone) self.produtos(oXML=oXML, el_det=el_det, oPaginator=oPag, list_desc=list_desc, nHeight=77, list_cod_prod=list_cod_prod) @@ -180,11 +217,11 @@ class danfe(object): self.newpage() if cce_xml: for xml in cce_xml: - self._generate_cce(cce_xml=xml, oXML=oXML) + self._generate_cce(cce_xml=xml, oXML=oXML, timezone=timezone) self.newpage() self.canvas.save() - def ide_emit(self, oXML=None): + def ide_emit(self, oXML=None, timezone=None): elem_infNFe = oXML.find( ".//{http://www.portalfiscal.inf.br/nfe}infNFe") elem_protNFe = oXML.find( @@ -262,7 +299,8 @@ class danfe(object): self.stringcenter(self.nLeft + 116.5 + nW_Rect, self.nlin + 19.5, ' '.join(chunks(cChave, 4))) # Chave self.canvas.setFont('NimbusSanL-Regu', 8) - cDt, cHr = getdateUTC(tagtext(oNode=elem_protNFe, cTag='dhRecbto')) + cDt, cHr = getdateByTimezone( + tagtext(oNode=elem_protNFe, cTag='dhRecbto'), timezone) cProtocolo = tagtext(oNode=elem_protNFe, cTag='nProt') cDt = cProtocolo + ' - ' + cDt + ' ' + cHr nW_Rect = (self.width - self.nLeft - self.nRight - 110) / 2 @@ -283,9 +321,9 @@ class danfe(object): # Razão Social emitente P = Paragraph(tagtext(oNode=elem_emit, cTag='xNome'), styleN) - w, h = P.wrap(55 * mm, 50 * mm) + w, h = P.wrap(55 * mm, 40 * mm) P.drawOn(self.canvas, (self.nLeft + 30) * mm, - (self.height - self.nlin - 12) * mm) + (self.height - self.nlin - ((5*h + 12)/12)) * mm) if self.logo: img = get_image(self.logo, width=2 * cm) @@ -332,7 +370,7 @@ class danfe(object): self.nlin += 48 - def destinatario(self, oXML=None): + def destinatario(self, oXML=None, timezone=None): elem_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide") elem_dest = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}dest") nMr = self.width - self.nRight @@ -376,9 +414,11 @@ class danfe(object): else: cnpj_cpf = format_cnpj_cpf(tagtext(oNode=elem_dest, cTag='CPF')) self.string(nMr - 69, self.nlin + 7.5, cnpj_cpf) - cDt, cHr = getdateUTC(tagtext(oNode=elem_ide, cTag='dhEmi')) + cDt, cHr = getdateByTimezone(tagtext(oNode=elem_ide, cTag='dhEmi'), + timezone) self.string(nMr - 24, self.nlin + 7.7, cDt + ' ' + cHr) - cDt, cHr = getdateUTC(tagtext(oNode=elem_ide, cTag='dhSaiEnt')) + cDt, cHr = getdateByTimezone( + tagtext(oNode=elem_ide, cTag='dhSaiEnt'), timezone) self.string(nMr - 24, self.nlin + 14.3, cDt + ' ' + cHr) # Dt saída cEnd = tagtext(oNode=elem_dest, cTag='xLgr') + ', ' + tagtext( oNode=elem_dest, cTag='nro') @@ -398,7 +438,7 @@ class danfe(object): self.nlin += 24 # Nr linhas ocupadas pelo bloco - def faturas(self, oXML=None): + def faturas(self, oXML=None, timezone=None): nMr = self.width - self.nRight @@ -431,7 +471,8 @@ class danfe(object): line_iter = iter(oXML[1:10]) # Salta elemt 1 e considera os próximos 9 for oXML_dup in line_iter: - cDt, cHr = getdateUTC(tagtext(oNode=oXML_dup, cTag='dVenc')) + cDt, cHr = getdateByTimezone(tagtext(oNode=oXML_dup, cTag='dVenc'), + timezone) self.string(self.nLeft + nCol + 1, self.nlin + nLin, tagtext(oNode=oXML_dup, cTag='nDup')) self.string(self.nLeft + nCol + 17, self.nlin + nLin, cDt) @@ -759,7 +800,7 @@ obsCont[@xCampo='NomeVendedor']") P.drawOn(self.canvas, (self.nLeft + 1) * mm, altura - h) self.nlin += 36 - def recibo_entrega(self, oXML=None): + def recibo_entrega(self, oXML=None, timezone=None): el_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide") el_dest = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}dest") el_total = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}total") @@ -791,7 +832,8 @@ obsCont[@xCampo='NomeVendedor']") self.string(self.width - self.nRight - nW + 2, self.nlin + 14, "SÉRIE %s" % (tagtext(oNode=el_ide, cTag='serie'))) - cDt, cHr = getdateUTC(tagtext(oNode=el_ide, cTag='dhEmi')) + cDt, cHr = getdateByTimezone( + tagtext(oNode=el_ide, cTag='dhEmi'), timezone) cTotal = format_number(tagtext(oNode=el_total, cTag='vNF')) cEnd = tagtext(oNode=el_dest, cTag='xNome') + ' - ' @@ -858,7 +900,7 @@ obsCont[@xCampo='NomeVendedor']") self.oPDF_IO.close() fileObj.write(pdf_out) - def _generate_cce(self, cce_xml=None, oXML=None): + def _generate_cce(self, cce_xml=None, oXML=None, timezone=None): self.canvas.setLineWidth(.2) # labels @@ -897,8 +939,8 @@ obsCont[@xCampo='NomeVendedor']") self.string(82, 24, cnpj) chave_acesso = tagtext(oNode=elem_infNFe, cTag='chNFe') self.string(82, 30, chave_acesso) - data_correcao = getdateUTC(tagtext( - oNode=elem_infNFe, cTag='dhEvento')) + data_correcao = getdateByTimezone(tagtext( + oNode=elem_infNFe, cTag='dhEvento'), timezone) data_correcao = data_correcao[0] + " " + data_correcao[1] self.string(82, 36, data_correcao) cce_id = elem_infNFe.values()[0] diff --git a/requirements.txt b/requirements.txt index abb0836..92f9079 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,3 +14,4 @@ xmlsec >= 1.3.3 reportlab pytest pytest-cov +pytz diff --git a/setup.py b/setup.py index 8b6eabe..623b933 100644 --- a/setup.py +++ b/setup.py @@ -50,7 +50,8 @@ later (LGPLv2+)', 'lxml >= 3.5.0, < 5', 'suds-jurko >= 0.6', 'suds-jurko-requests >= 1.2', - 'reportlab' + 'reportlab', + 'pytz', ], tests_require=[ 'pytest', From 236515b12d7a3627fdfaf290d0a7bce293eaaf38 Mon Sep 17 00:00:00 2001 From: Danimar Ribeiro Date: Mon, 16 Apr 2018 20:42:44 -0300 Subject: [PATCH 11/13] =?UTF-8?q?Ajuste=20ao=20imprimir=20nfe=20cancelada,?= =?UTF-8?q?=20corre=C3=A7=C3=A3o=20de=20encoding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/nfe/danfe.py | 3 +-- pytrustnfe/nfe/templates/RecepcaoEventoCarta.xml | 4 ++-- pytrustnfe/utils.py | 4 ++-- setup.py | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pytrustnfe/nfe/danfe.py b/pytrustnfe/nfe/danfe.py index 3790574..6561538 100644 --- a/pytrustnfe/nfe/danfe.py +++ b/pytrustnfe/nfe/danfe.py @@ -359,8 +359,7 @@ class danfe(object): self.canvas.restoreState() # Cancelado - if tagtext(oNode=elem_evento, cTag='xEvento') == \ - 'Cancelamento registrado': + if tagtext(oNode=elem_evento, cTag='cStat') == '135': self.canvas.saveState() self.canvas.rotate(45) self.canvas.setFont('NimbusSanL-Bold', 60) diff --git a/pytrustnfe/nfe/templates/RecepcaoEventoCarta.xml b/pytrustnfe/nfe/templates/RecepcaoEventoCarta.xml index 377fb96..ced435c 100644 --- a/pytrustnfe/nfe/templates/RecepcaoEventoCarta.xml +++ b/pytrustnfe/nfe/templates/RecepcaoEventoCarta.xml @@ -12,9 +12,9 @@ {{ nSeqEvento }} 1.00 - Carta de Correção + Carta de Correcao {{ xCorrecao|normalize|escape }} - A Carta de Correção é disciplinada pelo § 1º-A do art. 7º do Convênio S/N, de 15 de dezembro de 1970 e pode ser utilizada para regularização de erro ocorrido na emissão de documento fiscal, desde que o erro não esteja relacionado com: I - as variáveis que determinam o valor do imposto tais como: base de cálculo, alíquota, diferença de preço, quantidade, valor da operação ou da prestação; II - a correção de dados cadastrais que implique mudança do remetente ou do destinatário; III - a data de emissão ou de saída. + A Carta de Correcao e disciplinada pelo paragrafo 1o-A do art. 7o do Convenio S/N, de 15 de dezembro de 1970 e pode ser utilizada para regularizacao de erro ocorrido na emissao de documento fiscal, desde que o erro nao esteja relacionado com: I - as variaveis que determinam o valor do imposto tais como: base de calculo, aliquota, diferenca de preco, quantidade, valor da operacao ou da prestacao; II - a correcao de dados cadastrais que implique mudanca do remetente ou do destinatario; III - a data de emissao ou de saida. diff --git a/pytrustnfe/utils.py b/pytrustnfe/utils.py index b06fb6e..b5964ae 100644 --- a/pytrustnfe/utils.py +++ b/pytrustnfe/utils.py @@ -93,7 +93,7 @@ def gerar_nfeproc(envio, recibo): nfe = _find_node(docEnvio, "NFe") protocolo = _find_node(docRecibo, "protNFe") if nfe is None or protocolo is None: - return '' + return b'' root.append(nfe) root.append(protocolo) return ET.tostring(root) @@ -105,6 +105,6 @@ def gerar_nfeproc_cancel(nfe_proc, cancelamento): ev_cancelamento = _find_node(docCancel, "retEvento") if ev_cancelamento is None: - return '' + return b'' docEnvio.append(ev_cancelamento) return ET.tostring(docEnvio) diff --git a/setup.py b/setup.py index 623b933..17d5399 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages -VERSION = "0.9.19" +VERSION = "0.9.20" setup( From 3db71b7b9acc2575ae267a7863592951d22e6f07 Mon Sep 17 00:00:00 2001 From: Danimar Ribeiro Date: Mon, 7 May 2018 16:14:14 -0300 Subject: [PATCH 12/13] =?UTF-8?q?Nota=20de=20BH=20utiliza=20duas=20assinat?= =?UTF-8?q?uras=20Corre=C3=A7=C3=B5s=20nos=20templates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/nfse/bh/__init__.py | 14 ++++++---- pytrustnfe/nfse/bh/assinatura.py | 44 ++++++++++++++++++++++++++++++ pytrustnfe/nfse/bh/templates/GerarNfse.xml | 8 +++--- pytrustnfe/nfse/bh/templates/Rps.xml | 2 +- setup.py | 1 + 5 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 pytrustnfe/nfse/bh/assinatura.py diff --git a/pytrustnfe/nfse/bh/__init__.py b/pytrustnfe/nfse/bh/__init__.py index 4c9fd3f..c5f246b 100644 --- a/pytrustnfe/nfse/bh/__init__.py +++ b/pytrustnfe/nfse/bh/__init__.py @@ -2,14 +2,14 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import os - +from lxml import etree from requests import Session from zeep import Client from zeep.transports import Transport from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key from pytrustnfe.xml import render_xml, sanitize_response -from pytrustnfe.nfe.assinatura import Assinatura +from pytrustnfe.nfse.bh.assinatura import Assinatura def _render(certificado, method, **kwargs): @@ -18,12 +18,14 @@ def _render(certificado, method, **kwargs): reference = '' if method == 'GerarNfse': - reference = 'r%s' % kwargs['rps']['numero'] + reference = 'rps:%s' % kwargs['rps']['numero'] + ref_lote = 'lote%s' % kwargs['rps']['numero_lote'] elif method == 'CancelarNfse': reference = 'Cancelamento_NF%s' % kwargs['cancelamento']['numero_nfse'] signer = Assinatura(certificado.pfx, certificado.password) xml_send = signer.assina_xml(xml_send, reference) + xml_send = signer.assina_xml(etree.fromstring(xml_send), ref_lote) return xml_send.encode('utf-8') @@ -35,7 +37,9 @@ def _send(certificado, method, **kwargs): base_url = 'https://bhisshomologa.pbh.gov.br/bhiss-ws/nfse?wsdl' xml_send = kwargs["xml"].decode('utf-8') - xml_cabecalho = '' + xml_cabecalho = '\ + \ + 1.00' cert, key = extract_cert_and_key_from_pfx( certificado.pfx, certificado.password) @@ -50,7 +54,7 @@ def _send(certificado, method, **kwargs): response = client.service[method](xml_cabecalho, xml_send) - response, obj = sanitize_response(response) + response, obj = sanitize_response(response.encode('utf-8')) return { 'sent_xml': str(xml_send), 'received_xml': str(response), diff --git a/pytrustnfe/nfse/bh/assinatura.py b/pytrustnfe/nfse/bh/assinatura.py new file mode 100644 index 0000000..1831379 --- /dev/null +++ b/pytrustnfe/nfse/bh/assinatura.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# © 2016 Danimar Ribeiro, Trustcode +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import signxml +from lxml import etree +from pytrustnfe.certificado import extract_cert_and_key_from_pfx +from signxml import XMLSigner + + +class Assinatura(object): + + def __init__(self, arquivo, senha): + self.arquivo = arquivo + self.senha = senha + + def assina_xml(self, xml_element, reference): + cert, key = extract_cert_and_key_from_pfx(self.arquivo, self.senha) + + for element in xml_element.iter("*"): + if element.text is not None and not element.text.strip(): + element.text = None + + signer = XMLSigner( + method=signxml.methods.enveloped, signature_algorithm="rsa-sha1", + digest_algorithm='sha1', + c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315') + + ns = {} + ns[None] = signer.namespaces['ds'] + signer.namespaces = ns + + ref_uri = ('#%s' % reference) if reference else None + signed_root = signer.sign( + xml_element, key=key.encode(), cert=cert.encode(), + reference_uri=ref_uri) + if reference: + element_signed = signed_root.find(".//*[@Id='%s']" % reference) + signature = signed_root.find(".//*[@URI='#%s']" % reference).getparent().getparent() + + if element_signed is not None and signature is not None: + parent = element_signed.getparent() + parent.append(signature) + return etree.tostring(signed_root, encoding=str) diff --git a/pytrustnfe/nfse/bh/templates/GerarNfse.xml b/pytrustnfe/nfse/bh/templates/GerarNfse.xml index c6ead33..6b35d4d 100644 --- a/pytrustnfe/nfse/bh/templates/GerarNfse.xml +++ b/pytrustnfe/nfse/bh/templates/GerarNfse.xml @@ -1,8 +1,8 @@ - - {{ nfse.numero_lote }} - {{ nfse.cnpj_prestador }} - {{ nfse.inscricao_municipal }} + + {{ rps.numero_lote }} + {{ rps.prestador.cnpj }} + {{ rps.prestador.inscricao_municipal }} 1 {% include 'Rps.xml' %} diff --git a/pytrustnfe/nfse/bh/templates/Rps.xml b/pytrustnfe/nfse/bh/templates/Rps.xml index 7b883af..0dfd0cf 100644 --- a/pytrustnfe/nfse/bh/templates/Rps.xml +++ b/pytrustnfe/nfse/bh/templates/Rps.xml @@ -1,5 +1,5 @@ - + {{ rps.numero }} {{ rps.serie }} diff --git a/setup.py b/setup.py index 17d5399..4bf323b 100644 --- a/setup.py +++ b/setup.py @@ -37,6 +37,7 @@ later (LGPLv2+)', 'nfse/imperial/templates/*xml', 'nfse/floripa/templates/*xml', 'nfse/carioca/templates/*xml', + 'nfse/bh/templates/*xml', 'xml/schemas/*xsd', ]}, url='https://github.com/danimaribeiro/PyTrustNFe', From e712bf0d6aa7edc65773e15d99940899247b8b0b Mon Sep 17 00:00:00 2001 From: Danimar Ribeiro Date: Tue, 8 May 2018 16:48:43 -0300 Subject: [PATCH 13/13] =?UTF-8?q?Corre=C3=A7=C3=A3o=20de=20envio=20Ginfes?= =?UTF-8?q?=20-=20Mudan=C3=A7a=20para=20o=20zeep=20para=20webservice=20SOA?= =?UTF-8?q?P?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/nfse/ginfes/__init__.py | 35 ++++++++++++++++++++--------------- requirements.txt | 1 + setup.py | 3 ++- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/pytrustnfe/nfse/ginfes/__init__.py b/pytrustnfe/nfse/ginfes/__init__.py index 80e969b..fb27677 100644 --- a/pytrustnfe/nfse/ginfes/__init__.py +++ b/pytrustnfe/nfse/ginfes/__init__.py @@ -3,9 +3,12 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import os -import suds +from requests import Session +from zeep import Client +from zeep.transports import Transport +from requests.packages.urllib3 import disable_warnings + from pytrustnfe.xml import render_xml, sanitize_response -from pytrustnfe.client import get_authenticated_client from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key from pytrustnfe.nfe.assinatura import Assinatura @@ -33,19 +36,21 @@ def _send(certificado, method, **kwargs): cert, key = extract_cert_and_key_from_pfx( certificado.pfx, certificado.password) cert, key = save_cert_key(cert, key) - client = get_authenticated_client(base_url, cert, key) - try: - xml_send = kwargs['xml'] - header = '3' #noqa - response = getattr(client.service, method)(header, xml_send) - except suds.WebFault as e: - return { - 'sent_xml': xml_send, - 'received_xml': e.fault.faultstring, - 'object': None - } - - response, obj = sanitize_response(response) + + header = '3' #noqa + + disable_warnings() + session = Session() + session.cert = (cert, key) + session.verify = False + transport = Transport(session=session) + + client = Client(base_url, transport=transport) + + xml_send = kwargs['xml'] + response = client.service[method](header, xml_send) + + response, obj = sanitize_response(response.encode('utf-8')) return { 'sent_xml': xml_send, 'received_xml': response, diff --git a/requirements.txt b/requirements.txt index 92f9079..63f4786 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,3 +15,4 @@ reportlab pytest pytest-cov pytz +zeep diff --git a/setup.py b/setup.py index 4bf323b..c5686a4 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages -VERSION = "0.9.20" +VERSION = "0.9.21" setup( @@ -53,6 +53,7 @@ later (LGPLv2+)', 'suds-jurko-requests >= 1.2', 'reportlab', 'pytz', + 'zeep', ], tests_require=[ 'pytest',