diff --git a/pytrustnfe/nfse/campinas/__init__.py b/pytrustnfe/nfse/campinas/__init__.py new file mode 100644 index 0000000..e5536af --- /dev/null +++ b/pytrustnfe/nfse/campinas/__init__.py @@ -0,0 +1,76 @@ +# -*- encoding: utf-8 -*- +# © 2017 Fábio Luna, Trustcode +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import os +import suds +from lxml import etree +from pytrustnfe.xml import render_xml, sanitize_response +from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key +from pytrustnfe.nfse.assinatura import Assinatura +from pytrustnfe.client import get_client + + +def _render(certificado, method, **kwargs): + path = os.path.join(os.path.dirname(__file__), 'templates') + if method == "testeEnviar": + xml_send = render_xml(path, 'enviar.xml', True, **kwargs) + else: + xml_send = render_xml(path, '%s.xml' % method, False, **kwargs) + + if type(xml_send) != str: + xml_send = etree.tostring(xml_send) + + return xml_send + + +def _send(certificado, method, **kwargs): + url = 'http://issdigital.campinas.sp.gov.br/WsNFe2/LoteRps.jws?wsdl' # noqa + + path = os.path.join(os.path.dirname(__file__), 'templates') + + xml_send = _render(path, method, **kwargs) + client = get_client(url) + + if certificado: + cert, key = extract_cert_and_key_from_pfx( + certificado.pfx, certificado.password) + cert, key = save_cert_key(cert, key) + signer = Assinatura(cert, key, certificado.password) + xml_send = signer.assina_xml(xml_send, '') + + try: + response = getattr(client.service, method)(xml_send) + response, obj = sanitize_response(response.encode()) + except suds.WebFault as e: + return { + 'sent_xml': xml_send, + 'received_xml': e.fault.faultstring, + 'object': None + } + + return { + 'sent_xml': xml_send, + 'received_xml': response, + 'object': obj + } + + +def enviar(certificado, **kwargs): + return _send(certificado, 'enviar', **kwargs) + + +def teste_enviar(certificado, **kwargs): + return _send(certificado, 'testeEnviar', **kwargs) + + +def cancelar(certificado, ** kwargs): + return _send(certificado, 'cancelar', **kwargs) + + +def consulta_lote(**kwargs): + return _send(False, 'consultarLote', **kwargs) + + +def consultar_lote_rps(certificado, **kwarg): + return _send(certificado, 'consultarNFSeRps', **kwarg) diff --git a/pytrustnfe/nfse/campinas/templates/cancelar.xml b/pytrustnfe/nfse/campinas/templates/cancelar.xml new file mode 100644 index 0000000..d72086b --- /dev/null +++ b/pytrustnfe/nfse/campinas/templates/cancelar.xml @@ -0,0 +1,18 @@ + + + {{ cancelamento.cidade }} + {{ cancelamento.cpf_cnpj }} + true + 1 + + + + {{ cancelamento.inscricao_municipal }} + {{ cancelamento.nota_id }} + {{ cancelamento.assinatura }} + {{ cancelamento.motivo }} + + + diff --git a/pytrustnfe/nfse/campinas/templates/consulta_notas.xml b/pytrustnfe/nfse/campinas/templates/consulta_notas.xml new file mode 100644 index 0000000..4a666d0 --- /dev/null +++ b/pytrustnfe/nfse/campinas/templates/consulta_notas.xml @@ -0,0 +1,11 @@ + + +{{ consulta.cidade }} +{{ consulta.cpf_cnpj }} +{{ consulta.inscricao_municipal }} +{{ consulta.data_inicio }} +{{ consulta.data_final }} +{{ consulta.nota_inicial }} +1 + + \ No newline at end of file diff --git a/pytrustnfe/nfse/campinas/templates/consultarLote.xml b/pytrustnfe/nfse/campinas/templates/consultarLote.xml new file mode 100644 index 0000000..24afc5d --- /dev/null +++ b/pytrustnfe/nfse/campinas/templates/consultarLote.xml @@ -0,0 +1,10 @@ + + + {{ consulta.cidade }} + {{ consulta.cpf_cnpj }} + 1 + {{ consulta.lote }} + + \ No newline at end of file diff --git a/pytrustnfe/nfse/campinas/templates/enviar.xml b/pytrustnfe/nfse/campinas/templates/enviar.xml new file mode 100644 index 0000000..7e4b178 --- /dev/null +++ b/pytrustnfe/nfse/campinas/templates/enviar.xml @@ -0,0 +1,108 @@ + + + {{ nfse.cidade }} + {{ nfse.cpf_cnpj }} + {{ nfse.remetente }} + {{ nfse.transacao }} + {{ nfse.data_inicio|format_date }} + {{ nfse.data_fim|format_date }} + {{ nfse.total_rps }} + {{ nfse.total_servicos }} + {{ nfse.total_deducoes }} + 1 + WS + + + {% for rps in nfse.lista_rps -%} + + {{ rps.assinatura }} + {{ rps.prestador.inscricao_municipal }} + + {{ rps.prestador.razao_social }} + RPS + {{ rps.serie }} + {{ rps.numero }} + {{ rps.data_emissao|format_datetime }} + + {{ rps.situacao }} + + 0 + 0 + 1900-01-01 + {{ rps.serie_prestacao }} + {{ rps.tomador.inscricao_municipal }} + {{ rps.tomador.cpf_cnpj }} + {{ rps.tomador.razao_social }} + + {{ rps.tomador.tipo_logradouro }} + + {{ rps.tomador.logradouro }} + {{ rps.tomador.numero }} + + {{ rps.tomador.tipo_bairro }} + {{ rps.tomador.bairro }} + {{ rps.tomador.cidade }} + {{ rps.tomador.cidade_descricao }} + + {{ rps.tomador.cep }} + {{ rps.tomador.email }} + {{ rps.codigo_atividade }} + {{ rps.aliquota_atividade }} + {{ rps.tipo_recolhimento }} + {{ rps.municipio_prestacao }} + + {{ rps.municipio_descricao_prestacao }} + + {{ rps.operacao }} + {{ rps.tributacao }} + {{ rps.valor_pis }} + {{ rps.valor_cofins }} + {{ rps.valor_inss }} + {{ rps.valor_ir }} + {{ rps.valor_csll }} + {{ rps.aliquota_pis }} + {{ rps.aliquota_cofins }} + {{ rps.aliquota_inss }} + {{ rps.aliquota_ir }} + {{ rps.aliquota_csll }} + {{ rps.descricao }} + {{ rps.prestador.ddd }} + {{ rps.prestador.telefone }} + {{ rps.tomador.ddd }} + {{ rps.tomador.telefone }} + {{ rps.motivo_cancelamento }} + {% if rps.deducoes|count > 0 %} + + {% for deducao in rps.deducoes -%} + + {{ deducao.por }} + {{ deducao.tipo }} + {{ deducao.cnpj_referencia }} + {{ deducao.nf_referencia }} + {{ deducao.valor_referencia }} + {{ deducao.percentual_deduzir }} + {{ deducao.valor_deduzir }} + + {% endfor %} + + {% endif %} + {% if rps.deducoes|count == 0 %} + + {% endif %} + + {% for item in rps.itens -%} + + {{ item.descricao }} + {{ item.quantidade }} + {{ item.valor_unitario }} + {{ item.valor_total }} + S + + {% endfor %} + + + {% endfor %} + + diff --git a/pytrustnfe/nfse/campinas/templates/soap_header.xml b/pytrustnfe/nfse/campinas/templates/soap_header.xml new file mode 100644 index 0000000..e9d1dd2 --- /dev/null +++ b/pytrustnfe/nfse/campinas/templates/soap_header.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/pytrustnfe/xml/__init__.py b/pytrustnfe/xml/__init__.py index c49bc59..408e04e 100644 --- a/pytrustnfe/xml/__init__.py +++ b/pytrustnfe/xml/__init__.py @@ -2,7 +2,6 @@ # © 2016 Danimar Ribeiro, Trustcode # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -import unicodedata from lxml import etree from lxml import objectify @@ -17,6 +16,7 @@ def recursively_empty(e): def render_xml(path, template_name, remove_empty, **nfe): + nfe = recursively_normalize(nfe) env = Environment( loader=FileSystemLoader(path), extensions=['jinja2.ext.with_']) @@ -54,6 +54,19 @@ def sanitize_response(response): continue i = elem.tag.find('}') if i >= 0: - elem.tag = elem.tag[i+1:] + elem.tag = elem.tag[i + 1:] objectify.deannotate(tree, cleanup_namespaces=True) return response, objectify.fromstring(etree.tostring(tree)) + + +def recursively_normalize(vals): + for item in vals: + if type(vals[item]) is str: + vals[item] = vals[item].strip() + vals[item] = filters.normalize_str(vals[item]) + elif type(vals[item]) is dict: + recursively_normalize(vals[item]) + elif type(vals[item]) is list: + for a in vals[item]: + recursively_normalize(a) + return vals diff --git a/setup.py b/setup.py index 89374c1..ff137cf 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ # coding=utf-8 from setuptools import setup, find_packages -VERSION = "0.9.1" +VERSION = "0.9.3" setup( name="PyTrustNFe3", @@ -27,6 +27,7 @@ later (LGPLv2+)', 'nfe/templates/*xml', 'nfe/fonts/*ttf', 'nfse/paulistana/templates/*xml', + 'nfse/campinas/templates/*xml', 'nfse/ginfes/templates/*xml', 'nfse/simpliss/templates/*xml', 'nfse/betha/templates/*xml',