From 2138bd3ee2e663b501f7a47eecca9a9d386e0c4d Mon Sep 17 00:00:00 2001 From: Felipe Date: Thu, 14 Dec 2017 14:49:49 -0200 Subject: [PATCH 01/10] adicionado string.strip no recursive_normalize --- pytrustnfe/xml/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pytrustnfe/xml/__init__.py b/pytrustnfe/xml/__init__.py index 99cd580..0ed0ee5 100644 --- a/pytrustnfe/xml/__init__.py +++ b/pytrustnfe/xml/__init__.py @@ -61,6 +61,7 @@ def sanitize_response(response): 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]) From c2e2d1ed467e831b7223a0cabe4a3daf7a757ab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Luna?= Date: Thu, 14 Dec 2017 16:15:03 -0200 Subject: [PATCH 02/10] [WIP] Implementa NFS-e Campinas --- pytrustnfe/nfse/campinas/__init__.py | 80 +++++++++++ .../nfse/campinas/templates/ConsultaSeqRps.xsd | 30 +++++ .../nfse/campinas/templates/ConsultarLote.xml | 10 ++ .../nfse/campinas/templates/ConsultarLote.xsd | 46 +++++++ pytrustnfe/nfse/campinas/templates/cancelar.xml | 18 +++ pytrustnfe/nfse/campinas/templates/cancelar.xsd | 55 ++++++++ .../nfse/campinas/templates/consulta_notas.xml | 11 ++ .../nfse/campinas/templates/consulta_notas.xsd | 88 ++++++++++++ .../nfse/campinas/templates/consultarNFSeRps.xml | 19 +++ .../nfse/campinas/templates/consultarNFSeRps.xsd | 55 ++++++++ pytrustnfe/nfse/campinas/templates/enviar.xml | 108 +++++++++++++++ pytrustnfe/nfse/campinas/templates/enviar.xsd | 149 +++++++++++++++++++++ pytrustnfe/nfse/campinas/templates/soap_header.xml | 12 ++ 13 files changed, 681 insertions(+) create mode 100644 pytrustnfe/nfse/campinas/__init__.py create mode 100644 pytrustnfe/nfse/campinas/templates/ConsultaSeqRps.xsd create mode 100644 pytrustnfe/nfse/campinas/templates/ConsultarLote.xml create mode 100644 pytrustnfe/nfse/campinas/templates/ConsultarLote.xsd create mode 100644 pytrustnfe/nfse/campinas/templates/cancelar.xml create mode 100644 pytrustnfe/nfse/campinas/templates/cancelar.xsd create mode 100644 pytrustnfe/nfse/campinas/templates/consulta_notas.xml create mode 100644 pytrustnfe/nfse/campinas/templates/consulta_notas.xsd create mode 100644 pytrustnfe/nfse/campinas/templates/consultarNFSeRps.xml create mode 100644 pytrustnfe/nfse/campinas/templates/consultarNFSeRps.xsd create mode 100644 pytrustnfe/nfse/campinas/templates/enviar.xml create mode 100644 pytrustnfe/nfse/campinas/templates/enviar.xsd create mode 100644 pytrustnfe/nfse/campinas/templates/soap_header.xml diff --git a/pytrustnfe/nfse/campinas/__init__.py b/pytrustnfe/nfse/campinas/__init__.py new file mode 100644 index 0000000..ff5ef02 --- /dev/null +++ b/pytrustnfe/nfse/campinas/__init__.py @@ -0,0 +1,80 @@ +# -*- 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.nfse.assinatura import Assinatura +from pytrustnfe import HttpClient + + +def _render_xml(certificado, method, **kwargs): + path = os.path.join(os.path.dirname(__file__), 'templates') + xml_send = render_xml(path, '%s.xml' % method, True, **kwargs) + xml_send = etree.tostring(xml_send) + + return xml_send + + +def _validate(method, xml): + path = os.path.join(os.path.dirname(__file__), 'templates') + schema = os.path.join(path, '%s.xsd' % method) + + nfe = etree.fromstring(xml) + esquema = etree.XMLSchema(etree.parse(schema)) + esquema.validate(nfe) + erros = [x.message for x in esquema.error_log] + return erros + + +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') + + if method == "testeEnviar": + xml_send = render_xml(path, 'testeEnviar', **kwargs) + else: + xml_send = render_xml(path, '%s.xml' % method, False) + client = HttpClient(url) + + pfx_path = certificado.save_pfx() + signer = Assinatura(pfx_path, certificado.password) + xml_signed = signer.assina_xml(xml_send, '') + + try: + response = getattr(client.service, method)(xml_signed) + response, obj = sanitize_response(response) + 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): + if kwargs['ambiente'] == 'producao': + return _send(certificado, 'enviar', **kwargs) + else: + return _send(certificado, 'testeEnviar', **kwargs) + + +def cancelar(certificado, ** kwargs): + return _send(certificado, 'cancelar', **kwargs) + + +def consulta_lote(certificado, **kwargs): + return _send(certificado, 'ConsultarLote', **kwargs) + + +def consultar_lote_rps(certificado, **kwarg): + return _send(certificado, 'consultarNFSeRps', **kwarg) diff --git a/pytrustnfe/nfse/campinas/templates/ConsultaSeqRps.xsd b/pytrustnfe/nfse/campinas/templates/ConsultaSeqRps.xsd new file mode 100644 index 0000000..94491a7 --- /dev/null +++ b/pytrustnfe/nfse/campinas/templates/ConsultaSeqRps.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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/ConsultarLote.xsd b/pytrustnfe/nfse/campinas/templates/ConsultarLote.xsd new file mode 100644 index 0000000..068756f --- /dev/null +++ b/pytrustnfe/nfse/campinas/templates/ConsultarLote.xsd @@ -0,0 +1,46 @@ + + + + + + Schema utilizado para REQUISIçÂO de Consulta de Lote de RPS. + Este Schema XML é utilizado pelos Prestadores de serviços para consultarem Lote de RPS emitidos por eles. + + + + + + Cabeçalho do pedido. + + + + + + Informe o Codigo da Cidade. + + + + + Informe o CPF/CNPJ do Remetente autorizado a transmitir a mensagem XML. + + + + + Informe a Versão do Schema XML utilizado. + + + + + Informe o Número do Lote a ser consultado. + + + + + + + + + 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/cancelar.xsd b/pytrustnfe/nfse/campinas/templates/cancelar.xsd new file mode 100644 index 0000000..09f1938 --- /dev/null +++ b/pytrustnfe/nfse/campinas/templates/cancelar.xsd @@ -0,0 +1,55 @@ + + + + + + + + Schema utilizado para Cancelamento de NFSe. + Este Schema XML é utilizado pelos Prestadores de serviços cancelarem NFSe emitidas por eles. + + + + + + Cabeçalho do pedido. + + + + + + Informe o Codigo da Cidade. + + + + + Informe o CPF/CNPJ do Remetente autorizado a transmitir a mensagem XML. + + + + + Informe se as NF-e a serem canceladas farão parte de uma mesma transação. True - As NF-e só serão canceladas se não ocorrer nenhum evento de erro durante o processamento de todo o lote; False - As NF-e aptas a serem canceladas serão canceladas, mesmo que ocorram eventos de erro durante processamento do cancelamento de outras NF-e deste lote. + + + + + Informe a Versão do Schema XML utilizado. + + + + + + + + Detalhe do pedido de cancelamento de NFSe. Cada detalhe deverá conter a Chave de uma NFSe e sua respectiva assinatura de cancelamento. + + + + + + + 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/consulta_notas.xsd b/pytrustnfe/nfse/campinas/templates/consulta_notas.xsd new file mode 100644 index 0000000..5784eb8 --- /dev/null +++ b/pytrustnfe/nfse/campinas/templates/consulta_notas.xsd @@ -0,0 +1,88 @@ + + + + + + + Schema utilizado para REQUISIÇAO de consultas + de notas que foram enviadas por lote de RPS. + Este Schema XML é utilizado pelos prestadores + de serviços para consultas de notas que foram enviadas por lote de + RPS. + + + + + + Cabeçalho do pedido. + + + + + + Informe o Codigo da Cidade. + + + + + + Informe o CPF/CNPJ do Remetente + autorizado a transmitir a mensagem XML. + + + + + Informe a Inscrição Municipal do + Prestador + + + + + Informe a data de início do período + transmitido (AAAA-MM-DD). + + + + + Informe a data final do período + transmitido (AAAA-MM-DD). + + + + + Numero da nota inicial da consulta. Ou + seja a consulta ira retornar as notas no periodo, onde o + numero da nota seja maior ou igual a esse numero. O retorno + não pode ultrapassar 500Kb. Caso não tenha o numero da nota, + passar o valor Zero, será retornado as notas geradas no + periodo até o limite de 500kb. + + + + + Informe a Versão. + + + + + + + + + + + + + diff --git a/pytrustnfe/nfse/campinas/templates/consultarNFSeRps.xml b/pytrustnfe/nfse/campinas/templates/consultarNFSeRps.xml new file mode 100644 index 0000000..6535559 --- /dev/null +++ b/pytrustnfe/nfse/campinas/templates/consultarNFSeRps.xml @@ -0,0 +1,19 @@ + + + {{ consulta.cidade }} + {{ consulta.cpf_cnpj }} + true + 1 + + + + + {{ consulta.inscricao_municipal }} + {{ consulta.rps_id }} + {{ consulta.serie_prestacao }} + + + + diff --git a/pytrustnfe/nfse/campinas/templates/consultarNFSeRps.xsd b/pytrustnfe/nfse/campinas/templates/consultarNFSeRps.xsd new file mode 100644 index 0000000..c0e83bf --- /dev/null +++ b/pytrustnfe/nfse/campinas/templates/consultarNFSeRps.xsd @@ -0,0 +1,55 @@ + + + + + + + + Schema utilizado para Consulta de NFSe. + Este Schema XML é utilizado pelos Prestadores de serviços consultarem NFSe emitidas por eles. + + + + + + Cabeçalho do pedido. + + + + + + Informe o Codigo da Cidade. + + + + + Informe o CPF/CNPJ do Remetente autorizado a transmitir a mensagem XML. + + + + + Informe se as NF-e a serem consultadas farão parte de uma mesma transação. Informe sempre True. + + + + + Informe a Versão do Schema XML utilizado. + + + + + + + + Detalhe do pedido de consulta de NFSe. Cada detalhe deverá conter a Chave de uma NFSe e sua respectiva assinatura de consulta. + + + + + + + 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/enviar.xsd b/pytrustnfe/nfse/campinas/templates/enviar.xsd new file mode 100644 index 0000000..fedce41 --- /dev/null +++ b/pytrustnfe/nfse/campinas/templates/enviar.xsd @@ -0,0 +1,149 @@ + + + + + + Schema utilizado para envio de lote de RPS. + Este Schema XML é utilizado pelos prestadores + de serviços para substituição em lote de RPS por NFS-e. + + + + + + + Cabeçalho do Lote. + + + + + + Informe o Codigo da Cidade no Padrão SIAFI. + + + + + + + CNPJ do contribuinte ou CPF do Responsável Legal autorizado a entregar o lote. + + + + + + + Informe o Nome do Contribuinte ou do Responsável Legal + + + + + + + Informe se os RPS a serem + substituídos por + NF-e farão + parte de uma mesma transação. + True - Os RPS só serão + substituídos por NF-e se não + ocorrer nenhum evento de erro + durante o processamento de todo + o lote; False - Os RPS válidos + serão substituídos por NF-e, + mesmo que ocorram eventos de + erro durante processamento de + outros RPS deste lote. Por definição + estão sendo aceitos apenas lotes com RPS válidos, + o lote é + recusado caso haja algum RPS inválido. + + + + + + + Informe a data de início do + período + transmitido + (AAAA-MM-DD). + + + + + + + Informe a data final do período + transmitido + (AAAA-MM-DD). + + + + + + + Informe o total de RPS contidos + na mensagem + XML. OBS: O xml não pode ultrapassar o tamanho maximo de 500kb. + + + + + + + Informe o valor total dos + serviços prestados + dos RPS + contidos na mensagem XML. + + + + + + + Informe o valor total das + deduções dos RPS + contidos na + mensagem XML. + + + + + + + Informe a Versão do Schema XML + utilizado. + + + + + + Informe o Método de Envio + + + + + Versão da DLL de envio de lote. Não é necessário informar esse campo caso não utilize a DLL. + + + + + + + + + + Informe os RPS a serem substituidos por + NF-e. + + + + + + + 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 From 22ac348b8b722a1b500488477f48119b0b6edb4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Luna?= Date: Wed, 20 Dec 2017 18:00:03 -0200 Subject: [PATCH 03/10] Implementa NFse de Campinas --- pytrustnfe/nfse/campinas/__init__.py | 58 ++++---- .../nfse/campinas/templates/ConsultaSeqRps.xsd | 30 ----- .../nfse/campinas/templates/ConsultarLote.xml | 10 -- .../nfse/campinas/templates/ConsultarLote.xsd | 46 ------- pytrustnfe/nfse/campinas/templates/cancelar.xsd | 55 -------- .../nfse/campinas/templates/consulta_notas.xsd | 88 ------------ .../nfse/campinas/templates/consultarLote.xml | 10 ++ .../nfse/campinas/templates/consultarNFSeRps.xml | 19 --- .../nfse/campinas/templates/consultarNFSeRps.xsd | 55 -------- pytrustnfe/nfse/campinas/templates/enviar.xsd | 149 --------------------- setup.py | 3 +- 11 files changed, 39 insertions(+), 484 deletions(-) delete mode 100644 pytrustnfe/nfse/campinas/templates/ConsultaSeqRps.xsd delete mode 100644 pytrustnfe/nfse/campinas/templates/ConsultarLote.xml delete mode 100644 pytrustnfe/nfse/campinas/templates/ConsultarLote.xsd delete mode 100644 pytrustnfe/nfse/campinas/templates/cancelar.xsd delete mode 100644 pytrustnfe/nfse/campinas/templates/consulta_notas.xsd create mode 100644 pytrustnfe/nfse/campinas/templates/consultarLote.xml delete mode 100644 pytrustnfe/nfse/campinas/templates/consultarNFSeRps.xml delete mode 100644 pytrustnfe/nfse/campinas/templates/consultarNFSeRps.xsd delete mode 100644 pytrustnfe/nfse/campinas/templates/enviar.xsd diff --git a/pytrustnfe/nfse/campinas/__init__.py b/pytrustnfe/nfse/campinas/__init__.py index ff5ef02..e5536af 100644 --- a/pytrustnfe/nfse/campinas/__init__.py +++ b/pytrustnfe/nfse/campinas/__init__.py @@ -6,27 +6,22 @@ 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 import HttpClient +from pytrustnfe.client import get_client -def _render_xml(certificado, method, **kwargs): +def _render(certificado, method, **kwargs): path = os.path.join(os.path.dirname(__file__), 'templates') - xml_send = render_xml(path, '%s.xml' % method, True, **kwargs) - xml_send = etree.tostring(xml_send) - - return xml_send - + if method == "testeEnviar": + xml_send = render_xml(path, 'enviar.xml', True, **kwargs) + else: + xml_send = render_xml(path, '%s.xml' % method, False, **kwargs) -def _validate(method, xml): - path = os.path.join(os.path.dirname(__file__), 'templates') - schema = os.path.join(path, '%s.xsd' % method) + if type(xml_send) != str: + xml_send = etree.tostring(xml_send) - nfe = etree.fromstring(xml) - esquema = etree.XMLSchema(etree.parse(schema)) - esquema.validate(nfe) - erros = [x.message for x in esquema.error_log] - return erros + return xml_send def _send(certificado, method, **kwargs): @@ -34,19 +29,19 @@ def _send(certificado, method, **kwargs): path = os.path.join(os.path.dirname(__file__), 'templates') - if method == "testeEnviar": - xml_send = render_xml(path, 'testeEnviar', **kwargs) - else: - xml_send = render_xml(path, '%s.xml' % method, False) - client = HttpClient(url) + xml_send = _render(path, method, **kwargs) + client = get_client(url) - pfx_path = certificado.save_pfx() - signer = Assinatura(pfx_path, certificado.password) - xml_signed = signer.assina_xml(xml_send, '') + 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_signed) - response, obj = sanitize_response(response) + response = getattr(client.service, method)(xml_send) + response, obj = sanitize_response(response.encode()) except suds.WebFault as e: return { 'sent_xml': xml_send, @@ -62,18 +57,19 @@ def _send(certificado, method, **kwargs): def enviar(certificado, **kwargs): - if kwargs['ambiente'] == 'producao': - return _send(certificado, 'enviar', **kwargs) - else: - return _send(certificado, 'testeEnviar', **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(certificado, **kwargs): - return _send(certificado, 'ConsultarLote', **kwargs) +def consulta_lote(**kwargs): + return _send(False, 'consultarLote', **kwargs) def consultar_lote_rps(certificado, **kwarg): diff --git a/pytrustnfe/nfse/campinas/templates/ConsultaSeqRps.xsd b/pytrustnfe/nfse/campinas/templates/ConsultaSeqRps.xsd deleted file mode 100644 index 94491a7..0000000 --- a/pytrustnfe/nfse/campinas/templates/ConsultaSeqRps.xsd +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pytrustnfe/nfse/campinas/templates/ConsultarLote.xml b/pytrustnfe/nfse/campinas/templates/ConsultarLote.xml deleted file mode 100644 index 24afc5d..0000000 --- a/pytrustnfe/nfse/campinas/templates/ConsultarLote.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - {{ consulta.cidade }} - {{ consulta.cpf_cnpj }} - 1 - {{ consulta.lote }} - - \ No newline at end of file diff --git a/pytrustnfe/nfse/campinas/templates/ConsultarLote.xsd b/pytrustnfe/nfse/campinas/templates/ConsultarLote.xsd deleted file mode 100644 index 068756f..0000000 --- a/pytrustnfe/nfse/campinas/templates/ConsultarLote.xsd +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - Schema utilizado para REQUISIçÂO de Consulta de Lote de RPS. - Este Schema XML é utilizado pelos Prestadores de serviços para consultarem Lote de RPS emitidos por eles. - - - - - - Cabeçalho do pedido. - - - - - - Informe o Codigo da Cidade. - - - - - Informe o CPF/CNPJ do Remetente autorizado a transmitir a mensagem XML. - - - - - Informe a Versão do Schema XML utilizado. - - - - - Informe o Número do Lote a ser consultado. - - - - - - - - - diff --git a/pytrustnfe/nfse/campinas/templates/cancelar.xsd b/pytrustnfe/nfse/campinas/templates/cancelar.xsd deleted file mode 100644 index 09f1938..0000000 --- a/pytrustnfe/nfse/campinas/templates/cancelar.xsd +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - Schema utilizado para Cancelamento de NFSe. - Este Schema XML é utilizado pelos Prestadores de serviços cancelarem NFSe emitidas por eles. - - - - - - Cabeçalho do pedido. - - - - - - Informe o Codigo da Cidade. - - - - - Informe o CPF/CNPJ do Remetente autorizado a transmitir a mensagem XML. - - - - - Informe se as NF-e a serem canceladas farão parte de uma mesma transação. True - As NF-e só serão canceladas se não ocorrer nenhum evento de erro durante o processamento de todo o lote; False - As NF-e aptas a serem canceladas serão canceladas, mesmo que ocorram eventos de erro durante processamento do cancelamento de outras NF-e deste lote. - - - - - Informe a Versão do Schema XML utilizado. - - - - - - - - Detalhe do pedido de cancelamento de NFSe. Cada detalhe deverá conter a Chave de uma NFSe e sua respectiva assinatura de cancelamento. - - - - - - - diff --git a/pytrustnfe/nfse/campinas/templates/consulta_notas.xsd b/pytrustnfe/nfse/campinas/templates/consulta_notas.xsd deleted file mode 100644 index 5784eb8..0000000 --- a/pytrustnfe/nfse/campinas/templates/consulta_notas.xsd +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - Schema utilizado para REQUISIÇAO de consultas - de notas que foram enviadas por lote de RPS. - Este Schema XML é utilizado pelos prestadores - de serviços para consultas de notas que foram enviadas por lote de - RPS. - - - - - - Cabeçalho do pedido. - - - - - - Informe o Codigo da Cidade. - - - - - - Informe o CPF/CNPJ do Remetente - autorizado a transmitir a mensagem XML. - - - - - Informe a Inscrição Municipal do - Prestador - - - - - Informe a data de início do período - transmitido (AAAA-MM-DD). - - - - - Informe a data final do período - transmitido (AAAA-MM-DD). - - - - - Numero da nota inicial da consulta. Ou - seja a consulta ira retornar as notas no periodo, onde o - numero da nota seja maior ou igual a esse numero. O retorno - não pode ultrapassar 500Kb. Caso não tenha o numero da nota, - passar o valor Zero, será retornado as notas geradas no - periodo até o limite de 500kb. - - - - - Informe a Versão. - - - - - - - - - - - - - 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/consultarNFSeRps.xml b/pytrustnfe/nfse/campinas/templates/consultarNFSeRps.xml deleted file mode 100644 index 6535559..0000000 --- a/pytrustnfe/nfse/campinas/templates/consultarNFSeRps.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - {{ consulta.cidade }} - {{ consulta.cpf_cnpj }} - true - 1 - - - - - {{ consulta.inscricao_municipal }} - {{ consulta.rps_id }} - {{ consulta.serie_prestacao }} - - - - diff --git a/pytrustnfe/nfse/campinas/templates/consultarNFSeRps.xsd b/pytrustnfe/nfse/campinas/templates/consultarNFSeRps.xsd deleted file mode 100644 index c0e83bf..0000000 --- a/pytrustnfe/nfse/campinas/templates/consultarNFSeRps.xsd +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - Schema utilizado para Consulta de NFSe. - Este Schema XML é utilizado pelos Prestadores de serviços consultarem NFSe emitidas por eles. - - - - - - Cabeçalho do pedido. - - - - - - Informe o Codigo da Cidade. - - - - - Informe o CPF/CNPJ do Remetente autorizado a transmitir a mensagem XML. - - - - - Informe se as NF-e a serem consultadas farão parte de uma mesma transação. Informe sempre True. - - - - - Informe a Versão do Schema XML utilizado. - - - - - - - - Detalhe do pedido de consulta de NFSe. Cada detalhe deverá conter a Chave de uma NFSe e sua respectiva assinatura de consulta. - - - - - - - diff --git a/pytrustnfe/nfse/campinas/templates/enviar.xsd b/pytrustnfe/nfse/campinas/templates/enviar.xsd deleted file mode 100644 index fedce41..0000000 --- a/pytrustnfe/nfse/campinas/templates/enviar.xsd +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - Schema utilizado para envio de lote de RPS. - Este Schema XML é utilizado pelos prestadores - de serviços para substituição em lote de RPS por NFS-e. - - - - - - - Cabeçalho do Lote. - - - - - - Informe o Codigo da Cidade no Padrão SIAFI. - - - - - - - CNPJ do contribuinte ou CPF do Responsável Legal autorizado a entregar o lote. - - - - - - - Informe o Nome do Contribuinte ou do Responsável Legal - - - - - - - Informe se os RPS a serem - substituídos por - NF-e farão - parte de uma mesma transação. - True - Os RPS só serão - substituídos por NF-e se não - ocorrer nenhum evento de erro - durante o processamento de todo - o lote; False - Os RPS válidos - serão substituídos por NF-e, - mesmo que ocorram eventos de - erro durante processamento de - outros RPS deste lote. Por definição - estão sendo aceitos apenas lotes com RPS válidos, - o lote é - recusado caso haja algum RPS inválido. - - - - - - - Informe a data de início do - período - transmitido - (AAAA-MM-DD). - - - - - - - Informe a data final do período - transmitido - (AAAA-MM-DD). - - - - - - - Informe o total de RPS contidos - na mensagem - XML. OBS: O xml não pode ultrapassar o tamanho maximo de 500kb. - - - - - - - Informe o valor total dos - serviços prestados - dos RPS - contidos na mensagem XML. - - - - - - - Informe o valor total das - deduções dos RPS - contidos na - mensagem XML. - - - - - - - Informe a Versão do Schema XML - utilizado. - - - - - - Informe o Método de Envio - - - - - Versão da DLL de envio de lote. Não é necessário informar esse campo caso não utilize a DLL. - - - - - - - - - - Informe os RPS a serem substituidos por - NF-e. - - - - - - - diff --git a/setup.py b/setup.py index a1add7c..ff137cf 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ # coding=utf-8 from setuptools import setup, find_packages -VERSION = "0.9.2" +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', From 0a21cfaad8740022e2794ce10eb38c161d446923 Mon Sep 17 00:00:00 2001 From: carcaroff Date: Fri, 22 Dec 2017 15:35:45 -0200 Subject: [PATCH 04/10] [FIX]Campo CPF --- pytrustnfe/nfe/danfe.py | 1669 +++++++++++++++++++++++------------------------ setup.py | 2 +- 2 files changed, 831 insertions(+), 840 deletions(-) diff --git a/pytrustnfe/nfe/danfe.py b/pytrustnfe/nfe/danfe.py index 2233877..bfe9692 100644 --- a/pytrustnfe/nfe/danfe.py +++ b/pytrustnfe/nfe/danfe.py @@ -1,839 +1,830 @@ -# -*- coding: utf-8 -*- -# © 2017 Edson Bernardino, ITK Soft -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -# Classe para geração de PDF da DANFE a partir de xml etree.fromstring - -import os -from io import BytesIO -from textwrap import wrap - -from reportlab.lib import utils -from reportlab.pdfgen import canvas -from reportlab.lib.units import mm, cm -from reportlab.lib.pagesizes import A4 -from reportlab.lib.colors import black, gray -from reportlab.graphics.barcode import code128 -from reportlab.lib.styles import getSampleStyleSheet -from reportlab.lib.enums import TA_CENTER -from reportlab.platypus import Paragraph, Image -from reportlab.pdfbase import pdfmetrics -from reportlab.pdfbase.ttfonts import TTFont - - -def chunks(cString, nLen): - for start in range(0, len(cString), nLen): - yield cString[start:start+nLen] - - -def format_cnpj_cpf(value): - if len(value) < 12: # CPF - cValue = '%s.%s.%s-%s' % (value[:-8], value[-8:-5], - value[-5:-2], value[-2:]) - else: - cValue = '%s.%s.%s/%s-%s' % (value[:-12], value[-12:-9], - value[-9:-6], value[-6:-2], value[-2:]) - return cValue - - -def getdateUTC(cDateUTC): - cDt = cDateUTC[0:10].split('-') - cDt.reverse() - return '/'.join(cDt), cDateUTC[11:16] - - -def format_number(cNumber, precision=0, group_sep='.', decimal_sep=','): - if cNumber: - number = float(cNumber) - return ("{:,." + str(precision) + "f}").format(number).\ - replace(",", "X").replace(".", ",").replace("X", ".") - return "" - - -def tagtext(oNode=None, cTag=None): - try: - xpath = ".//{http://www.portalfiscal.inf.br/nfe}%s" % (cTag) - cText = oNode.find(xpath).text - except: - cText = '' - return cText - -REGIME_TRIBUTACAO = { - '1': 'Simples Nacional', - '2': 'Simples Nacional, excesso sublimite de receita bruta', - '3': 'Regime Normal' -} - - -def get_image(path, width=1*cm): - img = utils.ImageReader(path) - iw, ih = img.getSize() - aspect = ih / float(iw) - return Image(path, width=width, height=(width * aspect)) - - -class danfe(object): - def __init__(self, sizepage=A4, list_xml=None, recibo=True, - orientation='portrait', logo=None): - - path = os.path.join(os.path.dirname(__file__), 'fonts') - pdfmetrics.registerFont( - TTFont('NimbusSanL-Regu', - os.path.join(path, 'NimbusSanL Regular.ttf'))) - pdfmetrics.registerFont( - TTFont('NimbusSanL-Bold', - os.path.join(path, 'NimbusSanL Bold.ttf'))) - self.width = 210 # 21 x 29,7cm - self.height = 297 - self.nLeft = 10 - self.nRight = 10 - self.nTop = 7 - self.nBottom = 8 - self.nlin = self.nTop - self.logo = logo - self.oFrete = {'0': '0 - Emitente', - '1': '1 - Dest/Remet', - '2': '2 - Terceiros', - '9': '9 - Sem Frete'} - - self.oPDF_IO = BytesIO() - if orientation == 'landscape': - raise NameError('Rotina não implementada') - else: - size = sizepage - - self.canvas = canvas.Canvas(self.oPDF_IO, pagesize=size) - self.canvas.setTitle('DANFE') - self.canvas.setStrokeColor(black) - - for oXML in list_xml: - oXML_cobr = oXML.find( - ".//{http://www.portalfiscal.inf.br/nfe}cobr") - - self.NrPages = 1 - self.Page = 1 - - # Calculando total linhas usadas para descrições dos itens - # Com bloco fatura, apenas 29 linhas para itens na primeira folha - nNr_Lin_Pg_1 = 34 if oXML_cobr is None else 30 - # [ rec_ini , rec_fim , lines , limit_lines ] - oPaginator = [[0, 0, 0, nNr_Lin_Pg_1]] - el_det = oXML.findall(".//{http://www.portalfiscal.inf.br/nfe}det") - if el_det is not None: - list_desc = [] - list_cod_prod = [] - nPg = 0 - for nId, item in enumerate(el_det): - el_prod = item.find( - ".//{http://www.portalfiscal.inf.br/nfe}prod") - infAdProd = item.find( - ".//{http://www.portalfiscal.inf.br/nfe}infAdProd") - - list_ = wrap(tagtext(oNode=el_prod, cTag='xProd'), 56) - if infAdProd is not None: - list_.extend(wrap(infAdProd.text, 56)) - list_desc.append(list_) - - list_cProd = wrap(tagtext(oNode=el_prod, cTag='cProd'), 14) - list_cod_prod.append(list_cProd) - - # Nr linhas necessárias p/ descrição item - nLin_Itens = len(list_) - - if (oPaginator[nPg][2] + nLin_Itens) >= oPaginator[nPg][3]: - oPaginator.append([0, 0, 0, 77]) - nPg += 1 - oPaginator[nPg][0] = nId - oPaginator[nPg][1] = nId + 1 - oPaginator[nPg][2] = nLin_Itens - else: - # adiciona-se 1 pelo funcionamento de xrange - oPaginator[nPg][1] = nId + 1 - oPaginator[nPg][2] += nLin_Itens - - self.NrPages = len(oPaginator) # Calculando nr. páginas - - if recibo: - self.recibo_entrega(oXML=oXML) - - self.ide_emit(oXML=oXML) - self.destinatario(oXML=oXML) - - if oXML_cobr is not None: - self.faturas(oXML=oXML_cobr) - - self.impostos(oXML=oXML) - self.transportes(oXML=oXML) - self.produtos(oXML=oXML, el_det=el_det, oPaginator=oPaginator[0], - list_desc=list_desc, list_cod_prod=list_cod_prod) - - self.adicionais(oXML=oXML) - - # Gera o restante das páginas do XML - for oPag in oPaginator[1:]: - self.newpage() - self.ide_emit(oXML=oXML) - self.produtos(oXML=oXML, el_det=el_det, oPaginator=oPag, - list_desc=list_desc, nHeight=77, - list_cod_prod=list_cod_prod) - - self.newpage() - - self.canvas.save() - - def ide_emit(self, oXML=None): - elem_infNFe = oXML.find( - ".//{http://www.portalfiscal.inf.br/nfe}infNFe") - elem_protNFe = oXML.find( - ".//{http://www.portalfiscal.inf.br/nfe}protNFe") - elem_emit = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}emit") - elem_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide") - - cChave = elem_infNFe.attrib.get('Id')[3:] - barcode128 = code128.Code128(cChave, barHeight=10*mm, barWidth=0.25*mm) - - self.canvas.setLineWidth(.5) - self.rect(self.nLeft, self.nlin+1, self.nLeft+75, 32) - self.rect(self.nLeft+115, self.nlin+1, - self.width-self.nLeft-self.nRight-115, 39) - - self.hline(self.nLeft+85, self.nlin+1, 125) - - self.rect(self.nLeft+116, self.nlin+15, - self.width-self.nLeft-self.nRight-117, 6) - - self.rect(self.nLeft, self.nlin+33, - self.width-self.nLeft-self.nRight, 14) - self.hline(self.nLeft, self.nlin+40, self.width-self.nRight) - self.vline(self.nLeft+60, self.nlin+40, 7) - self.vline(self.nLeft+100, self.nlin+40, 7) - - # Labels - self.canvas.setFont('NimbusSanL-Bold', 12) - self.stringcenter(self.nLeft+98, self.nlin+5, 'DANFE') - self.stringcenter(self.nLeft+109, self.nlin+19.5, - tagtext(oNode=elem_ide, cTag='tpNF')) - self.canvas.setFont('NimbusSanL-Bold', 8) - cNF = tagtext(oNode=elem_ide, cTag='nNF') - cNF = '{0:011,}'.format(int(cNF)).replace(",", ".") - self.stringcenter(self.nLeft+100, self.nlin+25, "Nº %s" % (cNF)) - - self.stringcenter(self.nLeft+100, self.nlin+29, "SÉRIE %s" % ( - tagtext(oNode=elem_ide, cTag='serie'))) - cPag = "Página %s de %s" % (str(self.Page), str(self.NrPages)) - self.stringcenter(self.nLeft+100, self.nlin+32, cPag) - self.canvas.setFont('NimbusSanL-Regu', 6) - self.string(self.nLeft+86, self.nlin+8, 'Documento Auxiliar da') - self.string(self.nLeft+86, self.nlin+10.5, 'Nota Fiscal Eletrônica') - self.string(self.nLeft+86, self.nlin+16, '0 - Entrada') - self.string(self.nLeft+86, self.nlin+19, '1 - Saída') - self.rect(self.nLeft+105, self.nlin+15, 8, 6) - - self.stringcenter( - self.nLeft+152, self.nlin+25, - 'Consulta de autenticidade no portal nacional da NF-e') - self.stringcenter( - self.nLeft+152, self.nlin+28, - 'www.nfe.fazenda.gov.br/portal ou no site da SEFAZ Autorizadora') - self.canvas.setFont('NimbusSanL-Regu', 5) - self.string(self.nLeft+117, self.nlin+16.7, 'CHAVE DE ACESSO') - self.string(self.nLeft+116, self.nlin+2.7, 'CONTROLE DO FISCO') - - self.string(self.nLeft+1, self.nlin+34.7, 'NATUREZA DA OPERAÇÃO') - self.string(self.nLeft+116, self.nlin+34.7, - 'PROTOCOLO DE AUTORIZAÇÃO DE USO') - self.string(self.nLeft+1, self.nlin+41.7, 'INSCRIÇÃO ESTADUAL') - self.string(self.nLeft+61, self.nlin+41.7, - 'INSCRIÇÃO ESTADUAL DO SUBST. TRIB.') - self.string(self.nLeft+101, self.nlin+41.7, 'CNPJ') - - # Conteúdo campos - barcode128.drawOn(self.canvas, (self.nLeft+111.5)*mm, - (self.height-self.nlin-14)*mm) - self.canvas.setFont('NimbusSanL-Bold', 6) - nW_Rect = (self.width-self.nLeft-self.nRight-117) / 2 - self.stringcenter(self.nLeft+116.5+nW_Rect, self.nlin+19.5, - ' '.join(chunks(cChave, 4))) # Chave - self.canvas.setFont('NimbusSanL-Regu', 8) - cDt, cHr = getdateUTC(tagtext(oNode=elem_protNFe, cTag='dhRecbto')) - cProtocolo = tagtext(oNode=elem_protNFe, cTag='nProt') - cDt = cProtocolo + ' - ' + cDt + ' ' + cHr - nW_Rect = (self.width-self.nLeft-self.nRight-110) / 2 - self.stringcenter(self.nLeft+115+nW_Rect, self.nlin+38.7, cDt) - self.canvas.setFont('NimbusSanL-Regu', 8) - self.string(self.nLeft+1, self.nlin+38.7, - tagtext(oNode=elem_ide, cTag='natOp')) - self.string(self.nLeft+1, self.nlin+46, - tagtext(oNode=elem_emit, cTag='IE')) - self.string(self.nLeft+101, self.nlin+46, - format_cnpj_cpf(tagtext(oNode=elem_emit, cTag='CNPJ'))) - - styles = getSampleStyleSheet() - styleN = styles['Normal'] - styleN.fontSize = 10 - styleN.fontName = 'NimbusSanL-Bold' - styleN.alignment = TA_CENTER - - # Razão Social emitente - P = Paragraph(tagtext(oNode=elem_emit, cTag='xNome'), styleN) - w, h = P.wrap(55*mm, 50*mm) - P.drawOn(self.canvas, (self.nLeft+30)*mm, - (self.height-self.nlin-12)*mm) - - if self.logo: - img = get_image(self.logo, width=2*cm) - img.drawOn(self.canvas, (self.nLeft+5)*mm, - (self.height-self.nlin-22)*mm) - - cEnd = tagtext(oNode=elem_emit, cTag='xLgr') + ', ' + tagtext( - oNode=elem_emit, cTag='nro') + ' - ' - cEnd += tagtext(oNode=elem_emit, cTag='xBairro') + '
' + tagtext( - oNode=elem_emit, cTag='xMun') + ' - ' - cEnd += 'Fone: ' + tagtext(oNode=elem_emit, cTag='fone') + '
' - cEnd += tagtext(oNode=elem_emit, cTag='UF') + ' - ' + tagtext( - oNode=elem_emit, cTag='CEP') - - regime = tagtext(oNode=elem_emit, cTag='CRT') - cEnd += '
Regime Tributário: %s' % (REGIME_TRIBUTACAO[regime]) - - styleN.fontName = 'NimbusSanL-Regu' - styleN.fontSize = 7 - styleN.leading = 10 - P = Paragraph(cEnd, styleN) - w, h = P.wrap(55*mm, 30*mm) - P.drawOn(self.canvas, (self.nLeft+30)*mm, - (self.height-self.nlin-31)*mm) - - # Homologação - if tagtext(oNode=elem_ide, cTag='tpAmb') == '2': - self.canvas.saveState() - self.canvas.rotate(90) - self.canvas.setFont('Times-Bold', 40) - self.canvas.setFillColorRGB(0.57, 0.57, 0.57) - self.string(self.nLeft+65, 449, 'SEM VALOR FISCAL') - self.canvas.restoreState() - - self.nlin += 48 - - def destinatario(self, oXML=None): - elem_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide") - elem_dest = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}dest") - nMr = self.width-self.nRight - - self.nlin += 1 - - self.canvas.setFont('NimbusSanL-Bold', 7) - self.string(self.nLeft+1, self.nlin+1, 'DESTINATÁRIO/REMETENTE') - self.rect(self.nLeft, self.nlin+2, - self.width-self.nLeft-self.nRight, 20) - self.vline(nMr-25, self.nlin+2, 20) - self.hline(self.nLeft, self.nlin+8.66, self.width-self.nLeft) - self.hline(self.nLeft, self.nlin+15.32, self.width-self.nLeft) - self.vline(nMr-70, self.nlin+2, 6.66) - self.vline(nMr-53, self.nlin+8.66, 6.66) - self.vline(nMr-99, self.nlin+8.66, 6.66) - self.vline(nMr-90, self.nlin+15.32, 6.66) - self.vline(nMr-102, self.nlin+15.32, 6.66) - self.vline(nMr-136, self.nlin+15.32, 6.66) - # Labels/Fields - self.canvas.setFont('NimbusSanL-Bold', 5) - self.string(self.nLeft+1, self.nlin+3.7, 'NOME/RAZÃO SOCIAL') - self.string(nMr-69, self.nlin+3.7, 'CNPJ/CPF') - self.string(nMr-24, self.nlin+3.7, 'DATA DA EMISSÃO') - self.string(self.nLeft+1, self.nlin+10.3, 'ENDEREÇO') - self.string(nMr-98, self.nlin+10.3, 'BAIRRO/DISTRITO') - self.string(nMr-52, self.nlin+10.3, 'CEP') - self.string(nMr-24, self.nlin+10.3, 'DATA DE ENTRADA/SAÍDA') - self.string(self.nLeft+1, self.nlin+17.1, 'MUNICÍPIO') - self.string(nMr-135, self.nlin+17.1, 'FONE/FAX') - self.string(nMr-101, self.nlin+17.1, 'UF') - self.string(nMr-89, self.nlin+17.1, 'INSCRIÇÃO ESTADUAL') - self.string(nMr-24, self.nlin+17.1, 'HORA DE ENTRADA/SAÍDA') - # Conteúdo campos - self.canvas.setFont('NimbusSanL-Regu', 8) - self.string(self.nLeft+1, self.nlin+7.5, - tagtext(oNode=elem_dest, cTag='xNome')) - self.string(nMr-69, self.nlin+7.5, - format_cnpj_cpf(tagtext(oNode=elem_dest, cTag='CNPJ'))) - cDt, cHr = getdateUTC(tagtext(oNode=elem_ide, cTag='dhEmi')) - self.string(nMr-24, self.nlin+7.7, cDt + ' ' + cHr) - cDt, cHr = getdateUTC(tagtext(oNode=elem_ide, cTag='dhSaiEnt')) - self.string(nMr-24, self.nlin+14.3, cDt + ' ' + cHr) # Dt saída - cEnd = tagtext(oNode=elem_dest, cTag='xLgr') + ', ' + tagtext( - oNode=elem_dest, cTag='nro') - self.string(self.nLeft+1, self.nlin+14.3, cEnd) - self.string(nMr-98, self.nlin+14.3, - tagtext(oNode=elem_dest, cTag='xBairro')) - self.string(nMr-52, self.nlin+14.3, - tagtext(oNode=elem_dest, cTag='CEP')) - self.string(self.nLeft+1, self.nlin+21.1, - tagtext(oNode=elem_dest, cTag='xMun')) - self.string(nMr-135, self.nlin+21.1, - tagtext(oNode=elem_dest, cTag='fone')) - self.string(nMr-101, self.nlin+21.1, - tagtext(oNode=elem_dest, cTag='UF')) - self.string(nMr-89, self.nlin+21.1, - tagtext(oNode=elem_dest, cTag='IE')) - - self.nlin += 24 # Nr linhas ocupadas pelo bloco - - def faturas(self, oXML=None): - - nMr = self.width-self.nRight - - self.canvas.setFont('NimbusSanL-Bold', 7) - self.string(self.nLeft+1, self.nlin+1, 'FATURA') - self.rect(self.nLeft, self.nlin+2, - self.width-self.nLeft-self.nRight, 13) - self.vline(nMr-47.5, self.nlin+2, 13) - self.vline(nMr-95, self.nlin+2, 13) - self.vline(nMr-142.5, self.nlin+2, 13) - self.hline(nMr-47.5, self.nlin+8.5, self.width-self.nLeft) - # Labels - self.canvas.setFont('NimbusSanL-Regu', 5) - self.string(nMr-46.5, self.nlin+3.8, 'CÓDIGO VENDEDOR') - self.string(nMr-46.5, self.nlin+10.2, 'NOME VENDEDOR') - self.string(nMr-93.5, self.nlin+3.8, - 'FATURA VENCIMENTO VALOR') - self.string(nMr-140.5, self.nlin+3.8, - 'FATURA VENCIMENTO VALOR') - self.string(self.nLeft+2, self.nlin+3.8, - 'FATURA VENCIMENTO VALOR') - - # Conteúdo campos - self.canvas.setFont('NimbusSanL-Bold', 6) - nLin = 7 - nPar = 1 - nCol = 0 - nAju = 0 - - line_iter = iter(oXML[1:10]) # Salta elemt 1 e considera os próximos 9 - for oXML_dup in line_iter: - - cDt, cHr = getdateUTC(tagtext(oNode=oXML_dup, cTag='dVenc')) - self.string(self.nLeft+nCol+1, self.nlin+nLin, - tagtext(oNode=oXML_dup, cTag='nDup')) - self.string(self.nLeft+nCol+17, self.nlin+nLin, cDt) - self.stringRight( - self.nLeft+nCol+47, self.nlin+nLin, - format_number(tagtext(oNode=oXML_dup, cTag='vDup'), - precision=2)) - - if nPar == 3: - nLin = 7 - nPar = 1 - nCol += 47 - nAju += 1 - nCol += nAju * (0.3) - else: - nLin += 3.3 - nPar += 1 - - # Campos adicionais XML - Condicionados a existencia de financeiro - elem_infAdic = oXML.getparent().find( - ".//{http://www.portalfiscal.inf.br/nfe}infAdic") - if elem_infAdic is not None: - codvend = elem_infAdic.find( - ".//{http://www.portalfiscal.inf.br/nfe}obsCont\ -[@xCampo='CodVendedor']") - self.string(nMr-46.5, self.nlin+7.7, - tagtext(oNode=codvend, cTag='xTexto')) - vend = elem_infAdic.find(".//{http://www.portalfiscal.inf.br/nfe}\ -obsCont[@xCampo='NomeVendedor']") - self.string(nMr-46.5, self.nlin+14.3, - tagtext(oNode=vend, cTag='xTexto')[:36]) - - self.nlin += 16 # Nr linhas ocupadas pelo bloco - - def impostos(self, oXML=None): - # Impostos - el_total = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}total") - nMr = self.width-self.nRight - self.nlin += 1 - self.canvas.setFont('NimbusSanL-Bold', 7) - self.string(self.nLeft+1, self.nlin+1, 'CÁLCULO DO IMPOSTO') - self.rect(self.nLeft, self.nlin+2, - self.width-self.nLeft-self.nRight, 13) - self.hline(self.nLeft, self.nlin+8.5, self.width-self.nLeft) - self.vline(nMr-35, self.nlin+2, 6.5) - self.vline(nMr-65, self.nlin+2, 6.5) - self.vline(nMr-95, self.nlin+2, 6.5) - self.vline(nMr-125, self.nlin+2, 6.5) - self.vline(nMr-155, self.nlin+2, 6.5) - self.vline(nMr-35, self.nlin+8.5, 6.5) - self.vline(nMr-65, self.nlin+8.5, 6.5) - self.vline(nMr-95, self.nlin+8.5, 6.5) - self.vline(nMr-125, self.nlin+8.5, 6.5) - self.vline(nMr-155, self.nlin+8.5, 6.5) - # Labels - self.canvas.setFont('NimbusSanL-Regu', 5) - self.string(self.nLeft+1, self.nlin+3.8, 'BASE DE CÁLCULO DO ICMS') - self.string(nMr-154, self.nlin+3.8, 'VALOR DO ICMS') - self.string(nMr-124, self.nlin+3.8, 'BASE DE CÁLCULO DO ICMS ST') - self.string(nMr-94, self.nlin+3.8, 'VALOR DO ICMS ST') - self.string(nMr-64, self.nlin+3.8, 'VALOR APROX TRIBUTOS') - self.string(nMr-34, self.nlin+3.8, 'VALOR TOTAL DOS PRODUTOS') - - self.string(self.nLeft+1, self.nlin+10.2, 'VALOR DO FRETE') - self.string(nMr-154, self.nlin+10.2, 'VALOR DO SEGURO') - self.string(nMr-124, self.nlin+10.2, 'DESCONTO') - self.string(nMr-94, self.nlin+10.2, 'OUTRAS DESP. ACESSÓRIAS') - self.string(nMr-64, self.nlin+10.2, 'VALOR DO IPI') - self.string(nMr-34, self.nlin+10.2, 'VALOR TOTAL DA NOTA') - - # Conteúdo campos - self.canvas.setFont('NimbusSanL-Regu', 8) - self.stringRight( - self.nLeft+34, self.nlin+7.7, - format_number(tagtext(oNode=el_total, cTag='vBC'), precision=2)) - self.stringRight( - self.nLeft+64, self.nlin+7.7, - format_number(tagtext(oNode=el_total, cTag='vICMS'), precision=2)) - self.stringRight( - self.nLeft+94, self.nlin+7.7, - format_number(tagtext(oNode=el_total, cTag='vBCST'), precision=2)) - self.stringRight( - nMr-66, self.nlin+7.7, - format_number(tagtext(oNode=el_total, cTag='vST'), precision=2)) - self.stringRight( - nMr-36, self.nlin+7.7, - format_number(tagtext(oNode=el_total, cTag='vTotTrib'), - precision=2)) - self.stringRight( - nMr-1, self.nlin+7.7, - format_number(tagtext(oNode=el_total, cTag='vProd'), precision=2)) - self.stringRight( - self.nLeft+34, self.nlin+14.1, - format_number(tagtext(oNode=el_total, cTag='vFrete'), precision=2)) - self.stringRight( - self.nLeft+64, self.nlin+14.1, - format_number(tagtext(oNode=el_total, cTag='vSeg'), precision=2)) - self.stringRight( - self.nLeft+94, self.nlin+14.1, - format_number(tagtext(oNode=el_total, cTag='vDesc'), precision=2)) - self.stringRight( - self.nLeft+124, self.nlin+14.1, - format_number(tagtext(oNode=el_total, cTag='vOutro'), precision=2)) - self.stringRight( - self.nLeft+154, self.nlin+14.1, - format_number(tagtext(oNode=el_total, cTag='vIPI'), precision=2)) - self.stringRight( - nMr-1, self.nlin+14.1, - format_number(tagtext(oNode=el_total, cTag='vNF'), precision=2)) - - self.nlin += 17 # Nr linhas ocupadas pelo bloco - - def transportes(self, oXML=None): - el_transp = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}transp") - nMr = self.width-self.nRight - - self.canvas.setFont('NimbusSanL-Bold', 7) - self.string(self.nLeft+1, self.nlin+1, - 'TRANSPORTADOR/VOLUMES TRANSPORTADOS') - self.canvas.setFont('NimbusSanL-Regu', 5) - self.rect(self.nLeft, self.nlin+2, - self.width-self.nLeft-self.nRight, 20) - self.hline(self.nLeft, self.nlin+8.6, self.width-self.nLeft) - self.hline(self.nLeft, self.nlin+15.2, self.width-self.nLeft) - self.vline(nMr-40, self.nlin+2, 13.2) - self.vline(nMr-49, self.nlin+2, 20) - self.vline(nMr-92, self.nlin+2, 6.6) - self.vline(nMr-120, self.nlin+2, 6.6) - self.vline(nMr-75, self.nlin+2, 6.6) - self.vline(nMr-26, self.nlin+15.2, 6.6) - self.vline(nMr-102, self.nlin+8.6, 6.6) - self.vline(nMr-85, self.nlin+15.2, 6.6) - self.vline(nMr-121, self.nlin+15.2, 6.6) - self.vline(nMr-160, self.nlin+15.2, 6.6) - # Labels/Fields - self.string(nMr-39, self.nlin+3.8, 'CNPJ/CPF') - self.string(nMr-74, self.nlin+3.8, 'PLACA DO VEÍCULO') - self.string(nMr-91, self.nlin+3.8, 'CÓDIGO ANTT') - self.string(nMr-119, self.nlin+3.8, 'FRETE POR CONTA') - self.string(self.nLeft+1, self.nlin+3.8, 'RAZÃO SOCIAL') - self.string(nMr-48, self.nlin+3.8, 'UF') - self.string(nMr-39, self.nlin+10.3, 'INSCRIÇÃO ESTADUAL') - self.string(nMr-48, self.nlin+10.3, 'UF') - self.string(nMr-101, self.nlin+10.3, 'MUNICÍPIO') - self.string(self.nLeft+1, self.nlin+10.3, 'ENDEREÇO') - self.string(nMr-48, self.nlin+17, 'PESO BRUTO') - self.string(nMr-25, self.nlin+17, 'PESO LÍQUIDO') - self.string(nMr-84, self.nlin+17, 'NUMERAÇÃO') - self.string(nMr-120, self.nlin+17, 'MARCA') - self.string(nMr-159, self.nlin+17, 'ESPÉCIE') - self.string(self.nLeft+1, self.nlin+17, 'QUANTIDADE') - # Conteúdo campos - self.canvas.setFont('NimbusSanL-Regu', 8) - self.string(self.nLeft+1, self.nlin+7.7, - tagtext(oNode=el_transp, cTag='xNome')[:40]) - self.string(self.nLeft+71, self.nlin+7.7, - self.oFrete[tagtext(oNode=el_transp, cTag='modFrete')]) - self.string(nMr-39, self.nlin+7.7, - format_cnpj_cpf(tagtext(oNode=el_transp, cTag='CNPJ'))) - self.string(self.nLeft+1, self.nlin+14.2, - tagtext(oNode=el_transp, cTag='xEnder')[:45]) - self.string(self.nLeft+89, self.nlin+14.2, - tagtext(oNode=el_transp, cTag='xMun')) - self.string(nMr-48, self.nlin+14.2, - tagtext(oNode=el_transp, cTag='UF')) - self.string(nMr-39, self.nlin+14.2, - tagtext(oNode=el_transp, cTag='IE')) - self.string(self.nLeft+1, self.nlin+21.2, - tagtext(oNode=el_transp, cTag='qVol')) - self.string(self.nLeft+31, self.nlin+21.2, - tagtext(oNode=el_transp, cTag='esp')) - self.string(self.nLeft+70, self.nlin+21.2, - tagtext(oNode=el_transp, cTag='marca')) - self.string(self.nLeft+106, self.nlin+21.2, - tagtext(oNode=el_transp, cTag='nVol')) - self.stringRight( - nMr-27, self.nlin+21.2, - format_number(tagtext(oNode=el_transp, cTag='pesoB'), precision=3)) - self.stringRight( - nMr-1, self.nlin+21.2, - format_number(tagtext(oNode=el_transp, cTag='pesoL'), precision=3)) - - self.nlin += 23 - - def produtos(self, oXML=None, el_det=None, oPaginator=None, - list_desc=None, list_cod_prod=None, nHeight=29): - - nMr = self.width-self.nRight - nStep = 2.5 # Passo entre linhas - nH = 7.5 + (nHeight * nStep) # cabeçalho 7.5 - self.nlin += 1 - - self.canvas.setFont('NimbusSanL-Bold', 7) - self.string(self.nLeft+1, self.nlin+1, 'DADOS DO PRODUTO/SERVIÇO') - self.rect(self.nLeft, self.nlin+2, - self.width-self.nLeft-self.nRight, nH) - self.hline(self.nLeft, self.nlin+8, self.width-self.nLeft) - - self.canvas.setFont('NimbusSanL-Regu', 5.5) - # Colunas - self.vline(self.nLeft+15, self.nlin+2, nH) - self.stringcenter(self.nLeft+7.5, self.nlin+5.5, 'CÓDIGO') - self.vline(nMr-7, self.nlin+2, nH) - self.stringcenter(nMr-3.5, self.nlin+4.5, 'ALÍQ') - self.stringcenter(nMr-3.5, self.nlin+6.5, 'IPI') - self.vline(nMr-14, self.nlin+2, nH) - self.stringcenter(nMr-10.5, self.nlin+4.5, 'ALÍQ') - self.stringcenter(nMr-10.5, self.nlin+6.5, 'ICMS') - self.vline(nMr-26, self.nlin+2, nH) - self.stringcenter(nMr-20, self.nlin+5.5, 'VLR. IPI') - self.vline(nMr-38, self.nlin+2, nH) - self.stringcenter(nMr-32, self.nlin+5.5, 'VLR. ICMS') - self.vline(nMr-50, self.nlin+2, nH) - self.stringcenter(nMr-44, self.nlin+5.5, 'BC ICMS') - self.vline(nMr-64, self.nlin+2, nH) - self.stringcenter(nMr-57, self.nlin+5.5, 'VLR TOTAL') - self.vline(nMr-77, self.nlin+2, nH) - self.stringcenter(nMr-70.5, self.nlin+5.5, 'VLR UNIT') - self.vline(nMr-90, self.nlin+2, nH) - self.stringcenter(nMr-83.5, self.nlin+5.5, 'QTD') - self.vline(nMr-96, self.nlin+2, nH) - self.stringcenter(nMr-93, self.nlin+5.5, 'UNID') - self.vline(nMr-102, self.nlin+2, nH) - self.stringcenter(nMr-99, self.nlin+5.5, 'CFOP') - self.vline(nMr-108, self.nlin+2, nH) - self.stringcenter(nMr-105, self.nlin+5.5, 'CST') - self.vline(nMr-117, self.nlin+2, nH) - self.stringcenter(nMr-112.5, self.nlin+5.5, 'NCM/SH') - - nWidth_Prod = nMr-135-self.nLeft-11 - nCol_ = self.nLeft+20 + (nWidth_Prod / 2) - self.stringcenter(nCol_, self.nlin+5.5, 'DESCRIÇÃO DO PRODUTO/SERVIÇO') - - # Conteúdo campos - self.canvas.setFont('NimbusSanL-Regu', 5) - nLin = self.nlin+10.5 - - for id in range(oPaginator[0], oPaginator[1]): - item = el_det[id] - el_prod = item.find(".//{http://www.portalfiscal.inf.br/nfe}prod") - el_imp = item.find( - ".//{http://www.portalfiscal.inf.br/nfe}imposto") - - el_imp_ICMS = el_imp.find( - ".//{http://www.portalfiscal.inf.br/nfe}ICMS") - el_imp_IPI = el_imp.find( - ".//{http://www.portalfiscal.inf.br/nfe}IPI") - - cCST = tagtext(oNode=el_imp_ICMS, cTag='orig') + \ - tagtext(oNode=el_imp_ICMS, cTag='CST') - vBC = tagtext(oNode=el_imp_ICMS, cTag='vBC') - vICMS = tagtext(oNode=el_imp_ICMS, cTag='vICMS') - pICMS = tagtext(oNode=el_imp_ICMS, cTag='pICMS') - - vIPI = tagtext(oNode=el_imp_IPI, cTag='vIPI') - pIPI = tagtext(oNode=el_imp_IPI, cTag='pIPI') - - self.stringcenter(nMr-112.5, nLin, - tagtext(oNode=el_prod, cTag='NCM')) - self.stringcenter(nMr-105, nLin, cCST) - self.stringcenter(nMr-99, nLin, - tagtext(oNode=el_prod, cTag='CFOP')) - self.stringcenter(nMr-93, nLin, - tagtext(oNode=el_prod, cTag='uCom')) - self.stringRight(nMr-77.5, nLin, format_number( - tagtext(oNode=el_prod, cTag='qCom'), precision=4)) - self.stringRight(nMr-64.5, nLin, format_number( - tagtext(oNode=el_prod, cTag='vUnCom'), precision=2)) - self.stringRight(nMr-50.5, nLin, format_number( - tagtext(oNode=el_prod, cTag='vProd'), precision=2)) - self.stringRight(nMr-38.5, nLin, format_number(vBC, precision=2)) - self.stringRight(nMr-26.5, nLin, format_number(vICMS, precision=2)) - self.stringRight(nMr-7.5, nLin, format_number(pICMS, precision=2)) - - if vIPI: - self.stringRight(nMr-14.5, nLin, - format_number(vIPI, precision=2)) - if pIPI: - self.stringRight(nMr-0.5, nLin, - format_number(pIPI, precision=2)) - - # Código Item - line_cod = nLin - for des in list_cod_prod[id]: - self.string(self.nLeft+0.2, line_cod, des) - line_cod += nStep - - # Descrição Item - line_desc = nLin - for des in list_desc[id]: - self.string(self.nLeft+15.5, line_desc, des) - line_desc += nStep - - nLin = max(line_cod, line_desc) - self.canvas.setStrokeColor(gray) - self.hline(self.nLeft, nLin-2, self.width-self.nLeft) - self.canvas.setStrokeColor(black) - - self.nlin += nH + 3 - - def adicionais(self, oXML=None): - el_infAdic = oXML.find( - ".//{http://www.portalfiscal.inf.br/nfe}infAdic") - - self.nlin += 2 - self.canvas.setFont('NimbusSanL-Bold', 6) - self.string(self.nLeft+1, self.nlin+1, 'DADOS ADICIONAIS') - self.canvas.setFont('NimbusSanL-Regu', 5) - self.string(self.nLeft+1, self.nlin+4, 'INFORMAÇÕES COMPLEMENTARES') - self.string((self.width/2)+1, self.nlin+4, 'RESERVADO AO FISCO') - self.rect(self.nLeft, self.nlin+2, - self.width-self.nLeft-self.nRight, 42) - self.vline(self.width/2, self.nlin+2, 42) - # Conteúdo campos - styles = getSampleStyleSheet() - styleN = styles['Normal'] - styleN.fontSize = 6 - styleN.fontName = 'NimbusSanL-Regu' - styleN.leading = 7 - - fisco = tagtext(oNode=el_infAdic, cTag='infAdFisco') - observacoes = tagtext(oNode=el_infAdic, cTag='infCpl') - if fisco: - observacoes = fisco + ' ' + observacoes - P = Paragraph(observacoes, styles['Normal']) - w, h = P.wrap(92*mm, 32*mm) - altura = (self.height-self.nlin-5)*mm - P.drawOn(self.canvas, (self.nLeft+1)*mm, altura - h) - self.nlin += 36 - - def recibo_entrega(self, oXML=None): - el_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide") - el_dest = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}dest") - el_total = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}total") - el_emit = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}emit") - - # self.nlin = self.height-self.nBottom-18 # 17 altura recibo - nW = 40 - nH = 17 - self.canvas.setLineWidth(.5) - self.rect(self.nLeft, self.nlin, - self.width-(self.nLeft+self.nRight), nH) - self.hline(self.nLeft, self.nlin+8.5, self.width-self.nRight-nW) - self.vline(self.width-self.nRight-nW, self.nlin, nH) - self.vline(self.nLeft+nW, self.nlin+8.5, 8.5) - - # Labels - self.canvas.setFont('NimbusSanL-Regu', 5) - self.string(self.nLeft+1, self.nlin+10.2, 'DATA DE RECEBIMENTO') - self.string(self.nLeft+41, self.nlin+10.2, - 'IDENTIFICAÇÃO E ASSINATURA DO RECEBEDOR') - self.stringcenter(self.width-self.nRight-(nW/2), self.nlin+2, 'NF-e') - # Conteúdo campos - self.canvas.setFont('NimbusSanL-Bold', 8) - cNF = tagtext(oNode=el_ide, cTag='nNF') - cNF = '{0:011,}'.format(int(cNF)).replace(",", ".") - self.string(self.width-self.nRight-nW+2, self.nlin+8, "Nº %s" % (cNF)) - self.string(self.width-self.nRight-nW+2, self.nlin+14, - "SÉRIE %s" % (tagtext(oNode=el_ide, cTag='serie'))) - - cDt, cHr = getdateUTC(tagtext(oNode=el_ide, cTag='dhEmi')) - cTotal = format_number(tagtext(oNode=el_total, cTag='vNF'), - precision=2) - - cEnd = tagtext(oNode=el_dest, cTag='xNome') + ' - ' - cEnd += tagtext(oNode=el_dest, cTag='xLgr') + ', ' + tagtext( - oNode=el_dest, cTag='nro') + ', ' - cEnd += tagtext(oNode=el_dest, cTag='xBairro') + ', ' + tagtext( - oNode=el_dest, cTag='xMun') + ' - ' - cEnd += tagtext(oNode=el_dest, cTag='UF') - - cString = """ - RECEBEMOS DE %s OS PRODUTOS/SERVIÇOS CONSTANTES DA NOTA FISCAL INDICADA - ABAIXO. EMISSÃO: %s VALOR TOTAL: %s - DESTINATARIO: %s""" % (tagtext(oNode=el_emit, cTag='xNome'), - cDt, cTotal, cEnd) - - styles = getSampleStyleSheet() - styleN = styles['Normal'] - styleN.fontName = 'NimbusSanL-Regu' - styleN.fontSize = 6 - styleN.leading = 7 - - P = Paragraph(cString, styleN) - w, h = P.wrap(149*mm, 7*mm) - P.drawOn(self.canvas, (self.nLeft+1)*mm, - ((self.height-self.nlin)*mm) - h) - - self.nlin += 20 - self.hline(self.nLeft, self.nlin, self.width-self.nRight) - self.nlin += 2 - - def newpage(self): - self.nlin = self.nTop - self.Page += 1 - self.canvas.showPage() - - def hline(self, x, y, width): - y = self.height - y - self.canvas.line(x*mm, y*mm, width*mm, y*mm) - - def vline(self, x, y, width): - width = self.height - y - width - y = self.height - y - self.canvas.line(x*mm, y*mm, x*mm, width*mm) - - def rect(self, col, lin, nWidth, nHeight, fill=False): - lin = self.height - nHeight - lin - self.canvas.rect(col*mm, lin*mm, nWidth*mm, nHeight*mm, - stroke=True, fill=fill) - - def string(self, x, y, value): - y = self.height - y - self.canvas.drawString(x*mm, y*mm, value) - - def stringRight(self, x, y, value): - y = self.height - y - self.canvas.drawRightString(x*mm, y*mm, value) - - def stringcenter(self, x, y, value): - y = self.height - y - self.canvas.drawCentredString(x*mm, y*mm, value) - - def writeto_pdf(self, fileObj): - pdf_out = self.oPDF_IO.getvalue() - self.oPDF_IO.close() - fileObj.write(pdf_out) +# -*- coding: utf-8 -*- +# © 2017 Edson Bernardino, ITK Soft +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# Classe para geração de PDF da DANFE a partir de xml etree.fromstring + + +from cStringIO import StringIO as IO +from textwrap import wrap + +from reportlab.lib import utils +from reportlab.pdfgen import canvas +from reportlab.lib.units import mm, cm +from reportlab.lib.pagesizes import A4 +from reportlab.lib.colors import black, gray +from reportlab.graphics.barcode import code128 +from reportlab.lib.styles import getSampleStyleSheet +from reportlab.lib.enums import TA_CENTER +from reportlab.platypus import Paragraph, Image + + +def chunks(cString, nLen): + for start in range(0, len(cString), nLen): + yield cString[start:start+nLen] + + +def format_cnpj_cpf(value): + if len(value) < 12: # CPF + cValue = '%s.%s.%s-%s' % (value[:-8], value[-8:-5], + value[-5:-2], value[-2:]) + else: + cValue = '%s.%s.%s/%s-%s' % (value[:-12], value[-12:-9], + value[-9:-6], value[-6:-2], value[-2:]) + return cValue + + +def getdateUTC(cDateUTC): + cDt = cDateUTC[0:10].split('-') + cDt.reverse() + return '/'.join(cDt), cDateUTC[11:16] + + +def format_number(cNumber, precision=0, group_sep='.', decimal_sep=','): + if cNumber: + number = float(cNumber) + return ("{:,." + str(precision) + "f}").format(number).\ + replace(",", "X").replace(".", ",").replace("X", ".") + return "" + + +def tagtext(oNode=None, cTag=None): + try: + xpath = ".//{http://www.portalfiscal.inf.br/nfe}%s" % (cTag) + cText = oNode.find(xpath).text + except: + cText = '' + return cText + + +REGIME_TRIBUTACAO = { + '1': 'Simples Nacional', + '2': 'Simples Nacional, excesso sublimite de receita bruta', + '3': 'Regime Normal' +} + + +def get_image(path, width=1*cm): + img = utils.ImageReader(path) + iw, ih = img.getSize() + aspect = ih / float(iw) + return Image(path, width=width, height=(width * aspect)) + + +class danfe(object): + def __init__(self, sizepage=A4, list_xml=None, recibo=True, + orientation='portrait', logo=None): + self.width = 210 # 21 x 29,7cm + self.height = 297 + self.nLeft = 10 + self.nRight = 10 + self.nTop = 7 + self.nBottom = 8 + self.nlin = self.nTop + self.logo = logo + self.oFrete = {'0': '0 - Emitente', + '1': '1 - Destinatário', + '2': '2 - Terceiros', + '9': '9 - Sem Frete'} + + self.oPDF_IO = IO() + if orientation == 'landscape': + raise NameError('Rotina não implementada') + else: + size = sizepage + + self.canvas = canvas.Canvas(self.oPDF_IO, pagesize=size) + self.canvas.setTitle('DANFE') + self.canvas.setStrokeColor(black) + + for oXML in list_xml: + oXML_cobr = oXML.find( + ".//{http://www.portalfiscal.inf.br/nfe}cobr") + + self.NrPages = 1 + self.Page = 1 + + # Calculando total linhas usadas para descrições dos itens + # Com bloco fatura, apenas 29 linhas para itens na primeira folha + nNr_Lin_Pg_1 = 34 if oXML_cobr is None else 30 + # [ rec_ini , rec_fim , lines , limit_lines ] + oPaginator = [[0, 0, 0, nNr_Lin_Pg_1]] + el_det = oXML.findall(".//{http://www.portalfiscal.inf.br/nfe}det") + if el_det is not None: + list_desc = [] + list_cod_prod = [] + nPg = 0 + for nId, item in enumerate(el_det): + el_prod = item.find( + ".//{http://www.portalfiscal.inf.br/nfe}prod") + infAdProd = item.find( + ".//{http://www.portalfiscal.inf.br/nfe}infAdProd") + + list_ = wrap(tagtext(oNode=el_prod, cTag='xProd'), 56) + if infAdProd is not None: + list_.extend(wrap(infAdProd.text, 56)) + list_desc.append(list_) + + list_cProd = wrap(tagtext(oNode=el_prod, cTag='cProd'), 14) + list_cod_prod.append(list_cProd) + + # Nr linhas necessárias p/ descrição item + nLin_Itens = len(list_) + + if (oPaginator[nPg][2] + nLin_Itens) >= oPaginator[nPg][3]: + oPaginator.append([0, 0, 0, 77]) + nPg += 1 + oPaginator[nPg][0] = nId + oPaginator[nPg][1] = nId + 1 + oPaginator[nPg][2] = nLin_Itens + else: + # adiciona-se 1 pelo funcionamento de xrange + oPaginator[nPg][1] = nId + 1 + oPaginator[nPg][2] += nLin_Itens + + self.NrPages = len(oPaginator) # Calculando nr. páginas + + if recibo: + self.recibo_entrega(oXML=oXML) + + self.ide_emit(oXML=oXML) + self.destinatario(oXML=oXML) + + if oXML_cobr is not None: + self.faturas(oXML=oXML_cobr) + + self.impostos(oXML=oXML) + self.transportes(oXML=oXML) + self.produtos(oXML=oXML, el_det=el_det, oPaginator=oPaginator[0], + list_desc=list_desc, list_cod_prod=list_cod_prod) + + self.adicionais(oXML=oXML) + + # Gera o restante das páginas do XML + for oPag in oPaginator[1:]: + self.newpage() + self.ide_emit(oXML=oXML) + self.produtos(oXML=oXML, el_det=el_det, oPaginator=oPag, + list_desc=list_desc, nHeight=77, + list_cod_prod=list_cod_prod) + + self.newpage() + + self.canvas.save() + + def ide_emit(self, oXML=None): + elem_infNFe = oXML.find( + ".//{http://www.portalfiscal.inf.br/nfe}infNFe") + elem_protNFe = oXML.find( + ".//{http://www.portalfiscal.inf.br/nfe}protNFe") + elem_emit = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}emit") + elem_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide") + + cChave = elem_infNFe.attrib.get('Id')[3:] + barcode128 = code128.Code128(cChave, barHeight=10*mm, barWidth=0.25*mm) + + self.canvas.setLineWidth(.5) + self.rect(self.nLeft, self.nlin+1, self.nLeft+75, 32) + self.rect(self.nLeft+115, self.nlin+1, + self.width-self.nLeft-self.nRight-115, 39) + + self.hline(self.nLeft+85, self.nlin+1, 125) + + self.rect(self.nLeft+116, self.nlin+15, + self.width-self.nLeft-self.nRight-117, 6) + + self.rect(self.nLeft, self.nlin+33, + self.width-self.nLeft-self.nRight, 14) + self.hline(self.nLeft, self.nlin+40, self.width-self.nRight) + self.vline(self.nLeft+60, self.nlin+40, 7) + self.vline(self.nLeft+100, self.nlin+40, 7) + + # Labels + self.canvas.setFont('NimbusSanL-Bold', 12) + self.stringcenter(self.nLeft+98, self.nlin+5, 'DANFE') + self.stringcenter(self.nLeft+109, self.nlin+19.5, + tagtext(oNode=elem_ide, cTag='tpNF')) + self.canvas.setFont('NimbusSanL-Bold', 8) + cNF = tagtext(oNode=elem_ide, cTag='nNF') + cNF = '{0:011,}'.format(int(cNF)).replace(",", ".") + self.stringcenter(self.nLeft+100, self.nlin+25, "Nº %s" % (cNF)) + + self.stringcenter(self.nLeft+100, self.nlin+29, u"SÉRIE %s" % ( + tagtext(oNode=elem_ide, cTag='serie'))) + cPag = "Página %s de %s" % (str(self.Page), str(self.NrPages)) + self.stringcenter(self.nLeft+100, self.nlin+32, cPag) + self.canvas.setFont('NimbusSanL-Regu', 6) + self.string(self.nLeft+86, self.nlin+8, 'Documento Auxiliar da') + self.string(self.nLeft+86, self.nlin+10.5, 'Nota Fiscal Eletrônica') + self.string(self.nLeft+86, self.nlin+16, '0 - Entrada') + self.string(self.nLeft+86, self.nlin+19, '1 - Saída') + self.rect(self.nLeft+105, self.nlin+15, 8, 6) + + self.stringcenter( + self.nLeft+152, self.nlin+25, + 'Consulta de autenticidade no portal nacional da NF-e') + self.stringcenter( + self.nLeft+152, self.nlin+28, + 'www.nfe.fazenda.gov.br/portal ou no site da SEFAZ Autorizadora') + self.canvas.setFont('NimbusSanL-Regu', 5) + self.string(self.nLeft+117, self.nlin+16.7, 'CHAVE DE ACESSO') + self.string(self.nLeft+116, self.nlin+2.7, 'CONTROLE DO FISCO') + + self.string(self.nLeft+1, self.nlin+34.7, 'NATUREZA DA OPERAÇÃO') + self.string(self.nLeft+116, self.nlin+34.7, + 'PROTOCOLO DE AUTORIZAÇÃO DE USO') + self.string(self.nLeft+1, self.nlin+41.7, 'INSCRIÇÃO ESTADUAL') + self.string(self.nLeft+61, self.nlin+41.7, + 'INSCRIÇÃO ESTADUAL DO SUBST. TRIB.') + self.string(self.nLeft+101, self.nlin+41.7, 'CNPJ') + + # Conteúdo campos + barcode128.drawOn(self.canvas, (self.nLeft+111.5)*mm, + (self.height-self.nlin-14)*mm) + self.canvas.setFont('NimbusSanL-Bold', 6) + nW_Rect = (self.width-self.nLeft-self.nRight-117) / 2 + self.stringcenter(self.nLeft+116.5+nW_Rect, self.nlin+19.5, + ' '.join(chunks(cChave, 4))) # Chave + self.canvas.setFont('NimbusSanL-Regu', 8) + cDt, cHr = getdateUTC(tagtext(oNode=elem_protNFe, cTag='dhRecbto')) + cProtocolo = tagtext(oNode=elem_protNFe, cTag='nProt') + cDt = cProtocolo + ' - ' + cDt + ' ' + cHr + nW_Rect = (self.width-self.nLeft-self.nRight-110) / 2 + self.stringcenter(self.nLeft+115+nW_Rect, self.nlin+38.7, cDt) + self.canvas.setFont('NimbusSanL-Regu', 8) + self.string(self.nLeft+1, self.nlin+38.7, + tagtext(oNode=elem_ide, cTag='natOp')) + self.string(self.nLeft+1, self.nlin+46, + tagtext(oNode=elem_emit, cTag='IE')) + self.string(self.nLeft+101, self.nlin+46, + format_cnpj_cpf(tagtext(oNode=elem_emit, cTag='CNPJ'))) + + styles = getSampleStyleSheet() + styleN = styles['Normal'] + styleN.fontSize = 10 + styleN.fontName = 'NimbusSanL-Bold' + styleN.alignment = TA_CENTER + + # Razão Social emitente + P = Paragraph(tagtext(oNode=elem_emit, cTag='xNome'), styleN) + w, h = P.wrap(55*mm, 50*mm) + P.drawOn(self.canvas, (self.nLeft+30)*mm, + (self.height-self.nlin-12)*mm) + + if self.logo: + img = get_image(self.logo, width=2*cm) + img.drawOn(self.canvas, (self.nLeft+5)*mm, + (self.height-self.nlin-22)*mm) + + cEnd = tagtext(oNode=elem_emit, cTag='xLgr') + ', ' + tagtext( + oNode=elem_emit, cTag='nro') + ' - ' + cEnd += tagtext(oNode=elem_emit, cTag='xBairro') + '
' + tagtext( + oNode=elem_emit, cTag='xMun') + ' - ' + cEnd += 'Fone: ' + tagtext(oNode=elem_emit, cTag='fone') + '
' + cEnd += tagtext(oNode=elem_emit, cTag='UF') + ' - ' + tagtext( + oNode=elem_emit, cTag='CEP') + + regime = tagtext(oNode=elem_emit, cTag='CRT') + cEnd += u'
Regime Tributário: %s' % (REGIME_TRIBUTACAO[regime]) + + styleN.fontName = 'NimbusSanL-Regu' + styleN.fontSize = 7 + styleN.leading = 10 + P = Paragraph(cEnd, styleN) + w, h = P.wrap(55*mm, 30*mm) + P.drawOn(self.canvas, (self.nLeft+30)*mm, + (self.height-self.nlin-31)*mm) + + # Homologação + if tagtext(oNode=elem_ide, cTag='tpAmb') == '2': + self.canvas.saveState() + self.canvas.rotate(90) + self.canvas.setFont('Times-Bold', 40) + self.canvas.setFillColorRGB(0.57, 0.57, 0.57) + self.string(self.nLeft+65, 449, 'SEM VALOR FISCAL') + self.canvas.restoreState() + + self.nlin += 48 + + def destinatario(self, oXML=None): + elem_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide") + elem_dest = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}dest") + nMr = self.width-self.nRight + + self.nlin += 1 + + self.canvas.setFont('NimbusSanL-Bold', 7) + self.string(self.nLeft+1, self.nlin+1, 'DESTINATÁRIO/REMETENTE') + self.rect(self.nLeft, self.nlin+2, + self.width-self.nLeft-self.nRight, 20) + self.vline(nMr-25, self.nlin+2, 20) + self.hline(self.nLeft, self.nlin+8.66, self.width-self.nLeft) + self.hline(self.nLeft, self.nlin+15.32, self.width-self.nLeft) + self.vline(nMr-70, self.nlin+2, 6.66) + self.vline(nMr-53, self.nlin+8.66, 6.66) + self.vline(nMr-99, self.nlin+8.66, 6.66) + self.vline(nMr-90, self.nlin+15.32, 6.66) + self.vline(nMr-102, self.nlin+15.32, 6.66) + self.vline(nMr-136, self.nlin+15.32, 6.66) + # Labels/Fields + self.canvas.setFont('NimbusSanL-Bold', 5) + self.string(self.nLeft+1, self.nlin+3.7, 'NOME/RAZÃO SOCIAL') + self.string(nMr-69, self.nlin+3.7, 'CNPJ/CPF') + self.string(nMr-24, self.nlin+3.7, 'DATA DA EMISSÃO') + self.string(self.nLeft+1, self.nlin+10.3, 'ENDEREÇO') + self.string(nMr-98, self.nlin+10.3, 'BAIRRO/DISTRITO') + self.string(nMr-52, self.nlin+10.3, 'CEP') + self.string(nMr-24, self.nlin+10.3, 'DATA DE ENTRADA/SAÍDA') + self.string(self.nLeft+1, self.nlin+17.1, 'MUNICÍPIO') + self.string(nMr-135, self.nlin+17.1, 'FONE/FAX') + self.string(nMr-101, self.nlin+17.1, 'UF') + self.string(nMr-89, self.nlin+17.1, 'INSCRIÇÃO ESTADUAL') + self.string(nMr-24, self.nlin+17.1, 'HORA DE ENTRADA/SAÍDA') + # Conteúdo campos + self.canvas.setFont('NimbusSanL-Regu', 8) + self.string(self.nLeft+1, self.nlin+7.5, + tagtext(oNode=elem_dest, cTag='xNome')) + cnpj_cpf = format_cnpj_cpf(tagtext(oNode=elem_dest, cTag='CNPJ')) + if cnpj_cpf == '..-' or not cnpj_cpf: + cnpj_cpf = format_cnpj_cpf(tagtext(oNode=elem_dest, cTag='CPF')) + self.string(nMr-69, self.nlin+7.5, cnpj_cpf) + cDt, cHr = getdateUTC(tagtext(oNode=elem_ide, cTag='dhEmi')) + self.string(nMr-24, self.nlin+7.7, cDt + ' ' + cHr) + cDt, cHr = getdateUTC(tagtext(oNode=elem_ide, cTag='dhSaiEnt')) + self.string(nMr-24, self.nlin+14.3, cDt + ' ' + cHr) # Dt saída + cEnd = tagtext(oNode=elem_dest, cTag='xLgr') + ', ' + tagtext( + oNode=elem_dest, cTag='nro') + self.string(self.nLeft+1, self.nlin+14.3, cEnd) + self.string(nMr-98, self.nlin+14.3, + tagtext(oNode=elem_dest, cTag='xBairro')) + self.string(nMr-52, self.nlin+14.3, + tagtext(oNode=elem_dest, cTag='CEP')) + self.string(self.nLeft+1, self.nlin+21.1, + tagtext(oNode=elem_dest, cTag='xMun')) + self.string(nMr-135, self.nlin+21.1, + tagtext(oNode=elem_dest, cTag='fone')) + self.string(nMr-101, self.nlin+21.1, + tagtext(oNode=elem_dest, cTag='UF')) + self.string(nMr-89, self.nlin+21.1, + tagtext(oNode=elem_dest, cTag='IE')) + + self.nlin += 24 # Nr linhas ocupadas pelo bloco + + def faturas(self, oXML=None): + + nMr = self.width-self.nRight + + self.canvas.setFont('NimbusSanL-Bold', 7) + self.string(self.nLeft+1, self.nlin+1, 'FATURA') + self.rect(self.nLeft, self.nlin+2, + self.width-self.nLeft-self.nRight, 13) + self.vline(nMr-47.5, self.nlin+2, 13) + self.vline(nMr-95, self.nlin+2, 13) + self.vline(nMr-142.5, self.nlin+2, 13) + self.hline(nMr-47.5, self.nlin+8.5, self.width-self.nLeft) + # Labels + self.canvas.setFont('NimbusSanL-Regu', 5) + self.string(nMr-46.5, self.nlin+3.8, 'CÓDIGO VENDEDOR') + self.string(nMr-46.5, self.nlin+10.2, 'NOME VENDEDOR') + self.string(nMr-93.5, self.nlin+3.8, + 'FATURA VENCIMENTO VALOR') + self.string(nMr-140.5, self.nlin+3.8, + 'FATURA VENCIMENTO VALOR') + self.string(self.nLeft+2, self.nlin+3.8, + 'FATURA VENCIMENTO VALOR') + + # Conteúdo campos + self.canvas.setFont('NimbusSanL-Bold', 6) + nLin = 7 + nPar = 1 + nCol = 0 + nAju = 0 + + line_iter = iter(oXML[1:10]) # Salta elemt 1 e considera os próximos 9 + for oXML_dup in line_iter: + + cDt, cHr = getdateUTC(tagtext(oNode=oXML_dup, cTag='dVenc')) + self.string(self.nLeft+nCol+1, self.nlin+nLin, + tagtext(oNode=oXML_dup, cTag='nDup')) + self.string(self.nLeft+nCol+17, self.nlin+nLin, cDt) + self.stringRight( + self.nLeft+nCol+47, self.nlin+nLin, + format_number(tagtext(oNode=oXML_dup, cTag='vDup'), + precision=2)) + + if nPar == 3: + nLin = 7 + nPar = 1 + nCol += 47 + nAju += 1 + nCol += nAju * (0.3) + else: + nLin += 3.3 + nPar += 1 + + # Campos adicionais XML - Condicionados a existencia de financeiro + elem_infAdic = oXML.getparent().find( + ".//{http://www.portalfiscal.inf.br/nfe}infAdic") + if elem_infAdic is not None: + codvend = elem_infAdic.find( + ".//{http://www.portalfiscal.inf.br/nfe}obsCont\ +[@xCampo='CodVendedor']") + self.string(nMr-46.5, self.nlin+7.7, + tagtext(oNode=codvend, cTag='xTexto')) + vend = elem_infAdic.find(".//{http://www.portalfiscal.inf.br/nfe}\ +obsCont[@xCampo='NomeVendedor']") + self.string(nMr-46.5, self.nlin+14.3, + tagtext(oNode=vend, cTag='xTexto')[:36]) + + self.nlin += 16 # Nr linhas ocupadas pelo bloco + + def impostos(self, oXML=None): + # Impostos + el_total = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}total") + nMr = self.width-self.nRight + self.nlin += 1 + self.canvas.setFont('NimbusSanL-Bold', 7) + self.string(self.nLeft+1, self.nlin+1, 'CÁLCULO DO IMPOSTO') + self.rect(self.nLeft, self.nlin+2, + self.width-self.nLeft-self.nRight, 13) + self.hline(self.nLeft, self.nlin+8.5, self.width-self.nLeft) + self.vline(nMr-35, self.nlin+2, 6.5) + self.vline(nMr-65, self.nlin+2, 6.5) + self.vline(nMr-95, self.nlin+2, 6.5) + self.vline(nMr-125, self.nlin+2, 6.5) + self.vline(nMr-155, self.nlin+2, 6.5) + self.vline(nMr-35, self.nlin+8.5, 6.5) + self.vline(nMr-65, self.nlin+8.5, 6.5) + self.vline(nMr-95, self.nlin+8.5, 6.5) + self.vline(nMr-125, self.nlin+8.5, 6.5) + self.vline(nMr-155, self.nlin+8.5, 6.5) + # Labels + self.canvas.setFont('NimbusSanL-Regu', 5) + self.string(self.nLeft+1, self.nlin+3.8, 'BASE DE CÁLCULO DO ICMS') + self.string(nMr-154, self.nlin+3.8, 'VALOR DO ICMS') + self.string(nMr-124, self.nlin+3.8, 'BASE DE CÁLCULO DO ICMS ST') + self.string(nMr-94, self.nlin+3.8, 'VALOR DO ICMS ST') + self.string(nMr-64, self.nlin+3.8, 'VALOR APROX TRIBUTOS') + self.string(nMr-34, self.nlin+3.8, 'VALOR TOTAL DOS PRODUTOS') + + self.string(self.nLeft+1, self.nlin+10.2, 'VALOR DO FRETE') + self.string(nMr-154, self.nlin+10.2, 'VALOR DO SEGURO') + self.string(nMr-124, self.nlin+10.2, 'DESCONTO') + self.string(nMr-94, self.nlin+10.2, 'OUTRAS DESP. ACESSÓRIAS') + self.string(nMr-64, self.nlin+10.2, 'VALOR DO IPI') + self.string(nMr-34, self.nlin+10.2, 'VALOR TOTAL DA NOTA') + + # Conteúdo campos + self.canvas.setFont('NimbusSanL-Regu', 8) + self.stringRight( + self.nLeft+34, self.nlin+7.7, + format_number(tagtext(oNode=el_total, cTag='vBC'), precision=2)) + self.stringRight( + self.nLeft+64, self.nlin+7.7, + format_number(tagtext(oNode=el_total, cTag='vICMS'), precision=2)) + self.stringRight( + self.nLeft+94, self.nlin+7.7, + format_number(tagtext(oNode=el_total, cTag='vBCST'), precision=2)) + self.stringRight( + nMr-66, self.nlin+7.7, + format_number(tagtext(oNode=el_total, cTag='vST'), precision=2)) + self.stringRight( + nMr-36, self.nlin+7.7, + format_number(tagtext(oNode=el_total, cTag='vTotTrib'), + precision=2)) + self.stringRight( + nMr-1, self.nlin+7.7, + format_number(tagtext(oNode=el_total, cTag='vProd'), precision=2)) + self.stringRight( + self.nLeft+34, self.nlin+14.1, + format_number(tagtext(oNode=el_total, cTag='vFrete'), precision=2)) + self.stringRight( + self.nLeft+64, self.nlin+14.1, + format_number(tagtext(oNode=el_total, cTag='vSeg'), precision=2)) + self.stringRight( + self.nLeft+94, self.nlin+14.1, + format_number(tagtext(oNode=el_total, cTag='vDesc'), precision=2)) + self.stringRight( + self.nLeft+124, self.nlin+14.1, + format_number(tagtext(oNode=el_total, cTag='vOutro'), precision=2)) + self.stringRight( + self.nLeft+154, self.nlin+14.1, + format_number(tagtext(oNode=el_total, cTag='vIPI'), precision=2)) + self.stringRight( + nMr-1, self.nlin+14.1, + format_number(tagtext(oNode=el_total, cTag='vNF'), precision=2)) + + self.nlin += 17 # Nr linhas ocupadas pelo bloco + + def transportes(self, oXML=None): + el_transp = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}transp") + nMr = self.width-self.nRight + + self.canvas.setFont('NimbusSanL-Bold', 7) + self.string(self.nLeft+1, self.nlin+1, + 'TRANSPORTADOR/VOLUMES TRANSPORTADOS') + self.canvas.setFont('NimbusSanL-Regu', 5) + self.rect(self.nLeft, self.nlin+2, + self.width-self.nLeft-self.nRight, 20) + self.hline(self.nLeft, self.nlin+8.6, self.width-self.nLeft) + self.hline(self.nLeft, self.nlin+15.2, self.width-self.nLeft) + self.vline(nMr-40, self.nlin+2, 13.2) + self.vline(nMr-49, self.nlin+2, 20) + self.vline(nMr-92, self.nlin+2, 6.6) + self.vline(nMr-120, self.nlin+2, 6.6) + self.vline(nMr-75, self.nlin+2, 6.6) + self.vline(nMr-26, self.nlin+15.2, 6.6) + self.vline(nMr-102, self.nlin+8.6, 6.6) + self.vline(nMr-85, self.nlin+15.2, 6.6) + self.vline(nMr-121, self.nlin+15.2, 6.6) + self.vline(nMr-160, self.nlin+15.2, 6.6) + # Labels/Fields + self.string(nMr-39, self.nlin+3.8, 'CNPJ/CPF') + self.string(nMr-74, self.nlin+3.8, 'PLACA DO VEÍCULO') + self.string(nMr-91, self.nlin+3.8, 'CÓDIGO ANTT') + self.string(nMr-119, self.nlin+3.8, 'FRETE POR CONTA') + self.string(self.nLeft+1, self.nlin+3.8, 'RAZÃO SOCIAL') + self.string(nMr-48, self.nlin+3.8, 'UF') + self.string(nMr-39, self.nlin+10.3, 'INSCRIÇÃO ESTADUAL') + self.string(nMr-48, self.nlin+10.3, 'UF') + self.string(nMr-101, self.nlin+10.3, 'MUNICÍPIO') + self.string(self.nLeft+1, self.nlin+10.3, 'ENDEREÇO') + self.string(nMr-48, self.nlin+17, 'PESO BRUTO') + self.string(nMr-25, self.nlin+17, 'PESO LÍQUIDO') + self.string(nMr-84, self.nlin+17, 'NUMERAÇÃO') + self.string(nMr-120, self.nlin+17, 'MARCA') + self.string(nMr-159, self.nlin+17, 'ESPÉCIE') + self.string(self.nLeft+1, self.nlin+17, 'QUANTIDADE') + # Conteúdo campos + self.canvas.setFont('NimbusSanL-Regu', 8) + self.string(self.nLeft+1, self.nlin+7.7, + tagtext(oNode=el_transp, cTag='xNome')[:40]) + self.string(self.nLeft+71, self.nlin+7.7, + self.oFrete[tagtext(oNode=el_transp, cTag='modFrete')]) + self.string(nMr-39, self.nlin+7.7, + format_cnpj_cpf(tagtext(oNode=el_transp, cTag='CNPJ'))) + self.string(self.nLeft+1, self.nlin+14.2, + tagtext(oNode=el_transp, cTag='xEnder')[:45]) + self.string(self.nLeft+89, self.nlin+14.2, + tagtext(oNode=el_transp, cTag='xMun')) + self.string(nMr-48, self.nlin+14.2, + tagtext(oNode=el_transp, cTag='UF')) + self.string(nMr-39, self.nlin+14.2, + tagtext(oNode=el_transp, cTag='IE')) + self.string(self.nLeft+1, self.nlin+21.2, + tagtext(oNode=el_transp, cTag='qVol')) + self.string(self.nLeft+31, self.nlin+21.2, + tagtext(oNode=el_transp, cTag='esp')) + self.string(self.nLeft+70, self.nlin+21.2, + tagtext(oNode=el_transp, cTag='marca')) + self.string(self.nLeft+106, self.nlin+21.2, + tagtext(oNode=el_transp, cTag='nVol')) + self.stringRight( + nMr-27, self.nlin+21.2, + format_number(tagtext(oNode=el_transp, cTag='pesoB'), precision=3)) + self.stringRight( + nMr-1, self.nlin+21.2, + format_number(tagtext(oNode=el_transp, cTag='pesoL'), precision=3)) + + self.nlin += 23 + + def produtos(self, oXML=None, el_det=None, oPaginator=None, + list_desc=None, list_cod_prod=None, nHeight=29): + + nMr = self.width-self.nRight + nStep = 2.5 # Passo entre linhas + nH = 7.5 + (nHeight * nStep) # cabeçalho 7.5 + self.nlin += 1 + + self.canvas.setFont('NimbusSanL-Bold', 7) + self.string(self.nLeft+1, self.nlin+1, 'DADOS DO PRODUTO/SERVIÇO') + self.rect(self.nLeft, self.nlin+2, + self.width-self.nLeft-self.nRight, nH) + self.hline(self.nLeft, self.nlin+8, self.width-self.nLeft) + + self.canvas.setFont('NimbusSanL-Regu', 5.5) + # Colunas + self.vline(self.nLeft+15, self.nlin+2, nH) + self.stringcenter(self.nLeft+7.5, self.nlin+5.5, 'CÓDIGO') + self.vline(nMr-7, self.nlin+2, nH) + self.stringcenter(nMr-3.5, self.nlin+4.5, 'ALÍQ') + self.stringcenter(nMr-3.5, self.nlin+6.5, 'IPI') + self.vline(nMr-14, self.nlin+2, nH) + self.stringcenter(nMr-10.5, self.nlin+4.5, 'ALÍQ') + self.stringcenter(nMr-10.5, self.nlin+6.5, 'ICMS') + self.vline(nMr-26, self.nlin+2, nH) + self.stringcenter(nMr-20, self.nlin+5.5, 'VLR. IPI') + self.vline(nMr-38, self.nlin+2, nH) + self.stringcenter(nMr-32, self.nlin+5.5, 'VLR. ICMS') + self.vline(nMr-50, self.nlin+2, nH) + self.stringcenter(nMr-44, self.nlin+5.5, 'BC ICMS') + self.vline(nMr-64, self.nlin+2, nH) + self.stringcenter(nMr-57, self.nlin+5.5, 'VLR TOTAL') + self.vline(nMr-77, self.nlin+2, nH) + self.stringcenter(nMr-70.5, self.nlin+5.5, 'VLR UNIT') + self.vline(nMr-90, self.nlin+2, nH) + self.stringcenter(nMr-83.5, self.nlin+5.5, 'QTD') + self.vline(nMr-96, self.nlin+2, nH) + self.stringcenter(nMr-93, self.nlin+5.5, 'UNID') + self.vline(nMr-102, self.nlin+2, nH) + self.stringcenter(nMr-99, self.nlin+5.5, 'CFOP') + self.vline(nMr-108, self.nlin+2, nH) + self.stringcenter(nMr-105, self.nlin+5.5, 'CST') + self.vline(nMr-117, self.nlin+2, nH) + self.stringcenter(nMr-112.5, self.nlin+5.5, 'NCM/SH') + + nWidth_Prod = nMr-135-self.nLeft-11 + nCol_ = self.nLeft+20 + (nWidth_Prod / 2) + self.stringcenter(nCol_, self.nlin+5.5, 'DESCRIÇÃO DO PRODUTO/SERVIÇO') + + # Conteúdo campos + self.canvas.setFont('NimbusSanL-Regu', 5) + nLin = self.nlin+10.5 + + for id in xrange(oPaginator[0], oPaginator[1]): + item = el_det[id] + el_prod = item.find(".//{http://www.portalfiscal.inf.br/nfe}prod") + el_imp = item.find( + ".//{http://www.portalfiscal.inf.br/nfe}imposto") + + el_imp_ICMS = el_imp.find( + ".//{http://www.portalfiscal.inf.br/nfe}ICMS") + el_imp_IPI = el_imp.find( + ".//{http://www.portalfiscal.inf.br/nfe}IPI") + cCST = tagtext(oNode=el_imp_ICMS, cTag='orig') + \ + tagtext(oNode=el_imp_ICMS, cTag='CSOSN') + vBC = tagtext(oNode=el_imp_ICMS, cTag='vBC') + vICMS = tagtext(oNode=el_imp_ICMS, cTag='vICMS') + pICMS = tagtext(oNode=el_imp_ICMS, cTag='pICMS') + + vIPI = tagtext(oNode=el_imp_IPI, cTag='vIPI') + pIPI = tagtext(oNode=el_imp_IPI, cTag='pIPI') + + self.stringcenter(nMr-112.5, nLin, + tagtext(oNode=el_prod, cTag='NCM')) + self.stringcenter(nMr-105, nLin, cCST) + self.stringcenter(nMr-99, nLin, + tagtext(oNode=el_prod, cTag='CFOP')) + self.stringcenter(nMr-93, nLin, + tagtext(oNode=el_prod, cTag='uCom')) + self.stringRight(nMr-77.5, nLin, format_number( + tagtext(oNode=el_prod, cTag='qCom'), precision=4)) + self.stringRight(nMr-64.5, nLin, format_number( + tagtext(oNode=el_prod, cTag='vUnCom'), precision=2)) + self.stringRight(nMr-50.5, nLin, format_number( + tagtext(oNode=el_prod, cTag='vProd'), precision=2)) + self.stringRight(nMr-38.5, nLin, format_number(vBC, precision=2)) + self.stringRight(nMr-26.5, nLin, format_number(vICMS, precision=2)) + self.stringRight(nMr-7.5, nLin, format_number(pICMS, precision=2)) + + if vIPI: + self.stringRight(nMr-14.5, nLin, + format_number(vIPI, precision=2)) + if pIPI: + self.stringRight(nMr-0.5, nLin, + format_number(pIPI, precision=2)) + + # Código Item + line_cod = nLin + for des in list_cod_prod[id]: + self.string(self.nLeft+0.2, line_cod, des) + line_cod += nStep + + # Descrição Item + line_desc = nLin + for des in list_desc[id]: + self.string(self.nLeft+15.5, line_desc, des) + line_desc += nStep + + nLin = max(line_cod, line_desc) + self.canvas.setStrokeColor(gray) + self.hline(self.nLeft, nLin-2, self.width-self.nLeft) + self.canvas.setStrokeColor(black) + + self.nlin += nH + 3 + + def adicionais(self, oXML=None): + el_infAdic = oXML.find( + ".//{http://www.portalfiscal.inf.br/nfe}infAdic") + + self.nlin += 2 + self.canvas.setFont('NimbusSanL-Bold', 6) + self.string(self.nLeft+1, self.nlin+1, 'DADOS ADICIONAIS') + self.canvas.setFont('NimbusSanL-Regu', 5) + self.string(self.nLeft+1, self.nlin+4, 'INFORMAÇÕES COMPLEMENTARES') + self.string((self.width/2)+1, self.nlin+4, 'RESERVADO AO FISCO') + self.rect(self.nLeft, self.nlin+2, + self.width-self.nLeft-self.nRight, 42) + self.vline(self.width/2, self.nlin+2, 42) + # Conteúdo campos + styles = getSampleStyleSheet() + styleN = styles['Normal'] + styleN.fontSize = 6 + styleN.fontName = 'NimbusSanL-Regu' + styleN.leading = 7 + fisco = tagtext(oNode=el_infAdic, cTag='infAdFisco') + observacoes = tagtext(oNode=el_infAdic, cTag='infCpl') + if fisco: + observacoes = fisco + ' ' + observacoes + P = Paragraph(observacoes, styles['Normal']) + w, h = P.wrap(92*mm, 32*mm) + altura = (self.height-self.nlin-5)*mm + P.drawOn(self.canvas, (self.nLeft+1)*mm, altura - h) + self.nlin += 36 + + def recibo_entrega(self, oXML=None): + el_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide") + el_dest = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}dest") + el_total = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}total") + el_emit = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}emit") + + # self.nlin = self.height-self.nBottom-18 # 17 altura recibo + nW = 40 + nH = 17 + self.canvas.setLineWidth(.5) + self.rect(self.nLeft, self.nlin, + self.width-(self.nLeft+self.nRight), nH) + self.hline(self.nLeft, self.nlin+8.5, self.width-self.nRight-nW) + self.vline(self.width-self.nRight-nW, self.nlin, nH) + self.vline(self.nLeft+nW, self.nlin+8.5, 8.5) + + # Labels + self.canvas.setFont('NimbusSanL-Regu', 5) + self.string(self.nLeft+1, self.nlin+10.2, 'DATA DE RECEBIMENTO') + self.string(self.nLeft+41, self.nlin+10.2, + 'IDENTIFICAÇÃO E ASSINATURA DO RECEBEDOR') + self.stringcenter(self.width-self.nRight-(nW/2), self.nlin+2, 'NF-e') + # Conteúdo campos + self.canvas.setFont('NimbusSanL-Bold', 8) + cNF = tagtext(oNode=el_ide, cTag='nNF') + cNF = '{0:011,}'.format(int(cNF)).replace(",", ".") + self.string(self.width-self.nRight-nW+2, self.nlin+8, "Nº %s" % (cNF)) + self.string(self.width-self.nRight-nW+2, self.nlin+14, + u"SÉRIE %s" % (tagtext(oNode=el_ide, cTag='serie'))) + + cDt, cHr = getdateUTC(tagtext(oNode=el_ide, cTag='dhEmi')) + cTotal = format_number(tagtext(oNode=el_total, cTag='vNF'), + precision=2) + + cEnd = tagtext(oNode=el_dest, cTag='xNome') + ' - ' + cEnd += tagtext(oNode=el_dest, cTag='xLgr') + ', ' + tagtext( + oNode=el_dest, cTag='nro') + ', ' + cEnd += tagtext(oNode=el_dest, cTag='xBairro') + ', ' + tagtext( + oNode=el_dest, cTag='xMun') + ' - ' + cEnd += tagtext(oNode=el_dest, cTag='UF') + + cString = u""" + RECEBEMOS DE %s OS PRODUTOS/SERVIÇOS CONSTANTES DA NOTA FISCAL INDICADA + ABAIXO. EMISSÃO: %s VALOR TOTAL: %s + DESTINATARIO: %s""" % (tagtext(oNode=el_emit, cTag='xNome'), + cDt, cTotal, cEnd) + + styles = getSampleStyleSheet() + styleN = styles['Normal'] + styleN.fontName = 'NimbusSanL-Regu' + styleN.fontSize = 6 + styleN.leading = 7 + + P = Paragraph(cString, styleN) + w, h = P.wrap(149*mm, 7*mm) + P.drawOn(self.canvas, (self.nLeft+1)*mm, + ((self.height-self.nlin)*mm) - h) + + self.nlin += 20 + self.hline(self.nLeft, self.nlin, self.width-self.nRight) + self.nlin += 2 + + def newpage(self): + self.nlin = self.nTop + self.Page += 1 + self.canvas.showPage() + + def hline(self, x, y, width): + y = self.height - y + self.canvas.line(x*mm, y*mm, width*mm, y*mm) + + def vline(self, x, y, width): + width = self.height - y - width + y = self.height - y + self.canvas.line(x*mm, y*mm, x*mm, width*mm) + + def rect(self, col, lin, nWidth, nHeight, fill=False): + lin = self.height - nHeight - lin + self.canvas.rect(col*mm, lin*mm, nWidth*mm, nHeight*mm, + stroke=True, fill=fill) + + def string(self, x, y, value): + y = self.height - y + self.canvas.drawString(x*mm, y*mm, value) + + def stringRight(self, x, y, value): + y = self.height - y + self.canvas.drawRightString(x*mm, y*mm, value) + + def stringcenter(self, x, y, value): + y = self.height - y + self.canvas.drawCentredString(x*mm, y*mm, value) + + def writeto_pdf(self, fileObj): + pdf_out = self.oPDF_IO.getvalue() + self.oPDF_IO.close() + fileObj.write(pdf_out) diff --git a/setup.py b/setup.py index ff137cf..a4bf038 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ # coding=utf-8 from setuptools import setup, find_packages -VERSION = "0.9.3" +VERSION = "0.9.4" setup( name="PyTrustNFe3", From 4915472aa7b1347f16c9f72d0269e970c9e5de31 Mon Sep 17 00:00:00 2001 From: carcaroff Date: Fri, 22 Dec 2017 15:49:52 -0200 Subject: [PATCH 05/10] [FIX] --- pytrustnfe/nfe/danfe.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/pytrustnfe/nfe/danfe.py b/pytrustnfe/nfe/danfe.py index bfe9692..1aeca9b 100644 --- a/pytrustnfe/nfe/danfe.py +++ b/pytrustnfe/nfe/danfe.py @@ -3,8 +3,8 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # Classe para geração de PDF da DANFE a partir de xml etree.fromstring - -from cStringIO import StringIO as IO +import os +from io import BytesIO from textwrap import wrap from reportlab.lib import utils @@ -16,6 +16,8 @@ from reportlab.graphics.barcode import code128 from reportlab.lib.styles import getSampleStyleSheet from reportlab.lib.enums import TA_CENTER from reportlab.platypus import Paragraph, Image +from reportlab.pdfbase import pdfmetrics +from reportlab.pdfbase.ttfonts import TTFont def chunks(cString, nLen): @@ -55,7 +57,6 @@ def tagtext(oNode=None, cTag=None): cText = '' return cText - REGIME_TRIBUTACAO = { '1': 'Simples Nacional', '2': 'Simples Nacional, excesso sublimite de receita bruta', @@ -73,6 +74,14 @@ def get_image(path, width=1*cm): class danfe(object): def __init__(self, sizepage=A4, list_xml=None, recibo=True, orientation='portrait', logo=None): + + path = os.path.join(os.path.dirname(__file__), 'fonts') + pdfmetrics.registerFont( + TTFont('NimbusSanL-Regu', + os.path.join(path, 'NimbusSanL Regular.ttf'))) + pdfmetrics.registerFont( + TTFont('NimbusSanL-Bold', + os.path.join(path, 'NimbusSanL Bold.ttf'))) self.width = 210 # 21 x 29,7cm self.height = 297 self.nLeft = 10 @@ -86,7 +95,7 @@ class danfe(object): '2': '2 - Terceiros', '9': '9 - Sem Frete'} - self.oPDF_IO = IO() + self.oPDF_IO = BytesIO() if orientation == 'landscape': raise NameError('Rotina não implementada') else: @@ -208,7 +217,7 @@ class danfe(object): cNF = '{0:011,}'.format(int(cNF)).replace(",", ".") self.stringcenter(self.nLeft+100, self.nlin+25, "Nº %s" % (cNF)) - self.stringcenter(self.nLeft+100, self.nlin+29, u"SÉRIE %s" % ( + self.stringcenter(self.nLeft+100, self.nlin+29, "SÉRIE %s" % ( tagtext(oNode=elem_ide, cTag='serie'))) cPag = "Página %s de %s" % (str(self.Page), str(self.NrPages)) self.stringcenter(self.nLeft+100, self.nlin+32, cPag) @@ -284,7 +293,7 @@ class danfe(object): oNode=elem_emit, cTag='CEP') regime = tagtext(oNode=elem_emit, cTag='CRT') - cEnd += u'
Regime Tributário: %s' % (REGIME_TRIBUTACAO[regime]) + cEnd += '
Regime Tributário: %s' % (REGIME_TRIBUTACAO[regime]) styleN.fontName = 'NimbusSanL-Regu' styleN.fontSize = 7 @@ -640,7 +649,7 @@ obsCont[@xCampo='NomeVendedor']") self.canvas.setFont('NimbusSanL-Regu', 5) nLin = self.nlin+10.5 - for id in xrange(oPaginator[0], oPaginator[1]): + for id in range(oPaginator[0], oPaginator[1]): item = el_det[id] el_prod = item.find(".//{http://www.portalfiscal.inf.br/nfe}prod") el_imp = item.find( @@ -650,6 +659,7 @@ obsCont[@xCampo='NomeVendedor']") ".//{http://www.portalfiscal.inf.br/nfe}ICMS") el_imp_IPI = el_imp.find( ".//{http://www.portalfiscal.inf.br/nfe}IPI") + cCST = tagtext(oNode=el_imp_ICMS, cTag='orig') + \ tagtext(oNode=el_imp_ICMS, cTag='CSOSN') vBC = tagtext(oNode=el_imp_ICMS, cTag='vBC') @@ -721,6 +731,7 @@ obsCont[@xCampo='NomeVendedor']") styleN.fontSize = 6 styleN.fontName = 'NimbusSanL-Regu' styleN.leading = 7 + fisco = tagtext(oNode=el_infAdic, cTag='infAdFisco') observacoes = tagtext(oNode=el_infAdic, cTag='infCpl') if fisco: @@ -759,7 +770,7 @@ obsCont[@xCampo='NomeVendedor']") cNF = '{0:011,}'.format(int(cNF)).replace(",", ".") self.string(self.width-self.nRight-nW+2, self.nlin+8, "Nº %s" % (cNF)) self.string(self.width-self.nRight-nW+2, self.nlin+14, - u"SÉRIE %s" % (tagtext(oNode=el_ide, cTag='serie'))) + "SÉRIE %s" % (tagtext(oNode=el_ide, cTag='serie'))) cDt, cHr = getdateUTC(tagtext(oNode=el_ide, cTag='dhEmi')) cTotal = format_number(tagtext(oNode=el_total, cTag='vNF'), @@ -772,7 +783,7 @@ obsCont[@xCampo='NomeVendedor']") oNode=el_dest, cTag='xMun') + ' - ' cEnd += tagtext(oNode=el_dest, cTag='UF') - cString = u""" + cString = """ RECEBEMOS DE %s OS PRODUTOS/SERVIÇOS CONSTANTES DA NOTA FISCAL INDICADA ABAIXO. EMISSÃO: %s VALOR TOTAL: %s DESTINATARIO: %s""" % (tagtext(oNode=el_emit, cTag='xNome'), From f42937ae5f2213e574483b523cfdf9c777d86158 Mon Sep 17 00:00:00 2001 From: carcaroff Date: Tue, 26 Dec 2017 11:58:10 -0200 Subject: [PATCH 06/10] [FIX] --- pytrustnfe/nfe/danfe.py | 563 +++++++++++++++++++++++++----------------------- 1 file changed, 288 insertions(+), 275 deletions(-) diff --git a/pytrustnfe/nfe/danfe.py b/pytrustnfe/nfe/danfe.py index 1aeca9b..7cb7303 100644 --- a/pytrustnfe/nfe/danfe.py +++ b/pytrustnfe/nfe/danfe.py @@ -22,7 +22,7 @@ from reportlab.pdfbase.ttfonts import TTFont def chunks(cString, nLen): for start in range(0, len(cString), nLen): - yield cString[start:start+nLen] + yield cString[start:start + nLen] def format_cnpj_cpf(value): @@ -64,7 +64,7 @@ REGIME_TRIBUTACAO = { } -def get_image(path, width=1*cm): +def get_image(path, width=1 * cm): img = utils.ImageReader(path) iw, ih = img.getSize() aspect = ih / float(iw) @@ -72,6 +72,7 @@ def get_image(path, width=1*cm): class danfe(object): + def __init__(self, sizepage=A4, list_xml=None, recibo=True, orientation='portrait', logo=None): @@ -189,82 +190,84 @@ class danfe(object): elem_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide") cChave = elem_infNFe.attrib.get('Id')[3:] - barcode128 = code128.Code128(cChave, barHeight=10*mm, barWidth=0.25*mm) + barcode128 = code128.Code128( + cChave, barHeight=10 * mm, barWidth=0.25 * mm) self.canvas.setLineWidth(.5) - self.rect(self.nLeft, self.nlin+1, self.nLeft+75, 32) - self.rect(self.nLeft+115, self.nlin+1, - self.width-self.nLeft-self.nRight-115, 39) + self.rect(self.nLeft, self.nlin + 1, self.nLeft + 75, 32) + self.rect(self.nLeft + 115, self.nlin + 1, + self.width - self.nLeft - self.nRight - 115, 39) - self.hline(self.nLeft+85, self.nlin+1, 125) + self.hline(self.nLeft + 85, self.nlin + 1, 125) - self.rect(self.nLeft+116, self.nlin+15, - self.width-self.nLeft-self.nRight-117, 6) + self.rect(self.nLeft + 116, self.nlin + 15, + self.width - self.nLeft - self.nRight - 117, 6) - self.rect(self.nLeft, self.nlin+33, - self.width-self.nLeft-self.nRight, 14) - self.hline(self.nLeft, self.nlin+40, self.width-self.nRight) - self.vline(self.nLeft+60, self.nlin+40, 7) - self.vline(self.nLeft+100, self.nlin+40, 7) + self.rect(self.nLeft, self.nlin + 33, + self.width - self.nLeft - self.nRight, 14) + self.hline(self.nLeft, self.nlin + 40, self.width - self.nRight) + self.vline(self.nLeft + 60, self.nlin + 40, 7) + self.vline(self.nLeft + 100, self.nlin + 40, 7) # Labels self.canvas.setFont('NimbusSanL-Bold', 12) - self.stringcenter(self.nLeft+98, self.nlin+5, 'DANFE') - self.stringcenter(self.nLeft+109, self.nlin+19.5, + self.stringcenter(self.nLeft + 98, self.nlin + 5, 'DANFE') + self.stringcenter(self.nLeft + 109, self.nlin + 19.5, tagtext(oNode=elem_ide, cTag='tpNF')) self.canvas.setFont('NimbusSanL-Bold', 8) cNF = tagtext(oNode=elem_ide, cTag='nNF') cNF = '{0:011,}'.format(int(cNF)).replace(",", ".") - self.stringcenter(self.nLeft+100, self.nlin+25, "Nº %s" % (cNF)) + self.stringcenter(self.nLeft + 100, self.nlin + 25, "Nº %s" % (cNF)) - self.stringcenter(self.nLeft+100, self.nlin+29, "SÉRIE %s" % ( + self.stringcenter(self.nLeft + 100, self.nlin + 29, "SÉRIE %s" % ( tagtext(oNode=elem_ide, cTag='serie'))) cPag = "Página %s de %s" % (str(self.Page), str(self.NrPages)) - self.stringcenter(self.nLeft+100, self.nlin+32, cPag) + self.stringcenter(self.nLeft + 100, self.nlin + 32, cPag) self.canvas.setFont('NimbusSanL-Regu', 6) - self.string(self.nLeft+86, self.nlin+8, 'Documento Auxiliar da') - self.string(self.nLeft+86, self.nlin+10.5, 'Nota Fiscal Eletrônica') - self.string(self.nLeft+86, self.nlin+16, '0 - Entrada') - self.string(self.nLeft+86, self.nlin+19, '1 - Saída') - self.rect(self.nLeft+105, self.nlin+15, 8, 6) + self.string(self.nLeft + 86, self.nlin + 8, 'Documento Auxiliar da') + self.string(self.nLeft + 86, self.nlin + + 10.5, 'Nota Fiscal Eletrônica') + self.string(self.nLeft + 86, self.nlin + 16, '0 - Entrada') + self.string(self.nLeft + 86, self.nlin + 19, '1 - Saída') + self.rect(self.nLeft + 105, self.nlin + 15, 8, 6) self.stringcenter( - self.nLeft+152, self.nlin+25, + self.nLeft + 152, self.nlin + 25, 'Consulta de autenticidade no portal nacional da NF-e') self.stringcenter( - self.nLeft+152, self.nlin+28, + self.nLeft + 152, self.nlin + 28, 'www.nfe.fazenda.gov.br/portal ou no site da SEFAZ Autorizadora') self.canvas.setFont('NimbusSanL-Regu', 5) - self.string(self.nLeft+117, self.nlin+16.7, 'CHAVE DE ACESSO') - self.string(self.nLeft+116, self.nlin+2.7, 'CONTROLE DO FISCO') + self.string(self.nLeft + 117, self.nlin + 16.7, 'CHAVE DE ACESSO') + self.string(self.nLeft + 116, self.nlin + 2.7, 'CONTROLE DO FISCO') - self.string(self.nLeft+1, self.nlin+34.7, 'NATUREZA DA OPERAÇÃO') - self.string(self.nLeft+116, self.nlin+34.7, + self.string(self.nLeft + 1, self.nlin + 34.7, 'NATUREZA DA OPERAÇÃO') + self.string(self.nLeft + 116, self.nlin + 34.7, 'PROTOCOLO DE AUTORIZAÇÃO DE USO') - self.string(self.nLeft+1, self.nlin+41.7, 'INSCRIÇÃO ESTADUAL') - self.string(self.nLeft+61, self.nlin+41.7, + self.string(self.nLeft + 1, self.nlin + 41.7, 'INSCRIÇÃO ESTADUAL') + self.string(self.nLeft + 61, self.nlin + 41.7, 'INSCRIÇÃO ESTADUAL DO SUBST. TRIB.') - self.string(self.nLeft+101, self.nlin+41.7, 'CNPJ') + self.string(self.nLeft + 101, self.nlin + 41.7, 'CNPJ') # Conteúdo campos - barcode128.drawOn(self.canvas, (self.nLeft+111.5)*mm, - (self.height-self.nlin-14)*mm) + barcode128.drawOn(self.canvas, (self.nLeft + 111.5) * mm, + (self.height - self.nlin - 14) * mm) self.canvas.setFont('NimbusSanL-Bold', 6) - nW_Rect = (self.width-self.nLeft-self.nRight-117) / 2 - self.stringcenter(self.nLeft+116.5+nW_Rect, self.nlin+19.5, + nW_Rect = (self.width - self.nLeft - self.nRight - 117) / 2 + self.stringcenter(self.nLeft + 116.5 + nW_Rect, self.nlin + 19.5, ' '.join(chunks(cChave, 4))) # Chave self.canvas.setFont('NimbusSanL-Regu', 8) cDt, cHr = getdateUTC(tagtext(oNode=elem_protNFe, cTag='dhRecbto')) cProtocolo = tagtext(oNode=elem_protNFe, cTag='nProt') cDt = cProtocolo + ' - ' + cDt + ' ' + cHr - nW_Rect = (self.width-self.nLeft-self.nRight-110) / 2 - self.stringcenter(self.nLeft+115+nW_Rect, self.nlin+38.7, cDt) + nW_Rect = (self.width - self.nLeft - self.nRight - 110) / 2 + self.stringcenter(self.nLeft + 115 + nW_Rect, self.nlin + 38.7, cDt) self.canvas.setFont('NimbusSanL-Regu', 8) - self.string(self.nLeft+1, self.nlin+38.7, + self.string(self.nLeft + 1, self.nlin + 38.7, tagtext(oNode=elem_ide, cTag='natOp')) - self.string(self.nLeft+1, self.nlin+46, + self.string(self.nLeft + 1, self.nlin + 46, tagtext(oNode=elem_emit, cTag='IE')) - self.string(self.nLeft+101, self.nlin+46, + self.string(self.nLeft + 101, self.nlin + 46, format_cnpj_cpf(tagtext(oNode=elem_emit, cTag='CNPJ'))) styles = getSampleStyleSheet() @@ -275,14 +278,14 @@ class danfe(object): # Razão Social emitente P = Paragraph(tagtext(oNode=elem_emit, cTag='xNome'), styleN) - w, h = P.wrap(55*mm, 50*mm) - P.drawOn(self.canvas, (self.nLeft+30)*mm, - (self.height-self.nlin-12)*mm) + w, h = P.wrap(55 * mm, 50 * mm) + P.drawOn(self.canvas, (self.nLeft + 30) * mm, + (self.height - self.nlin - 12) * mm) if self.logo: - img = get_image(self.logo, width=2*cm) - img.drawOn(self.canvas, (self.nLeft+5)*mm, - (self.height-self.nlin-22)*mm) + img = get_image(self.logo, width=2 * cm) + img.drawOn(self.canvas, (self.nLeft + 5) * mm, + (self.height - self.nlin - 22) * mm) cEnd = tagtext(oNode=elem_emit, cTag='xLgr') + ', ' + tagtext( oNode=elem_emit, cTag='nro') + ' - ' @@ -299,9 +302,9 @@ class danfe(object): styleN.fontSize = 7 styleN.leading = 10 P = Paragraph(cEnd, styleN) - w, h = P.wrap(55*mm, 30*mm) - P.drawOn(self.canvas, (self.nLeft+30)*mm, - (self.height-self.nlin-31)*mm) + w, h = P.wrap(55 * mm, 30 * mm) + P.drawOn(self.canvas, (self.nLeft + 30) * mm, + (self.height - self.nlin - 31) * mm) # Homologação if tagtext(oNode=elem_ide, cTag='tpAmb') == '2': @@ -309,7 +312,7 @@ class danfe(object): self.canvas.rotate(90) self.canvas.setFont('Times-Bold', 40) self.canvas.setFillColorRGB(0.57, 0.57, 0.57) - self.string(self.nLeft+65, 449, 'SEM VALOR FISCAL') + self.string(self.nLeft + 65, 449, 'SEM VALOR FISCAL') self.canvas.restoreState() self.nlin += 48 @@ -317,88 +320,90 @@ class danfe(object): def destinatario(self, oXML=None): elem_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide") elem_dest = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}dest") - nMr = self.width-self.nRight + nMr = self.width - self.nRight self.nlin += 1 self.canvas.setFont('NimbusSanL-Bold', 7) - self.string(self.nLeft+1, self.nlin+1, 'DESTINATÁRIO/REMETENTE') - self.rect(self.nLeft, self.nlin+2, - self.width-self.nLeft-self.nRight, 20) - self.vline(nMr-25, self.nlin+2, 20) - self.hline(self.nLeft, self.nlin+8.66, self.width-self.nLeft) - self.hline(self.nLeft, self.nlin+15.32, self.width-self.nLeft) - self.vline(nMr-70, self.nlin+2, 6.66) - self.vline(nMr-53, self.nlin+8.66, 6.66) - self.vline(nMr-99, self.nlin+8.66, 6.66) - self.vline(nMr-90, self.nlin+15.32, 6.66) - self.vline(nMr-102, self.nlin+15.32, 6.66) - self.vline(nMr-136, self.nlin+15.32, 6.66) + self.string(self.nLeft + 1, self.nlin + 1, 'DESTINATÁRIO/REMETENTE') + self.rect(self.nLeft, self.nlin + 2, + self.width - self.nLeft - self.nRight, 20) + self.vline(nMr - 25, self.nlin + 2, 20) + self.hline(self.nLeft, self.nlin + 8.66, self.width - self.nLeft) + self.hline(self.nLeft, self.nlin + 15.32, self.width - self.nLeft) + self.vline(nMr - 70, self.nlin + 2, 6.66) + self.vline(nMr - 53, self.nlin + 8.66, 6.66) + self.vline(nMr - 99, self.nlin + 8.66, 6.66) + self.vline(nMr - 90, self.nlin + 15.32, 6.66) + self.vline(nMr - 102, self.nlin + 15.32, 6.66) + self.vline(nMr - 136, self.nlin + 15.32, 6.66) # Labels/Fields self.canvas.setFont('NimbusSanL-Bold', 5) - self.string(self.nLeft+1, self.nlin+3.7, 'NOME/RAZÃO SOCIAL') - self.string(nMr-69, self.nlin+3.7, 'CNPJ/CPF') - self.string(nMr-24, self.nlin+3.7, 'DATA DA EMISSÃO') - self.string(self.nLeft+1, self.nlin+10.3, 'ENDEREÇO') - self.string(nMr-98, self.nlin+10.3, 'BAIRRO/DISTRITO') - self.string(nMr-52, self.nlin+10.3, 'CEP') - self.string(nMr-24, self.nlin+10.3, 'DATA DE ENTRADA/SAÍDA') - self.string(self.nLeft+1, self.nlin+17.1, 'MUNICÍPIO') - self.string(nMr-135, self.nlin+17.1, 'FONE/FAX') - self.string(nMr-101, self.nlin+17.1, 'UF') - self.string(nMr-89, self.nlin+17.1, 'INSCRIÇÃO ESTADUAL') - self.string(nMr-24, self.nlin+17.1, 'HORA DE ENTRADA/SAÍDA') + self.string(self.nLeft + 1, self.nlin + 3.7, 'NOME/RAZÃO SOCIAL') + self.string(nMr - 69, self.nlin + 3.7, 'CNPJ/CPF') + self.string(nMr - 24, self.nlin + 3.7, 'DATA DA EMISSÃO') + self.string(self.nLeft + 1, self.nlin + 10.3, 'ENDEREÇO') + self.string(nMr - 98, self.nlin + 10.3, 'BAIRRO/DISTRITO') + self.string(nMr - 52, self.nlin + 10.3, 'CEP') + self.string(nMr - 24, self.nlin + 10.3, 'DATA DE ENTRADA/SAÍDA') + self.string(self.nLeft + 1, self.nlin + 17.1, 'MUNICÍPIO') + self.string(nMr - 135, self.nlin + 17.1, 'FONE/FAX') + self.string(nMr - 101, self.nlin + 17.1, 'UF') + self.string(nMr - 89, self.nlin + 17.1, 'INSCRIÇÃO ESTADUAL') + self.string(nMr - 24, self.nlin + 17.1, 'HORA DE ENTRADA/SAÍDA') # Conteúdo campos self.canvas.setFont('NimbusSanL-Regu', 8) - self.string(self.nLeft+1, self.nlin+7.5, + self.string(self.nLeft + 1, self.nlin + 7.5, tagtext(oNode=elem_dest, cTag='xNome')) - cnpj_cpf = format_cnpj_cpf(tagtext(oNode=elem_dest, cTag='CNPJ')) - if cnpj_cpf == '..-' or not cnpj_cpf: + cnpj_cpf = tagtext(oNode=elem_dest, cTag='CNPJ') + if cnpj_cpf: + cnpj_cpf = format_cnpj_cpf(cnpj_cpf) + else: cnpj_cpf = format_cnpj_cpf(tagtext(oNode=elem_dest, cTag='CPF')) - self.string(nMr-69, self.nlin+7.5, cnpj_cpf) + self.string(nMr - 69, self.nlin + 7.5, cnpj_cpf) cDt, cHr = getdateUTC(tagtext(oNode=elem_ide, cTag='dhEmi')) - self.string(nMr-24, self.nlin+7.7, cDt + ' ' + cHr) + self.string(nMr - 24, self.nlin + 7.7, cDt + ' ' + cHr) cDt, cHr = getdateUTC(tagtext(oNode=elem_ide, cTag='dhSaiEnt')) - self.string(nMr-24, self.nlin+14.3, cDt + ' ' + cHr) # Dt saída + self.string(nMr - 24, self.nlin + 14.3, cDt + ' ' + cHr) # Dt saída cEnd = tagtext(oNode=elem_dest, cTag='xLgr') + ', ' + tagtext( oNode=elem_dest, cTag='nro') - self.string(self.nLeft+1, self.nlin+14.3, cEnd) - self.string(nMr-98, self.nlin+14.3, + self.string(self.nLeft + 1, self.nlin + 14.3, cEnd) + self.string(nMr - 98, self.nlin + 14.3, tagtext(oNode=elem_dest, cTag='xBairro')) - self.string(nMr-52, self.nlin+14.3, + self.string(nMr - 52, self.nlin + 14.3, tagtext(oNode=elem_dest, cTag='CEP')) - self.string(self.nLeft+1, self.nlin+21.1, + self.string(self.nLeft + 1, self.nlin + 21.1, tagtext(oNode=elem_dest, cTag='xMun')) - self.string(nMr-135, self.nlin+21.1, + self.string(nMr - 135, self.nlin + 21.1, tagtext(oNode=elem_dest, cTag='fone')) - self.string(nMr-101, self.nlin+21.1, + self.string(nMr - 101, self.nlin + 21.1, tagtext(oNode=elem_dest, cTag='UF')) - self.string(nMr-89, self.nlin+21.1, + self.string(nMr - 89, self.nlin + 21.1, tagtext(oNode=elem_dest, cTag='IE')) self.nlin += 24 # Nr linhas ocupadas pelo bloco def faturas(self, oXML=None): - nMr = self.width-self.nRight + nMr = self.width - self.nRight self.canvas.setFont('NimbusSanL-Bold', 7) - self.string(self.nLeft+1, self.nlin+1, 'FATURA') - self.rect(self.nLeft, self.nlin+2, - self.width-self.nLeft-self.nRight, 13) - self.vline(nMr-47.5, self.nlin+2, 13) - self.vline(nMr-95, self.nlin+2, 13) - self.vline(nMr-142.5, self.nlin+2, 13) - self.hline(nMr-47.5, self.nlin+8.5, self.width-self.nLeft) + self.string(self.nLeft + 1, self.nlin + 1, 'FATURA') + self.rect(self.nLeft, self.nlin + 2, + self.width - self.nLeft - self.nRight, 13) + self.vline(nMr - 47.5, self.nlin + 2, 13) + self.vline(nMr - 95, self.nlin + 2, 13) + self.vline(nMr - 142.5, self.nlin + 2, 13) + self.hline(nMr - 47.5, self.nlin + 8.5, self.width - self.nLeft) # Labels self.canvas.setFont('NimbusSanL-Regu', 5) - self.string(nMr-46.5, self.nlin+3.8, 'CÓDIGO VENDEDOR') - self.string(nMr-46.5, self.nlin+10.2, 'NOME VENDEDOR') - self.string(nMr-93.5, self.nlin+3.8, + self.string(nMr - 46.5, self.nlin + 3.8, 'CÓDIGO VENDEDOR') + self.string(nMr - 46.5, self.nlin + 10.2, 'NOME VENDEDOR') + self.string(nMr - 93.5, self.nlin + 3.8, 'FATURA VENCIMENTO VALOR') - self.string(nMr-140.5, self.nlin+3.8, + self.string(nMr - 140.5, self.nlin + 3.8, 'FATURA VENCIMENTO VALOR') - self.string(self.nLeft+2, self.nlin+3.8, + self.string(self.nLeft + 2, self.nlin + 3.8, 'FATURA VENCIMENTO VALOR') # Conteúdo campos @@ -412,11 +417,11 @@ class danfe(object): for oXML_dup in line_iter: cDt, cHr = getdateUTC(tagtext(oNode=oXML_dup, cTag='dVenc')) - self.string(self.nLeft+nCol+1, self.nlin+nLin, + self.string(self.nLeft + nCol + 1, self.nlin + nLin, tagtext(oNode=oXML_dup, cTag='nDup')) - self.string(self.nLeft+nCol+17, self.nlin+nLin, cDt) + self.string(self.nLeft + nCol + 17, self.nlin + nLin, cDt) self.stringRight( - self.nLeft+nCol+47, self.nlin+nLin, + self.nLeft + nCol + 47, self.nlin + nLin, format_number(tagtext(oNode=oXML_dup, cTag='vDup'), precision=2)) @@ -437,11 +442,11 @@ class danfe(object): codvend = elem_infAdic.find( ".//{http://www.portalfiscal.inf.br/nfe}obsCont\ [@xCampo='CodVendedor']") - self.string(nMr-46.5, self.nlin+7.7, + self.string(nMr - 46.5, self.nlin + 7.7, tagtext(oNode=codvend, cTag='xTexto')) vend = elem_infAdic.find(".//{http://www.portalfiscal.inf.br/nfe}\ obsCont[@xCampo='NomeVendedor']") - self.string(nMr-46.5, self.nlin+14.3, + self.string(nMr - 46.5, self.nlin + 14.3, tagtext(oNode=vend, cTag='xTexto')[:36]) self.nlin += 16 # Nr linhas ocupadas pelo bloco @@ -449,149 +454,149 @@ obsCont[@xCampo='NomeVendedor']") def impostos(self, oXML=None): # Impostos el_total = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}total") - nMr = self.width-self.nRight + nMr = self.width - self.nRight self.nlin += 1 self.canvas.setFont('NimbusSanL-Bold', 7) - self.string(self.nLeft+1, self.nlin+1, 'CÁLCULO DO IMPOSTO') - self.rect(self.nLeft, self.nlin+2, - self.width-self.nLeft-self.nRight, 13) - self.hline(self.nLeft, self.nlin+8.5, self.width-self.nLeft) - self.vline(nMr-35, self.nlin+2, 6.5) - self.vline(nMr-65, self.nlin+2, 6.5) - self.vline(nMr-95, self.nlin+2, 6.5) - self.vline(nMr-125, self.nlin+2, 6.5) - self.vline(nMr-155, self.nlin+2, 6.5) - self.vline(nMr-35, self.nlin+8.5, 6.5) - self.vline(nMr-65, self.nlin+8.5, 6.5) - self.vline(nMr-95, self.nlin+8.5, 6.5) - self.vline(nMr-125, self.nlin+8.5, 6.5) - self.vline(nMr-155, self.nlin+8.5, 6.5) + self.string(self.nLeft + 1, self.nlin + 1, 'CÁLCULO DO IMPOSTO') + self.rect(self.nLeft, self.nlin + 2, + self.width - self.nLeft - self.nRight, 13) + self.hline(self.nLeft, self.nlin + 8.5, self.width - self.nLeft) + self.vline(nMr - 35, self.nlin + 2, 6.5) + self.vline(nMr - 65, self.nlin + 2, 6.5) + self.vline(nMr - 95, self.nlin + 2, 6.5) + self.vline(nMr - 125, self.nlin + 2, 6.5) + self.vline(nMr - 155, self.nlin + 2, 6.5) + self.vline(nMr - 35, self.nlin + 8.5, 6.5) + self.vline(nMr - 65, self.nlin + 8.5, 6.5) + self.vline(nMr - 95, self.nlin + 8.5, 6.5) + self.vline(nMr - 125, self.nlin + 8.5, 6.5) + self.vline(nMr - 155, self.nlin + 8.5, 6.5) # Labels self.canvas.setFont('NimbusSanL-Regu', 5) - self.string(self.nLeft+1, self.nlin+3.8, 'BASE DE CÁLCULO DO ICMS') - self.string(nMr-154, self.nlin+3.8, 'VALOR DO ICMS') - self.string(nMr-124, self.nlin+3.8, 'BASE DE CÁLCULO DO ICMS ST') - self.string(nMr-94, self.nlin+3.8, 'VALOR DO ICMS ST') - self.string(nMr-64, self.nlin+3.8, 'VALOR APROX TRIBUTOS') - self.string(nMr-34, self.nlin+3.8, 'VALOR TOTAL DOS PRODUTOS') - - self.string(self.nLeft+1, self.nlin+10.2, 'VALOR DO FRETE') - self.string(nMr-154, self.nlin+10.2, 'VALOR DO SEGURO') - self.string(nMr-124, self.nlin+10.2, 'DESCONTO') - self.string(nMr-94, self.nlin+10.2, 'OUTRAS DESP. ACESSÓRIAS') - self.string(nMr-64, self.nlin+10.2, 'VALOR DO IPI') - self.string(nMr-34, self.nlin+10.2, 'VALOR TOTAL DA NOTA') + self.string(self.nLeft + 1, self.nlin + 3.8, 'BASE DE CÁLCULO DO ICMS') + self.string(nMr - 154, self.nlin + 3.8, 'VALOR DO ICMS') + self.string(nMr - 124, self.nlin + 3.8, 'BASE DE CÁLCULO DO ICMS ST') + self.string(nMr - 94, self.nlin + 3.8, 'VALOR DO ICMS ST') + self.string(nMr - 64, self.nlin + 3.8, 'VALOR APROX TRIBUTOS') + self.string(nMr - 34, self.nlin + 3.8, 'VALOR TOTAL DOS PRODUTOS') + + self.string(self.nLeft + 1, self.nlin + 10.2, 'VALOR DO FRETE') + self.string(nMr - 154, self.nlin + 10.2, 'VALOR DO SEGURO') + self.string(nMr - 124, self.nlin + 10.2, 'DESCONTO') + self.string(nMr - 94, self.nlin + 10.2, 'OUTRAS DESP. ACESSÓRIAS') + self.string(nMr - 64, self.nlin + 10.2, 'VALOR DO IPI') + self.string(nMr - 34, self.nlin + 10.2, 'VALOR TOTAL DA NOTA') # Conteúdo campos self.canvas.setFont('NimbusSanL-Regu', 8) self.stringRight( - self.nLeft+34, self.nlin+7.7, + self.nLeft + 34, self.nlin + 7.7, format_number(tagtext(oNode=el_total, cTag='vBC'), precision=2)) self.stringRight( - self.nLeft+64, self.nlin+7.7, + self.nLeft + 64, self.nlin + 7.7, format_number(tagtext(oNode=el_total, cTag='vICMS'), precision=2)) self.stringRight( - self.nLeft+94, self.nlin+7.7, + self.nLeft + 94, self.nlin + 7.7, format_number(tagtext(oNode=el_total, cTag='vBCST'), precision=2)) self.stringRight( - nMr-66, self.nlin+7.7, + nMr - 66, self.nlin + 7.7, format_number(tagtext(oNode=el_total, cTag='vST'), precision=2)) self.stringRight( - nMr-36, self.nlin+7.7, + nMr - 36, self.nlin + 7.7, format_number(tagtext(oNode=el_total, cTag='vTotTrib'), precision=2)) self.stringRight( - nMr-1, self.nlin+7.7, + nMr - 1, self.nlin + 7.7, format_number(tagtext(oNode=el_total, cTag='vProd'), precision=2)) self.stringRight( - self.nLeft+34, self.nlin+14.1, + self.nLeft + 34, self.nlin + 14.1, format_number(tagtext(oNode=el_total, cTag='vFrete'), precision=2)) self.stringRight( - self.nLeft+64, self.nlin+14.1, + self.nLeft + 64, self.nlin + 14.1, format_number(tagtext(oNode=el_total, cTag='vSeg'), precision=2)) self.stringRight( - self.nLeft+94, self.nlin+14.1, + self.nLeft + 94, self.nlin + 14.1, format_number(tagtext(oNode=el_total, cTag='vDesc'), precision=2)) self.stringRight( - self.nLeft+124, self.nlin+14.1, + self.nLeft + 124, self.nlin + 14.1, format_number(tagtext(oNode=el_total, cTag='vOutro'), precision=2)) self.stringRight( - self.nLeft+154, self.nlin+14.1, + self.nLeft + 154, self.nlin + 14.1, format_number(tagtext(oNode=el_total, cTag='vIPI'), precision=2)) self.stringRight( - nMr-1, self.nlin+14.1, + nMr - 1, self.nlin + 14.1, format_number(tagtext(oNode=el_total, cTag='vNF'), precision=2)) self.nlin += 17 # Nr linhas ocupadas pelo bloco def transportes(self, oXML=None): el_transp = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}transp") - nMr = self.width-self.nRight + nMr = self.width - self.nRight self.canvas.setFont('NimbusSanL-Bold', 7) - self.string(self.nLeft+1, self.nlin+1, + self.string(self.nLeft + 1, self.nlin + 1, 'TRANSPORTADOR/VOLUMES TRANSPORTADOS') self.canvas.setFont('NimbusSanL-Regu', 5) - self.rect(self.nLeft, self.nlin+2, - self.width-self.nLeft-self.nRight, 20) - self.hline(self.nLeft, self.nlin+8.6, self.width-self.nLeft) - self.hline(self.nLeft, self.nlin+15.2, self.width-self.nLeft) - self.vline(nMr-40, self.nlin+2, 13.2) - self.vline(nMr-49, self.nlin+2, 20) - self.vline(nMr-92, self.nlin+2, 6.6) - self.vline(nMr-120, self.nlin+2, 6.6) - self.vline(nMr-75, self.nlin+2, 6.6) - self.vline(nMr-26, self.nlin+15.2, 6.6) - self.vline(nMr-102, self.nlin+8.6, 6.6) - self.vline(nMr-85, self.nlin+15.2, 6.6) - self.vline(nMr-121, self.nlin+15.2, 6.6) - self.vline(nMr-160, self.nlin+15.2, 6.6) + self.rect(self.nLeft, self.nlin + 2, + self.width - self.nLeft - self.nRight, 20) + self.hline(self.nLeft, self.nlin + 8.6, self.width - self.nLeft) + self.hline(self.nLeft, self.nlin + 15.2, self.width - self.nLeft) + self.vline(nMr - 40, self.nlin + 2, 13.2) + self.vline(nMr - 49, self.nlin + 2, 20) + self.vline(nMr - 92, self.nlin + 2, 6.6) + self.vline(nMr - 120, self.nlin + 2, 6.6) + self.vline(nMr - 75, self.nlin + 2, 6.6) + self.vline(nMr - 26, self.nlin + 15.2, 6.6) + self.vline(nMr - 102, self.nlin + 8.6, 6.6) + self.vline(nMr - 85, self.nlin + 15.2, 6.6) + self.vline(nMr - 121, self.nlin + 15.2, 6.6) + self.vline(nMr - 160, self.nlin + 15.2, 6.6) # Labels/Fields - self.string(nMr-39, self.nlin+3.8, 'CNPJ/CPF') - self.string(nMr-74, self.nlin+3.8, 'PLACA DO VEÍCULO') - self.string(nMr-91, self.nlin+3.8, 'CÓDIGO ANTT') - self.string(nMr-119, self.nlin+3.8, 'FRETE POR CONTA') - self.string(self.nLeft+1, self.nlin+3.8, 'RAZÃO SOCIAL') - self.string(nMr-48, self.nlin+3.8, 'UF') - self.string(nMr-39, self.nlin+10.3, 'INSCRIÇÃO ESTADUAL') - self.string(nMr-48, self.nlin+10.3, 'UF') - self.string(nMr-101, self.nlin+10.3, 'MUNICÍPIO') - self.string(self.nLeft+1, self.nlin+10.3, 'ENDEREÇO') - self.string(nMr-48, self.nlin+17, 'PESO BRUTO') - self.string(nMr-25, self.nlin+17, 'PESO LÍQUIDO') - self.string(nMr-84, self.nlin+17, 'NUMERAÇÃO') - self.string(nMr-120, self.nlin+17, 'MARCA') - self.string(nMr-159, self.nlin+17, 'ESPÉCIE') - self.string(self.nLeft+1, self.nlin+17, 'QUANTIDADE') + self.string(nMr - 39, self.nlin + 3.8, 'CNPJ/CPF') + self.string(nMr - 74, self.nlin + 3.8, 'PLACA DO VEÍCULO') + self.string(nMr - 91, self.nlin + 3.8, 'CÓDIGO ANTT') + self.string(nMr - 119, self.nlin + 3.8, 'FRETE POR CONTA') + self.string(self.nLeft + 1, self.nlin + 3.8, 'RAZÃO SOCIAL') + self.string(nMr - 48, self.nlin + 3.8, 'UF') + self.string(nMr - 39, self.nlin + 10.3, 'INSCRIÇÃO ESTADUAL') + self.string(nMr - 48, self.nlin + 10.3, 'UF') + self.string(nMr - 101, self.nlin + 10.3, 'MUNICÍPIO') + self.string(self.nLeft + 1, self.nlin + 10.3, 'ENDEREÇO') + self.string(nMr - 48, self.nlin + 17, 'PESO BRUTO') + self.string(nMr - 25, self.nlin + 17, 'PESO LÍQUIDO') + self.string(nMr - 84, self.nlin + 17, 'NUMERAÇÃO') + self.string(nMr - 120, self.nlin + 17, 'MARCA') + self.string(nMr - 159, self.nlin + 17, 'ESPÉCIE') + self.string(self.nLeft + 1, self.nlin + 17, 'QUANTIDADE') # Conteúdo campos self.canvas.setFont('NimbusSanL-Regu', 8) - self.string(self.nLeft+1, self.nlin+7.7, + self.string(self.nLeft + 1, self.nlin + 7.7, tagtext(oNode=el_transp, cTag='xNome')[:40]) - self.string(self.nLeft+71, self.nlin+7.7, + self.string(self.nLeft + 71, self.nlin + 7.7, self.oFrete[tagtext(oNode=el_transp, cTag='modFrete')]) - self.string(nMr-39, self.nlin+7.7, + self.string(nMr - 39, self.nlin + 7.7, format_cnpj_cpf(tagtext(oNode=el_transp, cTag='CNPJ'))) - self.string(self.nLeft+1, self.nlin+14.2, + self.string(self.nLeft + 1, self.nlin + 14.2, tagtext(oNode=el_transp, cTag='xEnder')[:45]) - self.string(self.nLeft+89, self.nlin+14.2, + self.string(self.nLeft + 89, self.nlin + 14.2, tagtext(oNode=el_transp, cTag='xMun')) - self.string(nMr-48, self.nlin+14.2, + self.string(nMr - 48, self.nlin + 14.2, tagtext(oNode=el_transp, cTag='UF')) - self.string(nMr-39, self.nlin+14.2, + self.string(nMr - 39, self.nlin + 14.2, tagtext(oNode=el_transp, cTag='IE')) - self.string(self.nLeft+1, self.nlin+21.2, + self.string(self.nLeft + 1, self.nlin + 21.2, tagtext(oNode=el_transp, cTag='qVol')) - self.string(self.nLeft+31, self.nlin+21.2, + self.string(self.nLeft + 31, self.nlin + 21.2, tagtext(oNode=el_transp, cTag='esp')) - self.string(self.nLeft+70, self.nlin+21.2, + self.string(self.nLeft + 70, self.nlin + 21.2, tagtext(oNode=el_transp, cTag='marca')) - self.string(self.nLeft+106, self.nlin+21.2, + self.string(self.nLeft + 106, self.nlin + 21.2, tagtext(oNode=el_transp, cTag='nVol')) self.stringRight( - nMr-27, self.nlin+21.2, + nMr - 27, self.nlin + 21.2, format_number(tagtext(oNode=el_transp, cTag='pesoB'), precision=3)) self.stringRight( - nMr-1, self.nlin+21.2, + nMr - 1, self.nlin + 21.2, format_number(tagtext(oNode=el_transp, cTag='pesoL'), precision=3)) self.nlin += 23 @@ -599,55 +604,56 @@ obsCont[@xCampo='NomeVendedor']") def produtos(self, oXML=None, el_det=None, oPaginator=None, list_desc=None, list_cod_prod=None, nHeight=29): - nMr = self.width-self.nRight + nMr = self.width - self.nRight nStep = 2.5 # Passo entre linhas nH = 7.5 + (nHeight * nStep) # cabeçalho 7.5 self.nlin += 1 self.canvas.setFont('NimbusSanL-Bold', 7) - self.string(self.nLeft+1, self.nlin+1, 'DADOS DO PRODUTO/SERVIÇO') - self.rect(self.nLeft, self.nlin+2, - self.width-self.nLeft-self.nRight, nH) - self.hline(self.nLeft, self.nlin+8, self.width-self.nLeft) + self.string(self.nLeft + 1, self.nlin + 1, 'DADOS DO PRODUTO/SERVIÇO') + self.rect(self.nLeft, self.nlin + 2, + self.width - self.nLeft - self.nRight, nH) + self.hline(self.nLeft, self.nlin + 8, self.width - self.nLeft) self.canvas.setFont('NimbusSanL-Regu', 5.5) # Colunas - self.vline(self.nLeft+15, self.nlin+2, nH) - self.stringcenter(self.nLeft+7.5, self.nlin+5.5, 'CÓDIGO') - self.vline(nMr-7, self.nlin+2, nH) - self.stringcenter(nMr-3.5, self.nlin+4.5, 'ALÍQ') - self.stringcenter(nMr-3.5, self.nlin+6.5, 'IPI') - self.vline(nMr-14, self.nlin+2, nH) - self.stringcenter(nMr-10.5, self.nlin+4.5, 'ALÍQ') - self.stringcenter(nMr-10.5, self.nlin+6.5, 'ICMS') - self.vline(nMr-26, self.nlin+2, nH) - self.stringcenter(nMr-20, self.nlin+5.5, 'VLR. IPI') - self.vline(nMr-38, self.nlin+2, nH) - self.stringcenter(nMr-32, self.nlin+5.5, 'VLR. ICMS') - self.vline(nMr-50, self.nlin+2, nH) - self.stringcenter(nMr-44, self.nlin+5.5, 'BC ICMS') - self.vline(nMr-64, self.nlin+2, nH) - self.stringcenter(nMr-57, self.nlin+5.5, 'VLR TOTAL') - self.vline(nMr-77, self.nlin+2, nH) - self.stringcenter(nMr-70.5, self.nlin+5.5, 'VLR UNIT') - self.vline(nMr-90, self.nlin+2, nH) - self.stringcenter(nMr-83.5, self.nlin+5.5, 'QTD') - self.vline(nMr-96, self.nlin+2, nH) - self.stringcenter(nMr-93, self.nlin+5.5, 'UNID') - self.vline(nMr-102, self.nlin+2, nH) - self.stringcenter(nMr-99, self.nlin+5.5, 'CFOP') - self.vline(nMr-108, self.nlin+2, nH) - self.stringcenter(nMr-105, self.nlin+5.5, 'CST') - self.vline(nMr-117, self.nlin+2, nH) - self.stringcenter(nMr-112.5, self.nlin+5.5, 'NCM/SH') - - nWidth_Prod = nMr-135-self.nLeft-11 - nCol_ = self.nLeft+20 + (nWidth_Prod / 2) - self.stringcenter(nCol_, self.nlin+5.5, 'DESCRIÇÃO DO PRODUTO/SERVIÇO') + self.vline(self.nLeft + 15, self.nlin + 2, nH) + self.stringcenter(self.nLeft + 7.5, self.nlin + 5.5, 'CÓDIGO') + self.vline(nMr - 7, self.nlin + 2, nH) + self.stringcenter(nMr - 3.5, self.nlin + 4.5, 'ALÍQ') + self.stringcenter(nMr - 3.5, self.nlin + 6.5, 'IPI') + self.vline(nMr - 14, self.nlin + 2, nH) + self.stringcenter(nMr - 10.5, self.nlin + 4.5, 'ALÍQ') + self.stringcenter(nMr - 10.5, self.nlin + 6.5, 'ICMS') + self.vline(nMr - 26, self.nlin + 2, nH) + self.stringcenter(nMr - 20, self.nlin + 5.5, 'VLR. IPI') + self.vline(nMr - 38, self.nlin + 2, nH) + self.stringcenter(nMr - 32, self.nlin + 5.5, 'VLR. ICMS') + self.vline(nMr - 50, self.nlin + 2, nH) + self.stringcenter(nMr - 44, self.nlin + 5.5, 'BC ICMS') + self.vline(nMr - 64, self.nlin + 2, nH) + self.stringcenter(nMr - 57, self.nlin + 5.5, 'VLR TOTAL') + self.vline(nMr - 77, self.nlin + 2, nH) + self.stringcenter(nMr - 70.5, self.nlin + 5.5, 'VLR UNIT') + self.vline(nMr - 90, self.nlin + 2, nH) + self.stringcenter(nMr - 83.5, self.nlin + 5.5, 'QTD') + self.vline(nMr - 96, self.nlin + 2, nH) + self.stringcenter(nMr - 93, self.nlin + 5.5, 'UNID') + self.vline(nMr - 102, self.nlin + 2, nH) + self.stringcenter(nMr - 99, self.nlin + 5.5, 'CFOP') + self.vline(nMr - 108, self.nlin + 2, nH) + self.stringcenter(nMr - 105, self.nlin + 5.5, 'CST') + self.vline(nMr - 117, self.nlin + 2, nH) + self.stringcenter(nMr - 112.5, self.nlin + 5.5, 'NCM/SH') + + nWidth_Prod = nMr - 135 - self.nLeft - 11 + nCol_ = self.nLeft + 20 + (nWidth_Prod / 2) + self.stringcenter(nCol_, self.nlin + 5.5, + 'DESCRIÇÃO DO PRODUTO/SERVIÇO') # Conteúdo campos self.canvas.setFont('NimbusSanL-Regu', 5) - nLin = self.nlin+10.5 + nLin = self.nlin + 10.5 for id in range(oPaginator[0], oPaginator[1]): item = el_det[id] @@ -661,7 +667,8 @@ obsCont[@xCampo='NomeVendedor']") ".//{http://www.portalfiscal.inf.br/nfe}IPI") cCST = tagtext(oNode=el_imp_ICMS, cTag='orig') + \ - tagtext(oNode=el_imp_ICMS, cTag='CSOSN') + (tagtext(oNode=el_imp_ICMS, cTag='CST') or + tagtext(oNode=el_imp_ICMS, cTag='CSOSN')) vBC = tagtext(oNode=el_imp_ICMS, cTag='vBC') vICMS = tagtext(oNode=el_imp_ICMS, cTag='vICMS') pICMS = tagtext(oNode=el_imp_ICMS, cTag='pICMS') @@ -669,45 +676,47 @@ obsCont[@xCampo='NomeVendedor']") vIPI = tagtext(oNode=el_imp_IPI, cTag='vIPI') pIPI = tagtext(oNode=el_imp_IPI, cTag='pIPI') - self.stringcenter(nMr-112.5, nLin, + self.stringcenter(nMr - 112.5, nLin, tagtext(oNode=el_prod, cTag='NCM')) - self.stringcenter(nMr-105, nLin, cCST) - self.stringcenter(nMr-99, nLin, + self.stringcenter(nMr - 105, nLin, cCST) + self.stringcenter(nMr - 99, nLin, tagtext(oNode=el_prod, cTag='CFOP')) - self.stringcenter(nMr-93, nLin, + self.stringcenter(nMr - 93, nLin, tagtext(oNode=el_prod, cTag='uCom')) - self.stringRight(nMr-77.5, nLin, format_number( + self.stringRight(nMr - 77.5, nLin, format_number( tagtext(oNode=el_prod, cTag='qCom'), precision=4)) - self.stringRight(nMr-64.5, nLin, format_number( + self.stringRight(nMr - 64.5, nLin, format_number( tagtext(oNode=el_prod, cTag='vUnCom'), precision=2)) - self.stringRight(nMr-50.5, nLin, format_number( + self.stringRight(nMr - 50.5, nLin, format_number( tagtext(oNode=el_prod, cTag='vProd'), precision=2)) - self.stringRight(nMr-38.5, nLin, format_number(vBC, precision=2)) - self.stringRight(nMr-26.5, nLin, format_number(vICMS, precision=2)) - self.stringRight(nMr-7.5, nLin, format_number(pICMS, precision=2)) + self.stringRight(nMr - 38.5, nLin, format_number(vBC, precision=2)) + self.stringRight(nMr - 26.5, nLin, + format_number(vICMS, precision=2)) + self.stringRight( + nMr - 7.5, nLin, format_number(pICMS, precision=2)) if vIPI: - self.stringRight(nMr-14.5, nLin, + self.stringRight(nMr - 14.5, nLin, format_number(vIPI, precision=2)) if pIPI: - self.stringRight(nMr-0.5, nLin, + self.stringRight(nMr - 0.5, nLin, format_number(pIPI, precision=2)) # Código Item line_cod = nLin for des in list_cod_prod[id]: - self.string(self.nLeft+0.2, line_cod, des) + self.string(self.nLeft + 0.2, line_cod, des) line_cod += nStep # Descrição Item line_desc = nLin for des in list_desc[id]: - self.string(self.nLeft+15.5, line_desc, des) + self.string(self.nLeft + 15.5, line_desc, des) line_desc += nStep nLin = max(line_cod, line_desc) self.canvas.setStrokeColor(gray) - self.hline(self.nLeft, nLin-2, self.width-self.nLeft) + self.hline(self.nLeft, nLin - 2, self.width - self.nLeft) self.canvas.setStrokeColor(black) self.nlin += nH + 3 @@ -718,13 +727,14 @@ obsCont[@xCampo='NomeVendedor']") self.nlin += 2 self.canvas.setFont('NimbusSanL-Bold', 6) - self.string(self.nLeft+1, self.nlin+1, 'DADOS ADICIONAIS') + self.string(self.nLeft + 1, self.nlin + 1, 'DADOS ADICIONAIS') self.canvas.setFont('NimbusSanL-Regu', 5) - self.string(self.nLeft+1, self.nlin+4, 'INFORMAÇÕES COMPLEMENTARES') - self.string((self.width/2)+1, self.nlin+4, 'RESERVADO AO FISCO') - self.rect(self.nLeft, self.nlin+2, - self.width-self.nLeft-self.nRight, 42) - self.vline(self.width/2, self.nlin+2, 42) + self.string(self.nLeft + 1, self.nlin + 4, + 'INFORMAÇÕES COMPLEMENTARES') + self.string((self.width / 2) + 1, self.nlin + 4, 'RESERVADO AO FISCO') + self.rect(self.nLeft, self.nlin + 2, + self.width - self.nLeft - self.nRight, 42) + self.vline(self.width / 2, self.nlin + 2, 42) # Conteúdo campos styles = getSampleStyleSheet() styleN = styles['Normal'] @@ -737,9 +747,9 @@ obsCont[@xCampo='NomeVendedor']") if fisco: observacoes = fisco + ' ' + observacoes P = Paragraph(observacoes, styles['Normal']) - w, h = P.wrap(92*mm, 32*mm) - altura = (self.height-self.nlin-5)*mm - P.drawOn(self.canvas, (self.nLeft+1)*mm, altura - h) + w, h = P.wrap(92 * mm, 32 * mm) + altura = (self.height - self.nlin - 5) * mm + P.drawOn(self.canvas, (self.nLeft + 1) * mm, altura - h) self.nlin += 36 def recibo_entrega(self, oXML=None): @@ -753,23 +763,25 @@ obsCont[@xCampo='NomeVendedor']") nH = 17 self.canvas.setLineWidth(.5) self.rect(self.nLeft, self.nlin, - self.width-(self.nLeft+self.nRight), nH) - self.hline(self.nLeft, self.nlin+8.5, self.width-self.nRight-nW) - self.vline(self.width-self.nRight-nW, self.nlin, nH) - self.vline(self.nLeft+nW, self.nlin+8.5, 8.5) + self.width - (self.nLeft + self.nRight), nH) + self.hline(self.nLeft, self.nlin + 8.5, self.width - self.nRight - nW) + self.vline(self.width - self.nRight - nW, self.nlin, nH) + self.vline(self.nLeft + nW, self.nlin + 8.5, 8.5) # Labels self.canvas.setFont('NimbusSanL-Regu', 5) - self.string(self.nLeft+1, self.nlin+10.2, 'DATA DE RECEBIMENTO') - self.string(self.nLeft+41, self.nlin+10.2, + self.string(self.nLeft + 1, self.nlin + 10.2, 'DATA DE RECEBIMENTO') + self.string(self.nLeft + 41, self.nlin + 10.2, 'IDENTIFICAÇÃO E ASSINATURA DO RECEBEDOR') - self.stringcenter(self.width-self.nRight-(nW/2), self.nlin+2, 'NF-e') + self.stringcenter(self.width - self.nRight - + (nW / 2), self.nlin + 2, 'NF-e') # Conteúdo campos self.canvas.setFont('NimbusSanL-Bold', 8) cNF = tagtext(oNode=el_ide, cTag='nNF') cNF = '{0:011,}'.format(int(cNF)).replace(",", ".") - self.string(self.width-self.nRight-nW+2, self.nlin+8, "Nº %s" % (cNF)) - self.string(self.width-self.nRight-nW+2, self.nlin+14, + self.string(self.width - self.nRight - nW + + 2, self.nlin + 8, "Nº %s" % (cNF)) + self.string(self.width - self.nRight - nW + 2, self.nlin + 14, "SÉRIE %s" % (tagtext(oNode=el_ide, cTag='serie'))) cDt, cHr = getdateUTC(tagtext(oNode=el_ide, cTag='dhEmi')) @@ -796,12 +808,12 @@ obsCont[@xCampo='NomeVendedor']") styleN.leading = 7 P = Paragraph(cString, styleN) - w, h = P.wrap(149*mm, 7*mm) - P.drawOn(self.canvas, (self.nLeft+1)*mm, - ((self.height-self.nlin)*mm) - h) + w, h = P.wrap(149 * mm, 7 * mm) + P.drawOn(self.canvas, (self.nLeft + 1) * mm, + ((self.height - self.nlin) * mm) - h) self.nlin += 20 - self.hline(self.nLeft, self.nlin, self.width-self.nRight) + self.hline(self.nLeft, self.nlin, self.width - self.nRight) self.nlin += 2 def newpage(self): @@ -811,31 +823,32 @@ obsCont[@xCampo='NomeVendedor']") def hline(self, x, y, width): y = self.height - y - self.canvas.line(x*mm, y*mm, width*mm, y*mm) + self.canvas.line(x * mm, y * mm, width * mm, y * mm) def vline(self, x, y, width): width = self.height - y - width y = self.height - y - self.canvas.line(x*mm, y*mm, x*mm, width*mm) + self.canvas.line(x * mm, y * mm, x * mm, width * mm) def rect(self, col, lin, nWidth, nHeight, fill=False): lin = self.height - nHeight - lin - self.canvas.rect(col*mm, lin*mm, nWidth*mm, nHeight*mm, + self.canvas.rect(col * mm, lin * mm, nWidth * mm, nHeight * mm, stroke=True, fill=fill) def string(self, x, y, value): y = self.height - y - self.canvas.drawString(x*mm, y*mm, value) + self.canvas.drawString(x * mm, y * mm, value) def stringRight(self, x, y, value): y = self.height - y - self.canvas.drawRightString(x*mm, y*mm, value) + self.canvas.drawRightString(x * mm, y * mm, value) def stringcenter(self, x, y, value): y = self.height - y - self.canvas.drawCentredString(x*mm, y*mm, value) + self.canvas.drawCentredString(x * mm, y * mm, value) def writeto_pdf(self, fileObj): pdf_out = self.oPDF_IO.getvalue() self.oPDF_IO.close() fileObj.write(pdf_out) + From 4fc6b9dc89ac64fbbea22a51dcddd9d9f85e0e99 Mon Sep 17 00:00:00 2001 From: Felipe Date: Thu, 21 Dec 2017 17:14:45 -0200 Subject: [PATCH 07/10] adicionados urls de cidades do dsf --- pytrustnfe/nfse/campinas/__init__.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/pytrustnfe/nfse/campinas/__init__.py b/pytrustnfe/nfse/campinas/__init__.py index e5536af..80d839d 100644 --- a/pytrustnfe/nfse/campinas/__init__.py +++ b/pytrustnfe/nfse/campinas/__init__.py @@ -24,8 +24,35 @@ def _render(certificado, method, **kwargs): return xml_send +def _get_url(**kwargs): + + try: + cod_cidade = kwargs['CodCidade'] + except (KeyError, TypeError): + return '' + + urls = { + # Belém - PA + '2715': 'http://www.issdigitalbel.com.br/WsNFe2/LoteRps.jws', + # Sorocaba - SP + '5363': 'http://issdigital.sorocaba.sp.gov.br/WsNFe2/LoteRps.jws', + # Teresina - PI + '3182': 'http://www.issdigitalthe.com.br/WsNFe2/LoteRps.jws', + # Campinas - SP + '4888': 'http://issdigital.campinas.sp.gov.br/WsNFe2/LoteRps.jws?wsdl', + # Uberlandia - MG + '2170': 'http://udigital.uberlandia.mg.gov.br/WsNFe2/LoteRps.jws', + # São Luis - MA + '1314': 'https://stm.semfaz.saoluis.ma.gov.br/WsNFe2/LoteRps?wsdl', + # Campo Grande - MS + '2218': 'http://issdigital.pmcg.ms.gov.br/WsNFe2/LoteRps.jws', + } + + return urls[str(cod_cidade)] + + def _send(certificado, method, **kwargs): - url = 'http://issdigital.campinas.sp.gov.br/WsNFe2/LoteRps.jws?wsdl' # noqa + url = _get_url(**kwargs) path = os.path.join(os.path.dirname(__file__), 'templates') From d2a0c26b1a7162cc75d01d07ef8a259ef69f7c00 Mon Sep 17 00:00:00 2001 From: Felipe Date: Fri, 22 Dec 2017 11:09:17 -0200 Subject: [PATCH 08/10] =?UTF-8?q?mudan=C3=A7a=20nfse=20campinas=20para=20d?= =?UTF-8?q?sf,=20adicionadas=20outras=20cidades=20tamb=C3=A9m=20emitidas?= =?UTF-8?q?=20pela=20dsf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/nfse/campinas/__init__.py | 103 -------------------- pytrustnfe/nfse/campinas/templates/cancelar.xml | 18 ---- .../nfse/campinas/templates/consulta_notas.xml | 11 --- .../nfse/campinas/templates/consultarLote.xml | 10 -- pytrustnfe/nfse/campinas/templates/enviar.xml | 108 --------------------- pytrustnfe/nfse/campinas/templates/soap_header.xml | 12 --- pytrustnfe/nfse/dsf/__init__.py | 103 ++++++++++++++++++++ pytrustnfe/nfse/dsf/templates/cancelar.xml | 18 ++++ pytrustnfe/nfse/dsf/templates/consulta_notas.xml | 11 +++ pytrustnfe/nfse/dsf/templates/consultarLote.xml | 10 ++ pytrustnfe/nfse/dsf/templates/enviar.xml | 108 +++++++++++++++++++++ pytrustnfe/nfse/dsf/templates/soap_header.xml | 12 +++ setup.py | 2 +- 13 files changed, 263 insertions(+), 263 deletions(-) delete mode 100644 pytrustnfe/nfse/campinas/__init__.py delete mode 100644 pytrustnfe/nfse/campinas/templates/cancelar.xml delete mode 100644 pytrustnfe/nfse/campinas/templates/consulta_notas.xml delete mode 100644 pytrustnfe/nfse/campinas/templates/consultarLote.xml delete mode 100644 pytrustnfe/nfse/campinas/templates/enviar.xml delete mode 100644 pytrustnfe/nfse/campinas/templates/soap_header.xml create mode 100644 pytrustnfe/nfse/dsf/__init__.py create mode 100644 pytrustnfe/nfse/dsf/templates/cancelar.xml create mode 100644 pytrustnfe/nfse/dsf/templates/consulta_notas.xml create mode 100644 pytrustnfe/nfse/dsf/templates/consultarLote.xml create mode 100644 pytrustnfe/nfse/dsf/templates/enviar.xml create mode 100644 pytrustnfe/nfse/dsf/templates/soap_header.xml diff --git a/pytrustnfe/nfse/campinas/__init__.py b/pytrustnfe/nfse/campinas/__init__.py deleted file mode 100644 index 80d839d..0000000 --- a/pytrustnfe/nfse/campinas/__init__.py +++ /dev/null @@ -1,103 +0,0 @@ -# -*- 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 _get_url(**kwargs): - - try: - cod_cidade = kwargs['CodCidade'] - except (KeyError, TypeError): - return '' - - urls = { - # Belém - PA - '2715': 'http://www.issdigitalbel.com.br/WsNFe2/LoteRps.jws', - # Sorocaba - SP - '5363': 'http://issdigital.sorocaba.sp.gov.br/WsNFe2/LoteRps.jws', - # Teresina - PI - '3182': 'http://www.issdigitalthe.com.br/WsNFe2/LoteRps.jws', - # Campinas - SP - '4888': 'http://issdigital.campinas.sp.gov.br/WsNFe2/LoteRps.jws?wsdl', - # Uberlandia - MG - '2170': 'http://udigital.uberlandia.mg.gov.br/WsNFe2/LoteRps.jws', - # São Luis - MA - '1314': 'https://stm.semfaz.saoluis.ma.gov.br/WsNFe2/LoteRps?wsdl', - # Campo Grande - MS - '2218': 'http://issdigital.pmcg.ms.gov.br/WsNFe2/LoteRps.jws', - } - - return urls[str(cod_cidade)] - - -def _send(certificado, method, **kwargs): - url = _get_url(**kwargs) - - 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 deleted file mode 100644 index d72086b..0000000 --- a/pytrustnfe/nfse/campinas/templates/cancelar.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - {{ 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 deleted file mode 100644 index 4a666d0..0000000 --- a/pytrustnfe/nfse/campinas/templates/consulta_notas.xml +++ /dev/null @@ -1,11 +0,0 @@ - - -{{ 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 deleted file mode 100644 index 24afc5d..0000000 --- a/pytrustnfe/nfse/campinas/templates/consultarLote.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - {{ 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 deleted file mode 100644 index 7e4b178..0000000 --- a/pytrustnfe/nfse/campinas/templates/enviar.xml +++ /dev/null @@ -1,108 +0,0 @@ - - - {{ 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 deleted file mode 100644 index e9d1dd2..0000000 --- a/pytrustnfe/nfse/campinas/templates/soap_header.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/pytrustnfe/nfse/dsf/__init__.py b/pytrustnfe/nfse/dsf/__init__.py new file mode 100644 index 0000000..80d839d --- /dev/null +++ b/pytrustnfe/nfse/dsf/__init__.py @@ -0,0 +1,103 @@ +# -*- 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 _get_url(**kwargs): + + try: + cod_cidade = kwargs['CodCidade'] + except (KeyError, TypeError): + return '' + + urls = { + # Belém - PA + '2715': 'http://www.issdigitalbel.com.br/WsNFe2/LoteRps.jws', + # Sorocaba - SP + '5363': 'http://issdigital.sorocaba.sp.gov.br/WsNFe2/LoteRps.jws', + # Teresina - PI + '3182': 'http://www.issdigitalthe.com.br/WsNFe2/LoteRps.jws', + # Campinas - SP + '4888': 'http://issdigital.campinas.sp.gov.br/WsNFe2/LoteRps.jws?wsdl', + # Uberlandia - MG + '2170': 'http://udigital.uberlandia.mg.gov.br/WsNFe2/LoteRps.jws', + # São Luis - MA + '1314': 'https://stm.semfaz.saoluis.ma.gov.br/WsNFe2/LoteRps?wsdl', + # Campo Grande - MS + '2218': 'http://issdigital.pmcg.ms.gov.br/WsNFe2/LoteRps.jws', + } + + return urls[str(cod_cidade)] + + +def _send(certificado, method, **kwargs): + url = _get_url(**kwargs) + + 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/dsf/templates/cancelar.xml b/pytrustnfe/nfse/dsf/templates/cancelar.xml new file mode 100644 index 0000000..d72086b --- /dev/null +++ b/pytrustnfe/nfse/dsf/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/dsf/templates/consulta_notas.xml b/pytrustnfe/nfse/dsf/templates/consulta_notas.xml new file mode 100644 index 0000000..4a666d0 --- /dev/null +++ b/pytrustnfe/nfse/dsf/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/dsf/templates/consultarLote.xml b/pytrustnfe/nfse/dsf/templates/consultarLote.xml new file mode 100644 index 0000000..24afc5d --- /dev/null +++ b/pytrustnfe/nfse/dsf/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/dsf/templates/enviar.xml b/pytrustnfe/nfse/dsf/templates/enviar.xml new file mode 100644 index 0000000..7e4b178 --- /dev/null +++ b/pytrustnfe/nfse/dsf/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/dsf/templates/soap_header.xml b/pytrustnfe/nfse/dsf/templates/soap_header.xml new file mode 100644 index 0000000..e9d1dd2 --- /dev/null +++ b/pytrustnfe/nfse/dsf/templates/soap_header.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/setup.py b/setup.py index a4bf038..d047457 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ later (LGPLv2+)', 'nfe/templates/*xml', 'nfe/fonts/*ttf', 'nfse/paulistana/templates/*xml', - 'nfse/campinas/templates/*xml', + 'nfse/dsf/templates/*xml', 'nfse/ginfes/templates/*xml', 'nfse/simpliss/templates/*xml', 'nfse/betha/templates/*xml', From 36611c8f812e790dff5e1fb935e72327e338c5c2 Mon Sep 17 00:00:00 2001 From: Felipe Date: Tue, 26 Dec 2017 16:11:11 -0200 Subject: [PATCH 09/10] =?UTF-8?q?altera=C3=A7=C3=A3o=20nfse=20campinas=20p?= =?UTF-8?q?ara=20dsf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/nfse/dsf/__init__.py | 47 +++++++++++++++++----- pytrustnfe/nfse/dsf/templates/consultarNFSeRps.xml | 22 ++++++++++ pytrustnfe/xml/__init__.py | 1 - 3 files changed, 58 insertions(+), 12 deletions(-) create mode 100644 pytrustnfe/nfse/dsf/templates/consultarNFSeRps.xml diff --git a/pytrustnfe/nfse/dsf/__init__.py b/pytrustnfe/nfse/dsf/__init__.py index 80d839d..60273d0 100644 --- a/pytrustnfe/nfse/dsf/__init__.py +++ b/pytrustnfe/nfse/dsf/__init__.py @@ -27,28 +27,32 @@ def _render(certificado, method, **kwargs): def _get_url(**kwargs): try: - cod_cidade = kwargs['CodCidade'] + cod_cidade = kwargs['nfse']['cidade'] except (KeyError, TypeError): - return '' + raise KeyError("Código de cidade inválido!") urls = { # Belém - PA '2715': 'http://www.issdigitalbel.com.br/WsNFe2/LoteRps.jws', # Sorocaba - SP - '5363': 'http://issdigital.sorocaba.sp.gov.br/WsNFe2/LoteRps.jws', + '7145': 'http://issdigital.sorocaba.sp.gov.br/WsNFe2/LoteRps.jws', # Teresina - PI - '3182': 'http://www.issdigitalthe.com.br/WsNFe2/LoteRps.jws', + '1219': 'http://www.issdigitalthe.com.br/WsNFe2/LoteRps.jws', # Campinas - SP - '4888': 'http://issdigital.campinas.sp.gov.br/WsNFe2/LoteRps.jws?wsdl', + '6291': 'http://issdigital.campinas.sp.gov.br/WsNFe2/LoteRps.jws?wsdl', # Uberlandia - MG - '2170': 'http://udigital.uberlandia.mg.gov.br/WsNFe2/LoteRps.jws', + '5403': 'http://udigital.uberlandia.mg.gov.br/WsNFe2/LoteRps.jws', # São Luis - MA - '1314': 'https://stm.semfaz.saoluis.ma.gov.br/WsNFe2/LoteRps?wsdl', + '0921': 'https://stm.semfaz.saoluis.ma.gov.br/WsNFe2/LoteRps?wsdl', # Campo Grande - MS - '2218': 'http://issdigital.pmcg.ms.gov.br/WsNFe2/LoteRps.jws', + '2729': 'http://issdigital.pmcg.ms.gov.br/WsNFe2/LoteRps.jws', } - return urls[str(cod_cidade)] + try: + return urls[str(cod_cidade)] + except KeyError: + raise KeyError("DSF não emite notas da cidade {}!".format( + cod_cidade)) def _send(certificado, method, **kwargs): @@ -75,6 +79,9 @@ def _send(certificado, method, **kwargs): 'received_xml': e.fault.faultstring, 'object': None } + except Exception as e: + print (response) + print (e) return { 'sent_xml': xml_send, @@ -83,11 +90,23 @@ def _send(certificado, method, **kwargs): } +def xml_enviar(certificado, **kwargs): + return _render(certificado, 'enviar', **kwargs) + + def enviar(certificado, **kwargs): + if "xml" not in kwargs: + kwargs['xml'] = xml_enviar(certificado, **kwargs) return _send(certificado, 'enviar', **kwargs) +def xml_teste_enviar(certificado, **kwargs): + return _render(certificado, 'testeEnviar', **kwargs) + + def teste_enviar(certificado, **kwargs): + if "xml" not in kwargs: + kwargs['xml'] = xml_teste_enviar(certificado, **kwargs) return _send(certificado, 'testeEnviar', **kwargs) @@ -99,5 +118,11 @@ def consulta_lote(**kwargs): return _send(False, 'consultarLote', **kwargs) -def consultar_lote_rps(certificado, **kwarg): - return _send(certificado, 'consultarNFSeRps', **kwarg) +def xml_consultar_nfse_rps(certificado, **kwargs): + return _render(certificado, 'consultarNFSeRps', **kwargs) + + +def consultar_nfse_rps(certificado, **kwargs): + if "xml" not in kwargs: + kwargs['xml'] = xml_consultar_nfse_rps(certificado, **kwargs) + return _send(certificado, 'consultarNFSeRps', **kwargs) diff --git a/pytrustnfe/nfse/dsf/templates/consultarNFSeRps.xml b/pytrustnfe/nfse/dsf/templates/consultarNFSeRps.xml new file mode 100644 index 0000000..a6a51bc --- /dev/null +++ b/pytrustnfe/nfse/dsf/templates/consultarNFSeRps.xml @@ -0,0 +1,22 @@ + + + {{ nfse.cidade }} + {{ nfse.cpf_cnpj }} + true + 1 + + + {% for rps in nfse.lista_rps -%} + + + {{ rps.prestador.inscricao_municipal }} + {{ rps.numero }} + {{ rps.serie_prestacao }} + + + {% endfor %} + + \ No newline at end of file diff --git a/pytrustnfe/xml/__init__.py b/pytrustnfe/xml/__init__.py index 0ed0ee5..413df5e 100644 --- a/pytrustnfe/xml/__init__.py +++ b/pytrustnfe/xml/__init__.py @@ -27,7 +27,6 @@ def render_xml(path, template_name, remove_empty, **nfe): env.filters["format_date"] = filters.format_date template = env.get_template(template_name) - xml = template.render(**nfe) parser = etree.XMLParser(remove_blank_text=True, remove_comments=True, strip_cdata=False) From 7d4f9079c8b14e4dd9cd1519014eb1e779a68248 Mon Sep 17 00:00:00 2001 From: pal0schi <31492998+pal0schi@users.noreply.github.com> Date: Fri, 5 Jan 2018 11:22:42 -0200 Subject: [PATCH 10/10] =?UTF-8?q?*=20corre=C3=A7=C3=A3o=20da=20url=20de=20?= =?UTF-8?q?campo=20grande-ms?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/nfse/dsf/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pytrustnfe/nfse/dsf/__init__.py b/pytrustnfe/nfse/dsf/__init__.py index 60273d0..df4ce4a 100644 --- a/pytrustnfe/nfse/dsf/__init__.py +++ b/pytrustnfe/nfse/dsf/__init__.py @@ -45,7 +45,7 @@ def _get_url(**kwargs): # São Luis - MA '0921': 'https://stm.semfaz.saoluis.ma.gov.br/WsNFe2/LoteRps?wsdl', # Campo Grande - MS - '2729': 'http://issdigital.pmcg.ms.gov.br/WsNFe2/LoteRps.jws', + '2729': 'http://issdigital.pmcg.ms.gov.br/WsNFe2/LoteRps.jws?wsdl', } try: @@ -62,6 +62,7 @@ def _send(certificado, method, **kwargs): xml_send = _render(path, method, **kwargs) client = get_client(url) + response = False if certificado: cert, key = extract_cert_and_key_from_pfx( @@ -80,8 +81,10 @@ def _send(certificado, method, **kwargs): 'object': None } except Exception as e: - print (response) - print (e) + if response: + raise Exception(response) + else: + raise e return { 'sent_xml': xml_send,