From ab57f6c4e0c1a8175aa4cc0b8bbb5c891569a70e Mon Sep 17 00:00:00 2001 From: Flavyo Henrique Date: Mon, 4 Jan 2021 17:18:10 -0300 Subject: [PATCH 1/7] =?UTF-8?q?Adicionado=20gera=C3=A7=C3=A3o=20do=20RPS?= =?UTF-8?q?=20para=20a=20prefeitura=20de=20Goi=C3=A2nia/GO.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/nfse/goiania/__init__.py | 73 +++++++++++++++++++++++++ pytrustnfe/nfse/goiania/assinatura.py | 38 +++++++++++++ pytrustnfe/nfse/goiania/templates/GerarNfse.xml | 4 ++ pytrustnfe/nfse/goiania/templates/Rps.xml | 60 ++++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 pytrustnfe/nfse/goiania/__init__.py create mode 100644 pytrustnfe/nfse/goiania/assinatura.py create mode 100644 pytrustnfe/nfse/goiania/templates/GerarNfse.xml create mode 100644 pytrustnfe/nfse/goiania/templates/Rps.xml diff --git a/pytrustnfe/nfse/goiania/__init__.py b/pytrustnfe/nfse/goiania/__init__.py new file mode 100644 index 0000000..726b548 --- /dev/null +++ b/pytrustnfe/nfse/goiania/__init__.py @@ -0,0 +1,73 @@ +import os +import suds + +from lxml import etree + +from pytrustnfe.client import get_authenticated_client +from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key +from pytrustnfe.xml import render_xml, sanitize_response + +from .assinatura import Assinatura + + +def _render(certificado, method, **kwargs): + path = os.path.join(os.path.dirname(__file__), "templates") + xml_send = render_xml(path, f"{method}.xml", False, **kwargs) + signer = Assinatura(certificado.pfx, certificado.password) + xml_send = etree.fromstring(xml_send) + xml_send = signer.assina_xml(xml_send) + return xml_send + + +def _send(certificado, method, **kwargs): + base_url = "https://nfse.goiania.go.gov.br/ws/nfse.asmx?wsdl" + xml_send = kwargs["xml"] + 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: + response = getattr(client.service, method)(xml_send) + except suds.WebFault as e: + return { + "send_xml": str(xml_send), + "received_xml": str(e.fault.faultstring), + "object": None, + } + + response, obj = sanitize_response(response) + return {"send_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 strip_result(xml_received: str): + """ Retorna o código e a mensagem de retorno vindo do webservice """ + + xml = etree.fromstring(xml_received) + + if not xml: + return None, None + + ns = './/{http://nfse.goiania.go.gov.br/xsd/nfse_gyn_v02.xsd}' + msg_return = xml.find(f'{ns}MensagemRetorno') + code = msg_return.find(f"{ns}Codigo").text + msg = msg_return.find(f"{ns}Mensagem").text + return code, msg + + +def is_success(xml_received: str): + """ Retorna se a emissão da NFS-e deu certo """ + + code, _ = strip_result(xml_received) + + # Code L000 significa que a nota foi aprovada + return code == 'L000' diff --git a/pytrustnfe/nfse/goiania/assinatura.py b/pytrustnfe/nfse/goiania/assinatura.py new file mode 100644 index 0000000..94009fd --- /dev/null +++ b/pytrustnfe/nfse/goiania/assinatura.py @@ -0,0 +1,38 @@ +from lxml import etree +from pytrustnfe.certificado import extract_cert_and_key_from_pfx +from signxml import XMLSigner, methods +from pytrustnfe.nfe.assinatura import Assinatura as _Assinatura + + +class Assinatura(_Assinatura): + + def assina_xml(self, xml_element): + 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=methods.enveloped, + signature_algorithm=u"rsa-sha1", + digest_algorithm=u"sha1", + c14n_algorithm=u"http://www.w3.org/TR/2001/REC-xml-c14n-20010315", + ) + + ns = {} + ns[None] = signer.namespaces["ds"] + signer.namespaces = ns + element_signed = xml_element.find(".//{http://nfse.goiania.go.gov.br/xsd/nfse_gyn_v02.xsd}Rps") + signed_root = signer.sign( + xml_element, key=key.encode(), cert=cert.encode() + ) + signature = signed_root.find( + ".//{http://www.w3.org/2000/09/xmldsig#}Signature" + ) + + if element_signed is not None and signature is not None: + parent = xml_element.getchildren()[0] + parent.append(signature) + + return etree.tostring(xml_element, encoding=str) diff --git a/pytrustnfe/nfse/goiania/templates/GerarNfse.xml b/pytrustnfe/nfse/goiania/templates/GerarNfse.xml new file mode 100644 index 0000000..7546f02 --- /dev/null +++ b/pytrustnfe/nfse/goiania/templates/GerarNfse.xml @@ -0,0 +1,4 @@ + + + {% include 'Rps.xml' %} + diff --git a/pytrustnfe/nfse/goiania/templates/Rps.xml b/pytrustnfe/nfse/goiania/templates/Rps.xml new file mode 100644 index 0000000..e4785f9 --- /dev/null +++ b/pytrustnfe/nfse/goiania/templates/Rps.xml @@ -0,0 +1,60 @@ + + + + + {{ numero }} + {{ serie }} + {{ tipo }} + + {{ data_emissao }} + {{ status }} + + + + {{ servico.valor_servicos }} + {{ servico.valor_pis }} + {{ servico.valor_confins }} + {{ servico.valor_inss }} + {{ servico.valor_csll }} + + {{ servico.codigo_tributacao_municipio }} + {{ servico.discriminacao }} + {{ servico.codigo_municipio }} + + + + {% if prestador.cnpj_cpf|length == 14 %} + {{ prestador.cnpj_cpf }} + {% endif %} + {% if prestador.cnpj_cpf|length == 11 %} + {{ prestador.cnpj_cpf }} + {% endif %} + + {{ prestador.inscricao_municipal }} + + + + + {% if tomador.cnpj_cpf|length == 14 %} + {{ tomador.cnpj_cpf }} + {% endif %} + {% if prestador.cnpj_cpf|length == 11 %} + {{ prestador.cnpj_cpf }} + {% endif %} + + {% if tomador.inscricao_municipal %} + {{ tomador.inscricao_municipal }} + {% endif %} + + {{ tomador.razao_social }} + + {{ tomador.endereco.rua }} + {{ tomador.endereco.numero }} + {{ tomador.endereco.complemento }} + {{ tomador.endereco.bairro }} + {{ tomador.endereco.codigo_municipio }} + {{ tomador.endereco.uf }} + + + + From 27c9ce902b8645a104a496ba16c63940dfceafb7 Mon Sep 17 00:00:00 2001 From: Flavyo Henrique Date: Mon, 4 Jan 2021 17:34:26 -0300 Subject: [PATCH 2/7] =?UTF-8?q?-=20Corre=C3=A7=C3=A3o=20no=20nome=20do=20m?= =?UTF-8?q?=C3=A9todo.=20-=20Atualiza=C3=A7=C3=A3o=20do=20README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + pytrustnfe/nfse/goiania/__init__.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e2a6e17..926353f 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ NFSe - Cidades atendidas * **Paulistana** - São Paulo/SP * **Nota Carioca** - Rio de Janeiro/RJ * **Imperial** - Petrópolis/RH +* **Goiânia** - Goiânia/GO * [Susesu](cidades/susesu.md) - 3 cidades atendidas * [Simpliss](cidades/simpliss.md) - 18 cidade atendidas * [GINFES](cidaes/ginfes.md) - 79 cidades atendidas diff --git a/pytrustnfe/nfse/goiania/__init__.py b/pytrustnfe/nfse/goiania/__init__.py index 726b548..832f1d9 100644 --- a/pytrustnfe/nfse/goiania/__init__.py +++ b/pytrustnfe/nfse/goiania/__init__.py @@ -49,12 +49,12 @@ def gerar_nfse(certificado, **kwargs): return _send(certificado, "GerarNfse", **kwargs) -def strip_result(xml_received: str): +def split_result(xml_received: str): """ Retorna o código e a mensagem de retorno vindo do webservice """ xml = etree.fromstring(xml_received) - if not xml: + if xml is None: return None, None ns = './/{http://nfse.goiania.go.gov.br/xsd/nfse_gyn_v02.xsd}' @@ -67,7 +67,7 @@ def strip_result(xml_received: str): def is_success(xml_received: str): """ Retorna se a emissão da NFS-e deu certo """ - code, _ = strip_result(xml_received) + code, _ = split_result(xml_received) # Code L000 significa que a nota foi aprovada return code == 'L000' From 486d2ad1d907680c46d31549ea4b853cf9e3b2fe Mon Sep 17 00:00:00 2001 From: Flavyo Henrique Date: Tue, 5 Jan 2021 11:03:02 -0300 Subject: [PATCH 3/7] =?UTF-8?q?-=20Adicionado=20consulta=20do=20RPS=20para?= =?UTF-8?q?=20a=20prefeitura=20de=20Goi=C3=A2nia/GO.=20-=20Colocado=20como?= =?UTF-8?q?=20opcional=20a=20tag=20endere=C3=A7o=20na=20gera=C3=A7=C3=A3o?= =?UTF-8?q?=20do=20rps=20conforme=20manual=20da=20prefeitura.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/nfse/goiania/__init__.py | 26 ++++++++++++++-------- .../nfse/goiania/templates/ConsultarNfseRps.xml | 19 ++++++++++++++++ pytrustnfe/nfse/goiania/templates/Rps.xml | 2 ++ 3 files changed, 38 insertions(+), 9 deletions(-) create mode 100644 pytrustnfe/nfse/goiania/templates/ConsultarNfseRps.xml diff --git a/pytrustnfe/nfse/goiania/__init__.py b/pytrustnfe/nfse/goiania/__init__.py index 832f1d9..ed790fa 100644 --- a/pytrustnfe/nfse/goiania/__init__.py +++ b/pytrustnfe/nfse/goiania/__init__.py @@ -10,6 +10,9 @@ from pytrustnfe.xml import render_xml, sanitize_response from .assinatura import Assinatura +NAMESPACE = './/{http://nfse.goiania.go.gov.br/xsd/nfse_gyn_v02.xsd}' + + def _render(certificado, method, **kwargs): path = os.path.join(os.path.dirname(__file__), "templates") xml_send = render_xml(path, f"{method}.xml", False, **kwargs) @@ -39,16 +42,22 @@ def _send(certificado, method, **kwargs): return {"send_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): + """" Gera uma NFSe de saída """ + if "xml" not in kwargs: - kwargs["xml"] = xml_gerar_nfse(certificado, **kwargs) + kwargs["xml"] = _render(certificado, "GerarNfse", **kwargs) return _send(certificado, "GerarNfse", **kwargs) +def consulta_nfse_por_rps(certificado, **kwargs): + """ Consulta os dados de um NFSe já emitida """ + + if "xml" not in kwargs: + kwargs["xml"] = _render(certificado, "ConsultarNfseRps", **kwargs) + return _send(certificado, "ConsultarNfseRps", **kwargs) + + def split_result(xml_received: str): """ Retorna o código e a mensagem de retorno vindo do webservice """ @@ -57,10 +66,9 @@ def split_result(xml_received: str): if xml is None: return None, None - ns = './/{http://nfse.goiania.go.gov.br/xsd/nfse_gyn_v02.xsd}' - msg_return = xml.find(f'{ns}MensagemRetorno') - code = msg_return.find(f"{ns}Codigo").text - msg = msg_return.find(f"{ns}Mensagem").text + msg_return = xml.find(f'{NAMESPACE}MensagemRetorno') + code = msg_return.find(f"{NAMESPACE}Codigo").text + msg = msg_return.find(f"{NAMESPACE}Mensagem").text return code, msg diff --git a/pytrustnfe/nfse/goiania/templates/ConsultarNfseRps.xml b/pytrustnfe/nfse/goiania/templates/ConsultarNfseRps.xml new file mode 100644 index 0000000..32085c3 --- /dev/null +++ b/pytrustnfe/nfse/goiania/templates/ConsultarNfseRps.xml @@ -0,0 +1,19 @@ + + + + {{ numero }} + {{ serie }} + {{ tipo }} + + + + {% if cnpj_cpf|length == 14 %} + {{ cnpj_cpf }} + {% endif %} + {% if cnpj_cpf|length == 11 %} + {{ cnpj_cpf }} + {% endif %} + + {{ inscricao_municipal }} + + diff --git a/pytrustnfe/nfse/goiania/templates/Rps.xml b/pytrustnfe/nfse/goiania/templates/Rps.xml index e4785f9..8160243 100644 --- a/pytrustnfe/nfse/goiania/templates/Rps.xml +++ b/pytrustnfe/nfse/goiania/templates/Rps.xml @@ -47,6 +47,7 @@ {% endif %} {{ tomador.razao_social }} + {% if tomador.endereco %} {{ tomador.endereco.rua }} {{ tomador.endereco.numero }} @@ -55,6 +56,7 @@ {{ tomador.endereco.codigo_municipio }} {{ tomador.endereco.uf }} + {% endif %} From 54bf90863ef83da4d9969c0a2d274b33e39f3ebe Mon Sep 17 00:00:00 2001 From: Flavyo Henrique Date: Tue, 5 Jan 2021 16:49:10 -0300 Subject: [PATCH 4/7] =?UTF-8?q?-=20Colocado=20como=20opcional=20tags=20de?= =?UTF-8?q?=20preenchimento=20de=20impostos=20do=20servi=C3=A7o=20conforme?= =?UTF-8?q?=20manual=20da=20prefeitura.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/nfse/goiania/templates/Rps.xml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pytrustnfe/nfse/goiania/templates/Rps.xml b/pytrustnfe/nfse/goiania/templates/Rps.xml index 8160243..7454cd4 100644 --- a/pytrustnfe/nfse/goiania/templates/Rps.xml +++ b/pytrustnfe/nfse/goiania/templates/Rps.xml @@ -12,10 +12,11 @@ {{ servico.valor_servicos }} - {{ servico.valor_pis }} - {{ servico.valor_confins }} - {{ servico.valor_inss }} - {{ servico.valor_csll }} + {% if servico.valor_pis %}{{ servico.valor_pis }}{% endif %} + {% if servico.valor_confins %}{{ servico.valor_confins }}{% endif %} + {% if servico.valor_inss %}{{ servico.valor_inss }}{% endif %} + {% if servico.valor_csll %}{{ servico.valor_csll }}{% endif %} + {% if servico.aliquota %}{{ servico.aliquota }}{% endif %} {{ servico.codigo_tributacao_municipio }} {{ servico.discriminacao }} From 17ee875c18ce216c00fa2d78cddc212ad89aeb18 Mon Sep 17 00:00:00 2001 From: Flavyo Henrique Date: Tue, 5 Jan 2021 17:34:01 -0300 Subject: [PATCH 5/7] =?UTF-8?q?-=20Colocado=20como=20opcional=20tags=20de?= =?UTF-8?q?=20preenchimento=20de=20endere=C3=A7o=20do=20tomador=20conforme?= =?UTF-8?q?=20manual.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/nfse/goiania/templates/Rps.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pytrustnfe/nfse/goiania/templates/Rps.xml b/pytrustnfe/nfse/goiania/templates/Rps.xml index 7454cd4..0667e96 100644 --- a/pytrustnfe/nfse/goiania/templates/Rps.xml +++ b/pytrustnfe/nfse/goiania/templates/Rps.xml @@ -50,12 +50,12 @@ {{ tomador.razao_social }} {% if tomador.endereco %} - {{ tomador.endereco.rua }} - {{ tomador.endereco.numero }} - {{ tomador.endereco.complemento }} - {{ tomador.endereco.bairro }} - {{ tomador.endereco.codigo_municipio }} - {{ tomador.endereco.uf }} + {% if tomador.endereco.rua %}{{ tomador.endereco.rua }}{%endif %} + {% if tomador.endereco.numero %}{{ tomador.endereco.numero }}{%endif %} + {% if tomador.endereco.complemento %}{{ tomador.endereco.complemento }}{%endif %} + {% if tomador.endereco.bairro %}{{ tomador.endereco.bairro }}{%endif %} + {% if tomador.endereco.codigo_municipio %}{{ tomador.endereco.codigo_municipio }}{%endif %} + {% if tomador.endereco.uf %}{{ tomador.endereco.uf }}{%endif %} {% endif %} From 79024b474795e19233ab3c523aeadde6ab0712d7 Mon Sep 17 00:00:00 2001 From: Flavyo Henrique Date: Thu, 7 Jan 2021 16:32:14 -0300 Subject: [PATCH 6/7] =?UTF-8?q?Adicionado=20m=C3=A9todo=20para=20gerar=20o?= =?UTF-8?q?=20xml=20da=20NFSe=20para=20a=20prefeitura=20de=20Goi=C3=A2nia.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/nfse/goiania/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pytrustnfe/nfse/goiania/__init__.py b/pytrustnfe/nfse/goiania/__init__.py index ed790fa..9390109 100644 --- a/pytrustnfe/nfse/goiania/__init__.py +++ b/pytrustnfe/nfse/goiania/__init__.py @@ -42,11 +42,17 @@ def _send(certificado, method, **kwargs): return {"send_xml": str(xml_send), "received_xml": str(response), "object": obj} +def xml_gerar_nfse(certificado, **kwargs): + """ Retorna o XML montado para ser enviado para o Webservice """ + + return _render(certificado, "GerarNfse", **kwargs) + + def gerar_nfse(certificado, **kwargs): """" Gera uma NFSe de saída """ if "xml" not in kwargs: - kwargs["xml"] = _render(certificado, "GerarNfse", **kwargs) + kwargs["xml"] = xml_gerar_nfse return _send(certificado, "GerarNfse", **kwargs) From a008f99434a7e36a57112b5355b03f03f0d2d450 Mon Sep 17 00:00:00 2001 From: Flavyo Henrique Date: Fri, 8 Jan 2021 18:00:42 -0300 Subject: [PATCH 7/7] =?UTF-8?q?Retirado=20m=C3=A9todos=20n=C3=A3o=20padr?= =?UTF-8?q?=C3=B5es=20na=20emiss=C3=A3o=20de=20NFSe=20para=20a=20prefeitur?= =?UTF-8?q?a=20de=20Goi=C3=A2nia/GO.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/nfse/goiania/__init__.py | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/pytrustnfe/nfse/goiania/__init__.py b/pytrustnfe/nfse/goiania/__init__.py index 9390109..7a7b013 100644 --- a/pytrustnfe/nfse/goiania/__init__.py +++ b/pytrustnfe/nfse/goiania/__init__.py @@ -10,9 +10,6 @@ from pytrustnfe.xml import render_xml, sanitize_response from .assinatura import Assinatura -NAMESPACE = './/{http://nfse.goiania.go.gov.br/xsd/nfse_gyn_v02.xsd}' - - def _render(certificado, method, **kwargs): path = os.path.join(os.path.dirname(__file__), "templates") xml_send = render_xml(path, f"{method}.xml", False, **kwargs) @@ -62,26 +59,3 @@ def consulta_nfse_por_rps(certificado, **kwargs): if "xml" not in kwargs: kwargs["xml"] = _render(certificado, "ConsultarNfseRps", **kwargs) return _send(certificado, "ConsultarNfseRps", **kwargs) - - -def split_result(xml_received: str): - """ Retorna o código e a mensagem de retorno vindo do webservice """ - - xml = etree.fromstring(xml_received) - - if xml is None: - return None, None - - msg_return = xml.find(f'{NAMESPACE}MensagemRetorno') - code = msg_return.find(f"{NAMESPACE}Codigo").text - msg = msg_return.find(f"{NAMESPACE}Mensagem").text - return code, msg - - -def is_success(xml_received: str): - """ Retorna se a emissão da NFS-e deu certo """ - - code, _ = split_result(xml_received) - - # Code L000 significa que a nota foi aprovada - return code == 'L000'