diff --git a/.travis.yml b/.travis.yml
index 4e404cd..c3e7bbd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,6 @@
language: python
python:
- "3.6"
-virtual_env:
- system_site_packages: true
install:
- pip install --upgrade pip
- pip install -r requirements.txt
diff --git a/pytrustnfe/nfse/paulistana/templates/EnvioLoteRPS.xml b/pytrustnfe/nfse/paulistana/templates/EnvioLoteRPS.xml
index 23eb9e7..bf5e52f 100644
--- a/pytrustnfe/nfse/paulistana/templates/EnvioLoteRPS.xml
+++ b/pytrustnfe/nfse/paulistana/templates/EnvioLoteRPS.xml
@@ -6,7 +6,7 @@
false
{{ nfse.data_inicio }}
{{ nfse.data_fim }}
- 1
+ {{ nfse.lista_rps|length }}
{{ nfse.total_servicos }}
{{ nfse.total_deducoes }}
@@ -32,6 +32,7 @@
{{ rps.codigo_atividade }}
{{ rps.aliquota_atividade }}
{{ rps.iss_retido | default('false') }}
+ {% if rps.tomador.tipo_cpfcnpj != 3 %}
{% if rps.tomador.tipo_cpfcnpj == 1 -%}
{{ rps.tomador.cpf_cnpj }}
@@ -40,20 +41,34 @@
{{ rps.tomador.cpf_cnpj }}
{% endif %}
+ {% endif %}
{% if rps.tomador.inscricao_municipal -%}
{{ rps.tomador.inscricao_municipal }}
{% endif %}
{{ rps.tomador.razao_social|normalize|escape }}
+ {% if rps.tomador.cidade %}
+ {% if rps.tomador.tipo_logradouro %}
{{ rps.tomador.tipo_logradouro }}
+ {% endif %}
+ {% if rps.tomador.logradouro %}
{{ rps.tomador.logradouro|normalize|escape }}
+ {% endif %}
+ {% if rps.tomador.numero %}
{{ rps.tomador.numero }}
+ {% endif %}
+ {% if rps.tomador.complemento %}
{{ rps.tomador.complemento|normalize|escape }}
+ {% endif %}
+ {% if rps.tomador.bairro %}
{{ rps.tomador.bairro }}
+ {% endif %}
{{ rps.tomador.cidade }}
{{ rps.tomador.uf }}
- {{ rps.tomador.cep }}
+ {% if rps.tomador.cep %}{{ rps.tomador.cep }}{% endif %}
+ {% endif %}
+ {% if rps.tomador.email %}{{ rps.tomador.email }}{% endif %}
{{ rps.descricao|normalize|escape }}
{{ rps.valor_carga_tributaria }}
{{ rps.fonte_carga_tributaria }}
diff --git a/pytrustnfe/nfse/paulistana/templates/EnvioRPS.xml b/pytrustnfe/nfse/paulistana/templates/EnvioRPS.xml
index ae35893..987a56a 100644
--- a/pytrustnfe/nfse/paulistana/templates/EnvioRPS.xml
+++ b/pytrustnfe/nfse/paulistana/templates/EnvioRPS.xml
@@ -12,20 +12,21 @@
{{ rps.serie }}
{{ rps.numero }}
- RPS-M
+ {{ rps.tipo_rps | default('RPS') }}
{{ rps.data_emissao }}
N
- T
- {{ nfse.total_servicos }}
- {{ nfse.total_deducoes }}
- {{ rps.valor_pis }}
- {{ rps.valor_cofins }}
- {{ rps.valor_inss }}
- {{ rps.valor_pis }}
- {{ rps.valor_csll }}
+ {{ rps.tributacao_rps | default('T') }}
+ {{ rps.valor_servico }}
+ {{ rps.valor_deducao }}
+ {{ rps.valor_pis | default('0.00') }}
+ {{ rps.valor_cofins | default('0.00') }}
+ {{ rps.valor_inss | default('0.00') }}
+ {{ rps.valor_ir | default('0.00') }}
+ {{ rps.valor_csll | default('0.00') }}
{{ rps.codigo_atividade }}
{{ rps.aliquota_atividade }}
- false
+ {{ rps.iss_retido | default('false') }}
+ {% if rps.tomador.tipo_cpfcnpj != 3 %}
{% if rps.tomador.tipo_cpfcnpj == 1 -%}
{{ rps.tomador.cpf_cnpj }}
@@ -34,20 +35,37 @@
{{ rps.tomador.cpf_cnpj }}
{% endif %}
- {{ rps.tomador.razao_social }}
+ {% endif %}
+ {% if rps.tomador.inscricao_municipal -%}
+ {{ rps.tomador.inscricao_municipal }}
+ {% endif %}
+ {{ rps.tomador.razao_social|normalize|escape }}
+ {% if rps.tomador.cidade %}
- {{ rps.tomador.tipo_logradouro }}
- {{ rps.tomador.logradouro }}
- {{ rps.tomador.numero }}
- {{ rps.tomador.complemento }}
- {{ rps.tomador.bairro }}
- {{ rps.tomador.cidade }}
- {{ rps.tomador.uf }}
- {{ rps.tomador.cep }}
+ {% if rps.tomador.tipo_logradouro %}
+ {{ rps.tomador.tipo_logradouro }}
+ {% endif %}
+ {% if rps.tomador.logradouro %}
+ {{ rps.tomador.logradouro|normalize|escape }}
+ {% endif %}
+ {% if rps.tomador.numero %}
+ {{ rps.tomador.numero }}
+ {% endif %}
+ {% if rps.tomador.complemento %}
+ {{ rps.tomador.complemento|normalize|escape }}
+ {% endif %}
+ {% if rps.tomador.bairro %}
+ {{ rps.tomador.bairro }}
+ {% endif %}
+ {{ rps.tomador.cidade }}
+ {{ rps.tomador.uf }}
+ {% if rps.tomador.cep %}{{ rps.tomador.cep }}{% endif %}
- {{ rps.descricao }}
+ {% endif %}
+ {% if rps.tomador.email %}{{ rps.tomador.email }}{% endif %}
+ {{ rps.descricao|normalize|escape }}
{{ rps.valor_carga_tributaria }}
{{ rps.fonte_carga_tributaria }}
- {% endfor %}
+ {% endfor %}
diff --git a/setup.py b/setup.py
index b007fa3..e0594d3 100644
--- a/setup.py
+++ b/setup.py
@@ -2,11 +2,11 @@
from setuptools import setup, find_packages
-VERSION = "1.0.45"
+VERSION = "1.0.46"
setup(
- name="PyTrustNFe3",
+ name="pytrustnfe3",
version=VERSION,
author="Danimar Ribeiro",
author_email='danimaribeiro@gmail.com',
@@ -48,6 +48,8 @@ later (LGPLv2+)',
long_description=open('README.md', 'r').read(),
long_description_content_type='text/markdown',
install_requires=[
+ 'urllib3',
+ 'xmlsec==1.3.3', # apt update;apt install libxmlsec1-dev pkg-config -y
'Jinja2 >= 2.8',
'pyOpenSSL >= 16.0.0, < 18',
'signxml >= 2.4.0',
diff --git a/tests/XMLs/paulistana_signature.xml b/tests/XMLs/paulistana_signature.xml
index 70d26dc..644d7a8 100644
--- a/tests/XMLs/paulistana_signature.xml
+++ b/tests/XMLs/paulistana_signature.xml
@@ -1,4 +1,4 @@
-12345678901234false2016-08-292016-08-291E4fpHYkQa7Naxn6IKGb7NwwZu5tPk/KXJ9hCwtZgq0xvKS450aQqqBL+7Iv46lTgqrSMu7+gLrl+LC1qs/8aT2mbHE8uaVFSbzwZ+sF/BkcT6nsFHLMswEiTAEs95Jb7hN1cC91xqQGRH4buw0TzxHKmhuLJ22WwtG/scxyKtjM=12345611RPS2016-08-29NT0.000.000.000.000.00074985.00false123456Trustcode1Vinicius de Moraes, 4242CorregoFloripaSC88037240Venda de servico
+12345678901234false2016-08-292016-08-291E4fpHYkQa7Naxn6IKGb7NwwZu5tPk/KXJ9hCwtZgq0xvKS450aQqqBL+7Iv46lTgqrSMu7+gLrl+LC1qs/8aT2mbHE8uaVFSbzwZ+sF/BkcT6nsFHLMswEiTAEs95Jb7hN1cC91xqQGRH4buw0TzxHKmhuLJ22WwtG/scxyKtjM=12345611RPS2016-08-29NT0.000.000.000.000.00074985.00false123456Trustcode1Vinicius de Moraes, 4242CorregoFloripaSC88037240Venda de servico
@@ -8,12 +8,12 @@
-IAh8GGlbp/Tnqma+2RZ7UrGZhTc=
+Thwvs++WdhRuXOVgMxXTY/9Zih0=
-gjkMTCq0uuaX8tkRBlLjgybn8a2O4Axl6HHq1MN8nnEMliERcziU3pa3r1jbghlE
-EUyIO8bTZ0V7c05pQvHQgVUHcSo6vHld4ZQNk7JfMfmpez4uxrUeuSrSqSLCwT9W
-NmTY9EJ16GyrQNELw+SkYuEFOvqZTU3qjDZkLddQ8bc=
+fvJR0msutiLI9KpUY/8VDPqmDeGYpXt/JvY6LUQZlGjjGb71jM2cLEHotM4lwJLi
+WKLvhSBbaLQQm/OFm1KbQ8TRrEJl8NMYv2bABNoH9OxIn5Ecnb4jxCCAaIDN3iXy
+B7oYCq5nqtfsFGplU29enQ//1SrRTE4MDsOwoN8bX0c=
MIICMTCCAZqgAwIBAgIQfYOsIEVuAJ1FwwcTrY0t1DANBgkqhkiG9w0BAQUFADBX
diff --git a/tests/XMLs/xml_send_rps_batch_to_paulistana.xml b/tests/XMLs/xml_send_rps_batch_to_paulistana.xml
new file mode 100644
index 0000000..2eebb3b
--- /dev/null
+++ b/tests/XMLs/xml_send_rps_batch_to_paulistana.xml
@@ -0,0 +1 @@
+12345678901234false2016-08-292016-08-29512312345610RPS2016-08-29NT0.000.000.000.000.00074985.00false12345678923256123456Trustcode1Vinicius de Moraes, 4242aaaCorregoFloripaSC88037240user@user.comVenda de servico12312345611RPS2016-08-29NT0.000.000.000.000.00074985.00false12345678923256123456Trustcode1Vinicius de Moraes, 4242aaaCorregoFloripaSC88037240user@user.comVenda de servico12312345612RPS2016-08-29NT0.000.000.000.000.00074985.00false12345678923256123456Trustcode1Vinicius de Moraes, 4242aaaCorregoFloripaSC88037240user@user.comVenda de servico12312345613RPS2016-08-29NT0.000.000.000.000.00074985.00false12345678923256123456Trustcode1Vinicius de Moraes, 4242aaaCorregoFloripaSC88037240user@user.comVenda de servico12312345614RPS2016-08-29NT0.000.000.000.000.00074985.00false12345678923256123456Trustcode1Vinicius de Moraes, 4242aaaCorregoFloripaSC88037240user@user.comVenda de servico
\ No newline at end of file
diff --git a/tests/const.py b/tests/const.py
new file mode 100644
index 0000000..41d77fb
--- /dev/null
+++ b/tests/const.py
@@ -0,0 +1,56 @@
+LOTE_RPS = [
+ {
+ 'assinatura': '123',
+ 'serie': '1',
+ 'numero': str(i),
+ 'data_emissao': '2016-08-29',
+ 'codigo_atividade': '07498',
+ 'total_servicos': '2.00',
+ 'total_deducoes': '3.00',
+ 'prestador': {
+ 'inscricao_municipal': '123456'
+ },
+ 'tomador': {
+ 'tipo_cpfcnpj': 1,
+ 'cpf_cnpj': '12345678923256',
+ 'inscricao_municipal': '123456',
+ 'razao_social': 'Trustcode',
+ 'tipo_logradouro': '1',
+ 'complemento': 'aaa',
+ 'logradouro': 'Vinicius de Moraes, 42',
+ 'numero': '42',
+ 'bairro': 'Corrego',
+ 'cidade': 'Floripa',
+ 'uf': 'SC',
+ 'cep': '88037240',
+ 'email': 'user@user.com'
+ },
+ 'codigo_atividade': '07498',
+ 'aliquota_atividade': '5.00',
+ 'descricao': 'Venda de servico'
+ } for i in range(5)
+]
+
+DEFAULT_RPS = [
+ {
+ 'assinatura': '123',
+ 'serie': '1',
+ 'numero': '1',
+ 'data_emissao': '2016-08-29',
+ 'codigo_atividade': '07498',
+ 'prestador': {
+ 'inscricao_municipal': '123456'
+ },
+ 'tomador': {
+ 'tipo_cpfcnpj': 1,
+ 'cpf_cnpj': '12345678923256',
+ },
+ }
+]
+
+NFSE = {
+ 'cpf_cnpj': '12345678901234',
+ 'data_inicio': '2016-08-29',
+ 'data_fim': '2016-08-29',
+ 'lista_rps': []
+}
diff --git a/tests/test_nfse_paulistana_email_tomador.py b/tests/test_nfse_paulistana_email_tomador.py
new file mode 100644
index 0000000..846edc1
--- /dev/null
+++ b/tests/test_nfse_paulistana_email_tomador.py
@@ -0,0 +1,35 @@
+# coding=utf-8
+
+import unittest
+from pytrustnfe.xml import render_xml, sanitize_response
+from tests.const import NFSE, DEFAULT_RPS
+
+template_path = 'pytrustnfe/nfse/paulistana/templates'
+
+
+def _get_nfse(lista_rps):
+ nfse = NFSE
+ nfse['lista_rps'] = lista_rps
+ return nfse
+
+
+def get_objects(nfse):
+ xml_rps = render_xml(template_path, 'EnvioRPS.xml', False, nfse=nfse)
+ _, obj_rps = sanitize_response(xml_rps)
+
+ xml_lote_rps = render_xml(template_path, 'EnvioLoteRPS.xml', False, nfse=nfse)
+ _, obj_lote_rps = sanitize_response(xml_lote_rps)
+
+ return obj_rps, obj_lote_rps
+
+
+class test_nfse_paulistana_email_tomador(unittest.TestCase):
+
+ def test_rps_sem_email(self):
+ nfse = _get_nfse(DEFAULT_RPS)
+
+ obj_rps, obj_lote_rps = get_objects(nfse)
+
+ self.assertFalse(hasattr(obj_rps.RPS, 'EmailTomador'))
+ self.assertFalse(hasattr(obj_lote_rps.RPS, 'EmailTomador'))
+
diff --git a/tests/test_nfse_paulistana_endereco_tomador.py b/tests/test_nfse_paulistana_endereco_tomador.py
new file mode 100644
index 0000000..0f7e354
--- /dev/null
+++ b/tests/test_nfse_paulistana_endereco_tomador.py
@@ -0,0 +1,54 @@
+# coding=utf-8
+
+import unittest
+from pytrustnfe.xml import render_xml, sanitize_response
+from tests.const import NFSE, DEFAULT_RPS
+
+
+attrs = ['TipoLogradouro', 'Logradouro', 'NumeroEndereco', 'ComplementoEndereco', 'Bairro', 'CEP']
+
+template_path = 'pytrustnfe/nfse/paulistana/templates'
+
+
+def _get_nfse(lista_rps):
+ nfse = NFSE
+ nfse['lista_rps'] = lista_rps
+ return nfse
+
+
+def get_objects(nfse):
+ xml_rps = render_xml(template_path, 'EnvioRPS.xml', False, nfse=nfse)
+ _, obj_rps = sanitize_response(xml_rps)
+
+ xml_lote_rps = render_xml(template_path, 'EnvioLoteRPS.xml', False, nfse=nfse)
+ _, obj_lote_rps = sanitize_response(xml_lote_rps)
+
+ return obj_rps, obj_lote_rps
+
+
+class test_nfse_paulistana_endereco_tomador(unittest.TestCase):
+
+ def test_rps_sem_cidade(self):
+ nfse = _get_nfse(DEFAULT_RPS)
+
+ obj_rps, obj_lote_rps = get_objects(nfse)
+
+ self.assertFalse(hasattr(obj_rps.RPS, 'EnderecoTomador'))
+ self.assertFalse(hasattr(obj_lote_rps.RPS, 'EnderecoTomador'))
+
+ def test_rps_sem_dados_endereco(self):
+ lista_rps = DEFAULT_RPS
+
+ for rps in lista_rps:
+ rps['tomador']['cidade'] = 'Florianópolis'
+
+ nfse = _get_nfse(lista_rps)
+
+ obj_rps, obj_lote_rps = get_objects(nfse)
+
+ self.assertTrue(hasattr(obj_rps.RPS, 'EnderecoTomador'))
+ self.assertTrue(hasattr(obj_lote_rps.RPS, 'EnderecoTomador'))
+
+ for attr in attrs:
+ self.assertFalse(hasattr(obj_rps.RPS.EnderecoTomador, attr))
+ self.assertFalse(hasattr(obj_lote_rps.RPS.EnderecoTomador, attr))
diff --git a/tests/test_nfse_paulistana_para_lote.py b/tests/test_nfse_paulistana_para_lote.py
new file mode 100644
index 0000000..2612992
--- /dev/null
+++ b/tests/test_nfse_paulistana_para_lote.py
@@ -0,0 +1,29 @@
+# coding=utf-8
+
+import os.path
+import unittest
+from pytrustnfe.xml import render_xml, sanitize_response
+from tests.const import LOTE_RPS, NFSE
+
+
+def _get_nfse():
+ nfse = NFSE
+ nfse['lista_rps'] = LOTE_RPS
+ return nfse
+
+
+class test_nfse_paulistana_para_lote(unittest.TestCase):
+ xml_path = os.path.join(os.path.dirname(__file__), 'XMLs')
+ template_path = 'pytrustnfe/nfse/paulistana/templates'
+ BATCH_SIZE = len(LOTE_RPS)
+
+ def test_envio_nfse(self):
+ nfse = _get_nfse()
+
+ xml_send = render_xml(self.template_path, 'EnvioLoteRPS.xml', False, nfse=nfse)
+ expected_xml = open(os.path.join(self.xml_path, 'xml_send_rps_batch_to_paulistana.xml'), 'r').read()
+
+ _, obj = sanitize_response(xml_send)
+
+ self.assertEqual(obj.Cabecalho.QtdRPS, self.BATCH_SIZE)
+ self.assertEqual(xml_send, expected_xml)
diff --git a/tests/test_nfse_paulistana_tipo_cpfcnpj.py b/tests/test_nfse_paulistana_tipo_cpfcnpj.py
new file mode 100644
index 0000000..5a8a4d2
--- /dev/null
+++ b/tests/test_nfse_paulistana_tipo_cpfcnpj.py
@@ -0,0 +1,60 @@
+# coding=utf-8
+
+import os.path
+import unittest
+from pytrustnfe.xml import render_xml, sanitize_response
+from tests.const import DEFAULT_RPS, NFSE
+
+template_path = 'pytrustnfe/nfse/paulistana/templates'
+
+
+def _get_nfse(tipo_cpfcnpj):
+ nfse = NFSE
+ lista_rps = DEFAULT_RPS
+
+ for rps in lista_rps:
+ rps['tomador']['tipo_cpfcnpj'] = tipo_cpfcnpj
+ rps['tomador']['cpf_cnpj'] = '12345678923256'
+
+ nfse['lista_rps'] = lista_rps
+ return nfse
+
+
+def get_objects(nfse):
+ xml_rps = render_xml(template_path, 'EnvioRPS.xml', False, nfse=nfse)
+ _, obj_rps = sanitize_response(xml_rps)
+
+ xml_lote_rps = render_xml(template_path, 'EnvioLoteRPS.xml', False, nfse=nfse)
+ _, obj_lote_rps = sanitize_response(xml_lote_rps)
+
+ return obj_rps, obj_lote_rps
+
+
+class test_nfse_paulistana_tipo_cpfcnpj(unittest.TestCase):
+
+ def test_tipo_cpfcnpj_1(self):
+ nfse = _get_nfse(tipo_cpfcnpj=1)
+
+ obj_rps, obj_lote_rps = get_objects(nfse)
+
+ self.assertTrue(hasattr(obj_rps.RPS, 'CPFCNPJTomador'))
+ self.assertTrue(hasattr(obj_rps.RPS.CPFCNPJTomador, 'CPF'))
+ self.assertTrue(hasattr(obj_lote_rps.RPS, 'CPFCNPJTomador'))
+ self.assertTrue(hasattr(obj_lote_rps.RPS.CPFCNPJTomador, 'CPF'))
+
+ def test_tipo_cpfcnpj_2(self):
+ nfse = _get_nfse(tipo_cpfcnpj=2)
+
+ obj_rps, obj_lote_rps = get_objects(nfse)
+
+ self.assertTrue(hasattr(obj_rps.RPS, 'CPFCNPJTomador'))
+ self.assertTrue(hasattr(obj_rps.RPS.CPFCNPJTomador, 'CNPJ'))
+ self.assertTrue(hasattr(obj_lote_rps.RPS, 'CPFCNPJTomador'))
+ self.assertTrue(hasattr(obj_lote_rps.RPS.CPFCNPJTomador, 'CNPJ'))
+
+ def test_tipo_cpfcnpj_3(self):
+ nfse = _get_nfse(tipo_cpfcnpj=3)
+
+ obj_rps, obj_lote_rps = get_objects(nfse)
+
+ self.assertFalse(hasattr(obj_rps.RPS, 'CPFCNPJTomador'))
diff --git a/tests/test_nfse_paulistana_valores_default.py b/tests/test_nfse_paulistana_valores_default.py
new file mode 100644
index 0000000..3440743
--- /dev/null
+++ b/tests/test_nfse_paulistana_valores_default.py
@@ -0,0 +1,47 @@
+# coding=utf-8
+
+import os.path
+import unittest
+from pytrustnfe.xml import render_xml, sanitize_response
+from tests.const import DEFAULT_RPS, NFSE
+
+default_values = {
+ 'TipoRPS': 'RPS',
+ 'TributacaoRPS': 'T',
+ 'ValorCOFINS': 0.0,
+ 'ValorINSS': 0.0,
+ 'ValorIR': 0.0,
+ 'ValorPIS': 0.0,
+ 'ValorCSLL': 0.0,
+ 'ISSRetido': False
+}
+attrs = ['TipoRPS', 'TributacaoRPS', 'ValorPIS', 'ValorCOFINS', 'ValorINSS', 'ValorIR', 'ValorCSLL', 'ISSRetido']
+
+
+def _get_nfse():
+ nfse = NFSE
+ nfse['lista_rps'] = DEFAULT_RPS
+ return nfse
+
+
+class test_nfse_paulistana_valores_default(unittest.TestCase):
+ template_path = 'pytrustnfe/nfse/paulistana/templates'
+ xml_path = os.path.join(os.path.dirname(__file__), 'XMLs')
+ nfse = _get_nfse()
+
+ def test_rps_sem_valores(self):
+
+ xml_rps = render_xml(self.template_path, 'EnvioRPS.xml', False, nfse=self.nfse)
+
+ _, obj = sanitize_response(xml_rps)
+
+ for attr in attrs:
+ self.assertEqual(getattr(obj.RPS, attr), default_values[attr])
+
+ def test_lote_rps_sem_valores(self):
+ xml_lote_rps = render_xml(self.template_path, 'EnvioLoteRPS.xml', False, nfse=self.nfse)
+
+ _, obj = sanitize_response(xml_lote_rps)
+
+ for attr in attrs:
+ self.assertEqual(getattr(obj.RPS, attr), default_values[attr])