Compare commits

...

130 Commits

Author SHA1 Message Date
Danimar Ribeiro fe6b84bc4b [FIX] Remove warnings de https insecure 7 years ago
Danimar Ribeiro 59d4cf94b4 [FIX] Ajuste de cancelamento NFSe BH 7 years ago
Danimar Ribeiro 8de6521e8d [FIX] Adicionando complemento de endereço na danfe 7 years ago
Danimar Ribeiro 8c2de797aa
* [FIX] Ajuste na URL do parana 7 years ago
Danimar Ribeiro a81c869a56 [FIX] Fix https warnings 7 years ago
Danimar Ribeiro 2723aece89 [ADD] Adiciona urls do qrcode para NFCe 7 years ago
Danimar Ribeiro f01405affa [FIX] Sempre mostra o valor zerado de IPI e aliquota caso não existir 7 years ago
Danimar Ribeiro ee047ef0cd [FIX] Corrige o encoding do retorno da nfse 7 years ago
Fábio Luna c7c380ce36 Webservices NFC-e Mato Grosso. (#194) 7 years ago
Fábio Luna b645b8ba78 Inclui Webservices para NFC-e do Mato Grosso do Sul. (#195) 7 years ago
marinaGD 8b54bfa081 [IMP] Habilita enviar apenas o CPF no destinatário 7 years ago
Fábio Luna a25dd8a9d3 Webservices NFC-e Paraná 7 years ago
Danimar Ribeiro e233abfb04 [FIX] Corrige envio de NFS-e Ginfes 7 years ago
Danimar Ribeiro ff75a6c77a [FIX] Calcula previamente o tamanho do proximo item 7 years ago
Danimar Ribeiro d4c3a8a0a7 [VER] New version 7 years ago
marinaGD 36142aaf58 add troco and valor pago with the added values 7 years ago
marinaGD b81f3ddbc6 [FIX] first page overwriting (#188) 7 years ago
Danimar Ribeiro 0ce7c3045f [FIX] Voltando as urls antigas, a principio DF não atualizaram ainda 7 years ago
Danimar Ribeiro dbe2ab2ec5 [FIX] Ajusta a consulta de download de xml 7 years ago
Danimar Ribeiro 790543d36a [FIX] Ajustando as urls de consulta da danfce 7 years ago
Danimar Ribeiro a252d86523 [FIX] Removing useless code for NFCe, adjusting xml template for NFCe 7 years ago
Danimar Ribeiro 4f3da1a075 [IMP] Fixing the fields for service invoices and danfe 7 years ago
Danimar Ribeiro 0c12982197 [FIX] Assign the correct variable when addin thousand separator 7 years ago
carcaroff 7f5fe48999 thousandseparator added to monetary fields 7 years ago
Danimar Ribeiro 21994c73e8 [FIX] Removendo previamente os espaços resolve o problema 7 years ago
Danimar Ribeiro 43289e84c1 [FIX] Percentuais de tributação para nota paulistana 7 years ago
Danimar Ribeiro 3c5e8cd24c [FIX] Try to fix strange with spaces in some eletronic documents 7 years ago
Fábio Luna 95e3e5710c Corrige webservice de retorno de autorização do Paraná. 7 years ago
Danimar Ribeiro 13a59b61b6 [FIX] Compare the operation name in lower case 7 years ago
Johny Chen Jy 260a7e5add [11.0][IMP]different method to compute lines per page (#172) 7 years ago
Johny Chen Jy aafb2cf92a DSF - xml decode and url fix (#171) 7 years ago
Danimar Ribeiro 0a1539ce1a [FIX] Corrige urls do MT 7 years ago
Danimar Ribeiro 59457090e9 [FIX] Ajusta os namespaces para envio de NFe no estado do PR 7 years ago
carcaroff eea7fd74f4 nfse_mga minor changes in singnature, removed encode for byte type response and id on cancel template is now correct 7 years ago
Danimar Ribeiro 17770982cc [IMP] Migrando restante das urls da nfe 4.0 7 years ago
Danimar Ribeiro de06defe5f [FIX] Realiza umas conversões de texto para evitar erros no Odoo 7 years ago
Johny Chen Jy 2e9d83568e [FIX] Ajusta o danfe nos campos de frete e transportadora. 7 years ago
Danimar Ribeiro 8c132a3bbb [FIX] Certifica que vamos usar a operação correta ao enviar a NFE 7 years ago
Alyson Brito 37506bd3ca Incrementa a versao da biblioteca 7 years ago
Alyson Brito 92c1191d8f Revisado as urls da sefaz 7 years ago
Alyson Brito 79abb1798c Atualizada as urls dos servidores sefaz ce 7 years ago
Carlos Alberto Cipriano Korovsky bb5509c781 Correção do número de versão da tag nfeProc 7 years ago
Johny Chen Jy c9f71c9ea1 New zeep version uses settings instead of options 7 years ago
Danimar Ribeiro 4691c28623 [IMP] Novo release da pytrusnfe3 7 years ago
Danimar Ribeiro bcbaf1474d [IMP] Atualizado schema da NFe 7 years ago
Danimar Ribeiro f63ad7abbf [FIX] Corrige o build para o deploy 7 years ago
Danimar Ribeiro 40c3068447
Merge pull request #147 from danimaribeiro/feature/zeep 7 years ago
Danimar Ribeiro 06764a249c [IMP] Fix unit tests and fix some urls 7 years ago
Danimar Ribeiro 16602cfa5b
Merge branch 'master3' into feature/zeep 7 years ago
Fábio Luna c95cba8ae7 Corrige webservice de recepção de eventos do Paraná 7 years ago
guilherme 9817897d30 update library version 8 years ago
guilherme f0f38237f2 corrected issues in layout of danfe 8 years ago
Danimar Ribeiro 70816097e9 [IMP] Implementing NFe 4.0 - Cleaning up code and improve readability 8 years ago
carcaroff 4572b99fcb [WIP]NFS-e Maringa 8 years ago
Danimar Ribeiro 5cbdd146f5 [WIP] Cleanup of useless code, using zeep for NFe soap, adjust xml files 8 years ago
Danimar Ribeiro f4bcbb071c [WIP] Finalização do layout do xml, usando zeep para fazer chamada SOAP 8 years ago
Carlos R. Silveira 3180759fd9 Ajustando conforme validação. 8 years ago
Carlos R. Silveira cbf17ee87d Adicionado campos novos da Nfe 4.0 8 years ago
Danimar Ribeiro 966ba5f795 [FIX] XML NFSe maringa and percent difal 8 years ago
Danimar Ribeiro 44bcedc33b Realiza a conversão do retorno corretamente com decode - BH 8 years ago
Danimar Ribeiro 8f4cf56a29 Adição das tags do veículo de transporte no danfe 8 years ago
Danimar Ribeiro b474ec9913 Pequenos ajustes na impressão do danfce 8 years ago
Danimar Ribeiro 4f25b76ec8 Ajuste na url do qrcode amazonas 8 years ago
carcaroff c0dd5d02dd Implementação da danfe para NFC-e 8 years ago
Fábio Luna 5893a0324b Correção de testes. 8 years ago
Fábio Luna c7bc21de2a Retira lixo código. 8 years ago
Fábio Luna 5c9908da57 Insere servidor de cancelamento de NFC-e. 8 years ago
Fábio Luna cab13549cc Correções para envio de NFC-e 8 years ago
Fábio Luna 4f8b752626 [WIP] NFC-e Corrige servidores. 8 years ago
Fábio Luna 1d9f4b0776 [WIP] Implementando NFC-e SVRS 8 years ago
Danimar Ribeiro e712bf0d6a Correção de envio Ginfes - Mudança para o zeep para webservice SOAP 8 years ago
Danimar Ribeiro 3db71b7b9a Nota de BH utiliza duas assinaturas 8 years ago
Danimar Ribeiro 236515b12d Ajuste ao imprimir nfe cancelada, correção de encoding 8 years ago
Johny Chen Jy 4f423e28f5 Implementa timezone ao imprimir a danfe 8 years ago
Danimar Ribeiro 9a1f406c62 Correção no envio de NFe para o estado do Ceará 8 years ago
Danimar Ribeiro 38874665b9 WIP - Implementação da nota de BH, tentativa de usar o zeep 8 years ago
Danimar Ribeiro eb1db219b8 Nova tag de base de cálculo para nfse-floripa 8 years ago
Danimar Ribeiro 7a3f6275b8 Incrementando versão do pyopenssl 8 years ago
Danimar Ribeiro 1b2e206fcc Versão 0.9.15 8 years ago
Johny Chen Jy 2aea7bae5d Update utils.py 8 years ago
carcaroff 1f5645cf8e [FEAT][10.0]Cancelado no DANFE 8 years ago
pal0schi 92a2505726 Correcao NFS-e Floripa - Descrição dos serviços 8 years ago
Felipe ed322847ed decode no sent_xml nfse imperial 8 years ago
Danimar Ribeiro 6760152ed5 Ajuste no readme com novas implementações, remoção do appveyor build 8 years ago
Danimar Ribeiro 42d2a8d1e3 [DONE] Finalização do layout Nota Carioca - Geração e Cancelamento 8 years ago
Danimar Ribeiro 2bbd78c295 Implementação da nota carioca 8 years ago
Danimar Ribeiro 141fecb6a0 FIX - Corrige eventos do manifesto e geração da nfe processada 8 years ago
pal0schi 9c2344c15e Master3 nfse imperial (#110) 8 years ago
pal0schi 105843d719 Master3 bug unicode (#109) 8 years ago
carcaroff 6117a8b63f 'Incrementar versão ;-;' 8 years ago
carcaroff 6b00132c4b [FIX]Normalize e escape em campos de texto 8 years ago
Danimar Ribeiro e1754c8e16 Incremento da versão do suds requests 8 years ago
Danimar Ribeiro 954dfcfecf Incremento de versão 8 years ago
Danimar Ribeiro baca382a0f
Merge pull request #98 from danimaribeiro/master-fix 8 years ago
Danimar Ribeiro f73aadada9
Merge branch 'master3' into master-fix 8 years ago
Danimar Ribeiro 57997f9164 Merge branch 'master3' into master-fix 8 years ago
Danimar Ribeiro 24be4815e1 Incremento de versão 8 years ago
Danimar Ribeiro 55031fe78d Implementação do cancelamento de NFSe - Floripa 8 years ago
Danimar Ribeiro 1f56dd5fa7 Retorna uma exceção correta quando não for possível obter o token 8 years ago
Danimar Ribeiro 7d6aacb655 Incremento de versões das dependencias 8 years ago
Danimar Ribeiro d0ce9460b1
Merge pull request #86 from danimaribeiro/feature/nfse-floripa 8 years ago
Danimar Ribeiro d53a0bb833
Merge branch 'master3' into feature/nfse-floripa 8 years ago
Danimar Ribeiro 78b0e47dfb Finalizado a parte de emissão de NFSe Floripa 8 years ago
pal0schi 7d4f9079c8 * correção da url de campo grande-ms 8 years ago
Felipe 36611c8f81 alteração nfse campinas para dsf 8 years ago
Felipe d2a0c26b1a mudança nfse campinas para dsf, adicionadas outras cidades também emitidas pela dsf 8 years ago
Felipe 4fc6b9dc89 adicionados urls de cidades do dsf 8 years ago
carcaroff f42937ae5f [FIX] 8 years ago
carcaroff 4915472aa7 [FIX] 8 years ago
carcaroff 0a21cfaad8 [FIX]Campo CPF 8 years ago
Fábio Luna 22ac348b8b Implementa NFse de Campinas 8 years ago
Fábio Luna c2e2d1ed46 [WIP] Implementa NFS-e Campinas 8 years ago
Danimar Ribeiro 5a85580ba4 [WIP] - NFSE Floripa 8 years ago
Felipe 2138bd3ee2 adicionado string.strip no recursive_normalize 8 years ago
Danimar Ribeiro 7cff7bb5f1 [WIP] Implementação da nfse de florianópolis 8 years ago
Fábio Luna 9219ec4243 [FIX] Corrige erro 225 na NF-e, devido a caracteres especiais. 8 years ago
Fábio Luna fa3f44b0de [FIX]Corrige envios de eventos 8 years ago
Danimar Ribeiro 914912ec7c Nova versão para publicar 8 years ago
Fábio Luna 582742ecca [FIX] Corrige consulta de distribuíção de NF-e 8 years ago
Danimar Ribeiro 9b21368d25 Nova versão, atualização de badges 8 years ago
Danimar Ribeiro 0b47eba7b5 Refactor - Permitir criar o xml de envio separadamente 8 years ago
Danimar Ribeiro b05bd19354 Build para windows 8 years ago
Danimar Ribeiro a1c65663bc Updating setup.py to include font files 8 years ago
Fábio Luna aa4fd2bb42 Inclui fonte NimbusSanL. 8 years ago
Danimar Ribeiro c44cd90103 Fixing build for windows 8 years ago
Danimar Ribeiro 7b31a2f78f Removendo print - Ajustando encodings 8 years ago
Danimar Ribeiro fabe0e0561 Preparando pypi package para versão python >= 3.4 8 years ago
Danimar Ribeiro 0c89b3e957 Modificação na estrutura do projeto - Movendo testes para fora do pacote 8 years ago
Danimar Ribeiro 5c5602acf9 Migração para python 3 - Correção de testes 8 years ago
Danimar Ribeiro 4581ea6dbe Ajuste de travis e dependencias da versão 3 8 years ago
  1. 4
      .gitignore
  2. 8
      .travis.yml
  3. 26
      README.md
  4. 7
      cidades/dsf.md
  5. 821
      pytrustnfe/Servidores.py
  6. 8
      pytrustnfe/__init__.py
  7. 13
      pytrustnfe/certificado.py
  8. 24
      pytrustnfe/client.py
  9. 320
      pytrustnfe/nfe/__init__.py
  10. 4
      pytrustnfe/nfe/assinatura.py
  11. 34
      pytrustnfe/nfe/comunicacao.py
  12. 461
      pytrustnfe/nfe/danfce.py
  13. 321
      pytrustnfe/nfe/danfe.py
  14. BIN
      pytrustnfe/nfe/fonts/NimbusSanL Bold.ttf
  15. BIN
      pytrustnfe/nfe/fonts/NimbusSanL Regular.ttf
  16. 30
      pytrustnfe/nfe/templates/NFeDistribuicaoDFe.xml
  17. 219
      pytrustnfe/nfe/templates/NfeAutorizacao.xml
  18. 5
      pytrustnfe/nfe/templates/NfeConsultaProtocolo.xml
  19. 2
      pytrustnfe/nfe/templates/NfeInutilizacao.xml
  20. 2
      pytrustnfe/nfe/templates/NfeRetAutorizacao.xml
  21. 4
      pytrustnfe/nfe/templates/NfeStatusServico.xml
  22. 6
      pytrustnfe/nfe/templates/RecepcaoEvento.xml
  23. 21
      pytrustnfe/nfe/templates/RecepcaoEventoCarta.xml
  24. 31
      pytrustnfe/nfe/templates/RecepcaoEventoEPEC.xml
  25. 2
      pytrustnfe/nfe/templates/RecepcaoEventoManifesto.xml
  26. 90
      pytrustnfe/nfse/assinatura.py
  27. 2
      pytrustnfe/nfse/betha/__init__.py
  28. 84
      pytrustnfe/nfse/bh/__init__.py
  29. 44
      pytrustnfe/nfse/bh/assinatura.py
  30. 13
      pytrustnfe/nfse/bh/templates/CancelarNfse.xml
  31. 11
      pytrustnfe/nfse/bh/templates/GerarNfse.xml
  32. 91
      pytrustnfe/nfse/bh/templates/Rps.xml
  33. 74
      pytrustnfe/nfse/carioca/__init__.py
  34. 13
      pytrustnfe/nfse/carioca/templates/CancelarNfse.xml
  35. 3
      pytrustnfe/nfse/carioca/templates/GerarNfse.xml
  36. 91
      pytrustnfe/nfse/carioca/templates/Rps.xml
  37. 131
      pytrustnfe/nfse/dsf/__init__.py
  38. 18
      pytrustnfe/nfse/dsf/templates/cancelar.xml
  39. 11
      pytrustnfe/nfse/dsf/templates/consulta_notas.xml
  40. 10
      pytrustnfe/nfse/dsf/templates/consultarLote.xml
  41. 22
      pytrustnfe/nfse/dsf/templates/consultarNFSeRps.xml
  42. 100
      pytrustnfe/nfse/dsf/templates/enviar.xml
  43. 12
      pytrustnfe/nfse/dsf/templates/soap_header.xml
  44. 118
      pytrustnfe/nfse/floripa/__init__.py
  45. 7
      pytrustnfe/nfse/floripa/templates/cancelar_nota.xml
  46. 41
      pytrustnfe/nfse/floripa/templates/processar_nota.xml
  47. 31
      pytrustnfe/nfse/ginfes/__init__.py
  48. 11
      pytrustnfe/nfse/imperial/__init__.py
  49. 75
      pytrustnfe/nfse/mga/__init__.py
  50. 45
      pytrustnfe/nfse/mga/assinatura.py
  51. 15
      pytrustnfe/nfse/mga/templates/CancelarNfse.xml
  52. 3
      pytrustnfe/nfse/mga/templates/GerarNfse.xml
  53. 81
      pytrustnfe/nfse/mga/templates/Rps.xml
  54. 9
      pytrustnfe/nfse/paulistana/__init__.py
  55. 2
      pytrustnfe/nfse/paulistana/templates/EnvioLoteRPS.xml
  56. 2
      pytrustnfe/nfse/paulistana/templates/EnvioRPS.xml
  57. 2
      pytrustnfe/nfse/susesu/__init__.py
  58. 1
      pytrustnfe/test/XMLs/paulistana_resultado.xml
  59. 34
      pytrustnfe/test/test_add_qr_code.py
  60. 20
      pytrustnfe/test/test_comunicacao.py
  61. 19
      pytrustnfe/test/test_consulta_cadastro.py
  62. 189
      pytrustnfe/test/xml_com_qrcode.xml
  63. 190
      pytrustnfe/test/xml_sem_qrcode.xml
  64. 87
      pytrustnfe/urls.py
  65. 28
      pytrustnfe/utils.py
  66. 44
      pytrustnfe/xml/__init__.py
  67. 18
      pytrustnfe/xml/filters.py
  68. 18
      pytrustnfe/xml/schemas/enviNFe_v4.00.xsd
  69. 12540
      pytrustnfe/xml/schemas/leiauteNFe_v4.00.xsd
  70. 18
      pytrustnfe/xml/schemas/nfe_v4.00.xsd
  71. 1160
      pytrustnfe/xml/schemas/tiposBasico_v4.00.xsd
  72. 0
      pytrustnfe/xml/schemas/xmldsig-core-schema_v1.01.xsd
  73. 31
      pytrustnfe/xml/validate.py
  74. 24
      requirements.txt
  75. 32
      setup.py
  76. 0
      tests/XMLs/NFe00000857.xml
  77. 0
      tests/XMLs/jinja_remove_empty.xml
  78. 0
      tests/XMLs/jinja_result.xml
  79. 0
      tests/XMLs/jinja_template.xml
  80. 0
      tests/XMLs/paulistana_canc.xml
  81. 1
      tests/XMLs/paulistana_canc_errado.xml
  82. 1
      tests/XMLs/paulistana_canc_ok.xml
  83. 1
      tests/XMLs/paulistana_resultado.xml
  84. 16
      tests/XMLs/paulistana_signature.xml
  85. 0
      tests/XMLs/recibo_envio_1.xml
  86. 0
      tests/XMLs/recibo_envio_2.xml
  87. 0
      tests/XMLs/recibo_protocolo_sucesso_1.xml
  88. 0
      tests/XMLs/recibo_protocolo_sucesso_2.xml
  89. 0
      tests/__init__.py
  90. 12
      tests/test_assinatura.py
  91. 8
      tests/test_certificado.py
  92. 2
      tests/test_danfe.py
  93. 2
      tests/test_ginfes.py
  94. 8
      tests/test_nfse_paulistana.py
  95. 17
      tests/test_servidores.py
  96. 24
      tests/test_utils.py
  97. 4
      tests/test_xml.py
  98. 5
      tests/test_xml_serializacao.py
  99. 0
      tests/teste.pfx
  100. 0
      tests/xml_assinado.xml

4
.gitignore

@ -11,4 +11,6 @@ dist/
*egg*/
docs/_build
.vscode/tags
.cache/
.cache
.pytest_cache
.vscode/

8
.travis.yml

@ -1,13 +1,13 @@
dist: precise
language: python
python:
- '2.7'
- "3.6"
virtual_env:
system_site_packages: true
install:
- pip install --upgrade pip
- pip install -r requirements.txt
script: coverage run --source=pytrustnfe setup.py nosetests
script:
pytest --cov=pytrustnfe
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq python-dev libffi-dev libxml2-dev libxslt1-dev libssl-dev
@ -19,5 +19,5 @@ deploy:
password:
secure: wV+DH+WVji4qyCRXxugOsu8/u9MgUN9YggIBozh2Si1z6OlONZVr/oCaJDW8UD+Qg0EF87RbHuEmmlpAZVERAZv5uGsxjSO/NyvAsr99sOlTy9TSLi6TLp4aPnOCgjBTFDWkwkNyDTGYGNfendS7KO2jaHUsr/eDZcpTz42lOfDgpmccz822wwI6Uu1hNC61qlskPkKVzFhHT61/XAgmjHvw1wAMWVmv9/E6J8VAlZoI9/v3K0RTRisB/+0+sSvY86crYyuW/zIEhQJnMu/gfFWDSxNdY+0S3VyFgERn5S7IYlpBPUUlukX5aPXy+OQD2ygeu7w9f6aOSaJZsoyhe4pPXDjA9XNyfiazuZrz51fzhricMvdsMPAcukK/sJzGICAFgOutAjy+nGBkNqA2genKL8gMtJGUrPW5Yq5MGMC7FEgEQi5SgEj+01FgSY5mHlR3qo9bEgXWcxhNL/uZ3C1ElnGNLbyn5hjWzCnMEe70JwfWNQxGgtNm73vrrsZJ7M5wGjrEKVAvTERQegRQm2ObX7YsPmTY+tF15Hxs8GiZ0T/MzpxGe6yAkIutKI0CxpoUMXBnrmcMbn74GT8KWQjS724AA3K5ePO5ogLECxIq3huyB9USeeXmYBhUtcLpKSSH7gA/8vT/tvXK0+/YNTKzIIrOjuZ9IOVrwq2PyUY=
on:
branch: master
branch: master3
distributions: "bdist_wheel"

26
README.md

@ -1,37 +1,37 @@
# PyTrustNFe
Biblioteca Python que tem por objetivo enviar NFe, NFCe e NFSe no Brasil
[![Coverage Status](https://coveralls.io/repos/danimaribeiro/PyTrustNFe/badge.svg?branch=master)](https://coveralls.io/r/danimaribeiro/PyTrustNFe?branch=master)
[![Code Health](https://landscape.io/github/danimaribeiro/PyTrustNFe/master/landscape.svg?style=flat)](https://landscape.io/github/danimaribeiro/PyTrustNFe/master)
[![Build Status](https://travis-ci.org/danimaribeiro/PyTrustNFe.svg?branch=master)](https://travis-ci.org/danimaribeiro/PyTrustNFe)
[![PyPI version](https://badge.fury.io/py/PyTrustNFe.svg)](https://badge.fury.io/py/PyTrustNFe)
[![Coverage Status](https://coveralls.io/repos/danimaribeiro/PyTrustNFe/badge.svg?branch=master3)](https://coveralls.io/r/danimaribeiro/PyTrustNFe?branch=master3)
[![Code Health](https://landscape.io/github/danimaribeiro/PyTrustNFe/master3/landscape.svg?style=flat)](https://landscape.io/github/danimaribeiro/PyTrustNFe/master3)
[![Build Status](https://travis-ci.org/danimaribeiro/PyTrustNFe.svg?branch=master3)](https://travis-ci.org/danimaribeiro/PyTrustNFe)
[![PyPI version](https://badge.fury.io/py/PyTrustNFe3.svg)](https://badge.fury.io/py/PyTrustNFe3)
Dependências:
* PyXmlSec
* lxml
* signxml
* suds
* suds_requests
* suds-jurko
* suds-jurko-requests
* reportlab
* Jinja2
NFSe - Cidades atendidas
--------------
* [Ariss](cidades/ariss.md) - 4 cidades atendidas
* **Paulistana** - São Paulo/SP
* **Nota Carioca** - Rio de Janeiro/RJ
* **Imperial** - Petrópolis/RH
* [Susesu](cidades/susesu.md) - 3 cidades atendidas
* [Simpliss](cidades/simpliss.md) - 18 cidade atendidas
* [GINFES](cidaes/ginfes.md) - 79 cidades atendidas
* [DSF](cidades/dsf.md) - 7 cidades atendidas
Roadmap
--------------
Teste unitários
Emissão de NFCe
Compatibilidade [python 2 e 3](https://github.com/danimaribeiro/PyTrustNFe/pull/6)
Implementar novos provedores de NFSe
* [Betha](cidades/betha.md) - 81 cidades atendidas WIP
* [GINFES](cidades/ginfes.md) - 79 cidades atendidas
* [WebISS](cidades/webiss.md) - 51 cidades atendidas
* [ISSIntel](cidades/issintel.md) - 32 cidades atendidas
* [ISSNET](cidades/issnet.md) - 32 cidades atendidas

7
cidades/dsf.md

@ -0,0 +1,7 @@
* Belém - PA
* Sorocaba - SP
* Teresina - PI
* Campinas - SP
* Uberlandia - MG
* São Luis - MA
* Campo Grande - MS

821
pytrustnfe/Servidores.py
File diff suppressed because it is too large
View File

8
pytrustnfe/__init__.py

@ -12,10 +12,10 @@ class HttpClient(object):
def _headers(self, action):
return {
u'Content-type':
u'text/xml; charset=utf-8;',
u'Accept': u'application/soap+xml; charset=utf-8',
u'SOAPAction': action
'Content-type':
'text/xml; charset=utf-8;',
'Accept': 'application/soap+xml; charset=utf-8',
'SOAPAction': action
}
def post_soap(self, xml_soap, action):

13
pytrustnfe/certificado.py

@ -2,8 +2,7 @@
# © 2016 Danimar Ribeiro, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from uuid import uuid4
import tempfile
from OpenSSL import crypto
@ -13,8 +12,8 @@ class Certificado(object):
self.password = password
def save_pfx(self):
pfx_temp = '/tmp/' + uuid4().hex
arq_temp = open(pfx_temp, 'w')
pfx_temp = tempfile.mkstemp()[1]
arq_temp = open(pfx_temp, 'wb')
arq_temp.write(self.pfx)
arq_temp.close()
return pfx_temp
@ -28,12 +27,12 @@ def extract_cert_and_key_from_pfx(pfx, password):
# PEM formatted certificate
cert = crypto.dump_certificate(crypto.FILETYPE_PEM,
pfx.get_certificate())
return cert, key
return cert.decode(), key.decode()
def save_cert_key(cert, key):
cert_temp = '/tmp/' + uuid4().hex
key_temp = '/tmp/' + uuid4().hex
cert_temp = tempfile.mkstemp()[1]
key_temp = tempfile.mkstemp()[1]
arq_temp = open(cert_temp, 'w')
arq_temp.write(cert)

24
pytrustnfe/client.py

@ -2,11 +2,9 @@
# © 2016 Danimar Ribeiro, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import urllib3
import requests
import suds.client
import suds_requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
def get_authenticated_client(base_url, cert, key):
@ -33,25 +31,3 @@ def get_client(base_url):
cache=cache,
transport=suds_requests.RequestsTransport(session)
)
class HttpClient(object):
def __init__(self, url, cert_path, key_path):
self.url = url
self.cert_path = cert_path
self.key_path = key_path
def _headers(self, action):
return {
u'Content-type': u'application/soap+xml; charset=utf-8; action="http://www.portalfiscal.inf.br/nfe/wsdl/%s"' % action,
u'Accept': u'application/soap+xml; charset=utf-8',
}
def post_soap(self, xml_soap, cabecalho):
header = self._headers(cabecalho.soap_action)
urllib3.disable_warnings(category=InsecureRequestWarning)
res = requests.post(self.url, data=xml_soap,
cert=(self.cert_path, self.key_path),
verify=False, headers=header)
return res.text

320
pytrustnfe/nfe/__init__.py

@ -4,34 +4,20 @@
import os
import hashlib
import requests
from lxml import etree
from .comunicacao import executar_consulta
from .assinatura import Assinatura
from pytrustnfe.xml import render_xml
from pytrustnfe.utils import CabecalhoSoap
from pytrustnfe.xml import render_xml, sanitize_response
from pytrustnfe.utils import gerar_chave, ChaveNFe
from pytrustnfe.Servidores import localizar_url, localizar_qrcode
from pytrustnfe.xml.validate import valida_nfe
from pytrustnfe.exceptions import NFeValidationException
def _build_header(method, **kwargs):
action = {
'NfeAutorizacao': ('NfeAutorizacao', '3.10'),
'NfeRetAutorizacao': ('NfeRetAutorizacao', '3.10'),
'NfeConsultaCadastro': ('CadConsultaCadastro2', '2.00'),
'NfeInutilizacao': ('NfeInutilizacao2', '3.10'),
'RecepcaoEventoCancelamento': ('RecepcaoEvento', '1.00'),
'RecepcaoEventoCarta': ('RecepcaoEvento', '1.00'),
'NFeDistribuicaoDFe': ('NFeDistribuicaoDFe/nfeDistDFeInteresse',
'1.00'),
'RecepcaoEventoManifesto': ('RecepcaoEvento', '1.00'),
}
vals = {'estado': kwargs['estado'],
'soap_action': action[method][0],
'versao': action[method][1]}
return CabecalhoSoap(**vals)
from pytrustnfe.Servidores import localizar_url
from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key
from requests.packages.urllib3.exceptions import InsecureRequestWarning
# Zeep
from requests import Session
from zeep import Client
from zeep.transports import Transport
def _generate_nfe_id(**kwargs):
@ -53,191 +39,209 @@ def _generate_nfe_id(**kwargs):
item['infNFe']['ide']['cDV'] = chave_nfe[len(chave_nfe) - 1:]
def _add_required_node(elemTree):
ns = elemTree.nsmap
if None in ns:
ns['ns'] = ns[None]
ns.pop(None)
prods = elemTree.findall('ns:NFe/ns:infNFe/ns:det/ns:prod', namespaces=ns)
for prod in prods:
element = prod.find('ns:cEAN', namespaces=ns)
if element is None:
cEan = etree.Element('cEAN')
prod.insert(1, cEan)
element = prod.find('ns:cEANTrib', namespaces=ns)
if element is None:
cEANTrib = etree.Element('cEANTrib')
vProd = prod.find('ns:vProd', namespaces=ns)
prod.insert(prod.index(vProd) + 1, cEANTrib)
return elemTree
def _add_qrCode(xml, **kwargs):
xml = etree.fromstring(xml)
inf_nfe = kwargs['NFes'][0]['infNFe']
nfe = xml.find(".//{http://www.portalfiscal.inf.br/nfe}NFe")
infnfesupl = etree.Element('infNFeSupl')
qrcode = etree.Element('qrCode')
chave_nfe = inf_nfe['Id'][3:]
dh_emissao = inf_nfe['ide']['dhEmi'].encode('hex')
versao = '100'
ambiente = kwargs['ambiente']
valor_total = inf_nfe['total']['vNF']
dest_cpf = 'Inexistente'
dest = nfe.find(".//{http://www.portalfiscal.inf.br/nfe}dest")
if dest:
dest_parent = dest.getparent()
dest_parent.remove(dest)
if inf_nfe.get('dest', False):
if inf_nfe['dest'].get('CPF', False):
dest_cpf = inf_nfe['dest']['CPF']
dest = etree.Element('dest')
cpf = etree.Element('CPF')
cpf.text = dest_cpf
dest.append(cpf)
dest_parent.append(dest)
icms_total = inf_nfe['total']['vICMS']
dig_val = xml.find(
".//{http://www.w3.org/2000/09/xmldsig#}DigestValue")\
.text.encode('hex')
cid_token = kwargs['NFes'][0]['infNFe']['codigo_seguranca']['cid_token']
csc = kwargs['NFes'][0]['infNFe']['codigo_seguranca']['csc']
c_hash_QR_code = "chNFe={0}&nVersao={1}&tpAmb={2}&cDest={3}&dhEmi={4}&vNF\
={5}&vICMS={6}&digVal={7}&cIdToken={8}{9}".\
format(chave_nfe, versao, ambiente, dest_cpf, dh_emissao,
valor_total, icms_total, dig_val, cid_token, csc)
c_hash_QR_code = hashlib.sha1(c_hash_QR_code).hexdigest()
QR_code_url = "?chNFe={0}&nVersao={1}&tpAmb={2}&{3}dhEmi={4}&vNF={5}&vICMS\
={6}&digVal={7}&cIdToken={8}&cHashQRCode={9}".\
format(chave_nfe, versao, ambiente,
'cDest={}&'.format(dest_cpf) if dest_cpf != 'Inexistente'
else '', dh_emissao, valor_total, icms_total, dig_val,
cid_token, c_hash_QR_code)
qr_code_server = localizar_qrcode(kwargs['estado'], ambiente)
qrcode_text = qr_code_server + QR_code_url
qrcode.text = etree.CDATA(qrcode_text)
infnfesupl.append(qrcode)
nfe.insert(1, infnfesupl)
return etree.tostring(xml)
def _send(certificado, method, sign, **kwargs):
def _render(certificado, method, sign, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates')
xmlElem_send = render_xml(path, '%s.xml' % method, True, **kwargs)
modelo = xmlElem_send.find(".//{http://www.portalfiscal.inf.br/nfe}mod")
modelo = modelo.text if modelo is not None else '55'
if modelo == '65':
pagamento = etree.Element('pag')
tipo_pagamento = etree.Element('tPag')
valor = etree.Element('vPag')
valor_pago = kwargs['NFes'][0]['infNFe']['total']['vNF']
metodo_pagamento = kwargs['NFes'][0]['infNFe']['pagamento']
tipo_pagamento.text, valor.text = metodo_pagamento, valor_pago
pagamento.append(tipo_pagamento)
pagamento.append(valor)
transp = xmlElem_send.find(
".//{http://www.portalfiscal.inf.br/nfe}transp")
transp.addnext(pagamento)
if sign:
# Caso for autorização temos que adicionar algumas tags tipo
# cEan, cEANTrib porque o governo sempre complica e não segue padrão
if method == 'NfeAutorizacao':
xmlElem_send = _add_required_node(xmlElem_send)
signer = Assinatura(certificado.pfx, certificado.password)
if method == 'NfeInutilizacao':
xml_send = signer.assina_xml(xmlElem_send, kwargs['obj']['id'])
if method == 'NfeAutorizacao':
xml_send = signer.assina_xml(
xmlElem_send, kwargs['NFes'][0]['infNFe']['Id'])
if 'validate' in kwargs:
erros = valida_nfe(xml_send)
if erros:
raise NFeValidationException('Erro ao validar NFe',
erros=erros,
sent_xml=xml_send)
elif method == 'RecepcaoEventoCancelamento':
elif method == 'RecepcaoEvento':
xml_send = signer.assina_xml(
xmlElem_send, kwargs['eventos'][0]['Id'])
if method == 'RecepcaoEventoCarta':
xml_send = signer.assina_xml(
xmlElem_send, kwargs['Id'])
elif method == 'RecepcaoEventoManifesto':
xml_send = signer.assina_xml(
xmlElem_send, kwargs['manifesto']['identificador'])
if modelo == '65':
xml_send = _add_qrCode(xml_send, **kwargs)
else:
xml_send = etree.tostring(xmlElem_send)
xml_send = etree.tostring(xmlElem_send, encoding=str)
return xml_send
url = localizar_url(method, kwargs['estado'], modelo,
kwargs['ambiente'])
cabecalho = _build_header(method, **kwargs)
send_raw = False
if method == 'NFeDistribuicaoDFe':
send_raw = True
def _send(certificado, method, **kwargs):
xml_send = kwargs["xml"]
base_url = localizar_url(
method, kwargs['estado'], kwargs['modelo'], kwargs['ambiente'])
response, obj = executar_consulta(certificado, url, cabecalho, xml_send,
send_raw=send_raw)
return {
'sent_xml': xml_send,
'received_xml': response,
'object': obj
}
cert, key = extract_cert_and_key_from_pfx(
certificado.pfx, certificado.password)
cert, key = save_cert_key(cert, key)
session = Session()
session.cert = (cert, key)
session.verify = False
transport = Transport(session=session)
def autorizar_nfe(certificado, **kwargs): # Assinar
parser = etree.XMLParser(strip_cdata=False)
xml = etree.fromstring(xml_send, parser=parser)
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
client = Client(base_url, transport=transport)
port = next(iter(client.wsdl.port_types))
first_operation = [x for x in iter(
client.wsdl.port_types[port].operations) if "zip" not in x.lower()][0]
namespaceNFe = xml.find(".//{http://www.portalfiscal.inf.br/nfe}NFe")
if namespaceNFe is not None:
namespaceNFe.set('xmlns', 'http://www.portalfiscal.inf.br/nfe')
with client.settings(raw_response=True):
response = client.service[first_operation](xml)
response, obj = sanitize_response(response.text)
return {
'sent_xml': xml_send,
'received_xml': response,
'object': obj.Body.getchildren()[0]
}
def xml_autorizar_nfe(certificado, **kwargs):
_generate_nfe_id(**kwargs)
return _send(certificado, 'NfeAutorizacao', True, **kwargs)
return _render(certificado, 'NfeAutorizacao', True, **kwargs)
def autorizar_nfe(certificado, **kwargs): # Assinar
if "xml" not in kwargs:
kwargs['xml'] = xml_autorizar_nfe(certificado, **kwargs)
return _send(certificado, 'NfeAutorizacao', **kwargs)
def xml_retorno_autorizar_nfe(certificado, **kwargs):
return _render(certificado, 'NfeRetAutorizacao', False, **kwargs)
def retorno_autorizar_nfe(certificado, **kwargs):
return _send(certificado, 'NfeRetAutorizacao', False, **kwargs)
if "xml" not in kwargs:
kwargs['xml'] = xml_retorno_autorizar_nfe(certificado, **kwargs)
return _send(certificado, 'NfeRetAutorizacao', **kwargs)
def xml_recepcao_evento_cancelamento(certificado, **kwargs): # Assinar
return _render(certificado, 'RecepcaoEvento', True, **kwargs)
def recepcao_evento_cancelamento(certificado, **kwargs): # Assinar
return _send(certificado, 'RecepcaoEventoCancelamento', True, **kwargs)
if "xml" not in kwargs:
kwargs['xml'] = xml_recepcao_evento_cancelamento(certificado, **kwargs)
return _send(certificado, 'RecepcaoEvento', **kwargs)
def xml_inutilizar_nfe(certificado, **kwargs):
return _render(certificado, 'NfeInutilizacao', True, **kwargs)
def inutilizar_nfe(certificado, **kwargs): # Assinar
return _send(certificado, 'NfeInutilizacao', True, **kwargs)
def inutilizar_nfe(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_inutilizar_nfe(certificado, **kwargs)
return _send(certificado, 'NfeInutilizacao', **kwargs)
def xml_consultar_protocolo_nfe(certificado, **kwargs):
return _render(certificado, 'NfeConsultaProtocolo', False, **kwargs)
def consultar_protocolo_nfe(certificado, **kwargs):
return _send(certificado, 'NfeConsultaProtocolo', True, **kwargs)
if "xml" not in kwargs:
kwargs['xml'] = xml_consultar_protocolo_nfe(certificado, **kwargs)
return _send(certificado, 'NfeConsultaProtocolo', **kwargs)
def xml_nfe_status_servico(certificado, **kwargs):
return _render(certificado, 'NfeStatusServico', False, **kwargs)
def nfe_status_servico(certificado, **kwargs):
return _send(certificado, 'NfeStatusServico', False, **kwargs)
if "xml" not in kwargs:
kwargs['xml'] = xml_nfe_status_servico(certificado, **kwargs)
return _send(certificado, 'NfeStatusServico', **kwargs)
def xml_consulta_cadastro(certificado, **kwargs):
return _render(certificado, 'NfeConsultaCadastro', False, **kwargs)
def consulta_cadastro(certificado, **kwargs):
return _send(certificado, 'NfeConsultaCadastro', False, **kwargs)
if "xml" not in kwargs:
kwargs['xml'] = xml_consulta_cadastro(certificado, **kwargs)
kwargs['modelo'] = '55'
return _send(certificado, 'NfeConsultaCadastro', **kwargs)
def xml_recepcao_evento_carta_correcao(certificado, **kwargs): # Assinar
return _render(certificado, 'RecepcaoEvento', True, **kwargs)
def recepcao_evento_carta_correcao(certificado, **kwargs): # Assinar
return _send(certificado, 'RecepcaoEventoCarta', True, **kwargs)
if "xml" not in kwargs:
kwargs['xml'] = xml_recepcao_evento_carta_correcao(
certificado, **kwargs)
return _send(certificado, 'RecepcaoEvento', **kwargs)
def recepcao_evento_manifesto(certificado, **kwargs): # Assinar
return _send(certificado, 'RecepcaoEventoManifesto', True, **kwargs)
def xml_recepcao_evento_manifesto(certificado, **kwargs): # Assinar
return _render(certificado, 'RecepcaoEvento', True, **kwargs)
def recepcao_evento_epec(certificado, **kwargs): # Assinar
return _send(certificado, 'RecepcaoEventoEPEC', True, **kwargs)
def recepcao_evento_manifesto(certificado, **kwargs): # Assinar
if "xml" not in kwargs:
kwargs['xml'] = xml_recepcao_evento_manifesto(certificado, **kwargs)
return _send(certificado, 'RecepcaoEvento', **kwargs)
def xml_consulta_distribuicao_nfe(certificado, **kwargs): # Assinar
return _render(certificado, 'NFeDistribuicaoDFe', False, **kwargs)
def _send_v310(certificado, **kwargs):
xml_send = kwargs["xml"]
base_url = localizar_url(
'NFeDistribuicaoDFe', kwargs['estado'], kwargs['modelo'],
kwargs['ambiente'])
cert, key = extract_cert_and_key_from_pfx(
certificado.pfx, certificado.password)
cert, key = save_cert_key(cert, key)
session = Session()
session.cert = (cert, key)
session.verify = False
transport = Transport(session=session)
xml = etree.fromstring(xml_send)
xml_um = etree.fromstring('<nfeCabecMsg xmlns="http://www.portalfiscal.inf.br/nfe/wsdl/"><cUF>AN</cUF><versaoDados>1.00</versaoDados></nfeCabecMsg>')
client = Client(base_url, transport=transport)
port = next(iter(client.wsdl.port_types))
first_operation = next(iter(client.wsdl.port_types[port].operations))
with client.settings(raw_response=True):
response = client.service[first_operation](nfeDadosMsg=xml, _soapheaders=[xml_um])
response, obj = sanitize_response(response.text)
return {
'sent_xml': xml_send,
'received_xml': response,
'object': obj.Body.nfeDistDFeInteresseResponse.nfeDistDFeInteresseResult
}
def consulta_distribuicao_nfe(certificado, **kwargs):
return _send(certificado, 'NFeDistribuicaoDFe', False, **kwargs)
if "xml" not in kwargs:
kwargs['xml'] = xml_consulta_distribuicao_nfe(certificado, **kwargs)
return _send_v310(certificado, **kwargs)
def xml_download_nfe(certificado, **kwargs): # Assinar
return _render(certificado, 'NFeDistribuicaoDFe', False, **kwargs)
def download_nfe(certificado, **kwargs):
return _send(certificado, 'NFeDistribuicaoDFe', False, **kwargs)
if "xml" not in kwargs:
kwargs['xml'] = xml_download_nfe(certificado, **kwargs)
return _send_v310(certificado, **kwargs)

4
pytrustnfe/nfe/assinatura.py

@ -32,7 +32,7 @@ class Assinatura(object):
ref_uri = ('#%s' % reference) if reference else None
signed_root = signer.sign(
xml_element, key=key, cert=cert,
xml_element, key=key.encode(), cert=cert.encode(),
reference_uri=ref_uri)
if reference:
element_signed = signed_root.find(".//*[@Id='%s']" % reference)
@ -42,4 +42,4 @@ class Assinatura(object):
if element_signed is not None and signature is not None:
parent = element_signed.getparent()
parent.append(signature)
return etree.tostring(signed_root)
return etree.tostring(signed_root, encoding=str)

34
pytrustnfe/nfe/comunicacao.py

@ -1,34 +0,0 @@
# -*- coding: utf-8 -*-
# © 2016 Danimar Ribeiro, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from pytrustnfe.client import HttpClient
from pytrustnfe.certificado import save_cert_key, extract_cert_and_key_from_pfx
from ..xml import sanitize_response
def _soap_xml(body, cabecalho):
xml = '<?xml version="1.0" encoding="utf-8"?>'
xml += '<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"><soap:Header>'
xml += '<nfeCabecMsg xmlns="http://www.portalfiscal.inf.br/nfe/wsdl/' + cabecalho.soap_action + '">'
xml += '<cUF>' + cabecalho.estado + '</cUF><versaoDados>' + cabecalho.versao + '</versaoDados></nfeCabecMsg></soap:Header><soap:Body>'
xml += '<nfeDadosMsg xmlns="http://www.portalfiscal.inf.br/nfe/wsdl/' + cabecalho.soap_action + '">'
xml += body
xml += '</nfeDadosMsg></soap:Body></soap:Envelope>'
return xml.rstrip('\n')
def executar_consulta(certificado, url, cabecalho, xmlEnviar, send_raw=False):
cert, key = extract_cert_and_key_from_pfx(
certificado.pfx, certificado.password)
cert_path, key_path = save_cert_key(cert, key)
client = HttpClient(url, cert_path, key_path)
xml_enviar = _soap_xml(xmlEnviar, cabecalho)
if send_raw:
xml = '<?xml version="1.0" encoding="utf-8"?>' + xmlEnviar.rstrip('\n')
xml_enviar = xml
xml_retorno = client.post_soap(xml_enviar, cabecalho)
return sanitize_response(xml_retorno)

461
pytrustnfe/nfe/danfce.py

@ -0,0 +1,461 @@
# -*- coding: utf-8 -*-
# © 2017 Johny Chen Jy, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import re
from textwrap import wrap
from io import BytesIO
from reportlab.lib import utils
from reportlab.pdfgen import canvas
from reportlab.lib.units import cm, mm
from reportlab.graphics.barcode import qr
from reportlab.graphics import renderPDF
from reportlab.graphics.shapes import Drawing
from reportlab.platypus import Table, TableStyle, Paragraph, Image
from reportlab.lib.enums import TA_CENTER
from reportlab.lib.styles import ParagraphStyle
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
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))
def format_telefone(telefone):
telefone = re.sub('[^0-9]', '', telefone)
if len(telefone) == 10:
telefone = '(%s) %s-%s' % (telefone[0:2],
telefone[2:6],
telefone[6:])
elif len(telefone) == 11:
telefone = '(%s) %s-%s' % (telefone[0:2],
telefone[2:7],
telefone[7:])
return telefone
class danfce(object):
def __init__(self, list_xml, logo=None, timezone=None):
self.current_font_size = 7
self.current_font_name = 'NimbusSanL-Regu'
self.max_height = 840
self.min_height = 1
self.min_width = 5
self.max_width = 200
self.current_height = 840
self.oPDF_IO = BytesIO()
self.canvas = canvas.Canvas(self.oPDF_IO, pagesize=(7.2 * cm, 30 * cm))
self.canvas.setTitle('DANFCE')
self.canvas.setLineWidth(.5)
self.canvas.setFont(self.current_font_name, self.current_font_size)
self.list_xml = list_xml
self.logo = logo
self.nfce_generate()
def ide_emit(self, oXML=None):
elem_emit = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}emit")
# Razão Social emitente
nomeEmpresa = tagtext(oNode=elem_emit, cTag='xFant')
self.drawTitle(nomeEmpresa, 10)
if self.logo:
img = get_image(self.logo, width=10 * mm)
img.drawOn(self.canvas, 5, 830)
cEnd = tagtext(oNode=elem_emit, cTag="xNome") + '<br />'
cEnd += "CNPJ: %s " % (format_cnpj_cpf(
tagtext(oNode=elem_emit, cTag='CNPJ')))
cEnd += "IE: %s" % (tagtext(oNode=elem_emit, cTag="IE")) + '<br />'
cEnd += tagtext(oNode=elem_emit, cTag='xLgr') + ', ' + tagtext(
oNode=elem_emit, cTag='nro') + ' - '
cEnd += tagtext(oNode=elem_emit, cTag='xBairro') + '<br />' + tagtext(
oNode=elem_emit, cTag='xMun') + ' - '
cEnd += tagtext(oNode=elem_emit, cTag='UF') + ' - ' + tagtext(
oNode=elem_emit, cTag='CEP') + '<br />'
cEnd += 'Fone: ' + format_telefone(tagtext(
oNode=elem_emit, cTag='fone'))
self._drawCenteredParagraph(cEnd)
self.drawLine()
def danfce_information(self):
self.drawTitle(
"DANFE NFC-e - Documento Auxiliar da Nota Fiscal de",
7, 'NimbusSanL-Bold')
self.drawTitle("Consumidor Eletrônica", 7, 'NimbusSanL-Bold')
self.drawString(
"NFC-e não permite aproveitamento de crédito de ICMS", True)
self.drawLine()
def produtos(self, oXML=None, el_det=None, oPaginator=None,
list_desc=None, list_cod_prod=None):
rows = [['Cód', 'Descrição', 'Qtde', 'Un', 'Unit.', 'Total']]
colWidths = (25, 90, 15, 15, 25, 25)
rowHeights = [7]
for id in range(oPaginator[0], oPaginator[1]):
item = el_det[id]
el_prod = item.find(".//{http://www.portalfiscal.inf.br/nfe}prod")
cod = tagtext(oNode=el_prod, cTag='cProd')
descricao = tagtext(oNode=el_prod, cTag='xProd')
descricao = (descricao[:20] + '..') if len(descricao) > 20 else descricao
Un = tagtext(oNode=el_prod, cTag='uCom')
Un = (Un[:2]) if len(Un) > 2 else Un
qtde = format_number(tagtext(oNode=el_prod, cTag='qCom'),
precision=2)
vl_unit = format_number(tagtext(oNode=el_prod, cTag='vUnCom'),
precision=2)
vl_total = format_number(
tagtext(oNode=el_prod, cTag='vProd'), precision=2)
new_row = [cod, descricao, qtde, Un, vl_unit, vl_total]
rows.append(new_row)
rowHeights.append(self.current_font_size + 2)
self._draw_product_table(rows, colWidths, rowHeights)
def _draw_product_table(self, rows, colWidths, rowHeights):
table = Table(rows, colWidths, tuple(rowHeights))
table.setStyle(TableStyle([
('FONTSIZE', (0, 0), (-1, -1), 7),
('FONT', (0, 1), (-1, -1), 'NimbusSanL-Regu'),
('FONT', (0, 0), (-1, 0), 'NimbusSanL-Bold'),
('ALIGN', (0, 0), (-1, 0), "LEFT"),
('ALIGN', (1, 0), (-1, 0), "LEFT"),
('ALIGN', (2, 0), (-1, 0), "CENTER"),
('ALIGN', (3, 0), (-1, 0), "CENTER"),
('ALIGN', (0, 1), (-1, -1), "LEFT"),
('ALIGN', (1, 1), (-1, -1), "LEFT"),
('ALIGN', (2, 1), (-1, -1), "CENTER"),
('ALIGN', (3, 1), (-1, -1), "CENTER"),
]))
w, h = table.wrapOn(self.canvas, 200, 450)
table.drawOn(self.canvas, 0, self.current_height - (h * 1.2))
self.current_height -= (h * 1.1)
def totais(self, oXML=None):
# Impostos
el_total = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}total")
total_tributo = format_number(
tagtext(oNode=el_total, cTag='vTotTrib'), precision=2)
valor_total = format_number(
tagtext(oNode=el_total, cTag='vProd'), precision=2)
desconto = format_number(
tagtext(oNode=el_total, cTag='vDesc'), precision=2)
valor_a_pagar = format_number(
tagtext(oNode=el_total, cTag='vNF'), precision=2)
el_pag = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}pag")
troco = tagtext(oNode=el_pag, cTag="vTroco")
payment_method_list = {'01': 'Dinheiro',
'02': 'Cheque',
'03': 'Cartão de Crédito',
'04': 'Cartão de Débito',
"05": "Crédito Loja",
'10': 'Vale Alimentação',
'11': 'Vale Refeição',
'12': 'Vale Presente',
'13': 'Vale Combustível',
'14': 'Duplicata Mercantil',
'15': 'Boleto Bancario',
'90': 'Sem Pagamento',
'99': 'Outros'}
quant_produtos = len(oXML.findall(
".//{http://www.portalfiscal.inf.br/nfe}det"))
payment_methods = []
for pagId, item in enumerate(el_pag):
payment = []
tipo_pagamento = tagtext(oNode=item, cTag="tPag")
val = format_number(tagtext(oNode=item, cTag="vPag"), precision=2)
method = payment_method_list.get(tipo_pagamento)
payment.append(method)
payment.append(val)
payment_methods.append(payment)
values = {
'quantidade_itens': quant_produtos,
'total_tributo': total_tributo,
'valor_total': valor_total,
'desconto': desconto,
'valor_a_pagar': valor_a_pagar,
'formas_de_pagamento': payment_methods,
'troco': troco,
}
self.draw_totals_table(values)
self.drawLine()
def draw_totals_table(self, values):
rowHeights = [7, 7, 7, 7, 7]
data = [
['QTD.TOTAL DE ITENS', values['quantidade_itens']],
['VALOR TOTAL R$', values['valor_total']],
['DESCONTO R$', values['desconto']],
['VALOR A PAGAR R$', values['valor_a_pagar']],
['FORMA DE PAGAMENTO', 'VALOR PAGO R$'],
]
for item in values['formas_de_pagamento']:
data.append([item[0], item[1]])
rowHeights.append(7)
data.append(['TROCO', format_number(values['troco'], precision=2)])
rowHeights.append(7)
table2 = Table(data, colWidths=(150, 50), rowHeights=tuple(rowHeights))
table2.setStyle(TableStyle([
('FONTSIZE', (0, 0), (-1, -1), 7),
('FONT', (0, 0), (1, -1), 'NimbusSanL-Regu'),
('FONT', (0, 4), (1, 4), 'NimbusSanL-Bold'),
('ALIGN', (1, 0), (1, -1), "RIGHT")
]))
w, h = table2.wrapOn(self.canvas, 200, 450)
table2.drawOn(self.canvas, 0, self.current_height - (h * 1.1))
self.current_height -= h
def inf_authentication(self, oXML=None):
el_infNFe = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}infNFe")
# n nfce, serie e data de solicitacao
el_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide")
el_NFeSupl = oXML.find(
".//{http://www.portalfiscal.inf.br/nfe}infNFeSupl")
el_dest = el_infNFe.find(".//{http://www.portalfiscal.inf.br/nfe}dest")
# chave, n protocolo, data autorizacao
el_prot_nfe = oXML.find(
".//{http://www.portalfiscal.inf.br/nfe}protNFe")
el_infAdic = oXML.find(
".//{http://www.portalfiscal.inf.br/nfe}infAdic")
url_chave = tagtext(oNode=el_NFeSupl, cTag='urlChave')
access_key = tagtext(oNode=el_prot_nfe, cTag="chNFe")
frase_chave_acesso = 'Consulte pela Chave de Acesso em:<br />\
%s<br />%s' % (url_chave, access_key)
qrcode = tagtext(oNode=el_NFeSupl, cTag='qrCode')
cnpj = tagtext(oNode=el_dest, cTag='CNPJ')
cpf = tagtext(oNode=el_dest, cTag='CPF')
if cnpj:
cnpj_cpf = format_cnpj_cpf(cnpj)
cnpj_cpf = "CONSUMIDOR CNPJ: %s" % (cnpj)
elif cpf:
cnpj_cpf = format_cnpj_cpf(cpf)
cnpj_cpf = "CONSUMIDOR CPF: %s" % (cpf)
else:
cnpj_cpf = u"CONSUMIDOR NÃO IDENTIFICADO"
nNFC = tagtext(oNode=el_ide, cTag="nNF")
serie = tagtext(oNode=el_ide, cTag='serie')
dataSolicitacao = getdateUTC(tagtext(oNode=el_ide, cTag="dhEmi"))
dataSolicitacao = dataSolicitacao[0] + " " + dataSolicitacao[1]
numProtocolo = tagtext(oNode=el_prot_nfe, cTag="nProt")
dataAutorizacao = getdateUTC(tagtext(oNode=el_prot_nfe,
cTag='dhRecbto'))
dataAutorizacao = dataAutorizacao[0] + " " + dataAutorizacao[1]
text = u"%s <br />%s <br />NFC-e nº%s Série %s %s<br />\
Protocolo de autorização: %s<br />Data de autorização %s<br />\
" % (frase_chave_acesso, cnpj_cpf, nNFC, serie, dataSolicitacao,
numProtocolo, dataAutorizacao)
self._drawCenteredParagraph(text)
self.draw_qr_code(qrcode)
infAdFisco = tagtext(oNode=el_infAdic, cTag='infAdFisco')
self._drawCenteredParagraph(infAdFisco)
infCpl = tagtext(oNode=el_infAdic, cTag='infCpl')
self._drawCenteredParagraph(infCpl)
def _drawCenteredParagraph(self, text):
style = ParagraphStyle(
name='Normal',
fontName='NimbusSanL-Regu',
fontSize=7,
alignment=TA_CENTER,
leading=7,
)
paragraph = Paragraph(text, style=style)
w, h = paragraph.wrapOn(self.canvas, 180, 300)
paragraph.drawOn(self.canvas, 10, self.current_height - h)
self.current_height -= (h*1.1)
def drawString(self, string, centered=False):
if centered:
self.canvas.drawCentredString(
self.max_width / 2, self.current_height, string)
self.current_height -= self.current_font_size
else:
self.canvas.drawString(self.min_width, self.current_height, string)
self.current_height -= self.current_font_size
def drawTitle(self, string, size, font='NimbusSanL-Regu'):
self.canvas.setFont(font, size)
self.canvas.drawCentredString(
self.max_width / 2, self.current_height, string)
self.current_height -= self.current_font_size
self.canvas.setFont(self.current_font_name, self.current_font_size)
def drawLine(self):
self.canvas.line(self.min_width, self.current_height,
self.max_width, self.current_height)
self.current_height -= self.current_font_size
def draw_qr_code(self, string):
qr_code = qr.QrCodeWidget(string)
drawing = Drawing(23 * mm, 23 * mm)
drawing.add(qr_code)
renderPDF.draw(drawing, self.canvas, 20 * mm, self.current_height - 85)
self.current_height -= 85
def newpage(self):
self.current_height = self.max_height
self.Page += 1
self.canvas.showPage()
def nfce_generate(self):
for oXML in self.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
self.ide_emit(oXML=oXML)
# self.destinatario(oXML=oXML)
self.danfce_information()
self.produtos(oXML=oXML, el_det=el_det, oPaginator=oPaginator[0],
list_desc=list_desc, list_cod_prod=list_cod_prod)
self.drawLine()
self.totais(oXML=oXML)
self.inf_authentication(oXML=oXML)
# Gera o restante das páginas do XML
for oPag in oPaginator[1:]:
if oPag:
self.newpage()
self.ide_emit(oXML=oXML)
# self.destinatario(oXML=oXML)
self.produtos(oXML=oXML, el_det=el_det, oPaginator=oPag,
list_desc=list_desc,
list_cod_prod=list_cod_prod)
self.totais(oXML=oXML)
self.inf_authentication(oXML=oXML)
self.newpage()
self.canvas.save()
def writeto_pdf(self, fileObj):
pdf_out = self.oPDF_IO.getvalue()
self.oPDF_IO.close()
fileObj.write(pdf_out)

321
pytrustnfe/nfe/danfe.py

@ -3,9 +3,10 @@
# 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
import math
from reportlab.lib import utils
from reportlab.pdfgen import canvas
@ -17,6 +18,11 @@ from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.enums import TA_CENTER
from reportlab.platypus import Paragraph, Image
from reportlab.lib.styles import ParagraphStyle
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
import pytz
from datetime import datetime, timedelta
def chunks(cString, nLen):
@ -34,14 +40,48 @@ def format_cnpj_cpf(value):
return cValue
def getdateUTC(cDateUTC):
cDt = cDateUTC[0:10].split('-')
def getdateByTimezone(cDateUTC, timezone=None):
'''
Esse método trata a data recebida de acordo com o timezone do
usuário. O seu retorno é dividido em duas partes:
1) A data em si;
2) As horas;
:param cDateUTC: string contendo as informações da data
:param timezone: timezone do usuário do sistema
:return: data e hora convertidos para a timezone do usuário
'''
# Aqui cortamos a informação do timezone da string (+03:00)
dt = cDateUTC[0:19]
# Verificamos se a string está completa (data + hora + timezone)
if timezone and len(cDateUTC) == 25:
# tz irá conter informações da timezone contida em cDateUTC
tz = cDateUTC[19:25]
tz = int(tz.split(':')[0])
dt = datetime.strptime(dt, '%Y-%m-%dT%H:%M:%S')
# dt agora será convertido para o horario em UTC
dt = dt - timedelta(hours=tz)
# tzinfo passará a apontar para <UTC>
dt = pytz.utc.localize(dt)
# valor de dt é convertido para a timezone do usuário
dt = timezone.normalize(dt)
dt = dt.strftime('%Y-%m-%dT%H:%M:%S')
cDt = dt[0:10].split('-')
cDt.reverse()
return '/'.join(cDt), cDateUTC[11:16]
return '/'.join(cDt), dt[11:16]
def format_number(cNumber):
if cNumber:
# Vírgula para a separação de milhar e 2f para 2 casas decimais
cNumber = "{:,.2f}".format(float(cNumber))
return cNumber.replace(",", "X").replace(".", ",").replace("X", ".")
return ""
@ -72,7 +112,16 @@ def get_image(path, width=1 * cm):
class danfe(object):
def __init__(self, sizepage=A4, list_xml=None, recibo=True,
orientation='portrait', logo=None, cce_xml=None):
orientation='portrait', logo=None, cce_xml=None,
timezone=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
@ -81,12 +130,15 @@ class danfe(object):
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()
self.oFrete = {
'0': '0 - Contratação por conta do Remetente (CIF)',
'1': '1 - Contratação por conta do Destinatário (FOB)',
'2': '2 - Contratação por conta de Terceiros',
'3': '3 - Transporte Próprio por conta do Remetente',
'4': '4 - Transporte Próprio por conta do Destinatário',
'9': '9 - Sem Ocorrência de Transporte'}
self.oPDF_IO = BytesIO()
if orientation == 'landscape':
raise NameError('Rotina não implementada')
else:
@ -103,16 +155,15 @@ class danfe(object):
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")
# Declaring variable to prevent future errors
nId = 0
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")
@ -127,60 +178,55 @@ class danfe(object):
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
# Calculando nr. aprox. de páginas
if nId > 25:
self.NrPages += math.ceil((nId - 25) / 70)
if recibo:
self.recibo_entrega(oXML=oXML)
self.recibo_entrega(oXML=oXML, timezone=timezone)
self.ide_emit(oXML=oXML)
self.destinatario(oXML=oXML)
self.ide_emit(oXML=oXML, timezone=timezone)
self.destinatario(oXML=oXML, timezone=timezone)
if oXML_cobr is not None:
self.faturas(oXML=oXML_cobr)
self.faturas(oXML=oXML_cobr, timezone=timezone)
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)
index = self.produtos(
oXML=oXML, el_det=el_det, max_index=nId,
list_desc=list_desc, list_cod_prod=list_cod_prod)
tamanho_ocupado = self.calculo_issqn(oXML=oXML)
self.adicionais(oXML=oXML, tamanho_diminuir=tamanho_ocupado)
# Gera o restante das páginas do XML
for oPag in oPaginator[1:]:
while index < nId:
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.ide_emit(oXML=oXML, timezone=timezone)
index = self.produtos(
oXML=oXML, el_det=el_det, index=index,
max_index=nId,
list_desc=list_desc, nHeight=77,
list_cod_prod=list_cod_prod)
self.newpage()
if cce_xml:
for xml in cce_xml:
self._generate_cce(cce_xml=xml, oXML=oXML)
self._generate_cce(cce_xml=xml, oXML=oXML, timezone=timezone)
self.newpage()
self.canvas.save()
def ide_emit(self, oXML=None):
def ide_emit(self, oXML=None, timezone=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")
elem_evento = oXML.find(
".//{http://www.portalfiscal.inf.br/nfe}infEvento")
cChave = elem_infNFe.attrib.get('Id')[3:]
barcode128 = code128.Code128(
@ -212,7 +258,7 @@ class danfe(object):
cNF = '{0:011,}'.format(int(cNF)).replace(",", ".")
self.stringcenter(self.nLeft + 100, self.nlin + 25, "%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)
@ -250,7 +296,8 @@ class danfe(object):
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'))
cDt, cHr = getdateByTimezone(
tagtext(oNode=elem_protNFe, cTag='dhRecbto'), timezone)
cProtocolo = tagtext(oNode=elem_protNFe, cTag='nProt')
cDt = cProtocolo + ' - ' + cDt + ' ' + cHr
nW_Rect = (self.width - self.nLeft - self.nRight - 110) / 2
@ -271,9 +318,9 @@ class danfe(object):
# Razão Social emitente
P = Paragraph(tagtext(oNode=elem_emit, cTag='xNome'), styleN)
w, h = P.wrap(55 * mm, 50 * mm)
w, h = P.wrap(55 * mm, 40 * mm)
P.drawOn(self.canvas, (self.nLeft + 30) * mm,
(self.height - self.nlin - 12) * mm)
(self.height - self.nlin - ((4.3 * h + 12) / 12)) * mm)
if self.logo:
img = get_image(self.logo, width=2 * cm)
@ -282,6 +329,7 @@ class danfe(object):
cEnd = tagtext(oNode=elem_emit, cTag='xLgr') + ', ' + tagtext(
oNode=elem_emit, cTag='nro') + ' - '
cEnd += tagtext(oNode=elem_emit, cTag='xCpl') + ' - '
cEnd += tagtext(oNode=elem_emit, cTag='xBairro') + '<br />' + tagtext(
oNode=elem_emit, cTag='xMun') + ' - '
cEnd += 'Fone: ' + tagtext(oNode=elem_emit, cTag='fone') + '<br />'
@ -289,7 +337,7 @@ class danfe(object):
oNode=elem_emit, cTag='CEP')
regime = tagtext(oNode=elem_emit, cTag='CRT')
cEnd += u'<br />Regime Tributário: %s' % (REGIME_TRIBUTACAO[regime])
cEnd += '<br />Regime Tributário: %s' % (REGIME_TRIBUTACAO[regime])
styleN.fontName = 'NimbusSanL-Regu'
styleN.fontSize = 7
@ -297,7 +345,7 @@ class danfe(object):
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)
(self.height - self.nlin - 33) * mm)
# Homologação
if tagtext(oNode=elem_ide, cTag='tpAmb') == '2':
@ -308,9 +356,18 @@ class danfe(object):
self.string(self.nLeft + 65, 449, 'SEM VALOR FISCAL')
self.canvas.restoreState()
# Cancelado
if tagtext(oNode=elem_evento, cTag='cStat') == '135':
self.canvas.saveState()
self.canvas.rotate(45)
self.canvas.setFont('NimbusSanL-Bold', 60)
self.canvas.setFillColorRGB(1, 0.2, 0.2)
self.string(self.nLeft + 80, 275, 'CANCELADO')
self.canvas.restoreState()
self.nlin += 48
def destinatario(self, oXML=None):
def destinatario(self, oXML=None, timezone=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
@ -354,12 +411,15 @@ class danfe(object):
else:
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'))
cDt, cHr = getdateByTimezone(tagtext(oNode=elem_ide, cTag='dhEmi'),
timezone)
self.string(nMr - 24, self.nlin + 7.7, cDt + ' ' + cHr)
cDt, cHr = getdateUTC(tagtext(oNode=elem_ide, cTag='dhSaiEnt'))
cDt, cHr = getdateByTimezone(
tagtext(oNode=elem_ide, cTag='dhSaiEnt'), timezone)
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')
cEnd = '%s, %s %s' % (tagtext(oNode=elem_dest, cTag='xLgr'),
tagtext(oNode=elem_dest, cTag='nro'),
tagtext(oNode=elem_dest, cTag='xCpl'))
self.string(self.nLeft + 1, self.nlin + 14.3, cEnd)
self.string(nMr - 98, self.nlin + 14.3,
tagtext(oNode=elem_dest, cTag='xBairro'))
@ -376,7 +436,7 @@ class danfe(object):
self.nlin += 24 # Nr linhas ocupadas pelo bloco
def faturas(self, oXML=None):
def faturas(self, oXML=None, timezone=None):
nMr = self.width - self.nRight
@ -409,7 +469,8 @@ class danfe(object):
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'))
cDt, cHr = getdateByTimezone(tagtext(oNode=oXML_dup, cTag='dVenc'),
timezone)
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)
@ -522,6 +583,8 @@ obsCont[@xCampo='NomeVendedor']")
def transportes(self, oXML=None):
el_transp = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}transp")
veic_transp = oXML.find(
".//{http://www.portalfiscal.inf.br/nfe}veicTransp")
nMr = self.width - self.nRight
self.canvas.setFont('NimbusSanL-Bold', 7)
@ -532,25 +595,26 @@ obsCont[@xCampo='NomeVendedor']")
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 + 2, 13.2)
self.vline(nMr - 33, self.nlin + 2, 13.2)
self.vline(nMr - 67, self.nlin + 2, 6.6)
self.vline(nMr - 123, self.nlin + 2, 6.6)
self.vline(nMr - 53, self.nlin + 2, 6.6)
self.vline(nMr - 26, self.nlin + 15.2, 6.6)
self.vline(nMr - 49, 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(nMr - 25, self.nlin + 3.8, 'CNPJ/CPF')
self.string(nMr - 52, self.nlin + 3.8, 'PLACA DO VEÍCULO')
self.string(nMr - 66, self.nlin + 3.8, 'CÓDIGO ANTT')
self.string(nMr - 122, 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 - 32, self.nlin + 3.8, 'UF')
self.string(nMr - 25, self.nlin + 10.3, 'INSCRIÇÃO ESTADUAL')
self.string(nMr - 32, 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')
@ -560,20 +624,27 @@ obsCont[@xCampo='NomeVendedor']")
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.canvas.setFont('NimbusSanL-Regu', 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,
tagtext(oNode=el_transp, cTag='xNome')[:42])
self.string(self.nLeft + 68, self.nlin + 7.7,
self.oFrete[tagtext(oNode=el_transp, cTag='modFrete')])
self.string(nMr - 39, self.nlin + 7.7,
self.string(self.nLeft + 122, self.nlin + 7.7,
tagtext(oNode=el_transp, cTag='RNTC'))
self.string(self.nLeft + 136, self.nlin + 7.7,
tagtext(oNode=el_transp, cTag='placa'))
self.string(self.nLeft + 157, self.nlin + 7.7,
tagtext(oNode=veic_transp, cTag='UF'))
self.string(nMr - 25, self.nlin + 7.7,
format_cnpj_cpf(tagtext(oNode=el_transp, cTag='CNPJ')))
self.canvas.setFont('NimbusSanL-Regu', 8)
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,
self.string(nMr - 32, self.nlin + 14.2,
tagtext(oNode=el_transp, cTag='UF'))
self.string(nMr - 39, self.nlin + 14.2,
self.string(nMr - 25, 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'))
@ -592,13 +663,16 @@ obsCont[@xCampo='NomeVendedor']")
self.nlin += 23
def produtos(self, oXML=None, el_det=None, oPaginator=None,
def produtos(self, oXML=None, el_det=None, index=0, max_index=0,
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
# nH é o altura da linha vertical, utilizar como referência
# somar a ele a altura atual que é nlin
maxHeight = self.nlin + nH
self.canvas.setFont('NimbusSanL-Bold', 7)
self.string(self.nLeft + 1, self.nlin + 1, 'DADOS DO PRODUTO/SERVIÇO')
@ -644,9 +718,15 @@ obsCont[@xCampo='NomeVendedor']")
# Conteúdo campos
self.canvas.setFont('NimbusSanL-Regu', 5)
nLin = self.nlin + 10.5
nLin = self.nlin + 10.0
for id in range(index, max_index + 1):
line_height = max(len(list_cod_prod[id]), len(list_desc[id]))
line_height *= nStep
if nLin + line_height > maxHeight:
break
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(
@ -677,16 +757,14 @@ obsCont[@xCampo='NomeVendedor']")
tagtext(oNode=el_prod, cTag='qCom')))
self.stringRight(nMr - 64.5, nLin, format_number(
tagtext(oNode=el_prod, cTag='vUnCom')))
self.stringRight(nMr - 50.5, nLin,
tagtext(oNode=el_prod, cTag='vProd'))
self.stringRight(nMr - 50.5, nLin, format_number(
tagtext(oNode=el_prod, cTag='vProd')))
self.stringRight(nMr - 38.5, nLin, format_number(vBC))
self.stringRight(nMr - 26.5, nLin, format_number(vICMS))
self.stringRight(nMr - 7.5, nLin, format_number(pICMS))
if vIPI:
self.stringRight(nMr - 14.5, nLin, format_number(vIPI))
if pIPI:
self.stringRight(nMr - 0.5, nLin, format_number(pIPI))
self.stringRight(nMr - 14.5, nLin, format_number(vIPI or '0.00'))
self.stringRight(nMr - 0.5, nLin, format_number(pIPI or '0.00'))
# Código Item
line_cod = nLin
@ -706,8 +784,51 @@ obsCont[@xCampo='NomeVendedor']")
self.canvas.setStrokeColor(black)
self.nlin += nH + 3
return id
def calculo_issqn(self, oXML=None):
elem_emit = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}emit")
el_total = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}total")
issqn_total = el_total.find(
".//{http://www.portalfiscal.inf.br/nfe}ISSQNtot")
if not issqn_total:
return 0
self.nlin += 1
nMr = self.width - self.nRight
self.canvas.setFont('NimbusSanL-Bold', 7)
self.string(self.nLeft + 1, self.nlin + 1, 'CÁLCULO DO ISSQN')
self.rect(self.nLeft, self.nlin + 2,
self.width - self.nLeft - self.nRight, 5.5)
self.vline(nMr - 47.5, self.nlin + 2, 5.5)
self.vline(nMr - 95, self.nlin + 2, 5.5)
self.vline(nMr - 142.5, self.nlin + 2, 5.5)
self.vline(nMr - 190, self.nlin + 2, 5.5)
# Labels
self.canvas.setFont('NimbusSanL-Regu', 5)
self.string(self.nLeft + 1, self.nlin + 3.8, 'INSCRIÇÃO MUNICIPAL')
self.string(nMr - 141.5, self.nlin + 3.8, 'VALOR TOTAL DOS SERVIÇOS')
self.string(nMr - 94, self.nlin + 3.8, 'BASE DE CÁLCULO DO ISSQN')
self.string(nMr - 46.5, self.nlin + 3.8, 'VALOR DO ISSQN')
# Conteúdo campos
self.canvas.setFont('NimbusSanL-Regu', 8)
self.string(
self.nLeft + 1, self.nlin + 6.7,
tagtext(oNode=elem_emit, cTag='IM'))
self.stringRight(
self.nLeft + 94, self.nlin + 6.7,
format_number(tagtext(oNode=issqn_total, cTag='vServ')))
self.stringRight(
self.nLeft + 141.5, self.nlin + 6.7,
format_number(tagtext(oNode=issqn_total, cTag='vBC')))
self.stringRight(
self.nLeft + 189, self.nlin + 6.7,
format_number(tagtext(oNode=issqn_total, cTag='vISS')))
self.nlin += 8 # Nr linhas ocupadas pelo bloco
return 8
def adicionais(self, oXML=None):
def adicionais(self, oXML=None, tamanho_diminuir=0):
el_infAdic = oXML.find(
".//{http://www.portalfiscal.inf.br/nfe}infAdic")
@ -717,10 +838,10 @@ obsCont[@xCampo='NomeVendedor']")
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.string(((self.width / 3) * 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.width - self.nLeft - self.nRight, 42 - tamanho_diminuir)
self.vline((self.width / 3) * 2, self.nlin + 2, 42 - tamanho_diminuir)
# Conteúdo campos
styles = getSampleStyleSheet()
styleN = styles['Normal']
@ -732,12 +853,12 @@ obsCont[@xCampo='NomeVendedor']")
if fisco:
observacoes = fisco + ' ' + observacoes
P = Paragraph(observacoes, styles['Normal'])
w, h = P.wrap(92 * mm, 32 * mm)
w, h = P.wrap(128 * 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):
def recibo_entrega(self, oXML=None, timezone=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")
@ -767,19 +888,21 @@ obsCont[@xCampo='NomeVendedor']")
self.string(self.width - self.nRight - nW +
2, self.nlin + 8, "%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'))
cDt, cHr = getdateByTimezone(
tagtext(oNode=el_ide, cTag='dhEmi'), timezone)
cTotal = format_number(tagtext(oNode=el_total, cTag='vNF'))
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='xCpl') + ' '
cEnd += tagtext(oNode=el_dest, cTag='xBairro') + ', ' + tagtext(
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'),
@ -836,7 +959,7 @@ obsCont[@xCampo='NomeVendedor']")
self.oPDF_IO.close()
fileObj.write(pdf_out)
def _generate_cce(self, cce_xml=None, oXML=None):
def _generate_cce(self, cce_xml=None, oXML=None, timezone=None):
self.canvas.setLineWidth(.2)
# labels
@ -875,8 +998,8 @@ obsCont[@xCampo='NomeVendedor']")
self.string(82, 24, cnpj)
chave_acesso = tagtext(oNode=elem_infNFe, cTag='chNFe')
self.string(82, 30, chave_acesso)
data_correcao = getdateUTC(tagtext(
oNode=elem_infNFe, cTag='dhEvento'))
data_correcao = getdateByTimezone(tagtext(
oNode=elem_infNFe, cTag='dhEvento'), timezone)
data_correcao = data_correcao[0] + " " + data_correcao[1]
self.string(82, 36, data_correcao)
cce_id = elem_infNFe.values()[0]

BIN
pytrustnfe/nfe/fonts/NimbusSanL Bold.ttf

BIN
pytrustnfe/nfe/fonts/NimbusSanL Regular.ttf

30
pytrustnfe/nfe/templates/NFeDistribuicaoDFe.xml

@ -1,19 +1,11 @@
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<nfeDistDFeInteresse xmlns="http://www.portalfiscal.inf.br/nfe/wsdl/NFeDistribuicaoDFe">
<nfeDadosMsg>
<distDFeInt xmlns="http://www.portalfiscal.inf.br/nfe" versao="1.01">
<tpAmb>{{ ambiente }}</tpAmb>
<cUFAutor>{{ estado }}</cUFAutor>
<CNPJ>{{ cnpj_cpf }}</CNPJ>
<distNSU>
<ultNSU>{{ ultimo_nsu }}</ultNSU>
</distNSU>
<consChNFe>
<chNFe>{{ chave_nfe }}</chNFe>
</consChNFe>
</distDFeInt>
</nfeDadosMsg>
</nfeDistDFeInteresse>
</Body>
</Envelope>
<distDFeInt xmlns="http://www.portalfiscal.inf.br/nfe" versao="1.01">
<tpAmb>{{ ambiente }}</tpAmb>
<cUFAutor>{{ estado }}</cUFAutor>
<CNPJ>{{ cnpj_cpf }}</CNPJ>
<distNSU>
<ultNSU>{{ ultimo_nsu }}</ultNSU>
</distNSU>
<consChNFe>
<chNFe>{{ chave_nfe }}</chNFe>
</consChNFe>
</distDFeInt>

219
pytrustnfe/nfe/templates/NfeAutorizacao.xml

@ -1,15 +1,14 @@
<enviNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="3.10">
<enviNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="4.00">
<idLote>{{ idLote }}</idLote>
<indSinc>{{ indSinc }}</indSinc>
{% for NFe in NFes %}
<NFe xmlns="http://www.portalfiscal.inf.br/nfe">
<infNFe versao="3.10" Id="{{ NFe.infNFe.Id }}">
<infNFe versao="4.00" Id="{{ NFe.infNFe.Id }}">
<ide>
{% with ide = NFe.infNFe.ide %}
<cUF>{{ ide.cUF }}</cUF>
<cNF>{{ ide.cNF }}</cNF>
<natOp>{{ ide.natOp }}</natOp>
<indPag>{{ ide.indPag }}</indPag>
<mod>{{ ide.mod }}</mod>
<serie>{{ ide.serie }}</serie>
<nNF>{{ ide.nNF }}</nNF>
@ -117,6 +116,7 @@
{% endif %}
{% endif %}
<xNome>{{ dest.xNome|normalize|escape }}</xNome>
{% if dest.enderDest is defined %}
<enderDest>
<xLgr>{{ dest.enderDest.xLgr|normalize|escape }}</xLgr>
<nro>{{ dest.enderDest.nro }}</nro>
@ -130,6 +130,7 @@
<xPais>{{ dest.enderDest.xPais }}</xPais>
<fone>{{ dest.enderDest.fone }}</fone>
</enderDest>
{% endif %}
<indIEDest>{{ dest.indIEDest }}</indIEDest>
{% if dest.IE != '' -%}<IE>{{ dest.IE }}</IE>{% endif %}
<ISUF>{{ dest.ISUF }}</ISUF>
@ -182,6 +183,9 @@
<NCM>{{ prod.NCM }}</NCM>
<NVE>{{ prod.NVE }}</NVE>
<CEST>{{ prod.CEST }}</CEST>
<indEscala>{{ prod.indEscala }}</indEscala>
<CNPJFab>{{ prod.CNPJFab }}</CNPJFab>
<cBenef>{{ prod.cBenef }}</cBenef>
<EXTIPI>{{ prod.EXTIPI }}</EXTIPI>
<CFOP>{{ prod.CFOP }}</CFOP>
<uCom>{{ prod.uCom }}</uCom>
@ -244,11 +248,27 @@
<xPed>{{ prod.xPed }}</xPed>
<nItemPed>{{ prod.nItemPed }}</nItemPed>
<nFCI>{{ prod.nFCI }}</nFCI>
{% for rastro in prod.rastro %}
<rastro>
<nLote>{{ rastro.nLote }}</nLote>
<qLote>{{ rastro.qLote }}</qLote>
<dFab>{{ rastro.dFab }}</dFab>
<dVal>{{ rastro.dVal }}</dVal>
<cAgreg>{{ rastro.cAgreg }}</cAgreg>
</rastro>
{% endfor %}
{% for med in prod.med %}
<med>
<cProdANVISA>{{ med.cProdANVISA }}</cProdANVISA>
<vPMC>{{ med.vPMC }}</vPMC>
</med>
{% endfor %}
{% endwith %}
</prod>
<imposto>
{% with imposto = det.imposto %}
<vTotTrib>{{ imposto.vTotTrib }}</vTotTrib>
{% if imposto.ICMS is defined %}
<ICMS>
{% if imposto.ICMS.CST == '00' -%}
<ICMS00>
@ -258,6 +278,8 @@
<vBC>{{ imposto.ICMS.vBC }}</vBC>
<pICMS>{{ imposto.ICMS.pICMS }}</pICMS>
<vICMS>{{ imposto.ICMS.vICMS }}</vICMS>
<pFCP>{{ imposto.ICMS.pFCP }}</pFCP>
<vFCP>{{ imposto.ICMS.vFCP }}</vFCP>
</ICMS00>
{% endif %}
{% if imposto.ICMS.CST == '10' -%}
@ -268,12 +290,18 @@
<vBC>{{ imposto.ICMS.vBC }}</vBC>
<pICMS>{{ imposto.ICMS.pICMS }}</pICMS>
<vICMS>{{ imposto.ICMS.vICMS }}</vICMS>
<vBCFCP>{{ imposto.ICMS.vBCFCP }}</vBCFCP>
<pFCP>{{ imposto.ICMS.pFCP }}</pFCP>
<vFCP>{{ imposto.ICMS.vFCP }}</vFCP>
<modBCST>{{ imposto.ICMS.modBCST }}</modBCST>
<pMVAST>{{ imposto.ICMS.pMVAST }}</pMVAST>
<pRedBCST>{{ imposto.ICMS.pRedBCST }}</pRedBCST>
<vBCST>{{ imposto.ICMS.vBCST }}</vBCST>
<pICMSST>{{ imposto.ICMS.pICMSST }}</pICMSST>
<vICMSST>{{ imposto.ICMS.vICMSST }}</vICMSST>
<vBCFCPST>{{ imposto.ICMS.vBCFCPST }}</vBCFCPST>
<pFCPST>{{ imposto.ICMS.pFCPST }}</pFCPST>
<vFCPST>{{ imposto.ICMS.vFCPST }}</vFCPST>
</ICMS10>
{% endif %}
{% if imposto.ICMS.CST == '20' -%}
@ -285,6 +313,9 @@
<vBC>{{ imposto.ICMS.vBC }}</vBC>
<pICMS>{{ imposto.ICMS.pICMS }}</pICMS>
<vICMS>{{ imposto.ICMS.vICMS }}</vICMS>
<vBCFCP>{{ imposto.ICMS.vBCFCP }}</vBCFCP>
<pFCP>{{ imposto.ICMS.pFCP }}</pFCP>
<vFCP>{{ imposto.ICMS.vFCP }}</vFCP>
<vICMSDeson>{{ imposto.ICMS.vICMSDeson }}</vICMSDeson>
<motDesICMS>{{ imposto.ICMS.motDesICMS }}</motDesICMS>
</ICMS20>
@ -299,6 +330,9 @@
<vBCST>{{ imposto.ICMS.vBCST }}</vBCST>
<pICMSST>{{ imposto.ICMS.pICMSST }}</pICMSST>
<vICMSST>{{ imposto.ICMS.vICMSST }}</vICMSST>
<vBCFCPST>{{ imposto.ICMS.vBCFCPST }}</vBCFCPST>
<pFCPST>{{ imposto.ICMS.pFCPST }}</pFCPST>
<vFCPST>{{ imposto.ICMS.vFCPST }}</vFCPST>
<vICMSDeson>{{ imposto.ICMS.vICMSDeson }}</vICMSDeson>
<motDesICMS>{{ imposto.ICMS.motDesICMS }}</motDesICMS>
</ICMS30>
@ -323,6 +357,9 @@
<pDif>{{ imposto.ICMS.pDif }}</pDif>
<vICMSDif>{{ imposto.ICMS.vICMSDif }}</vICMSDif>
<vICMS>{{ imposto.ICMS.vICMS }}</vICMS>
<vBCFCP>{{ imposto.ICMS.vBCFCP }}</vBCFCP>
<pFCP>{{ imposto.ICMS.pFCP }}</pFCP>
<vFCP>{{ imposto.ICMS.vFCP }}</vFCP>
</ICMS51>
{% endif %}
{% if imposto.ICMS.CST == '60' -%}
@ -330,7 +367,11 @@
<orig>{{ imposto.ICMS.orig }}</orig>
<CST>{{ imposto.ICMS.CST }}</CST>
<vBCSTRet>{{ imposto.ICMS.vBCSTRet }}</vBCSTRet>
<pST>{{ imposto.ICMS.pST }}</pST>
<vICMSSTRet>{{ imposto.ICMS.vICMSSTRet }}</vICMSSTRet>
<vBCFCPSTRet>{{ imposto.ICMS.vBCFCPSTRet }}</vBCFCPSTRet>
<pFCPSTRet>{{ imposto.ICMS.pFCPSTRet }}</pFCPSTRet>
<vFCPSTRet>{{ imposto.ICMS.vFCPSTRet }}</vFCPSTRet>
</ICMS60>
{% endif %}
{% if imposto.ICMS.CST == '70' -%}
@ -342,12 +383,18 @@
<vBC>{{ imposto.ICMS.vBC }}</vBC>
<pICMS>{{ imposto.ICMS.pICMS }}</pICMS>
<vICMS>{{ imposto.ICMS.vICMS }}</vICMS>
<vBCFCP>{{ imposto.ICMS.vBCFCP }}</vBCFCP>
<pFCP>{{ imposto.ICMS.pFCP }}</pFCP>
<vFCP>{{ imposto.ICMS.vFCP }}</vFCP>
<modBCST>{{ imposto.ICMS.modBCST }}</modBCST>
<pMVAST>{{ imposto.ICMS.pMVAST }}</pMVAST>
<pRedBCST>{{ imposto.ICMS.pRedBCST }}</pRedBCST>
<vBCST>{{ imposto.ICMS.vBCST }}</vBCST>
<pICMSST>{{ imposto.ICMS.pICMSST }}</pICMSST>
<vICMSST>{{ imposto.ICMS.vICMSST }}</vICMSST>
<vBCFCPST>{{ imposto.ICMS.vBCFCPST }}</vBCFCPST>
<pFCPST>{{ imposto.ICMS.pFCPST }}</pFCPST>
<vFCPST>{{ imposto.ICMS.vFCPST }}</vFCPST>
<vICMSDeson>{{ imposto.ICMS.vICMSDeson }}</vICMSDeson>
<motDesICMS>{{ imposto.ICMS.motDesICMS }}</motDesICMS>
</ICMS70>
@ -361,12 +408,18 @@
<pRedBC>{{ imposto.ICMS.pRedBC }}</pRedBC>
<pICMS>{{ imposto.ICMS.pICMS }}</pICMS>
<vICMS>{{ imposto.ICMS.vICMS }}</vICMS>
<vBCFCP>{{ imposto.ICMS.vBCFCP }}</vBCFCP>
<pFCP>{{ imposto.ICMS.pFCP }}</pFCP>
<vFCP>{{ imposto.ICMS.pFCP }}</vFCP>
<modBCST>{{ imposto.ICMS.modBCST }}</modBCST>
<pMVAST>{{ imposto.ICMS.pMVAST }}</pMVAST>
<pRedBCST>{{ imposto.ICMS.pRedBCST }}</pRedBCST>
<vBCST>{{ imposto.ICMS.vBCST }}</vBCST>
<pICMSST>{{ imposto.ICMS.pICMSST }}</pICMSST>
<vICMSST>{{ imposto.ICMS.vICMSST }}</vICMSST>
<vBCFCPST>{{ imposto.ICMS.vBCFCPST }}</vBCFCPST>
<pFCPST>{{ imposto.ICMS.pFCPST }}</pFCPST>
<vFCPST>{{ imposto.ICMS.vFCPST }}</vFCPST>
<vICMSDeson>{{ imposto.ICMS.vICMSDeson }}</vICMSDeson>
<motDesICMS>{{ imposto.ICMS.motDesICMS }}</motDesICMS>
</ICMS90>
@ -380,12 +433,10 @@
<pRedBC>{{ imposto.ICMSPart.pRedBC }}</pRedBC>
<pICMS>{{ imposto.ICMSPart.pICMS }}</pICMS>
<vICMS>{{ imposto.ICMSPart.vICMS }}</vICMS>
<vBCST>{{ imposto.ICMS.vBCST }}</vBCST>
<modBCST>{{ imposto.ICMSPart.modBCST }}</modBCST>
<pMVAST>{{ imposto.ICMSPart.pMVAST }}</pMVAST>
<pRedBCST>{{ imposto.ICMSPart.pRedBCST }}</pRedBCST>
<vBCST>{{ imposto.ICMSPart.vBCST }}</vBCST>
<pICMSST>{{ imposto.ICMSPart.pICMSST }}</pICMSST>
<vICMSST>{{ imposto.ICMSPart.vICMSST }}</vICMSST>
<pBCOp>{{ imposto.ICMSPart.pBCOp }}</pBCOp>
<UFST>{{ imposto.ICMSPart.UFST }}</UFST>
</ICMSPart>
@ -424,6 +475,9 @@
<vBCST>{{ imposto.ICMS.vBCST }}</vBCST>
<pICMSST>{{ imposto.ICMS.pICMSST }}</pICMSST>
<vICMSST>{{ imposto.ICMS.vICMSST }}</vICMSST>
<vBCFCPST>{{ imposto.ICMS.vBCFCPST }}</vBCFCPST>
<pFCPST>{{ imposto.ICMS.pFCPST }}</pFCPST>
<vFCPST>{{ imposto.ICMS.vFCPST }}</vFCPST>
<pCredSN>{{ imposto.ICMS.pCredSN }}</pCredSN>
<vCredICMSSN>{{ imposto.ICMS.vCredICMSSN }}</vCredICMSSN>
</ICMSSN201>
@ -438,6 +492,9 @@
<vBCST>{{ imposto.ICMS.vBCST }}</vBCST>
<pICMSST>{{ imposto.ICMS.pICMSST }}</pICMSST>
<vICMSST>{{ imposto.ICMS.vICMSST }}</vICMSST>
<vBCFCPST>{{ imposto.ICMS.vBCFCPST }}</vBCFCPST>
<pFCPST>{{ imposto.ICMS.pFCPST }}</pFCPST>
<vFCPST>{{ imposto.ICMS.vFCPST }}</vFCPST>
</ICMSSN202>
{% endif %}
{% if imposto.ICMS.CST == '500' -%}
@ -445,7 +502,11 @@
<orig>{{ imposto.ICMS.orig }}</orig>
<CSOSN>{{ imposto.ICMS.CST }}</CSOSN>
<vBCSTRet>{{ imposto.ICMS.vBCSTRet }}</vBCSTRet>
<pST>{{ imposto.ICMS.pST }}</pST>
<vICMSSTRet>{{ imposto.ICMS.vICMSSTRet }}</vICMSSTRet>
<vBCFCPSTRet>{{ imposto.ICMS.vBCFCPSTRet }}</vBCFCPSTRet>
<pFCPSTRet>{{ imposto.ICMS.pFCPSTRet }}</pFCPSTRet>
<pFCPSTRet>{{ imposto.ICMS.pFCPSTRet }}</pFCPSTRet>
</ICMSSN500>
{% endif %}
{% if imposto.ICMS.CST == '900' -%}
@ -463,42 +524,66 @@
<vBCST>{{ imposto.ICMS.vBCST }}</vBCST>
<pICMSST>{{ imposto.ICMS.pICMSST }}</pICMSST>
<vICMSST>{{ imposto.ICMS.vICMSST }}</vICMSST>
<vBCFCPST>{{ imposto.ICMS.vBCFCPST }}</vBCFCPST>
<pFCPST>{{ imposto.ICMS.pFCPST }}</pFCPST>
<vFCPST>{{ imposto.ICMS.vFCPST }}</vFCPST>
<pCredSN>{{ imposto.ICMS.pCredSN }}</pCredSN>
<vCredICMSSN>{{ imposto.ICMS.vCredICMSSN }}</vCredICMSSN>
</ICMSSN900>
{% endif %}
</ICMS>
{% if NFe.infNFe.ide.mod != '65' %}
<IPI>
<clEnq>{{ imposto.IPI.clEnq }}</clEnq>
<CNPJProd>{{ imposto.IPI.CNPJProd }}</CNPJProd>
<cSelo>{{ imposto.IPI.cSelo }}</cSelo>
<qSelo>{{ imposto.IPI.qSelo }}</qSelo>
<cEnq>{{ imposto.IPI.cEnq }}</cEnq>
{% if imposto.IPI.CST in ('00', '49', '50', '99') %}
<IPITrib>
<CST>{{ imposto.IPI.CST }}</CST>
<vBC>{{ imposto.IPI.vBC }}</vBC>
<pIPI>{{ imposto.IPI.pIPI }}</pIPI>
<qUnid>{{ imposto.IPI.qUnid }}</qUnid>
<vUnid>{{ imposto.IPI.vUnid }}</vUnid>
<vIPI>{{ imposto.IPI.vIPI }}</vIPI>
</IPITrib>
{% endif %}
{% if imposto.IPI.CST in ('01', '02', '03', '04', '51', '52', '53', '54', '55') %}
<IPINT>
<CST>{{ imposto.IPI.CST }}</CST>
</IPINT>
{% endif %}
</IPI>
{% endif %}
{% if imposto.II is defined %}
<II>
<vBC>{{ imposto.II.vBC }}</vBC>
<vDespAdu>{{ imposto.II.vDespAdu }}</vDespAdu>
<vII>{{ imposto.II.vII }}</vII>
<vIOF>{{ imposto.II.vIOF }}</vIOF>
</II>
{% if NFe.infNFe.ide.mod != '65' and imposto.IPI is defined %}
<IPI>
<clEnq>{{ imposto.IPI.clEnq }}</clEnq>
<CNPJProd>{{ imposto.IPI.CNPJProd }}</CNPJProd>
<cSelo>{{ imposto.IPI.cSelo }}</cSelo>
<qSelo>{{ imposto.IPI.qSelo }}</qSelo>
<cEnq>{{ imposto.IPI.cEnq }}</cEnq>
{% if imposto.IPI.CST in ('00', '49', '50', '99') %}
<IPITrib>
<CST>{{ imposto.IPI.CST }}</CST>
<vBC>{{ imposto.IPI.vBC }}</vBC>
<pIPI>{{ imposto.IPI.pIPI }}</pIPI>
<qUnid>{{ imposto.IPI.qUnid }}</qUnid>
<vUnid>{{ imposto.IPI.vUnid }}</vUnid>
<vIPI>{{ imposto.IPI.vIPI }}</vIPI>
</IPITrib>
{% endif %}
{% if imposto.IPI.CST in ('01', '02', '03', '04', '51', '52', '53', '54', '55') %}
<IPINT>
<CST>{{ imposto.IPI.CST }}</CST>
</IPINT>
{% endif %}
</IPI>
{% if imposto.II is defined %}
<II>
<vBC>{{ imposto.II.vBC }}</vBC>
<vDespAdu>{{ imposto.II.vDespAdu }}</vDespAdu>
<vII>{{ imposto.II.vII }}</vII>
<vIOF>{{ imposto.II.vIOF }}</vIOF>
</II>
{% endif %}
{% endif %}
{% if imposto.ISSQN is defined %}
<ISSQN>
<vBC>{{ imposto.ISSQN.vBC }}</vBC>
<vAliq>{{ imposto.ISSQN.vAliq }}</vAliq>
<vISSQN>{{ imposto.ISSQN.vISSQN }}</vISSQN>
<cMunFG>{{ imposto.ISSQN.cMunFG }}</cMunFG>
<cListServ>{{ imposto.ISSQN.cListServ }}</cListServ>
<vDeducao>{{ imposto.ISSQN.vDeducao }}</vDeducao>
<vOutro>{{ imposto.ISSQN.vOutro }}</vOutro>
<vDescIncond>{{ imposto.ISSQN.vDescIncond }}</vDescIncond>
<vDescCond>{{ imposto.ISSQN.vDescCond }}</vDescCond>
<vISSRet>{{ imposto.ISSQN.vDeducao }}</vISSRet>
<indISS>{{ imposto.ISSQN.indISS }}</indISS>
<cServico>{{ imposto.ISSQN.cServico }}</cServico>
<cMun>{{ imposto.ISSQN.cMun }}</cMun>
<cPais>{{ imposto.ISSQN.cPais }}</cPais>
<nProcesso>{{ imposto.ISSQN.nProcesso }}</nProcesso>
<indIncentivo>{{ imposto.ISSQN.indIncentivo }}</indIncentivo>
</ISSQN>
{% endif %}
<PIS>
{% if imposto.PIS.CST in ('01', '02') %}
@ -576,39 +661,20 @@
{% endif %}
</COFINS>
{% if imposto.COFINSST is defined %}
<PISST>
<COFINSST>
<vBC>{{ imposto.COFINSST.vBC }}</vBC>
<pCOFINS>{{ imposto.COFINSST.pCOFINS }}</pCOFINS>
<qBCProd>{{ imposto.COFINSST.qBCProd }}</qBCProd>
<vAliqProd>{{ imposto.COFINSST.vAliqProd }}</vAliqProd>
<vCOFINS>{{ imposto.COFINSST.vCOFINS }}</vCOFINS>
</PISST>
{% endif %}
{% if imposto.ISSQN is defined %}
<ISSQN>
<vBC>{{ imposto.ISSQN.vBC }}</vBC>
<vAliq>{{ imposto.ISSQN.vAliq }}</vAliq>
<vISSQN>{{ imposto.ISSQN.vISSQN }}</vISSQN>
<cMunFG>{{ imposto.ISSQN.cMunFG }}</cMunFG>
<cListServ>{{ imposto.ISSQN.cListServ }}</cListServ>
<vDeducao>{{ imposto.ISSQN.vDeducao }}</vDeducao>
<vOutro>{{ imposto.ISSQN.vOutro }}</vOutro>
<vDescIncond>{{ imposto.ISSQN.vDescIncond }}</vDescIncond>
<vDescCond>{{ imposto.ISSQN.vDescCond }}</vDescCond>
<vISSRet>{{ imposto.ISSQN.vDeducao }}</vISSRet>
<indISS>{{ imposto.ISSQN.indISS }}</indISS>
<cServico>{{ imposto.ISSQN.cServico }}</cServico>
<cMun>{{ imposto.ISSQN.cMun }}</cMun>
<cPais>{{ imposto.ISSQN.cPais }}</cPais>
<nProcesso>{{ imposto.ISSQN.nProcesso }}</nProcesso>
<indIncentivo>{{ imposto.ISSQN.vDeducao }}</indIncentivo>
</ISSQN>
</COFINSST>
{% endif %}
{% if imposto.ICMSUFDest is defined %}
<ICMSUFDest>
<vBCUFDest>{{ imposto.ICMSUFDest.vBCUFDest }}</vBCUFDest>
<vBCFCPUFDest>{{ imposto.ICMSUFDest.vBCFCPUFDest }}</vBCFCPUFDest>
<pFCPUFDest>{{ imposto.ICMSUFDest.pFCPUFDest }}</pFCPUFDest>
<pICMSUFDest>{{ imposto.ICMSUFDest.pICMSInter }}</pICMSUFDest>
<pICMSUFDest>{{ imposto.ICMSUFDest.pICMSUFDest }}</pICMSUFDest>
<pICMSInter>{{ imposto.ICMSUFDest.pICMSInter }}</pICMSInter>
<pICMSInterPart>{{ imposto.ICMSUFDest.pICMSInterPart }}</pICMSInterPart>
<vFCPUFDest>{{ imposto.ICMSUFDest.vFCPUFDest }}</vFCPUFDest>
@ -639,14 +705,18 @@
<vFCPUFDest>{{ total.vFCPUFDest }}</vFCPUFDest>
<vICMSUFDest>{{ total.vICMSUFDest }}</vICMSUFDest>
<vICMSUFRemet>{{ total.vICMSUFRemet }}</vICMSUFRemet>
<vFCP>{{ total.vFCP }}</vFCP>
<vBCST>{{ total.vBCST }}</vBCST>
<vST>{{ total.vST }}</vST>
<vFCPST>{{ total.vFCPST }}</vFCPST>
<vFCPSTRet>{{ total.vFCPSTRet }}</vFCPSTRet>
<vProd>{{ total.vProd }}</vProd>
<vFrete>{{ total.vFrete }}</vFrete>
<vSeg>{{ total.vSeg }}</vSeg>
<vDesc>{{ total.vDesc }}</vDesc>
<vII>{{ total.vII }}</vII>
<vIPI>{{ total.vIPI }}</vIPI>
<vIPIDevol>{{ total.vIPIDevol }}</vIPIDevol>
<vPIS>{{ total.vPIS }}</vPIS>
<vCOFINS>{{ total.vCOFINS }}</vCOFINS>
<vOutro>{{ total.vOutro }}</vOutro>
@ -750,19 +820,24 @@
{% endif %}
{% endif %}
{% if NFe.infNFe.pag is defined %}
{% for pag in NFe.infNFe.pag %}
<pag>
<tPag>{{ pag.tPag }}</tPag>
<vPag>{{ pag.vPag }}</vPag>
{% if pag.card is defined %}
<card>
<CNPJ>{{ pag.card.CNPJ }}</CNPJ>
<tBand>{{ pag.card.tBand }}</tBand>
<cAut>{{ pag.card.cAut }}</cAut>
</card>
{% endif %}
{% for pag in NFe.infNFe.pag %}
<detPag>
<indPag>{{ pag.indPag }}</indPag>
<tPag>{{ pag.tPag }}</tPag>
<vPag>{{ pag.vPag }}</vPag>
{% if pag.card is defined %}
<card>
<tpIntegra>{{ pag.card.tpIntegra }}</tpIntegra>
<CNPJ>{{ pag.card.CNPJ }}</CNPJ>
<tBand>{{ pag.card.tBand }}</tBand>
<cAut>{{ pag.card.cAut }}</cAut>
</card>
{% endif %}
</detPag>
<vTroco>{{ pag.vTroco }}</vTroco>
{% endfor %}
</pag>
{% endfor %}
{% endif %}
<infAdic>
<infAdFisco>{{ NFe.infNFe.infAdic.infAdFisco|normalize|escape }}</infAdFisco>
@ -799,6 +874,10 @@
</compra>
{% endif %}
</infNFe>
<infNFeSupl>
<qrCode>{{ NFe.infNFe.qrCode }}</qrCode>
<urlChave>{{ NFe.infNFe.urlChave }}</urlChave>
</infNFeSupl>
</NFe>
{% endfor %}
</enviNFe>

5
pytrustnfe/nfe/templates/NfeConsultaProtocolo.xml

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<consSitNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="2.01">
<consSitNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="4.00">
<tpAmb>{{ obj.ambiente }}</tpAmb>
<xServ>CONSULTAR</xServ>
<chNFe>{{ obj.chave_nfe }}</chNFe>
</consSitNFe>
</consSitNFe>

2
pytrustnfe/nfe/templates/NfeInutilizacao.xml

@ -1,4 +1,4 @@
<inutNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="3.10">
<inutNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="4.00">
<infInut Id="{{ obj.id }}">
<tpAmb>{{ obj.ambiente }}</tpAmb>
<xServ>INUTILIZAR</xServ>

2
pytrustnfe/nfe/templates/NfeRetAutorizacao.xml

@ -1,4 +1,4 @@
<consReciNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="3.10">
<consReciNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="4.00">
<tpAmb>{{ obj.ambiente }}</tpAmb>
<nRec>{{ obj.numero_recibo }}</nRec>
</consReciNFe>

4
pytrustnfe/nfe/templates/NfeStatusServico.xml

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<consStatServ xmlns="http://www.portalfiscal.inf.br/nfe" versao="3.10">
<consStatServ xmlns="http://www.portalfiscal.inf.br/nfe" versao="4.00">
<tpAmb>{{ obj.ambiente }}</tpAmb>
<cUF>{{ obj.estado }}</cUF>
<xServ>STATUS</xServ>
</consStatServ>
</consStatServ>

6
pytrustnfe/nfe/templates/RecepcaoEventoCancelamento.xml → pytrustnfe/nfe/templates/RecepcaoEvento.xml

@ -8,13 +8,15 @@
<CNPJ>{{ evento.CNPJ }}</CNPJ>
<chNFe>{{ evento.chNFe }}</chNFe>
<dhEvento>{{ evento.dhEvento }}</dhEvento>
<tpEvento>110111</tpEvento>
<tpEvento>{{ evento.tpEvento }}</tpEvento>
<nSeqEvento>{{ evento.nSeqEvento }}</nSeqEvento>
<verEvento>1.00</verEvento>
<detEvento versao="1.00">
<descEvento>Cancelamento</descEvento>
<descEvento>{{ evento.descEvento }}</descEvento>
<nProt>{{ evento.nProt }}</nProt>
<xJust>{{ evento.xJust|normalize|escape }}</xJust>
<xCorrecao>{{ evento.xCorrecao|normalize|escape }}</xCorrecao>
<xCondUso>{{ evento.xCondUso }}</xCondUso>
</detEvento>
</infEvento>
</evento>

21
pytrustnfe/nfe/templates/RecepcaoEventoCarta.xml

@ -1,21 +0,0 @@
<envEvento xmlns="http://www.portalfiscal.inf.br/nfe" versao="1.00">
<idLote>{{ idLote }}</idLote>
<evento xmlns="http://www.portalfiscal.inf.br/nfe" versao="1.00">
<infEvento Id="{{ Id }}">
<cOrgao>{{ cOrgao }}</cOrgao>
<tpAmb>{{ tpAmb }}</tpAmb>
<CNPJ>{{ CNPJ }}</CNPJ>
<CPF>{{ CPF }}</CPF>
<chNFe>{{ chNFe }}</chNFe>
<dhEvento>{{ dhEvento }}</dhEvento>
<tpEvento>{{ tpEvento }}</tpEvento>
<nSeqEvento>{{ nSeqEvento }}</nSeqEvento>
<verEvento>1.00</verEvento>
<detEvento versao="1.00">
<descEvento>Carta de Correção</descEvento>
<xCorrecao>{{ xCorrecao|normalize|escape }}</xCorrecao>
<xCondUso>A Carta de Correção é disciplinada pelo § 1º-A do art. 7º do Convênio S/N, de 15 de dezembro de 1970 e pode ser utilizada para regularização de erro ocorrido na emissão de documento fiscal, desde que o erro não esteja relacionado com: I - as variáveis que determinam o valor do imposto tais como: base de cálculo, alíquota, diferença de preço, quantidade, valor da operação ou da prestação; II - a correção de dados cadastrais que implique mudança do remetente ou do destinatário; III - a data de emissão ou de saída.</xCondUso>
</detEvento>
</infEvento>
</evento>
</envEvento>

31
pytrustnfe/nfe/templates/RecepcaoEventoEPEC.xml

@ -1,31 +0,0 @@
<envEvento xmlns="http://www.portalfiscal.inf.br/nfe" versao="1.00">
<idLote>00106151340701</idLote>
<evento xmlns="http://www.portalfiscal.inf.br/nfe" versao="1.00">
<infEvento Id="ID1101403514081014278500019055001001061513407959995201">
<cOrgao>91</cOrgao>
<tpAmb>2</tpAmb>
<CNPJ>10142785000190</CNPJ>
<chNFe>35140810142785000190550010010615134079599952</chNFe>
<dhEvento>2014-08-07T04:52:51-03:00</dhEvento>
<tpEvento>110140</tpEvento>
<nSeqEvento>1</nSeqEvento>
<verEvento>1.00</verEvento>
<detEvento versao="1.00">
<descEvento>EPEC</descEvento>
<cOrgaoAutor>35</cOrgaoAutor>
<tpAutor>1</tpAutor>
<verAplic>1.26</verAplic>
<dhEmi>2014-08-07T00:00:00-03:00</dhEmi>
<tpNF>1</tpNF>
<IE>495171423115</IE>
<dest>
<UF>SP</UF>
<CPF>00000000191</CPF>
<vNF>86.00</vNF>
<vICMS>6.02</vICMS>
<vST>0</vST>
</dest>
</detEvento>
</infEvento>
</evento>
</envEvento>

2
pytrustnfe/nfe/templates/RecepcaoEventoManifesto.xml

@ -1,7 +1,7 @@
<envEvento xmlns="http://www.portalfiscal.inf.br/nfe" versao="1.00">
<idLote>{{ lote }}</idLote>
<evento xmlns="http://www.portalfiscal.inf.br/nfe" versao="1.00">
<infEvento Id="{{ manifesto.identificador }}">
<infEvento Id="{{ evento.Id }}">
<cOrgao>91</cOrgao>
<tpAmb>{{ ambiente }}</tpAmb>
<CNPJ>{{ manifesto.cnpj_empresa }}</CNPJ>

90
pytrustnfe/nfse/assinatura.py

@ -2,86 +2,52 @@
# © 2016 Danimar Ribeiro, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from lxml import etree
import xmlsec
import libxml2
import os.path
consts = xmlsec.constants
NAMESPACE_SIG = 'http://www.w3.org/2000/09/xmldsig#'
class Assinatura(object):
def __init__(self, arquivo, senha):
self.arquivo = arquivo
self.senha = senha
def __init__(self, cert_pem, private_key, password):
self.cert_pem = cert_pem
self.private_key = private_key
self.password = password
def _checar_certificado(self):
if not os.path.isfile(self.arquivo):
if not os.path.isfile(self.private_key):
raise Exception('Caminho do certificado não existe.')
def _inicializar_cripto(self):
libxml2.initParser()
libxml2.substituteEntitiesDefault(1)
xmlsec.init()
xmlsec.cryptoAppInit(None)
xmlsec.cryptoInit()
def _finalizar_cripto(self):
xmlsec.cryptoShutdown()
xmlsec.cryptoAppShutdown()
xmlsec.shutdown()
libxml2.cleanupParser()
def assina_xml(self, xml, reference):
self._checar_certificado()
self._inicializar_cripto()
try:
doc_xml = libxml2.parseMemory(
xml, len(xml))
signNode = xmlsec.TmplSignature(doc_xml,
xmlsec.transformInclC14NId(),
xmlsec.transformRsaSha1Id(), None)
doc_xml.getRootElement().addChild(signNode)
refNode = signNode.addReference(xmlsec.transformSha1Id(),
None, reference, None)
template = etree.fromstring(xml)
refNode.addTransform(xmlsec.transformEnvelopedId())
refNode.addTransform(xmlsec.transformInclC14NId())
keyInfoNode = signNode.ensureKeyInfo()
keyInfoNode.addX509Data()
key = xmlsec.Key.from_file(
self.private_key, format=xmlsec.constants.KeyDataFormatPem,
password=self.password)
dsig_ctx = xmlsec.DSigCtx()
chave = xmlsec.cryptoAppKeyLoad(filename=str(self.arquivo),
format=xmlsec.KeyDataFormatPkcs12,
pwd=str(self.senha),
pwdCallback=None,
pwdCallbackCtx=None)
signature_node = xmlsec.template.create(
template, c14n_method=consts.TransformInclC14N,
sign_method=consts.TransformRsaSha1)
template.append(signature_node)
ref = xmlsec.template.add_reference(
signature_node, consts.TransformSha1, uri='')
dsig_ctx.signKey = chave
dsig_ctx.sign(signNode)
xmlsec.template.add_transform(ref, consts.TransformEnveloped)
xmlsec.template.add_transform(ref, consts.TransformInclC14N)
status = dsig_ctx.status
dsig_ctx.destroy()
ki = xmlsec.template.ensure_key_info(signature_node)
xmlsec.template.add_x509_data(ki)
if status != xmlsec.DSigStatusSucceeded:
raise RuntimeError(
'Erro ao realizar a assinatura do arquivo; status: "' +
str(status) +
'"')
ctx = xmlsec.SignatureContext()
ctx.key = key
xpath = doc_xml.xpathNewContext()
xpath.xpathRegisterNs('sig', NAMESPACE_SIG)
certificados = xpath.xpathEval(
'//sig:X509Data/sig:X509Certificate')
for i in range(len(certificados) - 1):
certificados[i].unlinkNode()
certificados[i].freeNode()
ctx.key.load_cert_from_file(
self.cert_pem, consts.KeyDataFormatPem)
xml = doc_xml.serialize()
return xml
finally:
doc_xml.freeDoc()
ctx.sign(signature_node)
return etree.tostring(template, encoding=str)

2
pytrustnfe/nfse/betha/__init__.py

@ -50,7 +50,7 @@ def _send(certificado, method, **kwargs):
try:
response = getattr(client.service, method)(1, xml_send)
except suds.WebFault, e:
except suds.WebFault as e:
return {
'sent_xml': xml_send,
'received_xml': e.fault.faultstring,

84
pytrustnfe/nfse/bh/__init__.py

@ -0,0 +1,84 @@
# © 2018 Danimar Ribeiro, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import os
from lxml import etree
from requests import Session
from zeep import Client
from zeep.transports import Transport
from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key
from pytrustnfe.xml import render_xml, sanitize_response
from pytrustnfe.nfse.bh.assinatura import Assinatura
def _render(certificado, method, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates')
xml_send = render_xml(path, '%s.xml' % method, True, **kwargs)
reference = ''
ref_lote = ''
if method == 'GerarNfse':
reference = 'rps:%s' % kwargs['rps']['numero']
ref_lote = 'lote%s' % kwargs['rps']['numero_lote']
elif method == 'CancelarNfse':
reference = 'pedidoCancelamento_%s' % kwargs['cancelamento']['numero_nfse']
signer = Assinatura(certificado.pfx, certificado.password)
xml_send = signer.assina_xml(xml_send, reference)
if ref_lote:
xml_send = signer.assina_xml(etree.fromstring(xml_send), ref_lote)
return xml_send.encode('utf-8')
def _send(certificado, method, **kwargs):
base_url = ''
if kwargs['ambiente'] == 'producao':
base_url = 'https://bhissdigital.pbh.gov.br/bhiss-ws/nfse?wsdl'
else:
base_url = 'https://bhisshomologa.pbh.gov.br/bhiss-ws/nfse?wsdl'
xml_send = kwargs["xml"].decode('utf-8')
xml_cabecalho = '<?xml version="1.0" encoding="UTF-8"?>\
<cabecalho xmlns="http://www.abrasf.org.br/nfse.xsd" versao="1.00">\
<versaoDados>1.00</versaoDados></cabecalho>'
cert, key = extract_cert_and_key_from_pfx(
certificado.pfx, certificado.password)
cert, key = save_cert_key(cert, key)
session = Session()
session.cert = (cert, key)
session.verify = False
transport = Transport(session=session)
client = Client(base_url, transport=transport)
response = client.service[method](xml_cabecalho, xml_send)
response, obj = sanitize_response(response)
return {
'sent_xml': xml_send,
'received_xml': response,
'object': obj
}
def xml_gerar_nfse(certificado, **kwargs):
return _render(certificado, 'GerarNfse', **kwargs)
def gerar_nfse(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_gerar_nfse(certificado, **kwargs)
return _send(certificado, 'GerarNfse', **kwargs)
def xml_cancelar_nfse(certificado, **kwargs):
return _render(certificado, 'CancelarNfse', **kwargs)
def cancelar_nfse(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_cancelar_nfse(certificado, **kwargs)
return _send(certificado, 'CancelarNfse', **kwargs)

44
pytrustnfe/nfse/bh/assinatura.py

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
# © 2016 Danimar Ribeiro, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import signxml
from lxml import etree
from pytrustnfe.certificado import extract_cert_and_key_from_pfx
from signxml import XMLSigner
class Assinatura(object):
def __init__(self, arquivo, senha):
self.arquivo = arquivo
self.senha = senha
def assina_xml(self, xml_element, reference):
cert, key = extract_cert_and_key_from_pfx(self.arquivo, self.senha)
for element in xml_element.iter("*"):
if element.text is not None and not element.text.strip():
element.text = None
signer = XMLSigner(
method=signxml.methods.enveloped, signature_algorithm="rsa-sha1",
digest_algorithm='sha1',
c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315')
ns = {}
ns[None] = signer.namespaces['ds']
signer.namespaces = ns
ref_uri = ('#%s' % reference) if reference else None
signed_root = signer.sign(
xml_element, key=key.encode(), cert=cert.encode(),
reference_uri=ref_uri)
if reference:
element_signed = signed_root.find(".//*[@Id='%s']" % reference)
signature = signed_root.find(".//*[@URI='#%s']" % reference).getparent().getparent()
if element_signed is not None and signature is not None:
parent = element_signed.getparent()
parent.append(signature)
return etree.tostring(signed_root, encoding=str)

13
pytrustnfe/nfse/bh/templates/CancelarNfse.xml

@ -0,0 +1,13 @@
<CancelarNfseEnvio xmlns="http://www.abrasf.org.br/nfse.xsd">
<Pedido xmlns="http://www.abrasf.org.br/nfse.xsd">
<InfPedidoCancelamento Id="pedidoCancelamento_{{ cancelamento.numero_nfse }}">
<IdentificacaoNfse>
<Numero>{{ cancelamento.numero_nfse }}</Numero>
<Cnpj>{{ cancelamento.cnpj_prestador }}</Cnpj>
<InscricaoMunicipal>{{ cancelamento.inscricao_municipal }}</InscricaoMunicipal>
<CodigoMunicipio>{{ cancelamento.cidade }}</CodigoMunicipio>
</IdentificacaoNfse>
<CodigoCancelamento>2</CodigoCancelamento>
</InfPedidoCancelamento>
</Pedido>
</CancelarNfseEnvio>

11
pytrustnfe/nfse/bh/templates/GerarNfse.xml

@ -0,0 +1,11 @@
<GerarNfseEnvio xmlns="http://www.abrasf.org.br/nfse.xsd">
<LoteRps Id="lote{{ rps.numero_lote }}" versao="1.00">
<NumeroLote>{{ rps.numero_lote }}</NumeroLote>
<Cnpj>{{ rps.prestador.cnpj }}</Cnpj>
<InscricaoMunicipal>{{ rps.prestador.inscricao_municipal }}</InscricaoMunicipal>
<QuantidadeRps>1</QuantidadeRps>
<ListaRps xmlns="http://www.abrasf.org.br/nfse.xsd">
{% include 'Rps.xml' %}
</ListaRps>
</LoteRps>
</GerarNfseEnvio>

91
pytrustnfe/nfse/bh/templates/Rps.xml

@ -0,0 +1,91 @@
<Rps>
<InfRps xmlns="http://www.abrasf.org.br/nfse.xsd" Id="rps:{{ rps.numero }}">
<IdentificacaoRps>
<Numero>{{ rps.numero }}</Numero>
<Serie>{{ rps.serie }}</Serie>
<Tipo>{{ rps.tipo_rps }}</Tipo>
</IdentificacaoRps>
<DataEmissao>{{ rps.data_emissao }}</DataEmissao>
<NaturezaOperacao>{{ rps.natureza_operacao }}</NaturezaOperacao>
<RegimeEspecialTributacao>{{ rps.regime_tributacao }}</RegimeEspecialTributacao>
<OptanteSimplesNacional>{{ rps.optante_simples }}</OptanteSimplesNacional>
<IncentivadorCultural>{{ rps.incentivador_cultural }}</IncentivadorCultural>
<Status>{{ rps.status }}</Status>
<RpsSubstituido>
<Numero>{{ rps.numero_substituido }}</Numero>
<Serie>{{ rps.serie_substituido }}</Serie>
<Tipo>{{ rps.tipo_substituido }}</Tipo>
</RpsSubstituido>
<Servico>
<Valores>
<ValorServicos>{{ rps.valor_servico }}</ValorServicos>
<ValorDeducoes>{{ rps.valor_deducao }}</ValorDeducoes>
<ValorPis>{{ rps.valor_pis }}</ValorPis>
<ValorCofins>{{ rps.valor_cofins }}</ValorCofins>
<ValorInss>{{ rps.valor_inss }}</ValorInss>
<ValorIr>{{ rps.valor_ir }}</ValorIr>
<ValorCsll>{{ rps.valor_csll }}</ValorCsll>
<IssRetido>{{ rps.iss_retido }}</IssRetido>
<ValorIss>{{ rps.valor_iss }}</ValorIss>
<ValorIssRetido>{{ rps.valor_iss_retido }}</ValorIssRetido>
<OutrasRetencoes>{{ rps.outras_retencoes }}</OutrasRetencoes>
<BaseCalculo>{{ rps.base_calculo }}</BaseCalculo>
<Aliquota>{{ rps.aliquota_issqn }}</Aliquota>
<ValorLiquidoNfse>{{ rps.valor_liquido_nfse }}</ValorLiquidoNfse>
<DescontoIncondicionado>{{ rps.desconto_incondicionado }}</DescontoIncondicionado>
<DescontoCondicionado>{{ rps.desconto_condicionado }}</DescontoCondicionado>
</Valores>
<ItemListaServico>{{ rps.codigo_servico }}</ItemListaServico>
<CodigoCnae>{{ rps.cnae_servico }}</CodigoCnae>
<CodigoTributacaoMunicipio>{{ rps.codigo_tributacao_municipio }}</CodigoTributacaoMunicipio>
<Discriminacao>{{ rps.descricao }}</Discriminacao>
<CodigoMunicipio>{{ rps.codigo_municipio }}</CodigoMunicipio>
</Servico>
<Prestador>
<Cnpj>{{ rps.prestador.cnpj }}</Cnpj>
<InscricaoMunicipal>{{ rps.prestador.inscricao_municipal }}</InscricaoMunicipal>
</Prestador>
<Tomador>
<IdentificacaoTomador>
<CpfCnpj>
{% if rps.tomador.cnpj_cpf|length == 14 %}
<Cnpj>{{ rps.tomador.cnpj_cpf }}</Cnpj>
{% endif %}
{% if rps.tomador.cnpj_cpf|length == 11 %}
<Cpf>{{ rps.tomador.cnpj_cpf }}</Cpf>
{% endif %}
</CpfCnpj>
<InscricaoMunicipal>{{ rps.tomador.inscricao_municipal }}</InscricaoMunicipal>
</IdentificacaoTomador>
<RazaoSocial>{{ rps.tomador.razao_social }}</RazaoSocial>
<Endereco>
<Endereco>{{ rps.tomador.logradouro }}</Endereco>
<Numero>{{ rps.tomador.numero }}</Numero>
<Complemento>{{ rps.tomador.complemento }}</Complemento>
<Bairro>{{ rps.tomador.bairro }}</Bairro>
<CodigoMunicipio>{{ rps.tomador.cidade }}</CodigoMunicipio>
<Uf>{{ rps.tomador.uf }}</Uf>
<Cep>{{ rps.tomador.cep }}</Cep>
</Endereco>
<Contato>
<Telefone>{{ rps.tomador.telefone }}</Telefone>
<Email>{{ rps.tomador.email }}</Email>
</Contato>
</Tomador>
{% if rps.intermediario is defined -%}
<IntermediarioServico>
<RazaoSocial>{{ rps.intermediario.razao_social }}</RazaoSocial>
<CpfCnpj>
<Cnpj>{{ rps.intermediario.cnpj }}</Cnpj>
</CpfCnpj>
<InscricaoMunicipal>{{ rps.intermediario.inscricao_municipal }}</InscricaoMunicipal>
</IntermediarioServico>
{% endif %}
{% if rps.construcao_civil is defined -%}
<ContrucaoCivil>
<CodigoObra>{{ rps.construcao_civil.codigo_obra }}</CodigoObra>
<Art>{{ rps.construcao_civil.art }}</Art>
</ContrucaoCivil>
{% endif %}
</InfRps>
</Rps>

74
pytrustnfe/nfse/carioca/__init__.py

@ -0,0 +1,74 @@
# © 2018 Danimar Ribeiro, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import os
import suds
from pytrustnfe.client import get_authenticated_client
from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key
from pytrustnfe.xml import render_xml, sanitize_response
from pytrustnfe.nfe.assinatura import Assinatura
def _render(certificado, method, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates')
xml_send = render_xml(path, '%s.xml' % method, True, **kwargs)
reference = ''
if method == 'GerarNfse':
reference = 'r%s' % kwargs['rps']['numero']
elif method == 'CancelarNfse':
reference = 'Cancelamento_NF%s' % kwargs['cancelamento']['numero_nfse']
signer = Assinatura(certificado.pfx, certificado.password)
xml_send = signer.assina_xml(xml_send, reference)
return xml_send.encode('utf-8')
def _send(certificado, method, **kwargs):
base_url = ''
if kwargs['ambiente'] == 'producao':
base_url = 'https://notacarioca.rio.gov.br/WSNacional/nfse.asmx?wsdl'
else:
base_url = 'https://homologacao.notacarioca.rio.gov.br/WSNacional/nfse.asmx?wsdl' # noqa
xml_send = kwargs["xml"].decode('utf-8')
cert, key = extract_cert_and_key_from_pfx(
certificado.pfx, certificado.password)
cert, key = save_cert_key(cert, key)
client = get_authenticated_client(base_url, cert, key)
try:
response = getattr(client.service, method)(xml_send)
except suds.WebFault as e:
return {
'sent_xml': str(xml_send),
'received_xml': str(e.fault.faultstring),
'object': None
}
response, obj = sanitize_response(response)
return {
'sent_xml': str(xml_send),
'received_xml': str(response),
'object': obj
}
def xml_gerar_nfse(certificado, **kwargs):
return _render(certificado, 'GerarNfse', **kwargs)
def gerar_nfse(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_gerar_nfse(certificado, **kwargs)
return _send(certificado, 'GerarNfse', **kwargs)
def xml_cancelar_nfse(certificado, **kwargs):
return _render(certificado, 'CancelarNfse', **kwargs)
def cancelar_nfse(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_cancelar_nfse(certificado, **kwargs)
return _send(certificado, 'CancelarNfse', **kwargs)

13
pytrustnfe/nfse/carioca/templates/CancelarNfse.xml

@ -0,0 +1,13 @@
<CancelarNfseEnvio xmlns="http://www.abrasf.org.br/ABRASF/arquivos/nfse.xsd">
<Pedido>
<InfPedidoCancelamento Id="Cancelamento_NF{{ cancelamento.numero_nfse }}">
<IdentificacaoNfse>
<Numero>{{ cancelamento.numero_nfse }}</Numero>
<Cnpj>{{ cancelamento.cnpj_prestador }}</Cnpj>
<InscricaoMunicipal>{{ cancelamento.inscricao_municipal }}</InscricaoMunicipal>
<CodigoMunicipio>{{ cancelamento.cidade }}</CodigoMunicipio>
</IdentificacaoNfse>
<CodigoCancelamento>1</CodigoCancelamento>
</InfPedidoCancelamento>
</Pedido>
</CancelarNfseEnvio>

3
pytrustnfe/nfse/carioca/templates/GerarNfse.xml

@ -0,0 +1,3 @@
<GerarNfseEnvio xmlns="http://notacarioca.rio.gov.br/WSNacional/XSD/1/nfse_pcrj_v01.xsd">
{% include 'Rps.xml' %}
</GerarNfseEnvio>

91
pytrustnfe/nfse/carioca/templates/Rps.xml

@ -0,0 +1,91 @@
<Rps>
<InfRps xmlns="http://www.abrasf.org.br/ABRASF/arquivos/nfse.xsd" Id="r{{ rps.numero }}">
<IdentificacaoRps>
<Numero>{{ rps.numero }}</Numero>
<Serie>{{ rps.serie }}</Serie>
<Tipo>{{ rps.tipo_rps }}</Tipo>
</IdentificacaoRps>
<DataEmissao>{{ rps.data_emissao }}</DataEmissao>
<NaturezaOperacao>{{ rps.natureza_operacao }}</NaturezaOperacao>
<RegimeEspecialTributacao>{{ rps.regime_tributacao }}</RegimeEspecialTributacao>
<OptanteSimplesNacional>{{ rps.optante_simples }}</OptanteSimplesNacional>
<IncentivadorCultural>{{ rps.incentivador_cultural }}</IncentivadorCultural>
<Status>{{ rps.status }}</Status>
<RpsSubstituido>
<Numero>{{ rps.numero_substituido }}</Numero>
<Serie>{{ rps.serie_substituido }}</Serie>
<Tipo>{{ rps.tipo_substituido }}</Tipo>
</RpsSubstituido>
<Servico>
<Valores>
<ValorServicos>{{ rps.valor_servico }}</ValorServicos>
<ValorDeducoes>{{ rps.valor_deducao }}</ValorDeducoes>
<ValorPis>{{ rps.valor_pis }}</ValorPis>
<ValorCofins>{{ rps.valor_cofins }}</ValorCofins>
<ValorInss>{{ rps.valor_inss }}</ValorInss>
<ValorIr>{{ rps.valor_ir }}</ValorIr>
<ValorCsll>{{ rps.valor_csll }}</ValorCsll>
<IssRetido>{{ rps.iss_retido }}</IssRetido>
<ValorIss>{{ rps.valor_iss }}</ValorIss>
<ValorIssRetido>{{ rps.valor_iss_retido }}</ValorIssRetido>
<OutrasRetencoes>{{ rps.outras_retencoes }}</OutrasRetencoes>
<BaseCalculo>{{ rps.base_calculo }}</BaseCalculo>
<Aliquota>{{ rps.aliquota_issqn }}</Aliquota>
<ValorLiquidoNfse>{{ rps.valor_liquido_nfse }}</ValorLiquidoNfse>
<DescontoIncondicionado>{{ rps.desconto_incondicionado }}</DescontoIncondicionado>
<DescontoCondicionado>{{ rps.desconto_condicionado }}</DescontoCondicionado>
</Valores>
<ItemListaServico>{{ rps.codigo_servico }}</ItemListaServico>
<CodigoCnae>{{ rps.cnae_servico }}</CodigoCnae>
<CodigoTributacaoMunicipio>{{ rps.codigo_tributacao_municipio }}</CodigoTributacaoMunicipio>
<Discriminacao>{{ rps.descricao }}</Discriminacao>
<CodigoMunicipio>{{ rps.codigo_municipio }}</CodigoMunicipio>
</Servico>
<Prestador>
<Cnpj>{{ rps.prestador.cnpj }}</Cnpj>
<InscricaoMunicipal>{{ rps.prestador.inscricao_municipal }}</InscricaoMunicipal>
</Prestador>
<Tomador>
<IdentificacaoTomador>
<CpfCnpj>
{% if rps.tomador.cnpj_cpf|length == 14 %}
<Cnpj>{{ rps.tomador.cnpj_cpf }}</Cnpj>
{% endif %}
{% if rps.tomador.cnpj_cpf|length == 11 %}
<Cpf>{{ rps.tomador.cnpj_cpf }}</Cpf>
{% endif %}
</CpfCnpj>
<InscricaoMunicipal>{{ rps.tomador.inscricao_municipal }}</InscricaoMunicipal>
</IdentificacaoTomador>
<RazaoSocial>{{ rps.tomador.razao_social }}</RazaoSocial>
<Endereco>
<Endereco>{{ rps.tomador.logradouro }}</Endereco>
<Numero>{{ rps.tomador.numero }}</Numero>
<Complemento>{{ rps.tomador.complemento }}</Complemento>
<Bairro>{{ rps.tomador.bairro }}</Bairro>
<CodigoMunicipio>{{ rps.tomador.cidade }}</CodigoMunicipio>
<Uf>{{ rps.tomador.uf }}</Uf>
<Cep>{{ rps.tomador.cep }}</Cep>
</Endereco>
<Contato>
<Telefone>{{ rps.tomador.telefone }}</Telefone>
<Email>{{ rps.tomador.email }}</Email>
</Contato>
</Tomador>
{% if rps.intermediario is defined -%}
<IntermediarioServico>
<RazaoSocial>{{ rps.intermediario.razao_social }}</RazaoSocial>
<CpfCnpj>
<Cnpj>{{ rps.intermediario.cnpj }}</Cnpj>
</CpfCnpj>
<InscricaoMunicipal>{{ rps.intermediario.inscricao_municipal }}</InscricaoMunicipal>
</IntermediarioServico>
{% endif %}
{% if rps.construcao_civil is defined -%}
<ContrucaoCivil>
<CodigoObra>{{ rps.construcao_civil.codigo_obra }}</CodigoObra>
<Art>{{ rps.construcao_civil.art }}</Art>
</ContrucaoCivil>
{% endif %}
</InfRps>
</Rps>

131
pytrustnfe/nfse/dsf/__init__.py

@ -0,0 +1,131 @@
# -*- 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.decode()
def _get_url(**kwargs):
try:
cod_cidade = kwargs['nfse']['cidade']
except (KeyError, TypeError):
raise KeyError("Código de cidade inválido!")
urls = {
# Belém - PA
'2715': 'http://www.issdigitalbel.com.br/WsNFe2/LoteRps.jws?wsdl',
# Sorocaba - SP
'7145': 'http://issdigital.sorocaba.sp.gov.br/WsNFe2/LoteRps.jws?wsdl',
# Teresina - PI
'1219': 'http://www.issdigitalthe.com.br/WsNFe2/LoteRps.jws?wsdl',
# Campinas - SP
'6291': 'http://issdigital.campinas.sp.gov.br/WsNFe2/LoteRps.jws?wsdl',
# Uberlandia - MG
'5403': 'http://udigital.uberlandia.mg.gov.br/WsNFe2/LoteRps.jws?wsdl',
# São Luis - MA
'0921':
'http://sistemas.semfaz.saoluis.ma.gov.br/WsNFe2/LoteRps.jws?wsdl',
# Campo Grande - MS
'2729': 'http://issdigital.pmcg.ms.gov.br/WsNFe2/LoteRps.jws?wsdl',
}
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):
url = _get_url(**kwargs)
path = os.path.join(os.path.dirname(__file__), 'templates')
xml_send = _render(path, method, **kwargs)
client = get_client(url)
response = False
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
}
except Exception as e:
if response:
raise Exception(response)
else:
raise e
return {
'sent_xml': xml_send,
'received_xml': response,
'object': obj
}
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)
def cancelar(certificado, ** kwargs):
return _send(certificado, 'cancelar', **kwargs)
def consulta_lote(**kwargs):
return _send(False, 'consultarLote', **kwargs)
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)

18
pytrustnfe/nfse/dsf/templates/cancelar.xml

@ -0,0 +1,18 @@
<ns1:ReqCancelamentoNFSe xmlns:ns1="http://localhost:8080/WsNFe2/lote"
xmlns:tipos="http://localhost:8080/WsNFe2/tp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://localhost:8080/WsNFe2/lote http://localhost:8080/WsNFe2/xsd/ReqCancelamentoNFSe.xsd">
<Cabecalho>
<CodCidade>{{ cancelamento.cidade }}</CodCidade>
<CPFCNPJRemetente>{{ cancelamento.cpf_cnpj }}</CPFCNPJRemetente>
<transacao>true</transacao>
<Versao>1</Versao>
</Cabecalho>
<Lote Id="lote:1ABCDZ">
<Nota Id="nota:{{ cancelamento.nota_id }}">
<InscricaoMunicipalPrestador>{{ cancelamento.inscricao_municipal }}</InscricaoMunicipalPrestador>
<NumeroNota>{{ cancelamento.nota_id }}</NumeroNota>
<CodigoVerificacao>{{ cancelamento.assinatura }}</CodigoVerificacao>
<MotivoCancelamento>{{ cancelamento.motivo }}</MotivoCancelamento>
</Nota>
</Lote>
</ns1:ReqCancelamentoNFSe>

11
pytrustnfe/nfse/dsf/templates/consulta_notas.xml

@ -0,0 +1,11 @@
<ns1:ReqConsultaNotas xmlns:ns1="http://localhost:8080/WsNFe2/lote" xmlns:tipos="http://localhost:8080/WsNFe2/tp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://localhost:8080/WsNFe2/lote http://localhost:8080/WsNFe2/xsd/ReqConsultaNotas.xsd">
<Cabecalho Id="Consulta:notas">
<CodCidade>{{ consulta.cidade }}</CodCidade>
<CPFCNPJRemetente>{{ consulta.cpf_cnpj }}</CPFCNPJRemetente>
<InscricaoMunicipalPrestador>{{ consulta.inscricao_municipal }}</InscricaoMunicipalPrestador>
<dtInicio>{{ consulta.data_inicio }}</dtInicio>
<dtFim>{{ consulta.data_final }}</dtFim>
<NotaInicial>{{ consulta.nota_inicial }}</NotaInicial>
<Versao>1</Versao>
</Cabecalho>
</ns1:ReqConsultaNotas>

10
pytrustnfe/nfse/dsf/templates/consultarLote.xml

@ -0,0 +1,10 @@
<ns1:ReqConsultaLote xmlns:ns1="http://localhost:8080/WsNFe2/lote"
xmlns:tipos="http://localhost:8080/WsNFe2/tp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://localhost:8080/WsNFe2/lote http://localhost:8080/WsNFe2/xsd/ReqConsultaLote.xsd">
<Cabecalho>
<CodCidade>{{ consulta.cidade }}</CodCidade>
<CPFCNPJRemetente>{{ consulta.cpf_cnpj }}</CPFCNPJRemetente>
<Versao>1</Versao>
<NumeroLote>{{ consulta.lote }}</NumeroLote>
</Cabecalho>
</ns1:ReqConsultaLote>

22
pytrustnfe/nfse/dsf/templates/consultarNFSeRps.xml

@ -0,0 +1,22 @@
<ns1:ReqConsultaNFSeRPS
xmlns:ns1="http://localhost:8080/WsNFe2/lote"
xmlns:tipos="http://localhost:8080/WsNFe2/tp"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://localhost:8080/WsNFe2/lote http://localhost:8080/WsNFe2/xsd/ReqConsultaNFSeRPS.xsd">
<Cabecalho>
<CodCidade>{{ nfse.cidade }}</CodCidade>
<CPFCNPJRemetente>{{ nfse.cpf_cnpj }}</CPFCNPJRemetente>
<transacao>true</transacao>
<Versao>1</Versao>
</Cabecalho>
<Lote Id="lote:{{ nfse.lote }}">
{% for rps in nfse.lista_rps -%}
<RPSConsulta>
<RPS Id="rps:{{ rps.numero }}">
<InscricaoMunicipalPrestador>{{ rps.prestador.inscricao_municipal }}</InscricaoMunicipalPrestador>
<NumeroRPS>{{ rps.numero }}</NumeroRPS>
<SeriePrestacao>{{ rps.serie_prestacao }}</SeriePrestacao>
</RPS>
</RPSConsulta>
{% endfor %}
</Lote>
</ns1:ReqConsultaNFSeRPS>

100
pytrustnfe/nfse/dsf/templates/enviar.xml

@ -0,0 +1,100 @@
<ns1:ReqEnvioLoteRPS xmlns:ns1="http://localhost:8080/WsNFe2/lote"
xmlns:tipos="http://localhost:8080/WsNFe2/tp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://localhost:8080/WsNFe2/lote http://localhost:8080/WsNFe2/xsd/ReqEnvioLoteRPS.xsd">
<Cabecalho>
<CodCidade>{{ nfse.cidade }}</CodCidade>
<CPFCNPJRemetente>{{ nfse.cpf_cnpj }}</CPFCNPJRemetente>
<RazaoSocialRemetente>{{ nfse.remetente }}</RazaoSocialRemetente>
<transacao>{{ nfse.transacao }}</transacao>
<dtInicio>{{ nfse.data_inicio|format_date }}</dtInicio>
<dtFim>{{ nfse.data_fim|format_date }}</dtFim>
<QtdRPS>{{ nfse.total_rps }}</QtdRPS>
<ValorTotalServicos>{{ nfse.total_servicos }}</ValorTotalServicos>
<ValorTotalDeducoes>{{ nfse.total_deducoes }}</ValorTotalDeducoes>
<Versao>1</Versao>
<MetodoEnvio>WS</MetodoEnvio>
</Cabecalho>
<Lote Id="{{ nfse.lote_id }}">
{% for rps in nfse.lista_rps -%}
<RPS Id="{{ rps.numero }}">
<Assinatura>{{ rps.assinatura }}</Assinatura>
<InscricaoMunicipalPrestador>{{ rps.prestador.inscricao_municipal }}</InscricaoMunicipalPrestador>
<RazaoSocialPrestador>{{ rps.prestador.razao_social }}</RazaoSocialPrestador>
<TipoRPS>RPS</TipoRPS>
<SerieRPS>{{ rps.serie }}</SerieRPS>
<NumeroRPS>{{ rps.numero }}</NumeroRPS>
<DataEmissaoRPS>{{ rps.data_emissao|format_datetime }}</DataEmissaoRPS>
<SituacaoRPS>{{ rps.situacao }}</SituacaoRPS>
<SerieRPSSubstituido></SerieRPSSubstituido>
<NumeroRPSSubstituido>0</NumeroRPSSubstituido>
<NumeroNFSeSubstituida>0</NumeroNFSeSubstituida>
<DataEmissaoNFSeSubstituida>1900-01-01</DataEmissaoNFSeSubstituida>
<SeriePrestacao>{{ rps.serie_prestacao }}</SeriePrestacao>
<InscricaoMunicipalTomador>{{ rps.tomador.inscricao_municipal }}</InscricaoMunicipalTomador>
<CPFCNPJTomador>{{ rps.tomador.cpf_cnpj }}</CPFCNPJTomador>
<RazaoSocialTomador>{{ rps.tomador.razao_social }}</RazaoSocialTomador>
<TipoLogradouroTomador>{{ rps.tomador.tipo_logradouro }}</TipoLogradouroTomador>
<LogradouroTomador>{{ rps.tomador.logradouro }}</LogradouroTomador>
<NumeroEnderecoTomador>{{ rps.tomador.numero }}</NumeroEnderecoTomador>
<TipoBairroTomador>{{ rps.tomador.tipo_bairro }}</TipoBairroTomador>
<BairroTomador>{{ rps.tomador.bairro }}</BairroTomador>
<CidadeTomador>{{ rps.tomador.cidade }}</CidadeTomador>
<CidadeTomadorDescricao>{{ rps.tomador.cidade_descricao }}</CidadeTomadorDescricao>
<CEPTomador>{{ rps.tomador.cep }}</CEPTomador>
<EmailTomador>{{ rps.tomador.email }}</EmailTomador>
<CodigoAtividade>{{ rps.codigo_atividade }}</CodigoAtividade>
<AliquotaAtividade>{{ rps.aliquota_atividade }}</AliquotaAtividade>
<TipoRecolhimento>{{ rps.tipo_recolhimento }}</TipoRecolhimento>
<MunicipioPrestacao>{{ rps.municipio_prestacao }}</MunicipioPrestacao>
<MunicipioPrestacaoDescricao>{{ rps.municipio_prestacao_descricao }}</MunicipioPrestacaoDescricao>
<Operacao>{{ rps.operacao }}</Operacao>
<Tributacao>{{ rps.tributacao }}</Tributacao>
<ValorPIS>{{ rps.valor_pis }}</ValorPIS>
<ValorCOFINS>{{ rps.valor_cofins }}</ValorCOFINS>
<ValorINSS>{{ rps.valor_inss }}</ValorINSS>
<ValorIR>{{ rps.valor_ir }}</ValorIR>
<ValorCSLL>{{ rps.valor_csll }}</ValorCSLL>
<AliquotaPIS>{{ rps.aliquota_pis }}</AliquotaPIS>
<AliquotaCOFINS>{{ rps.aliquota_cofins }}</AliquotaCOFINS>
<AliquotaINSS>{{ rps.aliquota_inss }}</AliquotaINSS>
<AliquotaIR>{{ rps.aliquota_ir }}</AliquotaIR>
<AliquotaCSLL>{{ rps.aliquota_csll }}</AliquotaCSLL>
<DescricaoRPS>{{ rps.descricao }}</DescricaoRPS>
<DDDPrestador>{{ rps.prestador.ddd }}</DDDPrestador>
<TelefonePrestador>{{ rps.prestador.telefone }}</TelefonePrestador>
<DDDTomador>{{ rps.tomador.ddd }}</DDDTomador>
<TelefoneTomador>{{ rps.tomador.telefone }}</TelefoneTomador>
<MotCancelamento>{{ rps.motivo_cancelamento }}</MotCancelamento>
{% if rps.deducoes|count > 0 %}
<Deducoes>
{% for deducao in rps.deducoes -%}
<Deducao>
<DeducaoPor>{{ deducao.por }}</DeducaoPor>
<TipoDeducao>{{ deducao.tipo }}</TipoDeducao>
<CPFCNPJReferencia>{{ deducao.cnpj_referencia }}</CPFCNPJReferencia>
<NumeroNFReferencia>{{ deducao.nf_referencia }}</NumeroNFReferencia>
<ValorTotalReferencia>{{ deducao.valor_referencia }}</ValorTotalReferencia>
<PercentualDeduzir>{{ deducao.percentual_deduzir }}</PercentualDeduzir>
<ValorDeduzir>{{ deducao.valor_deduzir }}</ValorDeduzir>
</Deducao>
{% endfor %}
</Deducoes>
{% endif %}
{% if rps.deducoes|count == 0 %}
<Deducoes />
{% endif %}
<Itens>
{% for item in rps.itens -%}
<Item>
<DiscriminacaoServico>{{ item.descricao }}</DiscriminacaoServico>
<Quantidade>{{ item.quantidade }}</Quantidade>
<ValorUnitario>{{ item.valor_unitario }}</ValorUnitario>
<ValorTotal>{{ item.valor_total }}</ValorTotal>
<Tributavel>S</Tributavel>
</Item>
{% endfor %}
</Itens>
</RPS>
{% endfor %}
</Lote>
</ns1:ReqEnvioLoteRPS>

12
pytrustnfe/nfse/dsf/templates/soap_header.xml

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:dsf="http://dsfnet.com.br">
<soapenv:Body>
<dsf:enviar soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<mensagemXml xsi:type="xsd:string"><![CDATA[
{% block content %}{% endblock %}
]]></mensagemXml>
</dsf:enviar>
</soapenv:Body>
</soapenv:Envelope>

118
pytrustnfe/nfse/floripa/__init__.py

@ -0,0 +1,118 @@
# -*- coding: utf-8 -*-
# © 2017 Danimar Ribeiro, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import os
import hashlib
import base64
import requests
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
URLS = {
'producao': {
'processar_nota': 'https://nfps-e.pmf.sc.gov.br/api/v1/processamento/notas/processa',
'cancelar_nota': 'https://nfps-e.pmf.sc.gov.br/api/v1/cancelamento/notas/cancela'
},
'homologacao': {
'processar_nota': 'https://nfps-e-hml.pmf.sc.gov.br/api/v1/processamento/notas/processa',
'cancelar_nota': 'https://nfps-e-hml.pmf.sc.gov.br/api/v1/cancelamento/notas/cancela'
}
}
def _render(certificado, method, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates')
xml_send = render_xml(path, '%s.xml' % method, False, **kwargs)
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, '')
return xml_send
def _get_oauth_token(**kwargs):
if kwargs['ambiente'] == 'producao':
url = 'https://nfps-e.pmf.sc.gov.br/api/v1/autenticacao/oauth/token'
else:
url = 'https://nfps-e-hml.pmf.sc.gov.br/api/v1/autenticacao/oauth/token'
m = hashlib.md5()
secret = "%s:%s" % (kwargs["client_id"], kwargs["secret_id"])
auth = base64.b64encode(secret.encode('utf-8'))
headers = {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": "Basic %s" % auth.decode('utf-8').replace('\n', '')
}
m.update(kwargs["password"].encode('utf-8'))
password = m.hexdigest().upper()
dados = "grant_type=password&username=%s&password=%s&client_id=%s&client_secret=%s" % (
kwargs["username"], password, kwargs["client_id"], kwargs["secret_id"])
r = requests.post(url, data=dados, headers=headers)
if r.status_code == 200:
return r.json()
else:
return r.json()
def _send(certificado, method, **kwargs):
url = URLS[kwargs['ambiente']][method]
xml_send = kwargs['xml']
token = _get_oauth_token(**kwargs)
if "access_token" not in token:
raise Exception("%s - %s: %s" % (token["status"], token["error"],
token["message"]))
kwargs.update({"numero": 1, 'access_token': token["access_token"]})
headers = {"Accept": "application/xml;charset=UTF-8",
"Content-Type": "application/xml",
"Authorization": "Bearer %s" % kwargs['access_token']}
r = requests.post(url, headers=headers, data=xml_send)
response, obj = sanitize_response(r.text.strip())
return {
'sent_xml': xml_send,
'received_xml': response.encode('utf-8'),
'object': obj,
'status_code': r.status_code,
}
def xml_processar_nota(certificado, **kwargs):
return _render(certificado, 'processar_nota', **kwargs)
def processar_nota(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_processar_nota(certificado, **kwargs)
return _send(certificado, 'processar_nota', **kwargs)
def xml_cancelar_nota(certificado, **kwargs):
return _render(certificado, 'cancelar_nota', **kwargs)
def cancelar_nota(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_cancelar_nota(certificado, **kwargs)
return _send(certificado, 'cancelar_nota', **kwargs)
def consultar_nota(certificado, **kwargs):
if kwargs['ambiente'] == 'producao':
url = "https://nfps-e.pmf.sc.gov.br/api/v1/consultas/notas/numero/%s" % (kwargs["numero"])
else:
url = "https://nfps-e-hml.pmf.sc.gov.br/api/v1/consultas/notas/numero/%s" % (kwargs["numero"])
headers = {"Accept": "application/json",
"Authorization": "Bearer %s" % kwargs['access_token']}
r = requests.get(url, headers=headers)
if r.status_code == 200:
return r.text
else:
return r.text

7
pytrustnfe/nfse/floripa/templates/cancelar_nota.xml

@ -0,0 +1,7 @@
<?xml version="1.0"?>
<xmlCancelamentoNfpse>
<motivoCancelamento>{{ cancelamento.motivo }}</motivoCancelamento>
<nuAedf>{{ cancelamento.aedf }}</nuAedf>
<nuNotaFiscal>{{ cancelamento.numero }}</nuNotaFiscal>
<codigoVerificacao>{{ cancelamento.codigo_verificacao }}</codigoVerificacao>
</xmlCancelamentoNfpse>

41
pytrustnfe/nfse/floripa/templates/processar_nota.xml

@ -0,0 +1,41 @@
<?xml version="1.0"?>
<xmlProcessamentoNfpse>
<bairroTomador>{{ rps.tomador.bairro|normalize|escape }}</bairroTomador>
<baseCalculo>{{ rps.base_calculo }}</baseCalculo>
<baseCalculoSubstituicao>0.0</baseCalculoSubstituicao>
<cfps>{{ rps.cfps }}</cfps>
<codigoMunicipioTomador>{{ rps.tomador.cidade }}</codigoMunicipioTomador>
<codigoPostalTomador>{{ rps.tomador.cep }}</codigoPostalTomador>
<complementoEnderecoTomador>{{ rps.tomador.complemento|normalize|escape }}</complementoEnderecoTomador>
<dadosAdicionais>{{ rps.observacoes|normalize|escape }}</dadosAdicionais>
<dataEmissao>{{ rps.data_emissao }}</dataEmissao>
<emailTomador>{{ rps.tomador.email }}</emailTomador>
<identificacao>{{ rps.numero }}</identificacao>
<identificacaoTomador>{{ rps.tomador.cnpj_cpf }}</identificacaoTomador>
<inscricaoMunicipalTomador>{{ rps.tomador.inscricao_municipal }}</inscricaoMunicipalTomador>
<itensServico>
{% for item in rps.itens_servico -%}
<itemServico>
<aliquota>{{ item.aliquota }}</aliquota>
<baseCalculo>{{ item.base_calculo }}</baseCalculo>
<cst>{{ item.cst_servico }}</cst>
<descricaoServico>{{ item.name|normalize|escape }}</descricaoServico>
<idCNAE>{{ item.cnae }}</idCNAE>
<quantidade>{{ item.quantidade }}</quantidade>
<valorTotal>{{ item.valor_total }}</valorTotal>
<valorUnitario>{{ item.valor_unitario }}</valorUnitario>
</itemServico>
{% endfor %}
</itensServico>
<logradouroTomador>{{ rps.tomador.logradouro|normalize|escape }}</logradouroTomador>
<nomeMunicipioTomador></nomeMunicipioTomador>
<numeroAEDF>{{ rps.aedf }}</numeroAEDF>
<numeroEnderecoTomador>{{ rps.tomador.numero }}</numeroEnderecoTomador>
<paisTomador>1058</paisTomador>
<razaoSocialTomador>{{ rps.tomador.razao_social|normalize|escape }}</razaoSocialTomador>
<telefoneTomador>{{ rps.tomador.telefone }}</telefoneTomador>
<ufTomador>{{ rps.tomador.uf }}</ufTomador>
<valorISSQN>{{rps.valor_issqn }}</valorISSQN>
<valorISSQNSubstituicao>0.0</valorISSQNSubstituicao>
<valorTotalServicos>{{ rps.valor_total }}</valorTotalServicos>
</xmlProcessamentoNfpse>

31
pytrustnfe/nfse/ginfes/__init__.py

@ -3,9 +3,12 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import os
import suds
from requests import Session
from zeep import Client
from zeep.transports import Transport
from requests.packages.urllib3 import disable_warnings
from pytrustnfe.xml import render_xml, sanitize_response
from pytrustnfe.client import get_authenticated_client
from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key
from pytrustnfe.nfe.assinatura import Assinatura
@ -33,17 +36,19 @@ def _send(certificado, method, **kwargs):
cert, key = extract_cert_and_key_from_pfx(
certificado.pfx, certificado.password)
cert, key = save_cert_key(cert, key)
client = get_authenticated_client(base_url, cert, key)
try:
xml_send = kwargs['xml']
header = '<ns2:cabecalho xmlns:ns2="http://www.ginfes.com.br/cabecalho_v03.xsd" versao="3"><versaoDados>3</versaoDados></ns2:cabecalho>' #noqa
response = getattr(client.service, method)(header, xml_send)
except suds.WebFault, e:
return {
'sent_xml': xml_send,
'received_xml': e.fault.faultstring,
'object': None
}
header = '<ns2:cabecalho xmlns:ns2="http://www.ginfes.com.br/cabecalho_v03.xsd" versao="3"><versaoDados>3</versaoDados></ns2:cabecalho>' #noqa
disable_warnings()
session = Session()
session.cert = (cert, key)
session.verify = False
transport = Transport(session=session)
client = Client(base_url, transport=transport)
xml_send = kwargs['xml']
response = client.service[method](header, xml_send)
response, obj = sanitize_response(response)
return {

11
pytrustnfe/nfse/imperial/__init__.py

@ -20,18 +20,15 @@ def _send(certificado, method, **kwargs):
base_url = 'https://nfe.etransparencia.com.br/rj.petropolis/webservice/aws_nfe.aspx' # noqa
else:
base_url = 'https://nfehomologacao.etransparencia.com.br/rj.petropolis/webservice/aws_nfe.aspx' # noqa
xml_send = kwargs["xml"]
path = os.path.join(os.path.dirname(__file__), 'templates')
soap = render_xml(path, 'SoapRequest.xml', False, soap_body=xml_send)
soap = render_xml(path, 'SoapRequest.xml', False, soap_body=xml_send.decode())
client = HttpClient(base_url)
response = client.post_soap(soap, 'NFeaction/AWS_NFE.%s' % method)
response, obj = sanitize_response(response)
response, obj = sanitize_response(response.encode('utf-8'))
return {
'sent_xml': xml_send,
'received_xml': response,
'sent_xml': xml_send.decode(),
'received_xml': response.decode(),
'object': obj
}

75
pytrustnfe/nfse/mga/__init__.py

@ -0,0 +1,75 @@
# © 2018 Danimar Ribeiro, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import os
from requests import Session
from zeep import Client
from zeep.transports import Transport
from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key
from pytrustnfe.xml import render_xml, sanitize_response
from pytrustnfe.nfse.mga.assinatura import Assinatura
def _render(certificado, method, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates')
xml_send = render_xml(path, '%s.xml' % method, True, **kwargs)
reference = ''
if method == 'GerarNfse':
reference = 'rps:%s' % kwargs['rps']['numero']
elif method == 'CancelarNfse':
reference = 'Cancelamento_NF%s' % kwargs['cancelamento']['numero_nfse']
signer = Assinatura(certificado.pfx, certificado.password)
xml_send = signer.assina_xml(xml_send, reference)
return xml_send.encode('utf-8')
def _send(certificado, method, **kwargs):
base_url = ''
if kwargs['ambiente'] == 'producao':
base_url = 'https://isse.maringa.pr.gov.br/ws/?wsdl'
else:
base_url = 'https://isseteste.maringa.pr.gov.br/ws/?wsdl'
xml_send = kwargs["xml"].decode('utf-8')
cert, key = extract_cert_and_key_from_pfx(
certificado.pfx, certificado.password)
cert, key = save_cert_key(cert, key)
session = Session()
session.cert = (cert, key)
session.verify = False
transport = Transport(session=session)
client = Client(base_url, transport=transport)
response = client.service[method](xml_send)
response, obj = sanitize_response(response)
return {
'sent_xml': str(xml_send),
'received_xml': str(response),
'object': obj
}
def xml_gerar_nfse(certificado, **kwargs):
return _render(certificado, 'GerarNfse', **kwargs)
def gerar_nfse(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_gerar_nfse(certificado, **kwargs)
return _send(certificado, 'GerarNfse', **kwargs)
def xml_cancelar_nfse(certificado, **kwargs):
return _render(certificado, 'CancelarNfse', **kwargs)
def cancelar_nfse(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_cancelar_nfse(certificado, **kwargs)
return _send(certificado, 'CancelarNfse', **kwargs)

45
pytrustnfe/nfse/mga/assinatura.py

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# © 2016 Danimar Ribeiro, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import signxml
from lxml import etree
from pytrustnfe.certificado import extract_cert_and_key_from_pfx
from signxml import XMLSigner
class Assinatura(object):
def __init__(self, arquivo, senha):
self.arquivo = arquivo
self.senha = senha
def assina_xml(self, xml_element, reference):
cert, key = extract_cert_and_key_from_pfx(self.arquivo, self.senha)
for element in xml_element.iter("*"):
if element.text is not None and not element.text.strip():
element.text = None
signer = XMLSigner(
method=signxml.methods.enveloped, signature_algorithm=u"rsa-sha1",
digest_algorithm=u'sha1',
c14n_algorithm=u'http://www.w3.org/TR/2001/REC-xml-c14n-20010315')
ns = {}
ns[None] = signer.namespaces['ds']
signer.namespaces = ns
element_to_be_signed = xml_element.getchildren()[0].getchildren()[0]
signed_root = signer.sign(
element_to_be_signed, key=key.encode(), cert=cert.encode())
if reference:
element_signed = xml_element.find(".//*[@Id='%s']" % reference)
signature = signed_root.find(
".//{http://www.w3.org/2000/09/xmldsig#}Signature")
if element_signed is not None and signature is not None:
parent = xml_element.getchildren()[0]
parent.append(signature)
return etree.tostring(xml_element, encoding=str)

15
pytrustnfe/nfse/mga/templates/CancelarNfse.xml

@ -0,0 +1,15 @@
<CancelarNfseEnvio xmlns="http://www.abrasf.org.br/nfse.xsd">
<Pedido>
<InfPedidoCancelamento Id="Cancelamento_NF{{ cancelamento.numero_nfse }}">
<IdentificacaoNfse>
<Numero>{{ cancelamento.numero_nfse }}</Numero>
<CpfCnpj>
<Cnpj>{{ cancelamento.cnpj_prestador }}</Cnpj>
</CpfCnpj>
<InscricaoMunicipal>{{ cancelamento.inscricao_municipal }}</InscricaoMunicipal>
<CodigoMunicipio>{{ cancelamento.cidade }}</CodigoMunicipio>
</IdentificacaoNfse>
<CodigoCancelamento>1</CodigoCancelamento>
</InfPedidoCancelamento>
</Pedido>
</CancelarNfseEnvio>

3
pytrustnfe/nfse/mga/templates/GerarNfse.xml

@ -0,0 +1,3 @@
<GerarNfseEnvio xmlns="http://www.abrasf.org.br/nfse.xsd">
{% include 'Rps.xml' %}
</GerarNfseEnvio>

81
pytrustnfe/nfse/mga/templates/Rps.xml

@ -0,0 +1,81 @@
<Rps>
<InfDeclaracaoPrestacaoServico>
<Rps Id="rps:{{ rps.numero }}">
<IdentificacaoRps>
<Numero>{{ rps.numero }}</Numero>
<Serie>{{ rps.serie }}</Serie>
<Tipo>{{ rps.tipo_rps }}</Tipo>
</IdentificacaoRps>
<DataEmissao>{{ rps.data_emissao }}</DataEmissao>
<Status>{{ rps.status }}</Status>
</Rps>
<Competencia>{{ rps.data_emissao }}</Competencia>
<Servico>
<Valores>
<ValorServicos>{{ rps.valor_servico }}</ValorServicos>
<ValorDeducoes>{{ rps.valor_deducao }}</ValorDeducoes>
<ValorPis>{{ rps.valor_pis }}</ValorPis>
<ValorCofins>{{ rps.valor_cofins }}</ValorCofins>
<ValorInss>{{ rps.valor_inss }}</ValorInss>
<ValorIr>{{ rps.valor_ir }}</ValorIr>
<ValorCsll>{{ rps.valor_csll }}</ValorCsll>
<OutrasRetencoes>{{ rps.outras_retencoes }}</OutrasRetencoes>
<ValorIss>{{ rps.valor_iss }}</ValorIss>
<Aliquota>{{ rps.aliquota_issqn }}</Aliquota>
<DescontoIncondicionado>{{ rps.desconto_incondicionado }}</DescontoIncondicionado>
<DescontoCondicionado>{{ rps.desconto_condicionado }}</DescontoCondicionado>
</Valores>
<IssRetido>{{ rps.iss_retido }}</IssRetido>
<ItemListaServico>{{ rps.codigo_servico }}</ItemListaServico>
<CodigoCnae>{{ rps.cnae_servico }}</CodigoCnae>
<CodigoTributacaoMunicipio>{{ rps.codigo_tributacao_municipio }}</CodigoTributacaoMunicipio>
<Discriminacao>{{ rps.descricao }}</Discriminacao>
<CodigoMunicipio>{{ rps.codigo_municipio }}</CodigoMunicipio>
<CodigoPais>{{ rps.codigo_pais }}</CodigoPais>
<ExigibilidadeISS>{{ rps.exigibilidade_iss}}</ExigibilidadeISS>
</Servico>
<Prestador>
<CpfCnpj>
<Cnpj>{{ rps.prestador.cnpj }}</Cnpj>
</CpfCnpj>
<InscricaoMunicipal>{{ rps.prestador.inscricao_municipal }}</InscricaoMunicipal>
</Prestador>
<Tomador>
<IdentificacaoTomador>
<CpfCnpj>
{% if rps.tomador.cnpj_cpf|length == 14 %}
<Cnpj>{{ rps.tomador.cnpj_cpf }}</Cnpj>
{% endif %}
{% if rps.tomador.cnpj_cpf|length == 11 %}
<Cpf>{{ rps.tomador.cnpj_cpf }}</Cpf>
{% endif %}
</CpfCnpj>
<InscricaoMunicipal>{{ rps.tomador.inscricao_municipal }}</InscricaoMunicipal>
</IdentificacaoTomador>
<RazaoSocial>{{ rps.tomador.razao_social }}</RazaoSocial>
<Endereco>
<Endereco>{{ rps.tomador.logradouro }}</Endereco>
<Numero>{{ rps.tomador.numero }}</Numero>
<Complemento>{{ rps.tomador.complemento }}</Complemento>
<Bairro>{{ rps.tomador.bairro }}</Bairro>
<CodigoMunicipio>{{ rps.tomador.cidade }}</CodigoMunicipio>
<Uf>{{ rps.tomador.uf }}</Uf>
<CodigoPais>{{ rps.tomador.codigo_pais }}</CodigoPais>
<Cep>{{ rps.tomador.cep }}</Cep>
</Endereco>
<Contato>
<Telefone>{{ rps.tomador.telefone }}</Telefone>
<Email>{{ rps.tomador.email }}</Email>
</Contato>
</Tomador>
{% if rps.construcao_civil is defined -%}
<ContrucaoCivil>
<CodigoObra>{{ rps.construcao_civil.codigo_obra }}</CodigoObra>
<Art>{{ rps.construcao_civil.art }}</Art>
</ContrucaoCivil>
{% endif %}
<RegimeEspecialTributacao>{{ rps.regime_tributacao }}</RegimeEspecialTributacao>
<OptanteSimplesNacional>{{ rps.optante_simples }}</OptanteSimplesNacional>
<IncentivoFiscal>{{ rps.incentivador_cultural }}</IncentivoFiscal>
</InfDeclaracaoPrestacaoServico>
</Rps>

9
pytrustnfe/nfse/paulistana/__init__.py

@ -18,10 +18,10 @@ def sign_tag(certificado, **kwargs):
if 'nfse' in kwargs:
for item in kwargs['nfse']['lista_rps']:
signed = crypto.sign(key, item['assinatura'], 'SHA1')
item['assinatura'] = b64encode(signed)
item['assinatura'] = b64encode(signed).decode()
if 'cancelamento' in kwargs:
signed = crypto.sign(key, kwargs['cancelamento']['assinatura'], 'SHA1')
kwargs['cancelamento']['assinatura'] = b64encode(signed)
kwargs['cancelamento']['assinatura'] = b64encode(signed).decode()
def _send(certificado, method, **kwargs):
@ -42,13 +42,12 @@ def _send(certificado, method, **kwargs):
cert, key = save_cert_key(cert, key)
client = get_authenticated_client(base_url, cert, key)
pfx_path = certificado.save_pfx()
signer = Assinatura(pfx_path, certificado.password)
signer = Assinatura(cert, key, certificado.password)
xml_send = signer.assina_xml(xml_send, '')
try:
response = getattr(client.service, method)(1, xml_send)
except suds.WebFault, e:
except suds.WebFault as e:
return {
'sent_xml': xml_send,
'received_xml': e.fault.faultstring,

2
pytrustnfe/nfse/paulistana/templates/EnvioLoteRPS.xml

@ -55,6 +55,8 @@
<CEP>{{ rps.tomador.cep }}</CEP>
</EnderecoTomador>
<Discriminacao>{{ rps.descricao|normalize|escape }}</Discriminacao>
<ValorCargaTributaria>{{ rps.valor_carga_tributaria }}</ValorCargaTributaria>
<FonteCargaTributaria>{{ rps.fonte_carga_tributaria }}</FonteCargaTributaria>
</RPS>
{% endfor %}
</PedidoEnvioLoteRPS>

2
pytrustnfe/nfse/paulistana/templates/EnvioRPS.xml

@ -46,6 +46,8 @@
<CEP>{{ rps.tomador.cep }}</CEP>
</EnderecoTomador>
<Discriminacao>{{ rps.descricao }}</Discriminacao>
<ValorCargaTributaria>{{ rps.valor_carga_tributaria }}</ValorCargaTributaria>
<FonteCargaTributaria>{{ rps.fonte_carga_tributaria }}</FonteCargaTributaria>
{% endfor %}
</RPS>
</PedidoEnvioRPS>

2
pytrustnfe/nfse/susesu/__init__.py

@ -29,7 +29,7 @@ def _send(method, **kwargs):
'sent_xml': xml_send,
'received_xml': e.fault.faultstring,
}
result = unicode(result)
result = str(result)
result = unicodedata.normalize('NFKD', result).encode('ascii', 'ignore')
return {
'sent_xml': xml_send,

1
pytrustnfe/test/XMLs/paulistana_resultado.xml

@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><RetornoEnvioLoteRPS xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.prefeitura.sp.gov.br/nfe"><Cabecalho Versao="1" xmlns=""><Sucesso>true</Sucesso><InformacoesLote><NumeroLote>2654364</NumeroLote><InscricaoPrestador>51212</InscricaoPrestador><CPFCNPJRemetente><CNPJ>21332900163</CNPJ></CPFCNPJRemetente><DataEnvioLote>2016-08-29T10:52:15</DataEnvioLote><QtdNotasProcessadas>1</QtdNotasProcessadas><TempoProcessamento>0</TempoProcessamento><ValorTotalServicos>1.35</ValorTotalServicos></InformacoesLote></Cabecalho><ChaveNFeRPS xmlns=""><ChaveNFe><InscricaoPrestador>52382</InscricaoPrestador><NumeroNFe>446</NumeroNFe><CodigoVerificacao>APR9MJR</CodigoVerificacao></ChaveNFe><ChaveRPS><InscricaoPrestador>51282</InscricaoPrestador><SerieRPS>1</SerieRPS><NumeroRPS>6</NumeroRPS></ChaveRPS></ChaveNFeRPS></RetornoEnvioLoteRPS>

34
pytrustnfe/test/test_add_qr_code.py

@ -1,34 +0,0 @@
# -*- coding: utf-8-*-
# © 2016 Alessandro Fernandes Martini
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import unittest
# from lxml import etree
from pytrustnfe.nfe import _add_qrCode
class TestAddQRCode(unittest.TestCase):
def setUp(self):
self.xml_sem_qrcode = open('pytrustnfe/test/xml_sem_qrcode.xml', 'r')
self.xml_com_qrcode = open('pytrustnfe/test/xml_com_qrcode.xml', 'r')
dhEmi = '2016-11-09T16:03:25-00:00'
chave_nfe = u'NFe35161121332917000163650010000000011448875034'
ambiente = 2
valor_total = '324.00'
icms_total = '61.56'
cid_token = '000001'
csc = '123456789'
estado = '35'
total = {'vNF': valor_total, 'vICMS': icms_total}
infnfe = {'ide': {'dhEmi': dhEmi}, 'Id': chave_nfe, 'total': total,
'codigo_seguranca': {'cid_token': cid_token, 'csc': csc}}
infnfe = {'infNFe': infnfe}
self.kwargs = {}
self.kwargs['ambiente'] = ambiente
self.kwargs['estado'] = estado
self.kwargs['NFes'] = [infnfe]
def test_add_qrCode(self):
xml_init = self.xml_sem_qrcode.read()
xml_end = _add_qrCode(xml_init, **self.kwargs)
self.assertEqual(xml_end, self.xml_com_qrcode.read())

20
pytrustnfe/test/test_comunicacao.py

@ -1,20 +0,0 @@
# coding=utf-8
'''
Created on Jun 16, 2015
@author: danimar
'''
import unittest
import os.path
XML_RETORNO = '<retEnviNFe><cStat>103</cStat>' \
'<cUF>42</cUF></retEnviNFe>'
class test_comunicacao(unittest.TestCase):
caminho = os.path.dirname(__file__)
def test_envio_nfe(self):
pass

19
pytrustnfe/test/test_consulta_cadastro.py

@ -1,19 +0,0 @@
# coding=utf-8
import mock
import os.path
import unittest
from pytrustnfe.certificado import Certificado
from pytrustnfe.nfe import consulta_cadastro
class test_consulta_cadastro(unittest.TestCase):
caminho = os.path.dirname(__file__)
def test_conta_de_cadastro(self):
pfx_source = open(os.path.join(self.caminho, 'teste.pfx'), 'r').read()
pfx = Certificado(pfx_source, '123456')
obj = {'cnpj': '12345678901234', 'estado': '42'}
consulta_cadastro(pfx, obj=obj, ambiente=1, estado='42')

189
pytrustnfe/test/xml_com_qrcode.xml

@ -1,189 +0,0 @@
<enviNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="3.10">
<idLote>103</idLote>
<indSinc>1</indSinc>
<NFe xmlns="http://www.portalfiscal.inf.br/nfe">
<infNFe versao="3.10" Id="NFe35161121332917000163650010000000011448875034">
<ide>
<cUF>35</cUF>
<cNF>44887503</cNF>
<natOp>Venda POS</natOp>
<indPag>0</indPag>
<mod>65</mod>
<serie>1</serie>
<nNF>1</nNF>
<dhEmi>2016-11-09T16:03:58-00:00</dhEmi>
<tpNF>1</tpNF>
<idDest>1</idDest>
<cMunFG>3550308</cMunFG>
<tpImp>4</tpImp>
<tpEmis>1</tpEmis>
<cDV>4</cDV>
<tpAmb>2</tpAmb>
<finNFe>1</finNFe>
<indFinal>1</indFinal>
<indPres>1</indPres>
<procEmi>0</procEmi>
<verProc>Odoo Brasil 10</verProc>
</ide>
<emit>
<CNPJ>21332917000163</CNPJ>
<xNome>LEL AMBIENTAL LTDA - EPP</xNome>
<xFant>Zell Ambiental</xFant>
<enderEmit>
<xLgr>Rua Padre Jo&#227;o</xLgr>
<nro>444</nro>
<xBairro>Penha de Fran&#231;a</xBairro>
<cMun>3550308</cMun>
<xMun>S&#227;o Paulo</xMun>
<UF>SP</UF>
<CEP>03637000</CEP>
<cPais>1058</cPais>
<xPais>Brasil</xPais>
<fone>3425323750</fone>
</enderEmit>
<IE>244694180116</IE>
<CRT>3</CRT>
</emit>
<det nItem="1">
<prod>
<cProd>MISC</cProd>
<cEAN/>
<xProd>NOTA FISCAL EMITIDA EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL</xProd>
<NCM>28431000</NCM>
<CEST>2806300</CEST>
<CFOP>5101</CFOP>
<uCom>Unit(s</uCom>
<qCom>18.0</qCom>
<vUnCom>18.00</vUnCom>
<vProd>324.00</vProd>
<cEANTrib/>
<uTrib>Unit(s</uTrib>
<qTrib>18.0</qTrib>
<vUnTrib>18.00</vUnTrib>
<indTot>1</indTot>
</prod>
<imposto>
<vTotTrib>0.00</vTotTrib>
<ICMS>
<ICMS00>
<orig>0</orig>
<CST>00</CST>
<modBC>3</modBC>
<vBC>324.00</vBC>
<pICMS>19.00</pICMS>
<vICMS>61.56</vICMS>
</ICMS00>
</ICMS>
<PIS>
<PISAliq>
<CST>01</CST>
<vBC>324.00</vBC>
<pPIS>0.00</pPIS>
<vPIS>2.11</vPIS>
</PISAliq>
</PIS>
<COFINS>
<COFINSAliq>
<CST>01</CST>
<vBC>324.00</vBC>
<pCOFINS>0.00</pCOFINS>
<vCOFINS>9.72</vCOFINS>
</COFINSAliq>
</COFINS>
</imposto>
</det>
<total>
<ICMSTot>
<vBC>324.00</vBC>
<vICMS>61.56</vICMS>
<vICMSDeson>0.00</vICMSDeson>
<vBCST>0.00</vBCST>
<vST>0.00</vST>
<vProd>324.00</vProd>
<vFrete>0.00</vFrete>
<vSeg>0.00</vSeg>
<vDesc>0.00</vDesc>
<vII>0.00</vII>
<vIPI>0.00</vIPI>
<vPIS>2.11</vPIS>
<vCOFINS>9.72</vCOFINS>
<vOutro>0.00</vOutro>
<vNF>324.00</vNF>
<vTotTrib>0.00</vTotTrib>
</ICMSTot>
</total>
<transp>
<modFrete>9</modFrete>
</transp>
<pag>
<tPag>01</tPag>
<vPag>324.00</vPag>
</pag>
</infNFe>
<infNFeSupl><qrCode><![CDATA[https://homologacao.nfce.fazenda.sp.gov.br/NFCEConsultaPublica/Paginas/ConstultaQRCode.aspx?chNFe=35161121332917000163650010000000011448875034&nVersao=100&tpAmb=2&dhEmi=323031362d31312d30395431363a30333a32352d30303a3030&vNF=324.00&vICMS=61.56&digVal=66664a5a2b30346a6d484e33754c7830386875796b7942733272343d&cIdToken=000001&cHashQRCode=6d674f77ece8067d1255c5ca33fc2acaab3a0e8e]]></qrCode></infNFeSupl><Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference URI="#NFe35161121332917000163650010000000011448875034">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>ffJZ+04jmHN3uLx08huykyBs2r4=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>u42y8bBqNM336yc7r0+5YWiePmCerKu3cFcp2J+lmQzIK6snba7txyeBtwVos8jQxrYLsnfWtKb5P6FxmhDeqAB41o2aoSIcnxc0yxdFn2ZDTquwU+AurH6E4hGclz8D/5qapv2+g3y+VpXMZFNNCqZSm2vJpvXBmPEY/7oO2cPB13N7WDhDRmI9H4kRDsPuxeeJetr2Bf1ThGJ0EUA4DXC7My0kl/Bw0rYwoPDhoqhxinr9Wocw1UyQ1vprLF0JVHXOtSq39Uqfqmj5QUDwUvvh4YL9bFZ8lvZZ0CZDmILaFGvr1XH3CwJ2Ws3RLlb12Arj5OT4vS8ZKhJtr/dpfA==</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIIPzCCBiegAwIBAgIQYdesnYUNG8VPne0qhTeKOzANBgkqhkiG9w0BAQsFADB4
MQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE2MDQGA1UECxMtU2Vj
cmV0YXJpYSBkYSBSZWNlaXRhIEZlZGVyYWwgZG8gQnJhc2lsIC0gUkZCMRwwGgYD
VQQDExNBQyBDZXJ0aXNpZ24gUkZCIEc0MB4XDTE2MDUxMDAwMDAwMFoXDTE3MDUw
OTIzNTk1OVowgekxCzAJBgNVBAYTAkJSMRMwEQYDVQQKFApJQ1AtQnJhc2lsMQsw
CQYDVQQIEwJTUDESMBAGA1UEBxQJU2FvIFBhdWxvMTYwNAYDVQQLFC1TZWNyZXRh
cmlhIGRhIFJlY2VpdGEgRmVkZXJhbCBkbyBCcmFzaWwgLSBSRkIxFjAUBgNVBAsU
DVJGQiBlLUNOUEogQTExJDAiBgNVBAsUG0F1dGVudGljYWRvIHBvciBBUiBTdW5z
aGluZTEuMCwGA1UEAxMlTEVaIEFNQklFTlRBTCBMVERBIEVQUDoyMTMzMjkxNzAw
MDE2MzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANEG6j0uXIvvHlMz
0IGzuY/vuFQncIoSE+cBUk0uq6J3dtmGAg4oaVWCHUfHbX9s2Ag1jIG+PFAo2dlt
sbLSEji74XhD+IpM/9aHm3ke8kb05ay+bYRuUjTNSwUbslT1+amAmIu7m1yPBi6u
v3+/Lj2I0g7VeBBAjv/TiBG0VRCURXvKrwWrv2Lpybo/yDnENGtRqQHihqeYFKin
nDzBsMbv4ripbi3XiAgcy/bF6NFgVMqxrNnGvSiSUhDRkmceVFIysRXUMke02Qo1
Q5Ik1j1goUIHP44QOruXCMiT0yOK8u0qNAXR0yzSaWcBR2aJCeWgFg7sNbB50Qcx
c+2GKUECAwEAAaOCA1EwggNNMIG2BgNVHREEga4wgaugPQYFYEwBAwSgNAQyMTYw
NjE5ODYzNDEzNzgyODg2NTAwMDAwMDAwMDAwMDAwMDAwMDQwMDAwMzczU1NQU1Cg
IgYFYEwBAwKgGQQXTEVPTkFSRE8gREUgTElNQSBTQU5UT1OgGQYFYEwBAwOgEAQO
MjEzMzI5MTcwMDAxNjOgFwYFYEwBAwegDgQMMDAwMDAwMDAwMDAwgRJ3YWduZXJA
emVsbC5jb20uYnIwCQYDVR0TBAIwADAfBgNVHSMEGDAWgBQukerWbeWyWYLcOIUp
djQWVjzQPjAOBgNVHQ8BAf8EBAMCBeAwfwYDVR0gBHgwdjB0BgZgTAECAQwwajBo
BggrBgEFBQcCARZcaHR0cDovL2ljcC1icmFzaWwuY2VydGlzaWduLmNvbS5ici9y
ZXBvc2l0b3Jpby9kcGMvQUNfQ2VydGlzaWduX1JGQi9EUENfQUNfQ2VydGlzaWdu
X1JGQi5wZGYwggEWBgNVHR8EggENMIIBCTBXoFWgU4ZRaHR0cDovL2ljcC1icmFz
aWwuY2VydGlzaWduLmNvbS5ici9yZXBvc2l0b3Jpby9sY3IvQUNDZXJ0aXNpZ25S
RkJHNC9MYXRlc3RDUkwuY3JsMFagVKBShlBodHRwOi8vaWNwLWJyYXNpbC5vdXRy
YWxjci5jb20uYnIvcmVwb3NpdG9yaW8vbGNyL0FDQ2VydGlzaWduUkZCRzQvTGF0
ZXN0Q1JMLmNybDBWoFSgUoZQaHR0cDovL3JlcG9zaXRvcmlvLmljcGJyYXNpbC5n
b3YuYnIvbGNyL0NlcnRpc2lnbi9BQ0NlcnRpc2lnblJGQkc0L0xhdGVzdENSTC5j
cmwwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMIGbBggrBgEFBQcBAQSB
jjCBizBfBggrBgEFBQcwAoZTaHR0cDovL2ljcC1icmFzaWwuY2VydGlzaWduLmNv
bS5ici9yZXBvc2l0b3Jpby9jZXJ0aWZpY2Fkb3MvQUNfQ2VydGlzaWduX1JGQl9H
NC5wN2MwKAYIKwYBBQUHMAGGHGh0dHA6Ly9vY3NwLmNlcnRpc2lnbi5jb20uYnIw
DQYJKoZIhvcNAQELBQADggIBAFIUBrNIyC4kBap/7hCW63tQhA/WNnWDNYpKM5wN
zwApVV2bqFMJURzO/7AUrHu7uZS1p/Ubo+w2dFjmnmj5DniQkY85Sd6HNa1fukJY
PK13wcUMVHMjeevIAcxnYraNdN4BIz1Svl6A8leGFgIEuDUll7Td+R7+aA8N5JYQ
dFFIe2VxvJNbWP/WA49oI8U2wkoPTfOZtfrgKf2msHm3FnTfnmyOPhIf8L31iFt6
MbKuFjOGIaWu+Z/gRDqj/EbFcEMUrDbeIYqz2724ZGBOJrkjHO7DBqXXoN9pzCTO
RB5+gILMEnMS7zFsCuLOtLVohxgYUzg8p4Fy3nsHEzb/7IDVOnKLfjh/c5GSTvOa
JT6qznYV2yav7NyuUSNUv+5bCIBNk45+qrQ8DwpsLBsFa+RLfCwvYVK95ad/xVgJ
QosPJuzW3t0fU/FWbc00sZWV6lgBPyWhdF8EodaRIWC+EOC2wJbODyw+vdX8pUxT
TUJKV2iAP8206gR2h07o2CZgXckJGJQ5MnBUbS78AaITXZ5JlPaS7ZdU9zWY3kD+
j5YERs0+UweijKi5eHZioGRZRDZ2uksh1wrgeLFLWuiSNaPFYVVrQ/ZGo+E5uVNl
8FuoR6P9TZjx1/A4XjqLQ9yPoPWgIWe14Vh/76dVcgz3ElWMbmPCDoc/wX+FoHX1
Fbux
</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</NFe>
</enviNFe>

190
pytrustnfe/test/xml_sem_qrcode.xml

@ -1,190 +0,0 @@
<?xml version="1.0"?>
<enviNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="3.10">
<idLote>103</idLote>
<indSinc>1</indSinc>
<NFe xmlns="http://www.portalfiscal.inf.br/nfe">
<infNFe versao="3.10" Id="NFe35161121332917000163650010000000011448875034">
<ide>
<cUF>35</cUF>
<cNF>44887503</cNF>
<natOp>Venda POS</natOp>
<indPag>0</indPag>
<mod>65</mod>
<serie>1</serie>
<nNF>1</nNF>
<dhEmi>2016-11-09T16:03:58-00:00</dhEmi>
<tpNF>1</tpNF>
<idDest>1</idDest>
<cMunFG>3550308</cMunFG>
<tpImp>4</tpImp>
<tpEmis>1</tpEmis>
<cDV>4</cDV>
<tpAmb>2</tpAmb>
<finNFe>1</finNFe>
<indFinal>1</indFinal>
<indPres>1</indPres>
<procEmi>0</procEmi>
<verProc>Odoo Brasil 10</verProc>
</ide>
<emit>
<CNPJ>21332917000163</CNPJ>
<xNome>LEL AMBIENTAL LTDA - EPP</xNome>
<xFant>Zell Ambiental</xFant>
<enderEmit>
<xLgr>Rua Padre Jo&#xE3;o</xLgr>
<nro>444</nro>
<xBairro>Penha de Fran&#xE7;a</xBairro>
<cMun>3550308</cMun>
<xMun>S&#xE3;o Paulo</xMun>
<UF>SP</UF>
<CEP>03637000</CEP>
<cPais>1058</cPais>
<xPais>Brasil</xPais>
<fone>3425323750</fone>
</enderEmit>
<IE>244694180116</IE>
<CRT>3</CRT>
</emit>
<det nItem="1">
<prod>
<cProd>MISC</cProd>
<cEAN/>
<xProd>NOTA FISCAL EMITIDA EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL</xProd>
<NCM>28431000</NCM>
<CEST>2806300</CEST>
<CFOP>5101</CFOP>
<uCom>Unit(s</uCom>
<qCom>18.0</qCom>
<vUnCom>18.00</vUnCom>
<vProd>324.00</vProd>
<cEANTrib/>
<uTrib>Unit(s</uTrib>
<qTrib>18.0</qTrib>
<vUnTrib>18.00</vUnTrib>
<indTot>1</indTot>
</prod>
<imposto>
<vTotTrib>0.00</vTotTrib>
<ICMS>
<ICMS00>
<orig>0</orig>
<CST>00</CST>
<modBC>3</modBC>
<vBC>324.00</vBC>
<pICMS>19.00</pICMS>
<vICMS>61.56</vICMS>
</ICMS00>
</ICMS>
<PIS>
<PISAliq>
<CST>01</CST>
<vBC>324.00</vBC>
<pPIS>0.00</pPIS>
<vPIS>2.11</vPIS>
</PISAliq>
</PIS>
<COFINS>
<COFINSAliq>
<CST>01</CST>
<vBC>324.00</vBC>
<pCOFINS>0.00</pCOFINS>
<vCOFINS>9.72</vCOFINS>
</COFINSAliq>
</COFINS>
</imposto>
</det>
<total>
<ICMSTot>
<vBC>324.00</vBC>
<vICMS>61.56</vICMS>
<vICMSDeson>0.00</vICMSDeson>
<vBCST>0.00</vBCST>
<vST>0.00</vST>
<vProd>324.00</vProd>
<vFrete>0.00</vFrete>
<vSeg>0.00</vSeg>
<vDesc>0.00</vDesc>
<vII>0.00</vII>
<vIPI>0.00</vIPI>
<vPIS>2.11</vPIS>
<vCOFINS>9.72</vCOFINS>
<vOutro>0.00</vOutro>
<vNF>324.00</vNF>
<vTotTrib>0.00</vTotTrib>
</ICMSTot>
</total>
<transp>
<modFrete>9</modFrete>
</transp>
<pag>
<tPag>01</tPag>
<vPag>324.00</vPag>
</pag>
</infNFe>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference URI="#NFe35161121332917000163650010000000011448875034">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>ffJZ+04jmHN3uLx08huykyBs2r4=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>u42y8bBqNM336yc7r0+5YWiePmCerKu3cFcp2J+lmQzIK6snba7txyeBtwVos8jQxrYLsnfWtKb5P6FxmhDeqAB41o2aoSIcnxc0yxdFn2ZDTquwU+AurH6E4hGclz8D/5qapv2+g3y+VpXMZFNNCqZSm2vJpvXBmPEY/7oO2cPB13N7WDhDRmI9H4kRDsPuxeeJetr2Bf1ThGJ0EUA4DXC7My0kl/Bw0rYwoPDhoqhxinr9Wocw1UyQ1vprLF0JVHXOtSq39Uqfqmj5QUDwUvvh4YL9bFZ8lvZZ0CZDmILaFGvr1XH3CwJ2Ws3RLlb12Arj5OT4vS8ZKhJtr/dpfA==</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIIPzCCBiegAwIBAgIQYdesnYUNG8VPne0qhTeKOzANBgkqhkiG9w0BAQsFADB4
MQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE2MDQGA1UECxMtU2Vj
cmV0YXJpYSBkYSBSZWNlaXRhIEZlZGVyYWwgZG8gQnJhc2lsIC0gUkZCMRwwGgYD
VQQDExNBQyBDZXJ0aXNpZ24gUkZCIEc0MB4XDTE2MDUxMDAwMDAwMFoXDTE3MDUw
OTIzNTk1OVowgekxCzAJBgNVBAYTAkJSMRMwEQYDVQQKFApJQ1AtQnJhc2lsMQsw
CQYDVQQIEwJTUDESMBAGA1UEBxQJU2FvIFBhdWxvMTYwNAYDVQQLFC1TZWNyZXRh
cmlhIGRhIFJlY2VpdGEgRmVkZXJhbCBkbyBCcmFzaWwgLSBSRkIxFjAUBgNVBAsU
DVJGQiBlLUNOUEogQTExJDAiBgNVBAsUG0F1dGVudGljYWRvIHBvciBBUiBTdW5z
aGluZTEuMCwGA1UEAxMlTEVaIEFNQklFTlRBTCBMVERBIEVQUDoyMTMzMjkxNzAw
MDE2MzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANEG6j0uXIvvHlMz
0IGzuY/vuFQncIoSE+cBUk0uq6J3dtmGAg4oaVWCHUfHbX9s2Ag1jIG+PFAo2dlt
sbLSEji74XhD+IpM/9aHm3ke8kb05ay+bYRuUjTNSwUbslT1+amAmIu7m1yPBi6u
v3+/Lj2I0g7VeBBAjv/TiBG0VRCURXvKrwWrv2Lpybo/yDnENGtRqQHihqeYFKin
nDzBsMbv4ripbi3XiAgcy/bF6NFgVMqxrNnGvSiSUhDRkmceVFIysRXUMke02Qo1
Q5Ik1j1goUIHP44QOruXCMiT0yOK8u0qNAXR0yzSaWcBR2aJCeWgFg7sNbB50Qcx
c+2GKUECAwEAAaOCA1EwggNNMIG2BgNVHREEga4wgaugPQYFYEwBAwSgNAQyMTYw
NjE5ODYzNDEzNzgyODg2NTAwMDAwMDAwMDAwMDAwMDAwMDQwMDAwMzczU1NQU1Cg
IgYFYEwBAwKgGQQXTEVPTkFSRE8gREUgTElNQSBTQU5UT1OgGQYFYEwBAwOgEAQO
MjEzMzI5MTcwMDAxNjOgFwYFYEwBAwegDgQMMDAwMDAwMDAwMDAwgRJ3YWduZXJA
emVsbC5jb20uYnIwCQYDVR0TBAIwADAfBgNVHSMEGDAWgBQukerWbeWyWYLcOIUp
djQWVjzQPjAOBgNVHQ8BAf8EBAMCBeAwfwYDVR0gBHgwdjB0BgZgTAECAQwwajBo
BggrBgEFBQcCARZcaHR0cDovL2ljcC1icmFzaWwuY2VydGlzaWduLmNvbS5ici9y
ZXBvc2l0b3Jpby9kcGMvQUNfQ2VydGlzaWduX1JGQi9EUENfQUNfQ2VydGlzaWdu
X1JGQi5wZGYwggEWBgNVHR8EggENMIIBCTBXoFWgU4ZRaHR0cDovL2ljcC1icmFz
aWwuY2VydGlzaWduLmNvbS5ici9yZXBvc2l0b3Jpby9sY3IvQUNDZXJ0aXNpZ25S
RkJHNC9MYXRlc3RDUkwuY3JsMFagVKBShlBodHRwOi8vaWNwLWJyYXNpbC5vdXRy
YWxjci5jb20uYnIvcmVwb3NpdG9yaW8vbGNyL0FDQ2VydGlzaWduUkZCRzQvTGF0
ZXN0Q1JMLmNybDBWoFSgUoZQaHR0cDovL3JlcG9zaXRvcmlvLmljcGJyYXNpbC5n
b3YuYnIvbGNyL0NlcnRpc2lnbi9BQ0NlcnRpc2lnblJGQkc0L0xhdGVzdENSTC5j
cmwwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMIGbBggrBgEFBQcBAQSB
jjCBizBfBggrBgEFBQcwAoZTaHR0cDovL2ljcC1icmFzaWwuY2VydGlzaWduLmNv
bS5ici9yZXBvc2l0b3Jpby9jZXJ0aWZpY2Fkb3MvQUNfQ2VydGlzaWduX1JGQl9H
NC5wN2MwKAYIKwYBBQUHMAGGHGh0dHA6Ly9vY3NwLmNlcnRpc2lnbi5jb20uYnIw
DQYJKoZIhvcNAQELBQADggIBAFIUBrNIyC4kBap/7hCW63tQhA/WNnWDNYpKM5wN
zwApVV2bqFMJURzO/7AUrHu7uZS1p/Ubo+w2dFjmnmj5DniQkY85Sd6HNa1fukJY
PK13wcUMVHMjeevIAcxnYraNdN4BIz1Svl6A8leGFgIEuDUll7Td+R7+aA8N5JYQ
dFFIe2VxvJNbWP/WA49oI8U2wkoPTfOZtfrgKf2msHm3FnTfnmyOPhIf8L31iFt6
MbKuFjOGIaWu+Z/gRDqj/EbFcEMUrDbeIYqz2724ZGBOJrkjHO7DBqXXoN9pzCTO
RB5+gILMEnMS7zFsCuLOtLVohxgYUzg8p4Fy3nsHEzb/7IDVOnKLfjh/c5GSTvOa
JT6qznYV2yav7NyuUSNUv+5bCIBNk45+qrQ8DwpsLBsFa+RLfCwvYVK95ad/xVgJ
QosPJuzW3t0fU/FWbc00sZWV6lgBPyWhdF8EodaRIWC+EOC2wJbODyw+vdX8pUxT
TUJKV2iAP8206gR2h07o2CZgXckJGJQ5MnBUbS78AaITXZ5JlPaS7ZdU9zWY3kD+
j5YERs0+UweijKi5eHZioGRZRDZ2uksh1wrgeLFLWuiSNaPFYVVrQ/ZGo+E5uVNl
8FuoR6P9TZjx1/A4XjqLQ9yPoPWgIWe14Vh/76dVcgz3ElWMbmPCDoc/wX+FoHX1
Fbux
</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</NFe>
</enviNFe>

87
pytrustnfe/urls.py

@ -0,0 +1,87 @@
AC = '12'
AL = '27'
AM = '13'
AP = '16'
BA = '29'
CE = '23'
DF = '53'
ES = '32'
GO = '52'
MA = '21'
MG = '31'
MS = '50'
MT = '51'
PA = '15'
PB = '25'
PE = '26'
PI = '22'
PR = '41'
RJ = '33'
RN = '24'
RO = '11'
RR = '14'
RS = '43'
SC = '42'
SE = '28'
SP = '35'
TO = '17'
PRODUCAO = '1'
HOMOLOGACAO = '2'
URLS = {
PRODUCAO: {
AC: 'http://www.sefaznet.ac.gov.br/nfce/qrcode?',
AL: 'http://nfce.sefaz.al.gov.br/QRCode/consultarNFCe.jsp?',
AM: 'http://sistemas.sefaz.am.gov.br/nfceweb/consultarNFCe.jsp?',
AP: 'https://www.sefaz.ap.gov.br/nfce/nfcep.php?',
BA: 'http://nfe.sefaz.ba.gov.br/servicos/nfce/qrcode.aspx?',
DF: 'http://www.fazenda.df.gov.br/nfce/qrcode?',
GO: 'http://nfe.sefaz.go.gov.br/nfeweb/sites/nfce/danfeNFCe?',
MA: 'http://nfce.sefaz.ma.gov.br/portal/consultarNFCe.jsp?',
MS: 'http://www.dfe.ms.gov.br/nfce/qrcode?',
MT: 'http://www.sefaz.mt.gov.br/nfce/consultanfce?',
PA: 'https://appnfc.sefa.pa.gov.br/portal/view/consultas/nfce/nfceForm.seam?', # noqa
PB: 'http://www.receita.pb.gov.br/nfce?',
PE: 'http://nfce.sefaz.pe.gov.br/nfce/consulta?',
PI: 'http://www.sefaz.pi.gov.br/nfce/qrcode?',
PR: 'http://www.fazenda.pr.gov.br/nfce/consulta?',
RJ: 'http://www4.fazenda.rj.gov.br/consultaNFCe/QRCode?',
RN: 'http://nfce.set.rn.gov.br/consultarNFCe.aspx?',
RO: 'http://www.nfce.sefin.ro.gov.br/consultanfce/consulta.jsp?',
RR: 'https://www.sefaz.rr.gov.br/nfce/servlet/qrcode?',
RS: 'https://www.sefaz.rs.gov.br/NFCE/NFCE-COM.aspx?',
SE: 'http://www.nfce.se.gov.br/nfce/qrcode?',
SP: 'https://www.nfce.fazenda.sp.gov.br/qrcode?',
TO: 'http://www.sefaz.to.gov.br/nfce/qrcode?',
},
HOMOLOGACAO: {
AC: 'http://www.hml.sefaznet.ac.gov.br/nfce/qrcode?',
AL: 'http://nfce.sefaz.al.gov.br/QRCode/consultarNFCe.jsp?',
AM: 'http://homnfce.sefaz.am.gov.br/nfceweb/consultarNFCe.jsp?',
AP: 'https://www.sefaz.ap.gov.br/nfcehml/nfce.php?',
BA: 'http://nfe.sefaz.ba.gov.br/servicos/nfce/qrcode.aspx?',
DF: 'http://www.fazenda.df.gov.br/nfce/qrcode?',
GO: 'http://homolog.sefaz.go.gov.br/nfeweb/sites/nfce/danfeNFCe?',
MA: 'http://homologacao.sefaz.ma.gov.br/portal/consultarNFCe.jsp?',
MS: 'http://www.dfe.ms.gov.br/nfce/qrcode?',
MT: 'http://homologacao.sefaz.mt.gov.br/nfce/consultanfce?',
PA: 'https://appnfc.sefa.pa.gov.br/portal-homologacao/view/consultas/nfce/nfceForm.seam?', # noqa
PB: 'http://www.receita.pb.gov.br/nfcehom?',
PE: 'http://nfcehomolog.sefaz.pe.gov.br/nfce/consulta?',
PI: 'http://www.sefaz.pi.gov.br/nfce/qrcode?',
PR: 'http://www.fazenda.pr.gov.br/nfce/consulta?',
RJ: 'http://www4.fazenda.rj.gov.br/consultaNFCe/QRCode?',
RN: 'http://hom.nfce.set.rn.gov.br/consultarNFCe.aspx?',
RO: 'http://200.174.88.103:8080/nfce/servlet/qrcode?',
RR: 'https://www.sefaz.rr.gov.br/nfce/servlet/qrcode?',
RS: 'https://www.sefaz.rs.gov.br/NFCE/NFCE-COM.aspx?',
SE: 'http://www.hom.nfe.se.gov.br/nfce/qrcode?',
SP: 'https://www.homologacao.nfce.fazenda.sp.gov.br/qrcode?',
TO: 'http://homologacao.sefaz.to.gov.br/nfce/qrcode?',
}
}
def url_qrcode(estado, ambiente):
return URLS[ambiente][estado]

28
pytrustnfe/utils.py

@ -8,14 +8,6 @@ from datetime import date, datetime
import lxml.etree as ET
class CabecalhoSoap(object):
def __init__(self, **kwargs):
self.versao = kwargs.pop('versao', '')
self.estado = kwargs.pop('estado', '')
self.soap_action = kwargs.pop('soap_action', '')
class ChaveNFe(object):
def __init__(self, **kwargs):
@ -84,14 +76,26 @@ def _find_node(xml, node):
def gerar_nfeproc(envio, recibo):
NSMAP = {None: 'http://www.portalfiscal.inf.br/nfe'}
root = ET.Element("nfeProc", versao="3.10", nsmap=NSMAP)
docEnvio = ET.fromstring(envio)
docRecibo = ET.fromstring(recibo)
root = ET.Element("nfeProc", versao="4.00", nsmap=NSMAP)
parser = ET.XMLParser(encoding='utf-8')
docEnvio = ET.fromstring(envio.encode('utf-8'), parser=parser)
docRecibo = ET.fromstring(recibo.encode('utf-8'), parser=parser)
nfe = _find_node(docEnvio, "NFe")
protocolo = _find_node(docRecibo, "protNFe")
if nfe is None or protocolo is None:
return ''
return b''
root.append(nfe)
root.append(protocolo)
return ET.tostring(root)
def gerar_nfeproc_cancel(nfe_proc, cancelamento):
docEnvio = ET.fromstring(nfe_proc)
docCancel = ET.fromstring(cancelamento)
ev_cancelamento = _find_node(docCancel, "retEvento")
if ev_cancelamento is None:
return b''
docEnvio.append(ev_cancelamento)
return ET.tostring(docEnvio)

44
pytrustnfe/xml/__init__.py

@ -2,7 +2,6 @@
# © 2016 Danimar Ribeiro, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import unicodedata
from lxml import etree
from lxml import objectify
@ -16,23 +15,10 @@ def recursively_empty(e):
return all((recursively_empty(c) for c in e.iterchildren()))
def recursively_normalize(vals):
for item in vals:
if type(vals[item]) is str:
vals[item] = vals[item].strip()
elif type(vals[item]) is dict:
recursively_normalize(vals[item])
elif type(vals[item]) is list:
for a in vals[item]:
recursively_normalize(a)
return vals
def render_xml(path, template_name, remove_empty, **nfe):
nfe = recursively_normalize(nfe)
env = Environment(
loader=FileSystemLoader(path), extensions=['jinja2.ext.with_'])
env.filters["normalize"] = filters.strip_line_feed
env.filters["normalize_str"] = filters.normalize_str
env.filters["format_percent"] = filters.format_percent
@ -41,11 +27,13 @@ def render_xml(path, template_name, remove_empty, **nfe):
env.filters["comma"] = filters.format_with_comma
template = env.get_template(template_name)
xml = template.render(**nfe)
xml = template.render(**nfe).replace('\n', '')
parser = etree.XMLParser(remove_blank_text=True, remove_comments=True,
strip_cdata=False)
root = etree.fromstring(xml, parser=parser)
for element in root.iter("*"): # remove espaços em branco
if element.text is not None and not element.text.strip():
element.text = None
if remove_empty:
context = etree.iterwalk(root)
for dummy, elem in context:
@ -53,21 +41,31 @@ def render_xml(path, template_name, remove_empty, **nfe):
if recursively_empty(elem):
parent.remove(elem)
return root
return etree.tostring(root)
return etree.tostring(root, encoding=str)
def sanitize_response(response):
response = unicode(response)
response = unicodedata.normalize('NFKD', response).encode('ascii',
'ignore')
tree = etree.fromstring(response)
parser = etree.XMLParser(encoding='utf-8')
tree = etree.fromstring(response.encode('UTF-8'), parser=parser)
# Remove namespaces inuteis na resposta
for elem in tree.getiterator():
if not hasattr(elem.tag, 'find'):
continue
i = elem.tag.find('}')
if i >= 0:
elem.tag = elem.tag[i+1:]
elem.tag = elem.tag[i + 1:]
objectify.deannotate(tree, cleanup_namespaces=True)
return response, objectify.fromstring(etree.tostring(tree))
def recursively_normalize(vals):
for item in vals:
if type(vals[item]) is str:
vals[item] = vals[item].strip()
vals[item] = filters.normalize_str(vals[item])
elif type(vals[item]) is dict:
recursively_normalize(vals[item])
elif type(vals[item]) is list:
for a in vals[item]:
recursively_normalize(a)
return vals

18
pytrustnfe/xml/filters.py

@ -13,24 +13,24 @@ def normalize_str(string):
Remove special characters and strip spaces
"""
if string:
if not isinstance(string, unicode):
string = unicode(string, 'utf-8', 'replace')
if not isinstance(string, str):
string = str(string, 'utf-8', 'replace')
string = string.encode('utf-8')
return normalize(
'NFKD', string.decode('utf-8')).encode('ASCII', 'ignore')
'NFKD', string.decode('utf-8')).encode('ASCII', 'ignore').decode()
return ''
def strip_line_feed(string):
if string:
if not isinstance(string, unicode):
string = unicode(string, 'utf-8', 'replace')
if not isinstance(string, str):
string = str(string, 'utf-8', 'replace')
remap = {
ord(u'\t'): u' ',
ord(u'\n'): u' ',
ord(u'\f'): u' ',
ord(u'\r'): None, # Delete
ord('\t'): ' ',
ord('\n'): ' ',
ord('\f'): ' ',
ord('\r'): None, # Delete
}
return string.translate(remap).strip()
return string

18
pytrustnfe/xml/schemas/enviNFe_v3.10.xsd → pytrustnfe/xml/schemas/enviNFe_v4.00.xsd

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.portalfiscal.inf.br/nfe" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" targetNamespace="http://www.portalfiscal.inf.br/nfe" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:include schemaLocation="leiauteNFe_v3.10.xsd"/>
<xs:element name="enviNFe" type="TEnviNFe">
<xs:annotation>
<xs:documentation>Schema XML de validação do Pedido de Concessão de Autorização da Nota Fiscal Eletrônica</xs:documentation>
</xs:annotation>
</xs:element>
</xs:schema>
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.portalfiscal.inf.br/nfe" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" targetNamespace="http://www.portalfiscal.inf.br/nfe" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:include schemaLocation="leiauteNFe_v4.00.xsd"/>
<xs:element name="enviNFe" type="TEnviNFe">
<xs:annotation>
<xs:documentation>Schema XML de validação do Pedido de Concessão de Autorização da Nota Fiscal Eletrônica</xs:documentation>
</xs:annotation>
</xs:element>
</xs:schema>

12540
pytrustnfe/xml/schemas/leiauteNFe_v4.00.xsd
File diff suppressed because it is too large
View File

18
pytrustnfe/xml/schemas/nfe_v3.10.xsd → pytrustnfe/xml/schemas/nfe_v4.00.xsd

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.portalfiscal.inf.br/nfe" targetNamespace="http://www.portalfiscal.inf.br/nfe" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:include schemaLocation="leiauteNFe_v3.10.xsd"/>
<xs:element name="NFe" type="TNFe">
<xs:annotation>
<xs:documentation>Nota Fiscal Eletrônica</xs:documentation>
</xs:annotation>
</xs:element>
</xs:schema>
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.portalfiscal.inf.br/nfe" targetNamespace="http://www.portalfiscal.inf.br/nfe" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:include schemaLocation="leiauteNFe_v4.00.xsd"/>
<xs:element name="NFe" type="TNFe">
<xs:annotation>
<xs:documentation>Nota Fiscal Eletrônica</xs:documentation>
</xs:annotation>
</xs:element>
</xs:schema>

1160
pytrustnfe/xml/schemas/tiposBasico_v4.00.xsd
File diff suppressed because it is too large
View File

0
pytrustnfe/xml/schemas/xmldsig-core-schema_v1.01.xsd

31
pytrustnfe/xml/validate.py

@ -3,39 +3,16 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import os
import re
from lxml import etree
PATH = os.path.dirname(os.path.abspath(__file__))
SCHEMA = os.path.join(PATH, 'schemas/nfe_v3.10.xsd')
SCHEMA = os.path.join(PATH, 'schemas/enviNFe_v4.00.xsd')
def pop_encoding(xml):
xml = xml.split('\n')
if re.match(r'<\?xml version=', xml[0]):
xml.pop(0)
return '\n'.join(xml)
def valida_nfe(nfe):
xml = pop_encoding(nfe).encode('utf-8')
nfe = etree.fromstring(xml)
def valida_nfe(xml_nfe):
nfe = etree.fromstring(xml_nfe)
esquema = etree.XMLSchema(etree.parse(SCHEMA))
esquema.validate(nfe)
erros = [x.message for x in esquema.error_log]
error_msg = '{field} inválido: {valor}.'
unexpected = '{unexpected} não é esperado. O valor esperado é {expected}'
namespace = '{http://www.portalfiscal.inf.br/nfe}'
mensagens = []
for erro in erros:
campo = re.findall(r"'([^']*)'", erro)[0]
nome = campo[campo.find('}') + 1: ]
valor = nfe.find('.//' + campo).text
if 'Expected is' in erro:
expected_name = re.findall('\(.*?\)', erro)
valor = unexpected.format(unexpected=nome, expected=expected_name)
mensagem = error_msg.format(field=campo.replace(namespace, ''),
valor=valor)
mensagens.append(mensagem)
return "\n".join(mensagens)
return "\n".join(erros)

24
requirements.txt

@ -1,16 +1,18 @@
lxml >= 3.5.0, < 4
nose
mock
lxml >= 3.5.0, < 5
coveralls
http://xmlsoft.org/sources/python/libxml2-python-2.6.21.tar.gz
https://github.com/odoo-brazil/pyxmlsec/archive/master.zip
Jinja2
signxml
suds >= 0.4
suds_requests >= 0.3
defusedxml >= 0.4.1, < 0.6
eight >= 0.3.0, < 0.5
cryptography >= 1.8, < 1.10
pyOpenSSL >= 16.0.0, < 17
urllib3 >= 1.22
suds-jurko >= 0.6
suds-jurko-requests >= 1.1
defusedxml >= 0.4.1, < 1
eight >= 0.3.0, < 1
cryptography >= 1.8, < 3
pyOpenSSL >= 16.0.0, < 18
certifi >= 2015.11.20.1
xmlsec >= 1.3.3
reportlab
pytest
pytest-cov
pytz
zeep

32
setup.py

@ -1,33 +1,44 @@
# coding=utf-8
from setuptools import setup, find_packages
VERSION = "0.1.44"
VERSION = "1.0.29"
setup(
name="PyTrustNFe",
name="PyTrustNFe3",
version=VERSION,
author="Danimar Ribeiro",
author_email='danimaribeiro@gmail.com',
keywords=['nfe', 'mdf-e'],
classifiers=[
'Development Status :: 3 - Alpha',
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU Lesser General Public License v2 or \
later (LGPLv2+)',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.4',
'Topic :: Software Development :: Libraries :: Python Modules',
],
packages=find_packages(exclude=['*test*']),
package_data={'pytrustnfe': [
'nfe/templates/*xml',
'nfe/fonts/*ttf',
'nfse/paulistana/templates/*xml',
'nfse/dsf/templates/*xml',
'nfse/ginfes/templates/*xml',
'nfse/simpliss/templates/*xml',
'nfse/betha/templates/*xml',
'nfse/susesu/templates/*xml',
'nfse/imperial/templates/*xml',
'nfse/floripa/templates/*xml',
'nfse/carioca/templates/*xml',
'nfse/bh/templates/*xml',
'nfse/mga/templates/*xml',
'xml/schemas/*xsd',
]},
url='https://github.com/danimaribeiro/PyTrustNFe',
@ -36,15 +47,16 @@ later (LGPLv2+)',
long_description=open('README.md', 'r').read(),
install_requires=[
'Jinja2 >= 2.8',
'pyOpenSSL >= 16.0.0, < 18',
'signxml >= 2.4.0',
'lxml >= 3.5.0, < 4',
'suds >= 0.4',
'suds_requests >= 0.3',
'reportlab'
'lxml >= 3.5.0, < 5',
'suds-jurko >= 0.6',
'suds-jurko-requests >= 1.2',
'reportlab',
'pytz',
'zeep',
],
test_suite='nose.collector',
tests_require=[
'nose',
'mock',
'pytest',
],
)

0
pytrustnfe/test/XMLs/NFe00000857.xml → tests/XMLs/NFe00000857.xml

0
pytrustnfe/test/XMLs/jinja_remove_empty.xml → tests/XMLs/jinja_remove_empty.xml

0
pytrustnfe/test/XMLs/jinja_result.xml → tests/XMLs/jinja_result.xml

0
pytrustnfe/test/XMLs/jinja_template.xml → tests/XMLs/jinja_template.xml

0
pytrustnfe/test/XMLs/paulistana_canc.xml → tests/XMLs/paulistana_canc.xml

1
pytrustnfe/test/XMLs/paulistana_canc_errado.xml → tests/XMLs/paulistana_canc_errado.xml

@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<RetornoCancelamentoNFe
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"

1
pytrustnfe/test/XMLs/paulistana_canc_ok.xml → tests/XMLs/paulistana_canc_ok.xml

@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<RetornoCancelamentoNFe
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"

1
tests/XMLs/paulistana_resultado.xml

@ -0,0 +1 @@
<RetornoEnvioLoteRPS xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.prefeitura.sp.gov.br/nfe"><Cabecalho Versao="1" xmlns=""><Sucesso>true</Sucesso><InformacoesLote><NumeroLote>2654364</NumeroLote><InscricaoPrestador>51212</InscricaoPrestador><CPFCNPJRemetente><CNPJ>21332900163</CNPJ></CPFCNPJRemetente><DataEnvioLote>2016-08-29T10:52:15</DataEnvioLote><QtdNotasProcessadas>1</QtdNotasProcessadas><TempoProcessamento>0</TempoProcessamento><ValorTotalServicos>1.35</ValorTotalServicos></InformacoesLote></Cabecalho><ChaveNFeRPS xmlns=""><ChaveNFe><InscricaoPrestador>52382</InscricaoPrestador><NumeroNFe>446</NumeroNFe><CodigoVerificacao>APR9MJR</CodigoVerificacao></ChaveNFe><ChaveRPS><InscricaoPrestador>51282</InscricaoPrestador><SerieRPS>1</SerieRPS><NumeroRPS>6</NumeroRPS></ChaveRPS></ChaveNFeRPS></RetornoEnvioLoteRPS>

16
pytrustnfe/test/XMLs/paulistana_signature.xml → tests/XMLs/paulistana_signature.xml

@ -1,8 +1,4 @@
<?xml version="1.0"?>
<PedidoEnvioLoteRPS xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.prefeitura.sp.gov.br/nfe"><Cabecalho xmlns="" Versao="1"><CPFCNPJRemetente><CNPJ>12345678901234</CNPJ></CPFCNPJRemetente><transacao>false</transacao><dtInicio>2016-08-29</dtInicio><dtFim>2016-08-29</dtFim><QtdRPS>1</QtdRPS><ValorTotalServicos/><ValorTotalDeducoes/></Cabecalho><RPS xmlns=""><Assinatura>E4fpHYkQa7Naxn6IKGb7NwwZu5tPk/KXJ9hCwtZgq0xvKS450aQqqBL+7Iv46lTgqrSMu7+gLrl+LC1qs/8aT2mbHE8uaVFSbzwZ+sF/BkcT6nsFHLMswEiTAEs95Jb7hN1cC91xqQGRH4buw0TzxHKmhuLJ22WwtG/scxyKtjM=</Assinatura><ChaveRPS><InscricaoPrestador>123456</InscricaoPrestador><SerieRPS>1</SerieRPS><NumeroRPS>1</NumeroRPS></ChaveRPS><TipoRPS>RPS</TipoRPS><DataEmissao>2016-08-29</DataEmissao><StatusRPS>N</StatusRPS><TributacaoRPS>T</TributacaoRPS><ValorServicos/><ValorDeducoes/><ValorPIS>0.00</ValorPIS><ValorCOFINS>0.00</ValorCOFINS><ValorINSS>0.00</ValorINSS><ValorIR>0.00</ValorIR><ValorCSLL>0.00</ValorCSLL><CodigoServico>07498</CodigoServico><AliquotaServicos>5.00</AliquotaServicos><ISSRetido>false</ISSRetido><CPFCNPJTomador>
</CPFCNPJTomador><InscricaoMunicipalTomador>123456</InscricaoMunicipalTomador><RazaoSocialTomador>Trustcode</RazaoSocialTomador><EnderecoTomador><TipoLogradouro>1</TipoLogradouro><Logradouro>Vinicius de Moraes, 42</Logradouro><NumeroEndereco>42</NumeroEndereco><ComplementoEndereco/><Bairro>Corrego</Bairro><Cidade>Floripa</Cidade><UF>SC</UF><CEP>88037240</CEP></EnderecoTomador><Discriminacao>Venda de servico</Discriminacao></RPS><Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<PedidoEnvioLoteRPS xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.prefeitura.sp.gov.br/nfe"><Cabecalho xmlns="" Versao="1"><CPFCNPJRemetente><CNPJ>12345678901234</CNPJ></CPFCNPJRemetente><transacao>false</transacao><dtInicio>2016-08-29</dtInicio><dtFim>2016-08-29</dtFim><QtdRPS>1</QtdRPS><ValorTotalServicos/><ValorTotalDeducoes/></Cabecalho><RPS xmlns=""><Assinatura>E4fpHYkQa7Naxn6IKGb7NwwZu5tPk/KXJ9hCwtZgq0xvKS450aQqqBL+7Iv46lTgqrSMu7+gLrl+LC1qs/8aT2mbHE8uaVFSbzwZ+sF/BkcT6nsFHLMswEiTAEs95Jb7hN1cC91xqQGRH4buw0TzxHKmhuLJ22WwtG/scxyKtjM=</Assinatura><ChaveRPS><InscricaoPrestador>123456</InscricaoPrestador><SerieRPS>1</SerieRPS><NumeroRPS>1</NumeroRPS></ChaveRPS><TipoRPS>RPS</TipoRPS><DataEmissao>2016-08-29</DataEmissao><StatusRPS>N</StatusRPS><TributacaoRPS>T</TributacaoRPS><ValorServicos/><ValorDeducoes/><ValorPIS>0.00</ValorPIS><ValorCOFINS>0.00</ValorCOFINS><ValorINSS>0.00</ValorINSS><ValorIR>0.00</ValorIR><ValorCSLL>0.00</ValorCSLL><CodigoServico>07498</CodigoServico><AliquotaServicos>5.00</AliquotaServicos><ISSRetido>false</ISSRetido><CPFCNPJTomador/><InscricaoMunicipalTomador>123456</InscricaoMunicipalTomador><RazaoSocialTomador>Trustcode</RazaoSocialTomador><EnderecoTomador><TipoLogradouro>1</TipoLogradouro><Logradouro>Vinicius de Moraes, 42</Logradouro><NumeroEndereco>42</NumeroEndereco><ComplementoEndereco/><Bairro>Corrego</Bairro><Cidade>Floripa</Cidade><UF>SC</UF><CEP>88037240</CEP></EnderecoTomador><Discriminacao>Venda de servico</Discriminacao><ValorCargaTributaria/><FonteCargaTributaria/></RPS><Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
@ -12,12 +8,12 @@
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>ivaOwkcrt0pfuMYsAdfyLaUAcIk=</DigestValue>
<DigestValue>IAh8GGlbp/Tnqma+2RZ7UrGZhTc=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>FjIHdfPavSEyaWYhAT0z0shPLuTsqBKyy78PUEZ8PUhTZ+iSV0MOvAIRq9MPPVK9
jjXOw1TE903uSK8aJon52RNKPd68ORVJ3bKFSjTqQLxFRR9tiiAQFrWDETf7FF89
EhG6dy6TGcgVbOyn0Jqm8MkqrE1XrJ44orN1X+Jt+7U=</SignatureValue>
<SignatureValue>gjkMTCq0uuaX8tkRBlLjgybn8a2O4Axl6HHq1MN8nnEMliERcziU3pa3r1jbghlE
EUyIO8bTZ0V7c05pQvHQgVUHcSo6vHld4ZQNk7JfMfmpez4uxrUeuSrSqSLCwT9W
NmTY9EJ16GyrQNELw+SkYuEFOvqZTU3qjDZkLddQ8bc=</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIICMTCCAZqgAwIBAgIQfYOsIEVuAJ1FwwcTrY0t1DANBgkqhkiG9w0BAQUFADBX
@ -34,4 +30,4 @@ QtgAhuZM9rxpOJuNKc+pM29EixpAiZZiRMCSWEItNyEVdUIi+YnKBcAHd88TwO86
d126MWQ2O8cu5W1VoDp7hYBYKOnLbYi11/StO+0rzK+oPYAvIw==</X509Certificate>
</X509Data>
</KeyInfo>
</Signature></PedidoEnvioLoteRPS>
</Signature></PedidoEnvioLoteRPS>

0
pytrustnfe/test/XMLs/recibo_envio_1.xml → tests/XMLs/recibo_envio_1.xml

0
pytrustnfe/test/XMLs/recibo_envio_2.xml → tests/XMLs/recibo_envio_2.xml

0
pytrustnfe/test/XMLs/recibo_protocolo_sucesso_1.xml → tests/XMLs/recibo_protocolo_sucesso_1.xml

0
pytrustnfe/test/XMLs/recibo_protocolo_sucesso_2.xml → tests/XMLs/recibo_protocolo_sucesso_2.xml

0
pytrustnfe/test/__init__.py → tests/__init__.py

12
pytrustnfe/test/test_assinatura.py → tests/test_assinatura.py

@ -11,16 +11,14 @@ from lxml import etree
from pytrustnfe.nfe.assinatura import Assinatura
XML_ASSINAR = '<?xml version="1.0" encoding="UTF-8"?>' \
'<Envelope xmlns="urn:envelope">' \
XML_ASSINAR = '<Envelope xmlns="urn:envelope">' \
' <Data Id="NFe43150602261542000143550010000000761792265342">'\
' Hello, World!' \
' </Data>' \
'</Envelope>'
XML_ERRADO = '<?xml version="1.0" encoding="UTF-8"?>' \
'<Envelope xmlns="urn:envelope">' \
XML_ERRADO = '<Envelope xmlns="urn:envelope">' \
' <Data Id="NFe">' \
' Hello, World!' \
' </Data>' \
@ -32,21 +30,21 @@ class test_assinatura(unittest.TestCase):
caminho = os.path.dirname(__file__)
def test_assinar_xml_senha_invalida(self):
pfx = open(os.path.join(self.caminho, 'teste.pfx')).read()
pfx = open(os.path.join(self.caminho, 'teste.pfx'), 'rb').read()
signer = Assinatura(pfx, '123')
self.assertRaises(Exception, signer.assina_xml, signer,
etree.fromstring(XML_ASSINAR),
'NFe43150602261542000143550010000000761792265342')
def test_assinar_xml_invalido(self):
pfx = open(os.path.join(self.caminho, 'teste.pfx')).read()
pfx = open(os.path.join(self.caminho, 'teste.pfx'), 'rb').read()
signer = Assinatura(pfx, '123456')
self.assertRaises(Exception, signer.assina_xml, signer,
etree.fromstring(XML_ERRADO),
'NFe43150602261542000143550010000000761792265342')
def test_assinar_xml_valido(self):
pfx = open(os.path.join(self.caminho, 'teste.pfx')).read()
pfx = open(os.path.join(self.caminho, 'teste.pfx'), 'rb').read()
signer = Assinatura(pfx, '123456')
xml = signer.assina_xml(
etree.fromstring(XML_ASSINAR),

8
pytrustnfe/test/test_certificado.py → tests/test_certificado.py

@ -49,21 +49,21 @@ class test_assinatura(unittest.TestCase):
caminho = os.path.dirname(__file__)
def test_preparar_pfx(self):
dir_pfx = open(os.path.join(self.caminho, 'teste.pfx'), 'r').read()
dir_pfx = open(os.path.join(self.caminho, 'teste.pfx'), 'rb').read()
cert, key = extract_cert_and_key_from_pfx(dir_pfx, '123456')
self.assertEqual(key, CHAVE, 'Chave gerada inválida')
self.assertEqual(cert, CERTIFICADO, 'Certificado inválido')
def test_save_pfx(self):
pfx_source = open(os.path.join(self.caminho, 'teste.pfx'), 'r').read()
pfx_source = open(os.path.join(self.caminho, 'teste.pfx'), 'rb').read()
pfx = Certificado(pfx_source, '123')
path = pfx.save_pfx()
saved = open(path, 'r').read()
saved = open(path, 'rb').read()
self.assertEqual(pfx_source, saved,
'Arquivo pfx salvo não bate com arquivo lido')
def test_save_cert_and_key(self):
dir_pfx = open(os.path.join(self.caminho, 'teste.pfx'), 'r').read()
dir_pfx = open(os.path.join(self.caminho, 'teste.pfx'), 'rb').read()
cert, key = extract_cert_and_key_from_pfx(dir_pfx, '123456')
cert_path, key_path = save_cert_key(cert, key)
cert_saved = open(cert_path, 'r').read()

2
pytrustnfe/test/test_danfe.py → tests/test_danfe.py

@ -22,5 +22,5 @@ class test_danfe(unittest.TestCase):
# Para testar localmente o Danfe
# with open('/home/danimar/danfe.pdf', 'w') as oFile:
with tempfile.TemporaryFile(mode='w') as oFile:
with tempfile.TemporaryFile(mode='wb') as oFile:
oDanfe.writeto_pdf(oFile)

2
pytrustnfe/test/test_ginfes.py → tests/test_ginfes.py

@ -13,7 +13,7 @@ class test_nfse_ginfes(unittest.TestCase):
@unittest.skip
def test_consulta_situacao_lote(self):
pfx_source = open('/home/danimar/Downloads/machado.pfx', 'r').read()
pfx_source = open('/home/danimar/Downloads/machado.pfx', 'rb').read()
pfx = Certificado(pfx_source, '123456789')
dados = {'ambiente': 'homologacao'}

8
pytrustnfe/test/test_nfse_paulistana.py → tests/test_nfse_paulistana.py

@ -54,7 +54,7 @@ class test_nfse_paulistana(unittest.TestCase):
return nfse
def test_envio_nfse(self):
pfx_source = open(os.path.join(self.caminho, 'teste.pfx'), 'r').read()
pfx_source = open(os.path.join(self.caminho, 'teste.pfx'), 'rb').read()
pfx = Certificado(pfx_source, '123456')
nfse = self._get_nfse()
@ -77,7 +77,7 @@ class test_nfse_paulistana(unittest.TestCase):
retorno['object'].ChaveNFeRPS.ChaveRPS.NumeroRPS, 6)
def test_nfse_signature(self):
pfx_source = open(os.path.join(self.caminho, 'teste.pfx'), 'r').read()
pfx_source = open(os.path.join(self.caminho, 'teste.pfx'), 'rb').read()
pfx = Certificado(pfx_source, '123456')
nfse = self._get_nfse()
@ -103,7 +103,7 @@ class test_nfse_paulistana(unittest.TestCase):
}
def test_cancelamento_nfse_ok(self):
pfx_source = open(os.path.join(self.caminho, 'teste.pfx'), 'r').read()
pfx_source = open(os.path.join(self.caminho, 'teste.pfx'), 'rb').read()
pfx = Certificado(pfx_source, '123456')
cancelamento = self._get_cancelamento()
@ -122,7 +122,7 @@ class test_nfse_paulistana(unittest.TestCase):
self.assertEqual(retorno['object'].Cabecalho.Sucesso, True)
def test_cancelamento_nfse_com_erro(self):
pfx_source = open(os.path.join(self.caminho, 'teste.pfx'), 'r').read()
pfx_source = open(os.path.join(self.caminho, 'teste.pfx'), 'rb').read()
pfx = Certificado(pfx_source, '123456')
cancelamento = self._get_cancelamento()

17
pytrustnfe/test/test_servidores.py → tests/test_servidores.py

@ -7,22 +7,21 @@ Created on Jun 14, 2015
import unittest
from pytrustnfe.Servidores import localizar_url, localizar_qrcode
url_ba = 'https://nfe.sefaz.ba.gov.br/webservices/NfeAutorizacao/NfeAutoriza\
cao.asmx'
url_ba = 'https://nfe.sefaz.ba.gov.br/webservices/NFeAutorizacao4/NFeAutoriza\
cao4.asmx?wsdl'
url_sp = 'https://nfe.fazenda.sp.gov.br/ws/nfeautorizacao.asmx'
url_sp = 'https://nfe.fazenda.sp.gov.br/ws/nfeautorizacao4.asmx?wsdl'
url_qrcode_homologacao_sp = 'https://homologacao.nfce.fazenda.sp.gov.br/NFCEConsultaPublica/Paginas/ConstultaQRCode.aspx'
url_sc = 'https://nfe.svrs.rs.gov.br/ws/NfeAutorizacao/NfeAutorizacao.asmx'
url_sc = 'https://nfe.svrs.rs.gov.br/ws/NfeAutorizacao/NFeAutorizacao4.asmx?wsdl'
url_rs = 'https://nfe.sefazrs.rs.gov.br/ws/NfeAutorizacao/NFeAutorizacao.asmx'
url_rs = 'https://nfe.sefazrs.rs.gov.br/ws/NfeAutorizacao/NFeAutorizacao4.asmx?wsdl'
url_cad_rs = 'https://cad.sefazrs.rs.gov.br/ws/cadconsultacadastro/cadcon\
sultacadastro2.asmx'
url_cad_rs = 'https://cad.sefazrs.rs.gov.br/ws/cadconsultacadastro/cadconsultacadastro4.asmx?wsdl'
url_cad_sc = 'https://cad.svrs.rs.gov.br/ws/CadConsultaCadastro/CadConsult\
aCadastro2.asmx'
url_cad_sc = 'https://cad.svrs.rs.gov.br/ws/cadconsultacadastro/cadconsulta\
cadastro2.asmx?wsdl'
class test_servidores(unittest.TestCase):

24
pytrustnfe/test/test_utils.py → tests/test_utils.py

@ -8,7 +8,7 @@ import unittest
import datetime
from pytrustnfe.utils import date_tostring, datetime_tostring, \
gerar_chave
from pytrustnfe.utils import ChaveNFe, CabecalhoSoap
from pytrustnfe.utils import ChaveNFe
class test_utils(unittest.TestCase):
@ -18,12 +18,6 @@ class test_utils(unittest.TestCase):
'tipo': 0, 'codigo': '26730161'
}
def test_cabecalho_soap(self):
head = CabecalhoSoap(versao=1, estado='SC', soap_action='Autorizacao')
self.assertEqual(head.versao, 1)
self.assertEqual(head.estado, 'SC')
self.assertEqual(head.soap_action, 'Autorizacao')
def test_date_tostring(self):
hoje = datetime.date.today()
data = date_tostring(hoje)
@ -60,7 +54,7 @@ class test_utils(unittest.TestCase):
chave.validar()
chave.cnpj = '1234567891011'
self.assertEqual('CNPJ necessário para criar chave NF-e',
cm.exception.message,
str(cm.exception),
'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm:
@ -68,7 +62,7 @@ class test_utils(unittest.TestCase):
chave.validar()
chave.estado = '42'
self.assertEqual('Estado necessário para criar chave NF-e',
cm.exception.message,
str(cm.exception),
'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm:
@ -76,7 +70,7 @@ class test_utils(unittest.TestCase):
chave.validar()
chave.emissao = '0'
self.assertEqual('Emissão necessário para criar chave NF-e',
cm.exception.message,
str(cm.exception),
'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm:
@ -84,7 +78,7 @@ class test_utils(unittest.TestCase):
chave.validar()
chave.modelo = '55'
self.assertEqual('Modelo necessário para criar chave NF-e',
cm.exception.message,
str(cm.exception),
'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm:
@ -92,7 +86,7 @@ class test_utils(unittest.TestCase):
chave.validar()
chave.serie = '012'
self.assertEqual('Série necessária para criar chave NF-e',
cm.exception.message,
str(cm.exception),
'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm:
@ -100,7 +94,7 @@ class test_utils(unittest.TestCase):
chave.validar()
chave.numero = '000000780'
self.assertEqual('Número necessário para criar chave NF-e',
cm.exception.message,
str(cm.exception),
'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm:
@ -108,12 +102,12 @@ class test_utils(unittest.TestCase):
chave.validar()
chave.tipo = '42'
self.assertEqual('Tipo necessário para criar chave NF-e',
cm.exception.message,
str(cm.exception),
'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm:
chave.codigo = ''
chave.validar()
self.assertEqual('Código necessário para criar chave NF-e',
cm.exception.message,
str(cm.exception),
'Validação da chave nf-e incorreta')

4
pytrustnfe/test/test_xml.py → tests/test_xml.py

@ -26,5 +26,5 @@ class test_xmlfilters(unittest.TestCase):
self.assertEqual('2016-09-17', format_date(dt.date()))
self.assertEqual('2016-09-17T12:12:12', format_datetime(dt))
word = strip_line_feed(u"olá\ncomo vai\r senhor ")
self.assertEqual(word, u"olá como vai senhor")
word = strip_line_feed("olá\ncomo vai\r senhor ")
self.assertEqual(word, "olá como vai senhor")

5
pytrustnfe/test/test_xml_serializacao.py → tests/test_xml_serializacao.py

@ -15,13 +15,13 @@ class test_xml_serializacao(unittest.TestCase):
tag2='ola', tag3='comovai')
result = open(os.path.join(path, 'jinja_result.xml'), 'r').read()
self.assertEqual(xml + '\n', result)
self.assertEqual(xml + "\n", result)
def test_serializacao_remove_empty(self):
path = os.path.join(os.path.dirname(__file__), 'XMLs')
xmlElem = render_xml(path, 'jinja_template.xml', True, tag1='oi',
tag2='ola', tag3='comovai')
xml = etree.tostring(xmlElem)
xml = etree.tostring(xmlElem, encoding=str)
result = open(os.path.join(path, 'jinja_remove_empty.xml'), 'r').read()
self.assertEqual(xml + '\n', result)
@ -29,7 +29,6 @@ class test_xml_serializacao(unittest.TestCase):
path = os.path.join(os.path.dirname(__file__), 'XMLs')
xml_to_clear = open(os.path.join(path, 'jinja_result.xml'), 'r').read()
xml, obj = sanitize_response(xml_to_clear)
self.assertEqual(xml, xml_to_clear)
self.assertEqual(obj.tpAmb, 'oi')
self.assertEqual(obj.CNPJ, 'ola')

0
pytrustnfe/test/teste.pfx → tests/teste.pfx

0
pytrustnfe/test/xml_assinado.xml → tests/xml_assinado.xml

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save