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',