diff --git a/README.md b/README.md index 5bcec73..671d27b 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](cidades/ginfes.md) - 79 cidades atendidas diff --git a/pytrustnfe/nfse/goiania/__init__.py b/pytrustnfe/nfse/goiania/__init__.py new file mode 100644 index 0000000..7a7b013 --- /dev/null +++ b/pytrustnfe/nfse/goiania/__init__.py @@ -0,0 +1,61 @@ +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): + """ 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"] = xml_gerar_nfse + 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) 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/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/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..0667e96 --- /dev/null +++ b/pytrustnfe/nfse/goiania/templates/Rps.xml @@ -0,0 +1,63 @@ + + + + + {{ numero }} + {{ serie }} + {{ tipo }} + + {{ data_emissao }} + {{ status }} + + + + {{ servico.valor_servicos }} + {% 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 }} + {{ 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 }} + {% if tomador.endereco %} + + {% 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 %} + + +