Compare commits

...

205 Commits

Author SHA1 Message Date
Danimar Ribeiro 7b6d4600a0 Adicionando prefeitura de duque de caxias 5 years ago
Danimar Ribeiro c5b4d9e4e8 Fix unit tests 5 years ago
Danimar Ribeiro 7729c1df50 Carga tributaria media como opcional 5 years ago
Danimar Ribeiro 325bb78234 New urls for nfce RS 5 years ago
Danimar Ribeiro ae1d57b5c6 Fixed overflow of address text 6 years ago
Fábio Luna 05472e59a2 Melhoria na DANFE 6 years ago
Danimar Ribeiro 6357ea46a0
Update XSD schemas for NFe (#272) 6 years ago
Danimar Ribeiro 4e6da7011e
Novos campos para icms retido por substituição tributária (#270) 6 years ago
Danimar Ribeiro 36c9b6c9ae Fix the xml template for sending nfe 6 years ago
Danimar Ribeiro 57672b4d9e
Bump version (#266) 6 years ago
Ednaldo Batista Barreto 09a0ff15be
Criando parametros para contingência (#233) 6 years ago
Danimar Ribeiro 09428c65a3
Merge pull request #265 from danimaribeiro/gabicavalcante-master3 6 years ago
Danimar Ribeiro 944c7145bf Revert a few things before for nfe 6 years ago
Danimar Ribeiro 66127b4b1e
Merge branch 'master3' into gabicavalcante-master3 6 years ago
Danimar Ribeiro 4eaaa83016
Merge pull request #264 from danimaribeiro/loggi/master3 6 years ago
Danimar Ribeiro 79e192c9f8 Increment version 6 years ago
Gabriela Cavalcante da Silva 3d2bd59f98 update gitignore 6 years ago
Isabela Morais 9c4650d5da
Merge branch 'master3' into master3 6 years ago
Isabela Morais 80950950d7
Merge pull request #7 from loggi/resolve-fork 6 years ago
Isabela Morais 13900c782b test batch size 6 years ago
Gabriela Cavalcante da Silva 7eb152da65 updates 6 years ago
Isabela Morais 547af9d609 adding tests for rps batch 6 years ago
Gabriela Cavalcante da Silva 77b4bfbfa9 fix tests 6 years ago
Gabriela Cavalcante da Silva 404c9dd743 fix natal 6 years ago
Gabriela Cavalcante da Silva a14a06c2e9 remove 6 years ago
Gabriela Cavalcante da Silva 0b5ffd82af base url 6 years ago
Gabriela Cavalcante da Silva 9b125f513e fix nfse natal 6 years ago
Gabriela Cavalcante da Silva ecea3543ad add tests natal 6 years ago
Gabriela Cavalcante da Silva 9574a48d8d flask8 6 years ago
Gabriela Cavalcante da Silva 8c311d4caf black - python code formater 6 years ago
Gabriela Cavalcante da Silva c7beb73a6b add natal nfse 6 years ago
regianenas 100d6aa909
(Pytrust) fixing templete and bump version to 1.0.33.post3 (#6) 6 years ago
regianenas 09362d0f48
feat(pytrust) changing amount of rps sent. (#3) 6 years ago
Danimar Ribeiro 0b2e3ce204 New release 6 years ago
rodriguesraphael 55c44c2beb Adicionado as URL's para emissão de NFC-e no estado de MG. close issue #255 6 years ago
rodriguesraphael 90544b7301 Alterado webservice de consulta CNPJ do Estado de Minas Gerais 6 years ago
Éder Brito a94092d128 [FIX] Corrigindo valor vISSRet 6 years ago
Felipe Paloschi 8f98065ffc Add value 0.00 when ICMS value is not provided 6 years ago
Danimar Ribeiro 8768b24202 [IMP] Implementação de danfce de contingência - mensagens no danfce 6 years ago
Danimar Ribeiro 8e2b326010 [FIX] Cancelamento pode também ser status 155 6 years ago
Danimar Ribeiro 4ebc848237 [VER] Increment version 6 years ago
Danimar Ribeiro a9154ba93d [FIX] Utilizando o campo xPed diretamente do produto 6 years ago
Danimar Ribeiro 704cd29b42 [FIX] Corrige NFS-e Simpliss 6 years ago
Danimar Ribeiro ad83071171 [WIP] Implementação de NFSe Aparecida SP. 6 years ago
Ednaldo Batista Barreto b9f567953b Correção no arquivo xsd referenciado 7 years ago
Danimar Ribeiro e229bd8f53 [ADD] new version 7 years ago
Danimar Ribeiro 7f360d39a8 [FIX] Corrige Urls de NFC-e para SP 7 years ago
Ednaldo Batista Barreto 2ada587c2a Melhorias no DFeDistribuicao (#230) 7 years ago
maiconkkl cd9b87da94 Corrigindo as URL de manaus 7 years ago
Michael Douglas Pereira de Morais 9b8a37ce41 New version 7 years ago
maiconkkl fd165c103b Correçao do erro gerado na mudança de versao 1.0.37 7 years ago
marinaGD a01421febd [FIX] servidor consulta svrs 7 years ago
Danimar Ribeiro ba2ca969c8 [FIX] Add markdown type to readme 7 years ago
marinaGD d8d6b947a6 [ADD] Adiciona tag xped no item da nfe 7 years ago
Danimar Ribeiro 0d3dd0693f [FIX] Ajusta a nota de petropolis ao novo formato 7 years ago
marinaGD 710490dc1d [FIX] Product description overwriting NCM (#223) 7 years ago
Fábio Luna 391dd1ae7d [IMP] NT 2018_005 v1.20 (#222) 7 years ago
marinaGD eadc7b7306 [VER] New version 7 years ago
Felipe Prenholato aea7429327 Merge branch 'master3' of github.com:danimaribeiro/PyTrustNFe into master3 7 years ago
marinaGD 051f3b8f12 [VERSION] Increments version 7 years ago
marinaGD b5bcf0cd88 [FIX] Last item missed when doesnt fit in current page 7 years ago
yk2kus ba9e05538e Fix 'bytes' object has no attribute 'encode' 7 years ago
Fábio Luna c16a31469e Incrementa versão. 7 years ago
Fábio Luna 452af1ecb3 Corrige url de consulta NFC-e Paraná. 7 years ago
marinaGD 945dd77bea [FIX] nfeInutilizacaoCE url (#211) 7 years ago
Danimar Ribeiro b01cde145e
Merge pull request #213 from danimaribeiro/Implanti-Solucoes-master3 7 years ago
Danimar Ribeiro bf03d54107 [VERSION] Increment pytest version to fix build 7 years ago
Ednaldo Batista Barreto 197f36b9ed Erro na url de produção 7 years ago
Ednaldo Batista Barreto 628c3a20bf Correção do p= 7 years ago
Ednaldo Batista Barreto 9c4f76446b urlChave e Webservices 7 years ago
Danimar Ribeiro b0a9e5370e [FIX] Fix patch method for inutilizacao - wrong merge 7 years ago
marinaGD c74893b658 [FIX] Inutilização NFe CE - Add a patch method 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
Felipe Prenholato d4fdda4dab
Merge pull request #1 from loggi/frp-better-templates-for-nfse-paulistana-py3 8 years ago
Felipe Prenholato 7fa4f4602f Add missing deps, bump version to 0.9.18.post2 8 years ago
Danimar Ribeiro 966ba5f795 [FIX] XML NFSe maringa and percent difal 8 years ago
Felipe Prenholato d616b3b9a6 Fix case name, bump version to 0.9.18.post1 8 years ago
Felipe Prenholato 49113d7dab Better templates, for real. 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. 6
      .flake8
  2. 8
      .gitignore
  3. 10
      .travis.yml
  4. 125
      README.md
  5. 7
      cidades/dsf.md
  6. 165
      docs/conf.py
  7. 1222
      pytrustnfe/Servidores.py
  8. 8
      pytrustnfe/__init__.py
  9. 23
      pytrustnfe/certificado.py
  10. 36
      pytrustnfe/client.py
  11. 4
      pytrustnfe/exceptions.py
  12. 501
      pytrustnfe/nfe/__init__.py
  13. 29
      pytrustnfe/nfe/assinatura.py
  14. 34
      pytrustnfe/nfe/comunicacao.py
  15. 529
      pytrustnfe/nfe/danfce.py
  16. 1268
      pytrustnfe/nfe/danfe.py
  17. BIN
      pytrustnfe/nfe/fonts/NimbusSanL Bold.ttf
  18. BIN
      pytrustnfe/nfe/fonts/NimbusSanL Regular.ttf
  19. 45
      pytrustnfe/nfe/patch.py
  20. 33
      pytrustnfe/nfe/templates/NFeDistribuicaoDFe.xml
  21. 279
      pytrustnfe/nfe/templates/NfeAutorizacao.xml
  22. 5
      pytrustnfe/nfe/templates/NfeConsultaProtocolo.xml
  23. 2
      pytrustnfe/nfe/templates/NfeInutilizacao.xml
  24. 2
      pytrustnfe/nfe/templates/NfeRetAutorizacao.xml
  25. 4
      pytrustnfe/nfe/templates/NfeStatusServico.xml
  26. 6
      pytrustnfe/nfe/templates/RecepcaoEvento.xml
  27. 21
      pytrustnfe/nfe/templates/RecepcaoEventoCarta.xml
  28. 31
      pytrustnfe/nfe/templates/RecepcaoEventoEPEC.xml
  29. 2
      pytrustnfe/nfe/templates/RecepcaoEventoManifesto.xml
  30. 76
      pytrustnfe/nfse/aparecida/__init__.py
  31. 91
      pytrustnfe/nfse/aparecida/templates/Rps.xml
  32. 15
      pytrustnfe/nfse/aparecida/templates/cancelarNfse.xml
  33. 7
      pytrustnfe/nfse/aparecida/templates/consultarLoteRps.xml
  34. 13
      pytrustnfe/nfse/aparecida/templates/recepcionarLoteRps.xml
  35. 99
      pytrustnfe/nfse/assinatura.py
  36. 78
      pytrustnfe/nfse/betha/__init__.py
  37. 79
      pytrustnfe/nfse/bh/__init__.py
  38. 47
      pytrustnfe/nfse/bh/assinatura.py
  39. 13
      pytrustnfe/nfse/bh/templates/CancelarNfse.xml
  40. 11
      pytrustnfe/nfse/bh/templates/GerarNfse.xml
  41. 91
      pytrustnfe/nfse/bh/templates/Rps.xml
  42. 69
      pytrustnfe/nfse/carioca/__init__.py
  43. 13
      pytrustnfe/nfse/carioca/templates/CancelarNfse.xml
  44. 3
      pytrustnfe/nfse/carioca/templates/GerarNfse.xml
  45. 91
      pytrustnfe/nfse/carioca/templates/Rps.xml
  46. 124
      pytrustnfe/nfse/dsf/__init__.py
  47. 18
      pytrustnfe/nfse/dsf/templates/cancelar.xml
  48. 11
      pytrustnfe/nfse/dsf/templates/consulta_notas.xml
  49. 10
      pytrustnfe/nfse/dsf/templates/consultarLote.xml
  50. 22
      pytrustnfe/nfse/dsf/templates/consultarNFSeRps.xml
  51. 100
      pytrustnfe/nfse/dsf/templates/enviar.xml
  52. 12
      pytrustnfe/nfse/dsf/templates/soap_header.xml
  53. 128
      pytrustnfe/nfse/floripa/__init__.py
  54. 7
      pytrustnfe/nfse/floripa/templates/cancelar_nota.xml
  55. 41
      pytrustnfe/nfse/floripa/templates/processar_nota.xml
  56. 86
      pytrustnfe/nfse/ginfes/__init__.py
  57. 74
      pytrustnfe/nfse/imperial/__init__.py
  58. 17
      pytrustnfe/nfse/imperial/templates/CANCELANOTAELETRONICA.xml
  59. 9
      pytrustnfe/nfse/imperial/templates/CONSULTANOTASPROTOCOLO.xml
  60. 9
      pytrustnfe/nfse/imperial/templates/CONSULTAPROTOCOLO.xml
  61. 14
      pytrustnfe/nfse/imperial/templates/CancelarNota.xml
  62. 43
      pytrustnfe/nfse/imperial/templates/GerarNota.xml
  63. 81
      pytrustnfe/nfse/imperial/templates/PROCESSARPS.xml
  64. 5
      pytrustnfe/nfse/imperial/templates/SoapRequest.xml
  65. 81
      pytrustnfe/nfse/issnet/__init__.py
  66. 13
      pytrustnfe/nfse/issnet/templates/CancelarNfse.xml
  67. 7
      pytrustnfe/nfse/issnet/templates/ConsultarLoteRps.xml
  68. 13
      pytrustnfe/nfse/issnet/templates/RecepcionarLoteRps.xml
  69. 91
      pytrustnfe/nfse/issnet/templates/Rps.xml
  70. 70
      pytrustnfe/nfse/mga/__init__.py
  71. 48
      pytrustnfe/nfse/mga/assinatura.py
  72. 15
      pytrustnfe/nfse/mga/templates/CancelarNfse.xml
  73. 3
      pytrustnfe/nfse/mga/templates/GerarNfse.xml
  74. 81
      pytrustnfe/nfse/mga/templates/Rps.xml
  75. 116
      pytrustnfe/nfse/natal/__init__.py
  76. 8
      pytrustnfe/nfse/natal/templates/EnvelopeSoap.xml
  77. 111
      pytrustnfe/nfse/natal/templates/Exemplo_LoteRPS.xml
  78. 112
      pytrustnfe/nfse/natal/templates/Rps.xml
  79. 3
      pytrustnfe/nfse/natal/templates/cabecalho.xml
  80. 15
      pytrustnfe/nfse/natal/templates/cancelarNfse.xml
  81. 7
      pytrustnfe/nfse/natal/templates/consultarLoteRps.xml
  82. 11
      pytrustnfe/nfse/natal/templates/recepcionarLoteRps.xml
  83. 73
      pytrustnfe/nfse/paulistana/__init__.py
  84. 23
      pytrustnfe/nfse/paulistana/templates/EnvioLoteRPS.xml
  85. 62
      pytrustnfe/nfse/paulistana/templates/EnvioRPS.xml
  86. 66
      pytrustnfe/nfse/simpliss/__init__.py
  87. 36
      pytrustnfe/nfse/susesu/__init__.py
  88. 9
      pytrustnfe/nfse/susesu/templates/EnviarNota.xml
  89. 1
      pytrustnfe/test/XMLs/paulistana_resultado.xml
  90. 34
      pytrustnfe/test/test_add_qr_code.py
  91. 57
      pytrustnfe/test/test_assinatura.py
  92. 82
      pytrustnfe/test/test_certificado.py
  93. 20
      pytrustnfe/test/test_comunicacao.py
  94. 19
      pytrustnfe/test/test_consulta_cadastro.py
  95. 143
      pytrustnfe/test/test_nfse_paulistana.py
  96. 48
      pytrustnfe/test/test_servidores.py
  97. 119
      pytrustnfe/test/test_utils.py
  98. 37
      pytrustnfe/test/test_xml_serializacao.py
  99. 189
      pytrustnfe/test/xml_com_qrcode.xml
  100. 190
      pytrustnfe/test/xml_sem_qrcode.xml

6
.flake8

@ -0,0 +1,6 @@
[flake8]
ignore = E203, E266, E501, W503, F403, E402, E265, E231
max-line-length = 79
max-complexity = 18
select = B,C,E,F,W,T4,B9
exclude = .git,__pycache__,api/specs/,migrations/

8
.gitignore

@ -11,4 +11,10 @@ dist/
*egg*/
docs/_build
.vscode/tags
.cache/
.cache
.pytest_cache
.vscode/
*.pfx
sent_xml.xml
received_xml.xml
nfse-exemplo.py

10
.travis.yml

@ -1,13 +1,11 @@
dist: precise
language: python
python:
- '2.7'
virtual_env:
system_site_packages: true
- "3.6"
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 +17,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"

125
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
@ -39,7 +39,7 @@ Implementar novos provedores de NFSe
Exemplos de uso da NFe
---------------
-----------------------------
Consulta Cadastro por CNPJ:
@ -52,7 +52,102 @@ certificado = Certificado(certificado, 'senha_pfx')
obj = {'cnpj': '12345678901234', 'estado': '42'}
resposta = consulta_cadastro(certificado, obj=obj, ambiente=1, estado='42')
```
Consulta Distribuição NF-e sem Validação de Esquema:
```python
from pytrustnfe.certificado import Certificado
from pytrustnfe.nfe import consulta_distribuicao_nfe, xml_consulta_distribuicao_nfe
certificado = open("/path/certificado.pfx", "r").read()
certificado = Certificado(certificado, 'senha_pfx')
# Gerando xml e enviado consulta por Ultimo NSU
response1 = consulta_distribuicao_nfe(
certificado,
ambiente=1,
estado='42',
modelo='55',
cnpj_cpf='12345678901234',
ultimo_nsu='123456789101213'
)
# Gerando xml e enviado consulta por Chave
response2 = consulta_distribuicao_nfe(
certificado,
ambiente=1,
estado='42',
modelo='55',
cnpj_cpf='12345678901234',
chave_nfe='012345678901234567890123456789012345678912'
)
# Gerando xml e enviado consulta por NSU
response3 = consulta_distribuicao_nfe(
certificado,
ambiente=1,
estado='42',
modelo='55',
cnpj_cpf='12345678901234',
nsu='123456789101213'
)
```
Consulta Distribuição NF-e com Validação de Esquema:
```python
from pytrustnfe.certificado import Certificado
from pytrustnfe.nfe import consulta_distribuicao_nfe, xml_consulta_distribuicao_nfe
from pytrustnfe.xml.validate import valida_nfe, SCHEMA_DFE
certificado = open("/path/certificado.pfx", "r").read()
certificado = Certificado(certificado, 'senha_pfx')
# Gerando XML para Consulta por Ultimo NSU
xml1 = xml_consulta_distribuicao_nfe(
certificado,
ambiente=1,
estado='42',
cnpj_cpf='12345678901234',
ultimo_nsu='123456789101213'
)
# Validando o XML com Esquema
if valida_nfe(xml1, SCHEMA_DFE):
Warning("Erro na validação do esquema")
# Gerando XML para Consulta por Chave
xml2 = xml_consulta_distribuicao_nfe(
certificado,
ambiente=1,
estado='42',
cnpj_cpf='12345678901234',
chave_nfe='012345678901234567890123456789012345678912'
)
# Validando o XML com Esquema
if valida_nfe(xml2, SCHEMA_DFE):
Warning("Erro na validação do esquema")
# Gerando XML para Consulta por NSU
xml3 = xml_consulta_distribuicao_nfe(
certificado,
ambiente=1,
estado='42',
cnpj_cpf='12345678901234',
nsu='123456789101213'
)
# Validando o XML com Esquema
if valida_nfe(xml3, SCHEMA_DFE):
Warning("Erro na validação do esquema")
# Enviando xml de consulta para sefaz
response = consulta_distribuicao_nfe(
certificado,
ambiente=1,
estado='42',
modelo='55',
xml=xml1
)
```
Exemplo de uso da NFSe Paulistana
---------------------------------

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

165
docs/conf.py

@ -19,89 +19,89 @@ import shlex
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.coverage',
"sphinx.ext.autodoc",
"sphinx.ext.coverage",
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
source_suffix = ".rst"
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
master_doc = "index"
# General information about the project.
project = u'PyTrustNFe'
copyright = u'2015, Danimar Ribeiro'
author = u'Danimar Ribeiro'
project = u"PyTrustNFe"
copyright = u"2015, Danimar Ribeiro"
author = u"Danimar Ribeiro"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '1.0'
version = "1.0"
# The full version, including alpha/beta/rc tags.
release = '1.0'
release = "1.0"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'pt_BR'
language = "pt_BR"
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# keep_warnings = False
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
@ -111,156 +111,155 @@ todo_include_todos = False
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'nature'
html_theme = "nature"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = ["_static"]
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# html_file_suffix = None
# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
#html_search_language = 'en'
# html_search_language = 'en'
# A dictionary with options for the search language support, empty by default.
# Now only 'ja' uses this config value
#html_search_options = {'type': 'default'}
# html_search_options = {'type': 'default'}
# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
#html_search_scorer = 'scorer.js'
# html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder.
htmlhelp_basename = 'PyTrustNFedoc'
htmlhelp_basename = "PyTrustNFedoc"
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
# Latex figure (float) alignment
#'figure_align': 'htbp',
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
# Latex figure (float) alignment
#'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'PyTrustNFe.tex', u'PyTrustNFe Documentation',
u'Danimar Ribeiro', 'manual'),
(
master_doc,
"PyTrustNFe.tex",
u"PyTrustNFe Documentation",
u"Danimar Ribeiro",
"manual",
),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'pytrustnfe', u'PyTrustNFe Documentation',
[author], 1)
]
man_pages = [(master_doc, "pytrustnfe", u"PyTrustNFe Documentation", [author], 1)]
# If true, show URL addresses after external links.
#man_show_urls = False
# man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
@ -269,19 +268,25 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'PyTrustNFe', u'PyTrustNFe Documentation',
author, 'PyTrustNFe', 'One line description of project.',
'Miscellaneous'),
(
master_doc,
"PyTrustNFe",
u"PyTrustNFe Documentation",
author,
"PyTrustNFe",
"One line description of project.",
"Miscellaneous",
),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
# texinfo_no_detailmenu = False

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

8
pytrustnfe/__init__.py

@ -6,16 +6,14 @@ import requests
class HttpClient(object):
def __init__(self, url):
self.url = url
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):

23
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
@ -23,23 +22,21 @@ class Certificado(object):
def extract_cert_and_key_from_pfx(pfx, password):
pfx = crypto.load_pkcs12(pfx, password)
# PEM formatted private key
key = crypto.dump_privatekey(crypto.FILETYPE_PEM,
pfx.get_privatekey())
key = crypto.dump_privatekey(crypto.FILETYPE_PEM, pfx.get_privatekey())
# PEM formatted certificate
cert = crypto.dump_certificate(crypto.FILETYPE_PEM,
pfx.get_certificate())
return cert, key
cert = crypto.dump_certificate(crypto.FILETYPE_PEM, pfx.get_certificate())
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 = open(cert_temp, "w")
arq_temp.write(cert)
arq_temp.close()
arq_temp = open(key_temp, 'w')
arq_temp = open(key_temp, "w")
arq_temp.write(key)
arq_temp.close()

36
pytrustnfe/client.py

@ -2,56 +2,28 @@
# © 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):
cache_location = '/tmp/suds'
cache_location = "/tmp/suds"
cache = suds.cache.DocumentCache(location=cache_location)
session = requests.Session()
session.cert = (cert, key)
return suds.client.Client(
base_url,
cache=cache,
transport=suds_requests.RequestsTransport(session)
base_url, cache=cache, transport=suds_requests.RequestsTransport(session)
)
def get_client(base_url):
cache_location = '/tmp/suds'
cache_location = "/tmp/suds"
cache = suds.cache.DocumentCache(location=cache_location)
session = requests.Session()
return suds.client.Client(
base_url,
cache=cache,
transport=suds_requests.RequestsTransport(session)
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

4
pytrustnfe/exceptions.py

@ -7,6 +7,6 @@ class NFeValidationException(ValueError):
"""Exceção para erro na validação do esquema da NFe"""
def __init__(self, message, *args, **kwargs):
self.erros = kwargs['erros']
self.sent_xml = kwargs['sent_xml']
self.erros = kwargs["erros"]
self.sent_xml = kwargs["sent_xml"]
super(NFeValidationException, self).__init__(message, *args, **kwargs)

501
pytrustnfe/nfe/__init__.py

@ -1,243 +1,378 @@
# -*- coding: utf-8 -*-
# © 2016 Danimar Ribeiro, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import os
import hashlib
import os
import requests
from lxml import etree
from .comunicacao import executar_consulta
from .patch import has_patch
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.urls import url_qrcode, url_qrcode_exibicao
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):
for item in kwargs['NFes']:
for item in kwargs["NFes"]:
vals = {
'cnpj': item['infNFe']['emit']['cnpj_cpf'],
'estado': item['infNFe']['ide']['cUF'],
'emissao': '%s%s' % (item['infNFe']['ide']['dhEmi'][2:4],
item['infNFe']['ide']['dhEmi'][5:7]),
'modelo': item['infNFe']['ide']['mod'],
'serie': item['infNFe']['ide']['serie'],
'numero': item['infNFe']['ide']['nNF'],
'tipo': item['infNFe']['ide']['tpEmis'],
'codigo': item['infNFe']['ide']['cNF'],
"cnpj": item["infNFe"]["emit"]["cnpj_cpf"],
"estado": item["infNFe"]["ide"]["cUF"],
"emissao": "%s%s"
% (
item["infNFe"]["ide"]["dhEmi"][2:4],
item["infNFe"]["ide"]["dhEmi"][5:7],
),
"modelo": item["infNFe"]["ide"]["mod"],
"serie": item["infNFe"]["ide"]["serie"],
"numero": item["infNFe"]["ide"]["nNF"],
"tipo": item["infNFe"]["ide"]["tpEmis"],
"codigo": item["infNFe"]["ide"]["cNF"],
}
chave_nfe = ChaveNFe(**vals)
chave_nfe = gerar_chave(chave_nfe, 'NFe')
item['infNFe']['Id'] = chave_nfe
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)
chave_nfe = gerar_chave(chave_nfe, "NFe")
item["infNFe"]["Id"] = chave_nfe
item["infNFe"]["ide"]["cDV"] = chave_nfe[len(chave_nfe) - 1 :]
def _send(certificado, method, sign, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates')
xmlElem_send = render_xml(path, '%s.xml' % method, True, **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)
modelo = modelo.text if modelo is not None else "55"
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':
xml_send = signer.assina_xml(
xmlElem_send, kwargs['eventos'][0]['Id'])
if method == 'RecepcaoEventoCarta':
if method == "NfeInutilizacao":
xml_send = signer.assina_xml(xmlElem_send, kwargs["obj"]["id"])
if method == "NfeAutorizacao":
xml_send = signer.assina_xml(
xmlElem_send, kwargs['Id'])
elif method == 'RecepcaoEventoManifesto':
xmlElem_send, kwargs["NFes"][0]["infNFe"]["Id"]
)
elif method == "RecepcaoEvento":
xml_send = signer.assina_xml(xmlElem_send, kwargs["eventos"][0]["Id"])
elif method == "RecepcaoEventoManifesto":
xml_send = signer.assina_xml(
xmlElem_send, kwargs['manifesto']['identificador'])
if modelo == '65':
xml_send = _add_qrCode(xml_send, **kwargs)
xmlElem_send, kwargs["manifesto"]["identificador"]
)
else:
xml_send = etree.tostring(xmlElem_send)
xml_send = etree.tostring(xmlElem_send, encoding=str)
return xml_send
def gerar_qrcode(id_csc: int, csc: str, xml_send: str, cert = False) -> str:
xml = etree.fromstring(xml_send)
signature = xml.find(
".//{http://www.w3.org/2000/09/xmldsig#}Signature")
id = xml.find(
".//{http://www.portalfiscal.inf.br/nfe}infNFe").get('Id')
if id is None:
raise Exception("XML Invalido - Sem o ID")
chave = id.replace('NFe', '')
emit_uf = chave[:2]
tp_amb = xml.find(".//{http://www.portalfiscal.inf.br/nfe}tpAmb")
if tp_amb is None:
raise Exception("XML Invalido - Sem o tipo de ambiente")
dh_emi = xml.find(".//{http://www.portalfiscal.inf.br/nfe}dhEmi")
if dh_emi is None:
raise Exception("XML Invalido - Sem data de Emissao")
dh_emi = dh_emi.text.split("-")[2].split("T")[0]
tp_emis = xml.find(".//{http://www.portalfiscal.inf.br/nfe}tpEmis")
if tp_emis is None:
raise Exception("XML Invalido - Sem tipo de emissao")
v_nf = xml.find(".//{http://www.portalfiscal.inf.br/nfe}vNF")
if v_nf is None:
raise Exception("XML Invalido - Sem o valor da NFe")
url_qrcode_str = url_qrcode(
estado=emit_uf,
ambiente=tp_amb.text)
url_qrcode_exibicao_str = url_qrcode_exibicao(
estado=emit_uf,
ambiente=tp_amb.text)
if tp_emis != 1:
if signature is None:
if cert is not False:
signer = Assinatura(certificado.pfx, certificado.password)
xml_send = signer.assina_xml(xmlElem_send, id)
else:
raise Exception("XML Invalido - Sem assinatura e não "
"foi enviado o certificado nos parametros")
digest_value = xml.find(
".//{http://www.w3.org/2000/09/xmldsig#}DigestValue")
c_hash_qr_code = \
"{ch_acesso}|{versao}|{tp_amb}|{dh_emi}|" \
"{v_nf}|{dig_val}|{id_csc}|{csc}".format(
ch_acesso=chave,
versao=2,
tp_amb=tp_amb.text,
dh_emi=dh_emi,
v_nf=float(v_nf.text),
dig_val=digest_value.text,
id_csc=int(id_csc),
csc=csc
)
c_hash_qr_code = hashlib.sha1(c_hash_qr_code.encode()). \
hexdigest()
qr_code_url = 'p={ch_acesso}|{versao}|{tp_amb}|{dh_emi}|" \
"{v_nf}|{dig_val}|{id_csc}|{hash}'.format(
ch_acesso=chave,
versao=2,
tp_amb=tp_amb.text,
dh_emi=dh_emi,
v_nf=float(v_nf.text),
dig_val=digest_value.text,
id_csc=int(id_csc),
hash=c_hash_qr_code
)
qrcode = url_qrcode_str + qr_code_url
url_consulta = url_qrcode_exibicao_str
qrCode = xml.find(
'.//{http://www.portalfiscal.inf.br/nfe}qrCode').text = \
qrcode
urlChave = xml.find(
'.//{http://www.portalfiscal.inf.br/nfe}urlChave').text = \
url_consulta
else:
c_hash_qr_code = \
"{ch_acesso}|{versao}|{tp_amb}|{id_csc}|{csc}".format(
ch_acesso=chave,
versao=2,
tp_amb=tp_amb.text,
id_csc=int(id_csc),
csc=csc
)
c_hash_qr_code = hashlib.sha1(c_hash_qr_code.encode()).hexdigest()
qr_code_url = "p={ch_acesso}|{versao}|{tp_amb}|{id_csc}|" \
"{hash}".\
format(
ch_acesso=chave,
versao=2,
tp_amb=tp_amb.text,
id_csc=int(id_csc),
hash=c_hash_qr_code
)
qrcode = url_qrcode_str + qr_code_url
url_consulta = url_qrcode_exibicao_str
qrCode = xml.find(
'.//{http://www.portalfiscal.inf.br/nfe}qrCode').text = \
qrcode
urlChave = xml.find(
'.//{http://www.portalfiscal.inf.br/nfe}urlChave').text = \
url_consulta
return etree.tostring(xml)
url = localizar_url(method, kwargs['estado'], modelo,
kwargs['ambiente'])
cabecalho = _build_header(method, **kwargs)
def _get_session(certificado):
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
return session
def _get_client(base_url, transport):
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]
return first_operation, client
def _send(certificado, method, **kwargs):
xml_send = kwargs["xml"]
base_url = localizar_url(
method, kwargs["estado"], kwargs["modelo"], kwargs["ambiente"]
)
session = _get_session(certificado)
patch = has_patch(kwargs["estado"], method)
if patch:
return patch(session, xml_send, kwargs["ambiente"])
transport = Transport(session=session)
first_op, client = _get_client(base_url, transport)
return _send_zeep(first_op, client, xml_send)
def _send_zeep(first_operation, client, xml_send):
parser = etree.XMLParser(strip_cdata=False)
xml = etree.fromstring(xml_send, parser=parser)
namespaceNFe = xml.find(".//{http://www.portalfiscal.inf.br/nfe}NFe")
if namespaceNFe is not None:
namespaceNFe.set("xmlns", "http://www.portalfiscal.inf.br/nfe")
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
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],
}
send_raw = False
if method == 'NFeDistribuicaoDFe':
send_raw = True
response, obj = executar_consulta(certificado, url, cabecalho, xml_send,
send_raw=send_raw)
return {
'sent_xml': xml_send,
'received_xml': response,
'object': obj
}
def xml_autorizar_nfe(certificado, **kwargs):
_generate_nfe_id(**kwargs)
return _render(certificado, "NfeAutorizacao", True, **kwargs)
def autorizar_nfe(certificado, **kwargs): # Assinar
_generate_nfe_id(**kwargs)
return _send(certificado, 'NfeAutorizacao', True, **kwargs)
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):
if "xml" not in kwargs:
kwargs["xml"] = xml_inutilizar_nfe(certificado, **kwargs)
return _send(certificado, "NfeInutilizacao", **kwargs)
def inutilizar_nfe(certificado, **kwargs): # Assinar
return _send(certificado, 'NfeInutilizacao', True, **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 xml_recepcao_evento_manifesto(certificado, **kwargs): # Assinar
return _render(certificado, "RecepcaoEvento", True, **kwargs)
def recepcao_evento_manifesto(certificado, **kwargs): # Assinar
return _send(certificado, 'RecepcaoEventoManifesto', True, **kwargs)
if "xml" not in kwargs:
kwargs["xml"] = xml_recepcao_evento_manifesto(certificado, **kwargs)
return _send(certificado, "RecepcaoEvento", **kwargs)
def recepcao_evento_epec(certificado, **kwargs): # Assinar
return _send(certificado, 'RecepcaoEventoEPEC', True, **kwargs)
def xml_consulta_distribuicao_nfe(certificado, **kwargs): # Assinar
return _render(certificado, "NFeDistribuicaoDFe", False, **kwargs)
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)
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,
}

29
pytrustnfe/nfe/assinatura.py

@ -9,12 +9,11 @@ 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):
def assina_xml(self, xml_element, reference, getchildren=False):
cert, key = extract_cert_and_key_from_pfx(self.arquivo, self.senha)
for element in xml_element.iter("*"):
@ -22,24 +21,30 @@ class Assinatura(object):
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')
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']
ns[None] = signer.namespaces["ds"]
signer.namespaces = ns
ref_uri = ('#%s' % reference) if reference else None
ref_uri = ("#%s" % reference) if reference else None
signed_root = signer.sign(
xml_element, key=key, cert=cert,
reference_uri=ref_uri)
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(
".//{http://www.w3.org/2000/09/xmldsig#}Signature")
".//{http://www.w3.org/2000/09/xmldsig#}Signature"
)
if element_signed is not None and signature is not None:
if getchildren and element_signed is not None and signature is not None:
child = element_signed.getchildren()
child.append(signature)
elif 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)

529
pytrustnfe/nfe/danfce.py

@ -0,0 +1,529 @@
# -*- coding: utf-8 -*-
# © 2017 Johny Chen Jy, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import os
import re
from textwrap import wrap
from io import BytesIO
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
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):
dir_fonts = os.path.dirname(os.path.realpath(__file__))
pdfmetrics.registerFont(
TTFont(
'NimbusSanL-Bold',
os.path.join(dir_fonts,'fonts/NimbusSanL Bold.ttf')))
pdfmetrics.registerFont(
TTFont(
'NimbusSanL-Regu',
os.path.join(dir_fonts, 'fonts/NimbusSanL Regular.ttf')))
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(0.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, oXML=None):
el_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide")
tipo_emissao = tagtext(oNode=el_ide, cTag="tpEmis")
if tipo_emissao in ("5", "9"):
self.current_height -= 5
self.drawTitle("EMITIDA EM CONTINGÊNCIA", 9, "NimbusSanL-Bold")
self.drawTitle("Pendente de autorização", 7, "NimbusSanL-Bold")
self.drawLine()
else:
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]
text = u"%s <br />%s <br />NFC-e nº%s Série %s %s<br />" % (
frase_chave_acesso,
cnpj_cpf,
nNFC,
serie,
dataSolicitacao,
)
self._drawCenteredParagraph(text)
tipo_emissao = tagtext(oNode=el_ide, cTag="tpEmis")
if tipo_emissao in ("5", "9"):
self.current_height -= 8
self.drawTitle("EMITIDA EM CONTINGÊNCIA", 9, "NimbusSanL-Bold")
self.drawTitle(
"Pendente de autorização - Via Consumidor", 7, "NimbusSanL-Bold"
)
else:
numProtocolo = tagtext(oNode=el_prot_nfe, cTag="nProt")
dataAutorizacao = getdateUTC(tagtext(oNode=el_prot_nfe, cTag="dhRecbto"))
dataAutorizacao = dataAutorizacao[0] + " " + dataAutorizacao[1]
text = "Protocolo de autorização: %s<br />Data de autorização %s<br />" % (
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(oXML=oXML)
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)

1268
pytrustnfe/nfe/danfe.py
File diff suppressed because it is too large
View File

BIN
pytrustnfe/nfe/fonts/NimbusSanL Bold.ttf

BIN
pytrustnfe/nfe/fonts/NimbusSanL Regular.ttf

45
pytrustnfe/nfe/patch.py

@ -0,0 +1,45 @@
from ..Servidores import SIGLA_ESTADO
from pytrustnfe.xml import sanitize_response
def nfeInutilizacaoCE(session, xml_send, ambiente):
soap = (
'<Envelope xmlns="http://www.w3.org/2003/05/soap-envelope"><Body>\
<nfeDadosMsg xmlns="http://www.portalfiscal.inf.br/nfe/wsdl/NFeInutilizacao4"\
>'
+ xml_send
+ "</nfeDadosMsg></Body></Envelope>"
)
headers = {
"SOAPAction": "",
"Content-Type": 'application/soap+xml; charset="utf-8"',
}
if ambiente == 1:
response = session.post(
"https://nfe.sefaz.ce.gov.br/nfe4/services/NFeInutilizacao4",
data=soap,
headers=headers,
)
else:
response = session.post(
"https://nfeh.sefaz.ce.gov.br/nfe4/services/NFeInutilizacao4",
data=soap,
headers=headers,
)
response, obj = sanitize_response(response.text)
return {
"sent_xml": xml_send,
"received_xml": response,
"object": obj.Body.getchildren()[0],
}
methods = {"NfeInutilizacaoCE": nfeInutilizacaoCE}
def has_patch(cod_estado, metodo):
uf = SIGLA_ESTADO[cod_estado]
method = metodo + uf
if method in methods:
return methods[method]
return None

33
pytrustnfe/nfe/templates/NFeDistribuicaoDFe.xml

@ -1,19 +1,14 @@
<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>
<consNSU>
<NSU>{{ nsu }}</NSU>
</consNSU>
</distDFeInt>

279
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>
@ -28,7 +27,11 @@
<indFinal>{{ ide.indFinal }}</indFinal>
<indPres>{{ ide.indPres }}</indPres>
<procEmi>{{ ide.procEmi }}</procEmi>
<verProc>Odoo Brasil 10</verProc>
<verProc>{{ ide.verProc }}</verProc>
{% if ide.tpEmis != 1 %}
<dhCont>{{ ide.dhCont }}</dhCont>
<xJust>{{ ide.xJust }}</xJust>
{% endif %}
{% if ide.NFref is defined -%}
{% for ref in ide.NFref %}
<NFref>
@ -117,6 +120,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 +134,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>
@ -142,6 +147,7 @@
<retirada>
<CNPJ>{{ NFe.infNFe.retirada.CNPJ }}</CNPJ>
<CPF>{{ NFe.infNFe.retirada.CPF }}</CPF>
<xNome>{{ NFe.infNFe.retirada.xNome|normalize|escape }}</xNome>
<xLgr>{{ NFe.infNFe.retirada.xLgr|normalize|escape }}</xLgr>
<nro>{{ NFe.infNFe.retirada.nro }}</nro>
<xCpl>{{ NFe.infNFe.retirada.xCpl|normalize|escape }}</xCpl>
@ -149,12 +155,19 @@
<cMun>{{ NFe.infNFe.retirada.cMun }}</cMun>
<xMun>{{ NFe.infNFe.retirada.xMun|normalize }}</xMun>
<UF>{{ NFe.infNFe.retirada.UF }}</UF>
<CEP>{{ NFe.infNFe.retirada.CEP }}</CEP>
<cPais>{{ NFe.infNFe.retirada.cPais }}</cPais>
<xPais>{{ NFe.infNFe.retirada.xPais|normalize|escape }}</xPais>
<fone>{{ NFe.infNFe.retirada.fone }}</fone>
<email>{{ NFe.infNFe.retirada.email }}</email>
<IE>{{ NFe.infNFe.retirada.IE }}</IE>
</retirada>
{% endif %}
{% if NFe.infNFe.entrega is defined %}
<entrega>
<CNPJ>{{ NFe.infNFe.entrega.CNPJ }}</CNPJ>
<CPF>{{ NFe.infNFe.entrega.CPF }}</CPF>
<xNome>{{ NFe.infNFe.entrega.xNome|normalize|escape }}</xNome>
<xLgr>{{ NFe.infNFe.entrega.xLgr|normalize|escape }}</xLgr>
<nro>{{ NFe.infNFe.entrega.nro }}</nro>
<xCpl>{{ NFe.infNFe.entrega.xCpl|normalize|escape }}</xCpl>
@ -162,6 +175,12 @@
<cMun>{{ NFe.infNFe.entrega.cMun }}</cMun>
<xMun>{{ NFe.infNFe.entrega.xMun }}</xMun>
<UF>{{ NFe.infNFe.entrega.UF }}</UF>
<CEP>{{ NFe.infNFe.entrega.CEP }}</CEP>
<cPais>{{ NFe.infNFe.entrega.cPais }}</cPais>
<xPais>{{ NFe.infNFe.entrega.xPais|normalize|escape }}</xPais>
<fone>{{ NFe.infNFe.entrega.fone }}</fone>
<email>{{ NFe.infNFe.entrega.email }}</email>
<IE>{{ NFe.infNFe.entrega.IE }}</IE>
</entrega>
{% endif %}
{% if NFe.infNFe.autXML %}
@ -182,6 +201,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 +266,28 @@
<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>
<xMotivoIsencao>{{ med.xMotivoIsencao }}</xMotivoIsencao>
<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 +297,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 +309,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 +332,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 +349,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 +376,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 +386,16 @@
<orig>{{ imposto.ICMS.orig }}</orig>
<CST>{{ imposto.ICMS.CST }}</CST>
<vBCSTRet>{{ imposto.ICMS.vBCSTRet }}</vBCSTRet>
<pST>{{ imposto.ICMS.pST }}</pST>
<vICMSSubstituto>{{ imposto.ICMS.vICMSSubstituto }}</vICMSSubstituto>
<vICMSSTRet>{{ imposto.ICMS.vICMSSTRet }}</vICMSSTRet>
<vBCFCPSTRet>{{ imposto.ICMS.vBCFCPSTRet }}</vBCFCPSTRet>
<pFCPSTRet>{{ imposto.ICMS.pFCPSTRet }}</pFCPSTRet>
<vFCPSTRet>{{ imposto.ICMS.vFCPSTRet }}</vFCPSTRet>
<pRedBCEfet>{{ imposto.ICMS.pRedBCEfet }}</pRedBCEfet>
<vBCEfet>{{ imposto.ICMS.vBCEfet }}</vBCEfet>
<pICMSEfet>{{ imposto.ICMS.pICMSEfet }}</pICMSEfet>
<vICMSEfet>{{ imposto.ICMS.vICMSEfet }}</vICMSEfet>
</ICMS60>
{% endif %}
{% if imposto.ICMS.CST == '70' -%}
@ -342,12 +407,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 +432,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 +457,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>
@ -395,9 +470,18 @@
<orig>{{ imposto.ICMSST.orig }}</orig>
<CST>{{ imposto.ICMSST.CST }}</CST>
<vBCSTRet>{{ imposto.ICMSST.vBCSTRet }}</vBCSTRet>
<pST>{{ imposto.ICMSST.pST }}</pST>
<vICMSSubstituto>{{ imposto.ICMSST.vICMSSubstituto }}</vICMSSubstituto>
<vICMSSTRet>{{ imposto.ICMSST.vICMSSTRet }}</vICMSSTRet>
<vBCFCPSTRet>{{ imposto.ICMSST.vBCFCPSTRet }}</vBCFCPSTRet>
<pFCPSTRet>{{ imposto.ICMSST.pFCPSTRet }}</pFCPSTRet>
<vFCPSTRet>{{ imposto.ICMSST.vFCPSTRet }}</vFCPSTRet>
<vBCSTDest>{{ imposto.ICMSST.vBCSTDest }}</vBCSTDest>
<vICMSSTDest>{{ imposto.ICMSST.vICMSSTDest }}</vICMSSTDest>
<pRedBCEfet>{{ imposto.ICMSST.pRedBCEfet }}</pRedBCEfet>
<vBCEfet>{{ imposto.ICMSST.vBCEfet }}</vBCEfet>
<pICMSEfet>{{ imposto.ICMSST.pICMSEfet }}</pICMSEfet>
<vICMSEfet>{{ imposto.ICMSST.vICMSEfet }}</vICMSEfet>
</ICMSST>
{% endif %}
{% if imposto.ICMS.CST == '101' -%}
@ -424,6 +508,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 +525,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 +535,16 @@
<orig>{{ imposto.ICMS.orig }}</orig>
<CSOSN>{{ imposto.ICMS.CST }}</CSOSN>
<vBCSTRet>{{ imposto.ICMS.vBCSTRet }}</vBCSTRet>
<pST>{{ imposto.ICMS.pST }}</pST>
<vICMSSubstituto>{{ imposto.ICMS.vICMSSubstituto }}</vICMSSubstituto>
<vICMSSTRet>{{ imposto.ICMS.vICMSSTRet }}</vICMSSTRet>
<vBCFCPSTRet>{{ imposto.ICMS.vBCFCPSTRet }}</vBCFCPSTRet>
<pFCPSTRet>{{ imposto.ICMS.pFCPSTRet }}</pFCPSTRet>
<pFCPSTRet>{{ imposto.ICMS.pFCPSTRet }}</pFCPSTRet>
<pRedBCEfet>{{ imposto.ICMS.pRedBCEfet }}</pRedBCEfet>
<vBCEfet>{{ imposto.ICMS.vBCEfet }}</vBCEfet>
<pICMSEfet>{{ imposto.ICMS.pICMSEfet }}</pICMSEfet>
<vICMSEfet>{{ imposto.ICMS.vICMSEfet }}</vICMSEfet>
</ICMSSN500>
{% endif %}
{% if imposto.ICMS.CST == '900' -%}
@ -463,42 +562,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.vISSRet }}</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 +699,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 +743,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 +858,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>
@ -784,6 +897,16 @@
{% endfor %}
{% endif %}
</infAdic>
{% if NFe.infNFe.infRespTec is defined %}
<infRespTec>
<CNPJ>{{ NFe.infNFe.infRespTec.CNPJ }}</CNPJ>
<xContato>{{ NFe.infNFe.infRespTec.xContato }}</xContato>
<email>{{ NFe.infNFe.infRespTec.email }}</email>
<fone>{{ NFe.infNFe.infRespTec.fone }}</fone>
<idCSRT>{{ NFe.infNFe.infRespTec.idCSRT }}</idCSRT>
<hashCSRT>{{ NFe.infNFe.infRespTec.hashCSRT }}</hashCSRT>
</infRespTec>
{% endif %}
{% if NFe.infNFe.exporta is defined %}
<exporta>
<UFSaidaPais>{{ NFe.infNFe.exporta.UFSaidaPais }}</UFSaidaPais>
@ -799,6 +922,20 @@
</compra>
{% endif %}
</infNFe>
{% if NFe.infNFe.ide.mod == '65' %}
<infNFeSupl>
{% if NFe.infNFe.qrCode is defined %}
<qrCode>{{ NFe.infNFe.qrCode }}</qrCode>
{% else %}
<qrCode>.</qrCode>
{% endif %}
{% if NFe.infNFe.urlChave is defined %}
<urlChave>{{ NFe.infNFe.urlChave }}</urlChave>
{% else %}
<urlChave>.</urlChave>
{% endif %}
</infNFeSupl>
{% endif %}
</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>

76
pytrustnfe/nfse/aparecida/__init__.py

@ -0,0 +1,76 @@
# © 2019 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 requests.packages.urllib3 import disable_warnings
from pytrustnfe.xml import render_xml, sanitize_response
from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key
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 = ""
signer = Assinatura(certificado.pfx, certificado.password)
xml_send = signer.assina_xml(xml_send, reference)
return xml_send
def _send(certificado, method, **kwargs):
base_url = ""
if kwargs["ambiente"] == "producao":
base_url = "https://aparecida.siltecnologia.com.br/tbw/services/Abrasf10?wsdl"
else:
base_url = "https://aparecida.siltecnologia.com.br/tbwhomologacao/services/Abrasf10?wsdl"
cert, key = extract_cert_and_key_from_pfx(certificado.pfx, certificado.password)
cert, key = save_cert_key(cert, key)
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](xml_send)
response, obj = sanitize_response(response)
return {"sent_xml": xml_send, "received_xml": response, "object": obj}
def xml_recepcionar_lote_rps(certificado, **kwargs):
return _render(certificado, "recepcionarLoteRps", **kwargs)
def recepcionar_lote_rps(certificado, **kwargs):
if "xml" not in kwargs:
kwargs["xml"] = xml_recepcionar_lote_rps(certificado, **kwargs)
return _send(certificado, "recepcionarLoteRps", **kwargs)
def xml_consultar_lote_rps(certificado, **kwargs):
return _render(certificado, "consultarLoteRps", **kwargs)
def consultar_lote_rps(certificado, **kwargs):
if "xml" not in kwargs:
kwargs["xml"] = xml_consultar_lote_rps(certificado, **kwargs)
return _send(certificado, "consultarLoteRps", **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)

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

@ -0,0 +1,91 @@
<Rps>
<InfRps 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>

15
pytrustnfe/nfse/aparecida/templates/cancelarNfse.xml

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

7
pytrustnfe/nfse/aparecida/templates/consultarLoteRps.xml

@ -0,0 +1,7 @@
<ConsultarLoteRpsEnvio xmlns="http://nfse.abrasf.org.br">
<Prestador>
<Cnpj>{{ consulta.cnpj_prestador }}</Cnpj>
<InscricaoMunicipal>{{ consulta.inscricao_municipal }}</InscricaoMunicipal>
</Prestador>
<Protocolo>{{ consulta.protocolo }}</Protocolo>
</ConsultarLoteRpsEnvio>

13
pytrustnfe/nfse/aparecida/templates/recepcionarLoteRps.xml

@ -0,0 +1,13 @@
<EnviarLoteRpsEnvio>
<LoteRps Id="lote{{ nfse.numero_lote }}">
<NumeroLote>{{ nfse.numero_lote }}</NumeroLote>
<Cnpj>{{ nfse.cnpj_prestador }}</Cnpj>
<InscricaoMunicipal>{{ nfse.inscricao_municipal }}</InscricaoMunicipal>
<QuantidadeRps>{{ nfse.lista_rps|length }}</QuantidadeRps>
<ListaRps>
{% for rps in nfse.lista_rps -%}
{% include 'Rps.xml' %}
{% endfor %}
</ListaRps>
</LoteRps>
</EnviarLoteRpsEnvio>

99
pytrustnfe/nfse/assinatura.py

@ -2,86 +2,55 @@
# © 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
NAMESPACE_SIG = 'http://www.w3.org/2000/09/xmldsig#'
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
class Assinatura(object):
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):
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()
if not os.path.isfile(self.private_key):
raise Exception("Caminho do certificado não existe.")
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)

78
pytrustnfe/nfse/betha/__init__.py

@ -15,99 +15,97 @@ from pytrustnfe.nfse.assinatura import Assinatura
def sign_tag(certificado, **kwargs):
pkcs12 = crypto.load_pkcs12(certificado.pfx, certificado.password)
key = pkcs12.get_privatekey()
if 'nfse' in kwargs:
for item in kwargs['nfse']['lista_rps']:
signed = crypto.sign(key, item['assinatura'], 'SHA1')
item['assinatura'] = b64encode(signed)
if 'cancelamento' in kwargs:
signed = crypto.sign(key, kwargs['cancelamento']['assinatura'], 'SHA1')
kwargs['cancelamento']['assinatura'] = b64encode(signed)
if "nfse" in kwargs:
for item in kwargs["nfse"]["lista_rps"]:
signed = crypto.sign(key, item["assinatura"], "SHA1")
item["assinatura"] = b64encode(signed)
if "cancelamento" in kwargs:
signed = crypto.sign(key, kwargs["cancelamento"]["assinatura"], "SHA1")
kwargs["cancelamento"]["assinatura"] = b64encode(signed)
def _send(certificado, method, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates')
if method in ('GerarNfse', 'RecepcionarLoteRps',
'RecepcionarLoteRpsSincrono',
'CancelarNfse', 'SubstituirNfse'):
path = os.path.join(os.path.dirname(__file__), "templates")
if method in (
"GerarNfse",
"RecepcionarLoteRps",
"RecepcionarLoteRpsSincrono",
"CancelarNfse",
"SubstituirNfse",
):
sign_tag(certificado, **kwargs)
if kwargs['ambiente'] == 'producao':
url = \
'http://e-gov.betha.com.br/e-nota-contribuinte-test-ws/nfseWS?wsdl'
if kwargs["ambiente"] == "producao":
url = "http://e-gov.betha.com.br/e-nota-contribuinte-test-ws/nfseWS?wsdl"
else:
url = 'http://e-gov.betha.com.br/e-nota-contribuinte-ws/nfseWS?wsdl'
url = "http://e-gov.betha.com.br/e-nota-contribuinte-ws/nfseWS?wsdl"
xml_send = render_xml(path, '%s.xml' % method, False, **kwargs)
xml_send = render_xml(path, "%s.xml" % method, False, **kwargs)
cert, key = extract_cert_and_key_from_pfx(
certificado.pfx, certificado.password)
cert, key = extract_cert_and_key_from_pfx(certificado.pfx, certificado.password)
cert, key = save_cert_key(cert, key)
client = get_authenticated_client(url, cert, key)
pfx_path = certificado.save_pfx()
signer = Assinatura(pfx_path, certificado.password)
xml_send = signer.assina_xml(xml_send, '')
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,
'object': None
"sent_xml": xml_send,
"received_xml": e.fault.faultstring,
"object": None,
}
response, obj = sanitize_response(response)
return {
'sent_xml': xml_send,
'received_xml': response,
'object': obj
}
return {"sent_xml": xml_send, "received_xml": response, "object": obj}
def gerar_nfse(certificado, **kwargs):
return _send(certificado, 'GerarNfse', **kwargs)
return _send(certificado, "GerarNfse", **kwargs)
def envio_lote_rps_assincrono(certificado, **kwargs):
return _send(certificado, 'RecepcionarLoteRps', **kwargs)
return _send(certificado, "RecepcionarLoteRps", **kwargs)
def envio_lote_rps(certificado, **kwargs):
return _send(certificado, 'RecepcionarLoteRpsSincrono', **kwargs)
return _send(certificado, "RecepcionarLoteRpsSincrono", **kwargs)
def cancelar_nfse(certificado, **kwargs):
return _send(certificado, 'CancelarNfse', **kwargs)
return _send(certificado, "CancelarNfse", **kwargs)
def substituir_nfse(certificado, **kwargs):
return _send(certificado, 'SubstituirNfse', **kwargs)
return _send(certificado, "SubstituirNfse", **kwargs)
def consulta_situacao_lote_rps(certificado, **kwargs):
return _send(certificado, 'ConsultaSituacaoLoteRPS', **kwargs)
return _send(certificado, "ConsultaSituacaoLoteRPS", **kwargs)
def consulta_nfse_por_rps(certificado, **kwargs):
return _send(certificado, 'ConsultaNfsePorRps', **kwargs)
return _send(certificado, "ConsultaNfsePorRps", **kwargs)
def consultar_lote_rps(certificado, **kwargs):
return _send(certificado, 'ConsultarLoteRps', **kwargs)
return _send(certificado, "ConsultarLoteRps", **kwargs)
def consulta_nfse_servico_prestado(certificado, **kwargs):
return _send(certificado, 'ConsultarNfseServicoPrestado', **kwargs)
return _send(certificado, "ConsultarNfseServicoPrestado", **kwargs)
def consultar_nfse_servico_tomado(certificado, **kwargs):
return _send(certificado, 'ConsultarNfseServicoTomado', **kwargs)
return _send(certificado, "ConsultarNfseServicoTomado", **kwargs)
def consulta_nfse_faixe(certificado, **kwargs):
return _send(certificado, 'ConsultarNfseFaixa', **kwargs)
return _send(certificado, "ConsultarNfseFaixa", **kwargs)
def consulta_cnpj(certificado, **kwargs):
return _send(certificado, 'ConsultaCNPJ', **kwargs)
return _send(certificado, "ConsultaCNPJ", **kwargs)

79
pytrustnfe/nfse/bh/__init__.py

@ -0,0 +1,79 @@
# © 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)

47
pytrustnfe/nfse/bh/assinatura.py

@ -0,0 +1,47 @@
# -*- 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>

69
pytrustnfe/nfse/carioca/__init__.py

@ -0,0 +1,69 @@
# © 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>

124
pytrustnfe/nfse/dsf/__init__.py

@ -0,0 +1,124 @@
# -*- 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>

128
pytrustnfe/nfse/floripa/__init__.py

@ -0,0 +1,128 @@
# -*- 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>

86
pytrustnfe/nfse/ginfes/__init__.py

@ -3,20 +3,23 @@
# 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
def _render(certificado, method, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates')
xml_send = render_xml(path, '%s.xml' % method, True, **kwargs)
path = os.path.join(os.path.dirname(__file__), "templates")
xml_send = render_xml(path, "%s.xml" % method, True, **kwargs)
reference = ''
if method == 'RecepcionarLoteRpsV3':
reference = 'rps%s' % kwargs['nfse']['lista_rps'][0]['numero']
reference = ""
if method == "RecepcionarLoteRpsV3":
reference = "rps%s" % kwargs["nfse"]["lista_rps"][0]["numero"]
signer = Assinatura(certificado.pfx, certificado.password)
xml_send = signer.assina_xml(xml_send, reference)
@ -24,78 +27,75 @@ def _render(certificado, method, **kwargs):
def _send(certificado, method, **kwargs):
base_url = ''
if kwargs['ambiente'] == 'producao':
base_url = 'https://producao.ginfes.com.br/ServiceGinfesImpl?wsdl'
base_url = ""
if kwargs["ambiente"] == "producao":
base_url = "https://producao.ginfes.com.br/ServiceGinfesImpl?wsdl"
else:
base_url = 'https://homologacao.ginfes.com.br/ServiceGinfesImpl?wsdl'
base_url = "https://homologacao.ginfes.com.br/ServiceGinfesImpl?wsdl"
cert, key = extract_cert_and_key_from_pfx(
certificado.pfx, certificado.password)
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 {
'sent_xml': xml_send,
'received_xml': response,
'object': obj
}
return {"sent_xml": xml_send, "received_xml": response, "object": obj}
def xml_recepcionar_lote_rps(certificado, **kwargs):
return _render(certificado, 'RecepcionarLoteRpsV3', **kwargs)
return _render(certificado, "RecepcionarLoteRpsV3", **kwargs)
def recepcionar_lote_rps(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_recepcionar_lote_rps(certificado, **kwargs)
return _send(certificado, 'RecepcionarLoteRpsV3', **kwargs)
kwargs["xml"] = xml_recepcionar_lote_rps(certificado, **kwargs)
return _send(certificado, "RecepcionarLoteRpsV3", **kwargs)
def xml_consultar_situacao_lote(certificado, **kwargs):
return _render(certificado, 'ConsultarSituacaoLoteRpsV3', **kwargs)
return _render(certificado, "ConsultarSituacaoLoteRpsV3", **kwargs)
def consultar_situacao_lote(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_consultar_situacao_lote(certificado, **kwargs)
return _send(certificado, 'ConsultarSituacaoLoteRpsV3', **kwargs)
kwargs["xml"] = xml_consultar_situacao_lote(certificado, **kwargs)
return _send(certificado, "ConsultarSituacaoLoteRpsV3", **kwargs)
def consultar_nfse_por_rps(certificado, **kwargs):
return _send(certificado, 'ConsultarNfsePorRpsV3', **kwargs)
return _send(certificado, "ConsultarNfsePorRpsV3", **kwargs)
def xml_consultar_lote_rps(certificado, **kwargs):
return _render(certificado, 'ConsultarLoteRpsV3', **kwargs)
return _render(certificado, "ConsultarLoteRpsV3", **kwargs)
def consultar_lote_rps(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_consultar_lote_rps(certificado, **kwargs)
return _send(certificado, 'ConsultarLoteRpsV3', **kwargs)
kwargs["xml"] = xml_consultar_lote_rps(certificado, **kwargs)
return _send(certificado, "ConsultarLoteRpsV3", **kwargs)
def consultar_nfse(certificado, **kwargs):
return _send(certificado, 'ConsultarNfseV3', **kwargs)
return _send(certificado, "ConsultarNfseV3", **kwargs)
def xml_cancelar_nfse(certificado, **kwargs):
return _render(certificado, 'CancelarNfseV3', **kwargs)
return _render(certificado, "CancelarNfseV3", **kwargs)
def cancelar_nfse(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_cancelar_nfse(certificado, **kwargs)
return _send(certificado, 'CancelarNfseV3', **kwargs)
kwargs["xml"] = xml_cancelar_nfse(certificado, **kwargs)
return _send(certificado, "CancelarNfseV3", **kwargs)

74
pytrustnfe/nfse/imperial/__init__.py

@ -3,74 +3,54 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import os
import requests
from lxml import etree
from pytrustnfe import HttpClient
from zeep import Client
from pytrustnfe.xml import render_xml, sanitize_response
def _render(certificado, method, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates')
xml_send = render_xml(path, '%s.xml' % method, True, **kwargs)
path = os.path.join(os.path.dirname(__file__), "templates")
xml_send = render_xml(path, "%s.xml" % method, True, **kwargs)
return etree.tostring(xml_send)
def _send(certificado, method, **kwargs):
base_url = ''
if kwargs['ambiente'] == 'producao':
base_url = 'https://nfe.etransparencia.com.br/rj.petropolis/webservice/aws_nfe.aspx' # noqa
base_url = ""
if kwargs["ambiente"] == "producao":
base_url = (
"https://petropolis.sigiss.com.br/petropolis/ws/sigiss_ws.php" # noqa
)
else:
base_url = 'https://nfehomologacao.etransparencia.com.br/rj.petropolis/webservice/aws_nfe.aspx' # noqa
raise Exception("Não existe ambiente de homologação!")
xml_send = kwargs["xml"]
path = os.path.join(os.path.dirname(__file__), 'templates')
soap = render_xml(path, 'SoapRequest.xml', False, soap_body=xml_send)
client = HttpClient(base_url)
response = client.post_soap(soap, 'NFeaction/AWS_NFE.%s' % method)
response, obj = sanitize_response(response)
return {
'sent_xml': xml_send,
'received_xml': response,
'object': obj
xml_send = kwargs["xml"].decode("utf-8")
headers = {
"SOAPAction": "urn:sigiss_ws#%s" % method,
"Content-Type": 'text/xml; charset="utf-8"',
}
r = requests.post(base_url, data=xml_send, headers=headers)
response, obj = sanitize_response(r.text.strip())
def xml_processa_rps(certificado, **kwargs):
return _render(certificado, 'PROCESSARPS', **kwargs)
def processa_rps(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_processa_rps(certificado, **kwargs)
return _send(certificado, 'PROCESSARPS', **kwargs)
def xml_consulta_protocolo(certificado, **kwargs):
return _render(certificado, 'CONSULTAPROTOCOLO', **kwargs)
def consulta_protocolo(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_consulta_protocolo(certificado, **kwargs)
return _send(certificado, 'CONSULTAPROTOCOLO', **kwargs)
return {"sent_xml": xml_send, "received_xml": response, "object": obj.Body}
def xml_consulta_notas_protocolo(certificado, **kwargs):
return _render(certificado, 'CONSULTANOTASPROTOCOLO', **kwargs)
def xml_gerar_nota(certificado, **kwargs):
return _render(certificado, "GerarNota", **kwargs)
def consulta_notas_protocolo(certificado, **kwargs):
def gerar_nota(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_consulta_notas_protocolo(certificado, **kwargs)
return _send(certificado, 'CONSULTANOTASPROTOCOLO', **kwargs)
kwargs["xml"] = xml_gerar_nota(certificado, **kwargs)
return _send(certificado, "GerarNota", **kwargs)
def xml_cancelar_nfse(certificado, **kwargs):
return _render(certificado, 'CANCELANOTAELETRONICA', **kwargs)
def xml_cancelar_nota(certificado, **kwargs):
return _render(certificado, "CancelarNota", **kwargs)
def cancelar_nfse(certificado, **kwargs):
def cancelar_nota(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_cancelar_nfse(certificado, **kwargs)
return _send(certificado, 'CANCELANOTAELETRONICA', **kwargs)
kwargs["xml"] = xml_cancelar_nota(certificado, **kwargs)
return _send(certificado, "CancelarNota", **kwargs)

17
pytrustnfe/nfse/imperial/templates/CANCELANOTAELETRONICA.xml

@ -1,17 +0,0 @@
<ws_nfe.CANCELANOTAELETRONICA xmlns="NFe">
<Sdt_cancelanfe>
<Login>
<CodigoUsuario>{{ cancelamento.codigo_usuario }}</CodigoUsuario>
<CodigoContribuinte>{{ cancelamento.codigo_contribuinte }}</CodigoContribuinte>
</Login>
<Nota>
<SerieNota>{{ cancelamento.serie_nota }}</SerieNota>
<NumeroNota>{{ cancelamento.numero_nota }}</NumeroNota>
<SerieRPS>{{ cancelamento.serie_rps }}</SerieRPS>
<NumeroRps>{{ cancelamento.numero_rps }}</NumeroRps>
<ValorNota>{{ cancelamento.valor }}</ValorNota>
<MotivoCancelamento>{{ cancelamento.motivo }}</MotivoCancelamento>
<PodeCancelarGuia>{{ cancelamento.cancelar_guia }}</PodeCancelarGuia>
</Nota>
</Sdt_cancelanfe>
</ws_nfe.CANCELANOTAELETRONICA>

9
pytrustnfe/nfse/imperial/templates/CONSULTANOTASPROTOCOLO.xml

@ -1,9 +0,0 @@
<ws_nfe.CONSULTANOTASPROTOCOLO xmlns="NFe">
<Sdt_consultanotasprotocoloin>
<Protocolo>{{ consulta.protocolo }}</Protocolo>
<Login>
<CodigoUsuario>{{ consulta.codigo_usuario }}</CodigoUsuario>
<CodigoContribuinte>{{ consulta.codigo_contribuinte }}</CodigoContribuinte>
</Login>
</Sdt_consultanotasprotocoloin>
</ws_nfe.CONSULTANOTASPROTOCOLO>

9
pytrustnfe/nfse/imperial/templates/CONSULTAPROTOCOLO.xml

@ -1,9 +0,0 @@
<ws_nfe.CONSULTAPROTOCOLO xmlns="NFe">
<Sdt_consultaprotocoloin>
<Protocolo>{{ consulta.protocolo }}</Protocolo>
<Login>
<CodigoUsuario>{{ consulta.codigo_usuario }}</CodigoUsuario>
<CodigoContribuinte>{{ consulta.codigo_contribuinte }}</CodigoContribuinte>
</Login>
</Sdt_consultaprotocoloin>
</ws_nfe.CONSULTAPROTOCOLO>

14
pytrustnfe/nfse/imperial/templates/CancelarNota.xml

@ -0,0 +1,14 @@
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<CancelarNota xmlns="urn:sigiss_ws">
<DadosCancelaNota>
<ccm>{{ cancelamento.ccm }}</ccm>
<cnpj>{{ cancelamento.cnpj }}</cnpj>
<senha>{{ cancelamento.senha }}</senha>
<nota>{{ cancelamento.nota }}</nota>
<motivo>{{ cancelamento.motivo }}</motivo>
<email>{{ cancelamento.email }}</email>
</DadosCancelaNota>
</CancelarNota>
</Body>
</Envelope>

43
pytrustnfe/nfse/imperial/templates/GerarNota.xml

@ -0,0 +1,43 @@
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<GerarNota xmlns="urn:sigiss_ws">
<DescricaoRps>
<ccm>{{ nfse.ccm }}</ccm>
<cnpj>{{ nfse.cnpj }}</cnpj>
<senha>{{ nfse.senha }}</senha>
<crc>{{ nfse.crc }}</crc>
<crc_estado>{{ nfse.crc_estado }}</crc_estado>
<aliquota_simples>{{ nfse.aliquota_simples }}</aliquota_simples>
<id_sis_legado>{{ nfse.id_sis_legado }}</id_sis_legado>
<servico>{{ nfse.servico }}</servico>
<situacao>{{ nfse.situacao }}</situacao>
<valor>{{ nfse.valor }}</valor>
<base>{{ nfse.base }}</base>
<descricaoNF>{{ nfse.descricaoNF }}</descricaoNF>
<tomador_tipo>{{ nfse.tomador_tipo }}</tomador_tipo>
<tomador_cnpj>{{ nfse.tomador_cnpj }}</tomador_cnpj>
<tomador_email>{{ nfse.tomador_email }}</tomador_email>
<tomador_ie>{{ nfse.tomador_ie }}</tomador_ie>
<tomador_razao>{{ nfse.tomador_razao }}</tomador_razao>
<tomador_fantasia>{{ nfse.tomador_fantasia }}</tomador_fantasia>
<tomador_endereco>{{ nfse.tomador_endereco }}</tomador_endereco>
<tomador_numero>{{ nfse.tomador_endereco }}</tomador_numero>
<tomador_complemento>{{ nfse.tomador_complemento }}</tomador_complemento>
<tomador_bairro>{{ nfse.tomador_bairro }}</tomador_bairro>
<tomador_CEP>{{ nfse.tomador_CEP }}</tomador_CEP>
<tomador_cod_cidade>{{ nfse.tomador_cod_cidade }}</tomador_cod_cidade>
<tomador_fone>{{ nfse.tomador_fone }}</tomador_fone>
<tomador_ramal>{{ nfse.tomador_ramal }}</tomador_ramal>
<tomador_fax>{{ nfse.tomador_fax }}</tomador_fax>
<outro_municipio>{{ nfse.outro_municipio }}</outro_municipio>
<cod_outro_municipio>{{ nfse.cod_outro_municipio }}</cod_outro_municipio>
<retencao_iss>{{ nfse.retencao_iss }}</retencao_iss>
<pis>{{ nfse.pis }}</pis>
<cofins>{{ nfse.cofins }}</cofins>
<inss>{{ nfse.inss }}</inss>
<irrf>{{ nfse.irrf }}</irrf>
<csll>{{ nfse.csll }}</csll>
</DescricaoRps>
</GerarNota>
</Body>
</Envelope>

81
pytrustnfe/nfse/imperial/templates/PROCESSARPS.xml

@ -1,81 +0,0 @@
<ws_nfe.PROCESSARPS xmlns="NFe">
<Sdt_processarpsin>
<Login>
<CodigoUsuario>{{ nfse.codigo_usuario }}</CodigoUsuario>
<CodigoContribuinte>{{ nfse.codigo_contribuinte }}</CodigoContribuinte>
</Login>
<SDTRPS>
<Ano>{{ nfse.ano }}</Ano>
<Mes>{{ nfse.mes }}</Mes>
<CPFCNPJ>{{ nfse.cnpj_prestador }}</CPFCNPJ>
<DTIni>{{ nfse.data_emissao }}</DTIni>
<DTFin>{{ nfse.data_emissao }}</DTFin>
<TipoTrib>{{ nfse.tipo_tributacao }}</TipoTrib>
<DtAdeSN>{{ nfse.data_adesao_simples }}</DtAdeSN>
<AlqIssSN_IP>{{ nfse.aliquota_simples_isencao|comma }}</AlqIssSN_IP>
<Versao>2.00</Versao>
{% for rps in nfse.lista_rps -%}
<Reg20>
<!-- Optional -->
<Reg20Item>
<TipoNFS>{{ rps.tipo_nfse }}</TipoNFS>
<NumRps>{{ rps.numero }}</NumRps>
<SerRps>{{ rps.serie }}</SerRps>
<DtEmi>{{ rps.data_emissao }}</DtEmi>
<RetFonte>{{ rps.iss_retido }}</RetFonte>
<CodSrv>{{ rps.codigo_servico }}</CodSrv>
<DiscrSrv>{{ rps.descricao}}</DiscrSrv>
<VlNFS>{{ rps.valor_liquido_nfse|comma }}</VlNFS>
<VlDed>{{ rps.valor_deducao|comma }}</VlDed>
<DiscrDed>{{ rps.discriminacao_deducao }}</DiscrDed>
<VlBasCalc>{{ rps.base_calculo|comma }}</VlBasCalc>
<AlqIss>{{ rps.aliquota_issqn|comma }}</AlqIss>
<VlIss>{{ rps.valor_iss|comma }}</VlIss>
<VlIssRet>{{ rps.valor_iss_retido|comma }}</VlIssRet>
<CpfCnpTom>{{ rps.tomador.cnpj_cpf }}</CpfCnpTom>
<RazSocTom>{{ rps.tomador.razao_social }}</RazSocTom>
<TipoLogtom>{{ rps.tomador.tipo_logradouro }}</TipoLogtom>
<LogTom>{{ rps.tomador.logradouro }}</LogTom>
<NumEndTom>{{ rps.tomador.numero }}</NumEndTom>
<ComplEndTom>{{ rps.tomador.complemento }}</ComplEndTom>
<BairroTom>{{ rps.tomador.bairro }}</BairroTom>
<MunTom>{{ rps.tomador.municipio }}</MunTom>
<SiglaUFTom>{{ rps.tomador.uf }}</SiglaUFTom>
<CepTom>{{ rps.tomador.cep }}</CepTom>
<Telefone>{{ rps.tomador.telefone }}</Telefone>
<InscricaoMunicipal>{{ rps.tomador.inscricao_municipal }}</InscricaoMunicipal>
{% if rps.local_prestacao == 'prestador' %}
<TipoLogLocPre>{{ rps.prestador.tipo_logradouro }}</TipoLogLocPre>
<LogLocPre>{{ rps.prestador.logradouro }}</LogLocPre>
<NumEndLocPre>{{ rps.prestador.numero }}</NumEndLocPre>
<ComplEndLocPre>{{ rps.prestador.complemento }}</ComplEndLocPre>
<BairroLocPre>{{ rps.prestador.bairro }}</BairroLocPre>
<MunLocPre>{{ rps.prestador.municipio }}</MunLocPre>
<SiglaUFLocpre>{{ rps.prestador.uf }}</SiglaUFLocpre>
<CepLocPre>{{ rps.prestador.cep }}</CepLocPre>
{% endif %}
<Email1>{{ rps.tomador.email }}</Email1>
{% for imposto in rps.impostos -%}
<Reg30>
<Reg30Item>
<TributoSigla>{{ imposto.sigla }}</TributoSigla>
<TributoAliquota>{{ imposto.aliquota|comma }}</TributoAliquota>
<TributoValor>{{ imposto.valor|comma }}</TributoValor>
</Reg30Item>
</Reg30>
{% endfor %}
</Reg20Item>
</Reg20>
{% endfor %}
<Reg90>
<QtdRegNormal>{{ nfse.lista_rps|length }}</QtdRegNormal>
<ValorNFS>{{ nfse.lista_rps|sum(attribute='valor_liquido_nfse')|comma }}</ValorNFS>
<ValorISS>{{ nfse.lista_rps|sum(attribute='valor_iss')|comma }}</ValorISS>
<ValorDed>{{ nfse.lista_rps|sum(attribute='valor_deducao')|comma }}</ValorDed>
<ValorIssRetTom>{{ nfse.lista_rps|sum(attribute='valor_iss_retido')|comma }}</ValorIssRetTom>
<QtdReg30>{{ nfse.quantidade_impostos }}</QtdReg30>
<ValorTributos>{{ nfse.valor_tributos|comma }}</ValorTributos>
</Reg90>
</SDTRPS>
</Sdt_processarpsin>
</ws_nfe.PROCESSARPS>

5
pytrustnfe/nfse/imperial/templates/SoapRequest.xml

@ -1,5 +0,0 @@
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
{{ soap_body }}
</Body>
</Envelope>

81
pytrustnfe/nfse/issnet/__init__.py

@ -1,3 +1,80 @@
# -*- coding: utf-8 -*-
# © 2016 Danimar Ribeiro, Trustcode
# © 2020 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 requests.packages.urllib3 import disable_warnings
from pytrustnfe.xml import render_xml, sanitize_response
from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key
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 == "RecepcionarLoteRps":
reference = "rps%s" % kwargs["nfse"]["lista_rps"][0]["numero"]
signer = Assinatura(certificado.pfx, certificado.password)
xml_send = signer.assina_xml(xml_send, reference)
return xml_send
def _send(certificado, method, **kwargs):
base_url = ""
if kwargs["ambiente"] == "producao":
base_url = "https://www.issnetonline.com.br/webserviceabrasf/duquedecaxias/servicos.asmx?wsdl"
else:
raise Exception('Não existe ambiente de homologação para este município!')
cert, key = extract_cert_and_key_from_pfx(certificado.pfx, certificado.password)
cert, key = save_cert_key(cert, key)
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](xml_send)
response, obj = sanitize_response(response)
return {"sent_xml": xml_send, "received_xml": response, "object": obj}
def xml_recepcionar_lote_rps(certificado, **kwargs):
return _render(certificado, "RecepcionarLoteRps", **kwargs)
def recepcionar_lote_rps(certificado, **kwargs):
if "xml" not in kwargs:
kwargs["xml"] = xml_recepcionar_lote_rps(certificado, **kwargs)
return _send(certificado, "RecepcionarLoteRps", **kwargs)
def xml_consultar_lote_rps(certificado, **kwargs):
return _render(certificado, "ConsultarLoteRps", **kwargs)
def consultar_lote_rps(certificado, **kwargs):
if "xml" not in kwargs:
kwargs["xml"] = xml_consultar_lote_rps(certificado, **kwargs)
return _send(certificado, "ConsultarLoteRps", **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/issnet/templates/CancelarNfse.xml

@ -0,0 +1,13 @@
<CancelarNfseEnvio xmlns="http://www.issnetonline.com.br/webserviceabrasf/vsd/servico_cancelar_nfse_envio.xsd">
<Pedido>
<InfPedidoCancelamento>
<IdentificacaoNfse>
<Numero>{{ cancelamento.numero_nfse }}</Numero>
<Cnpj>{{ cancelamento.cnpj_prestador }}</Cnpj>
<InscricaoMunicipal>{{ cancelamento.inscricao_municipal }}</InscricaoMunicipal>
<CodigoMunicipio>{{ cancelamento.cidade }}</CodigoMunicipio>
</IdentificacaoNfse>
<CodigoCancelamento>{{ cancelamento.codigo_cancelamento }}</CodigoCancelamento>
</InfPedidoCancelamento>
</Pedido>
</CancelarNfseEnvio>

7
pytrustnfe/nfse/issnet/templates/ConsultarLoteRps.xml

@ -0,0 +1,7 @@
<ConsultarLoteRpsEnvio xmlns="http://www.issnetonline.com.br/webserviceabrasf/vsd/servico_consultar_lote_rps_envio.xsd" xmlns:tipos="http://www.issnetonline.com.br/webserviceabrasf/vsd/tipos_complexos.xsd">
<Prestador>
<tipos:CpfCnpj><tipos:Cnpj>{{ consulta.cnpj_prestador }}</tipos:Cnpj></tipos:CpfCnpj>
<tipos:InscricaoMunicipal>{{ consulta.inscricao_municipal }}</tipos:InscricaoMunicipal>
</Prestador>
<Protocolo>{{ consulta.protocolo }}</Protocolo>
</ConsultarLoteRpsEnvio>

13
pytrustnfe/nfse/issnet/templates/RecepcionarLoteRps.xml

@ -0,0 +1,13 @@
<EnviarLoteRpsEnvio xmlns="http://www.issnetonline.com.br/webserviceabrasf/vsd/servico_enviar_lote_rps_envio.xsd" xmlns:tipos="http://www.issnetonline.com.br/webserviceabrasf/vsd/tipos_complexos.xsd">
<LoteRps Id="lote{{ nfse.numero_lote }}">
<tipos:NumeroLote>{{ nfse.numero_lote }}</tipos:NumeroLote>
<tipos:CpfCnpj><tipos:Cnpj>{{ nfse.cnpj_prestador }}</tipos:Cnpj></tipos:CpfCnpj>
<tipos:InscricaoMunicipal>{{ nfse.inscricao_municipal }}</tipos:InscricaoMunicipal>
<tipos:QuantidadeRps>{{ nfse.lista_rps|length }}</tipos:QuantidadeRps>
<ListaRps xmlns="http://www.issnetonline.com.br/webserviceabrasf/vsd/tipos_complexos.xsd">
{% for rps in nfse.lista_rps -%}
{% include 'Rps.xml' %}
{% endfor %}
</ListaRps>
</LoteRps>
</EnviarLoteRpsEnvio>

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

@ -0,0 +1,91 @@
<Rps>
<InfRps 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>
<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>
<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>

70
pytrustnfe/nfse/mga/__init__.py

@ -0,0 +1,70 @@
# © 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)

48
pytrustnfe/nfse/mga/assinatura.py

@ -0,0 +1,48 @@
# -*- 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>

116
pytrustnfe/nfse/natal/__init__.py

@ -0,0 +1,116 @@
# © 2019 Danimar Ribeiro, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import os
from OpenSSL import crypto
from base64 import b64encode
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.certificado import extract_cert_and_key_from_pfx, save_cert_key
from pytrustnfe.nfe.assinatura import Assinatura
from lxml import etree
def sign_rps(path, certificado, **kwargs):
if "nfse" in kwargs:
lote = ""
for item in kwargs["nfse"]["lista_rps"]:
data = {"rps": item}
xml_rps = render_xml(path, "Rps.xml", True, **data)
signer = Assinatura(certificado.pfx, certificado.password)
lote += signer.assina_xml(
xml_rps, f"rps:{item.get('numero')}{item.get('serie')}", getchildren=True
)
return lote
return ""
def _render(certificado, method, **kwargs):
path = os.path.join(os.path.dirname(__file__), "templates")
parser = etree.XMLParser(
remove_blank_text=True, remove_comments=True, strip_cdata=False
)
lote = ""
referencia = ""
if method == "RecepcionarLoteRps":
referencia = "lote"
lote = sign_rps(path, certificado, **kwargs)
kwargs["lote"] = lote
xml_send = render_xml(path, "%s.xml" % method, False, **kwargs)
signer = Assinatura(certificado.pfx, certificado.password)
xml_send = signer.assina_xml(etree.fromstring(
xml_send, parser=parser), f"{referencia}", getchildren=True)
return xml_send
def _send(certificado, method, **kwargs):
base_url = ""
if kwargs["ambiente"] == "producao":
base_url = "https://wsnfsev1.natal.rn.gov.br:8444"
else:
base_url = "https://wsnfsev1homologacao.natal.rn.gov.br:8443/axis2/services/NfseWSServiceV1?wsdl"
base_url = "https://wsnfsev1homologacao.natal.rn.gov.br:8443/axis2/services/NfseWSServiceV1?wsdl"
cert, key = extract_cert_and_key_from_pfx(
certificado.pfx, certificado.password)
cert, key = save_cert_key(cert, key)
disable_warnings()
session = Session()
session.cert = (cert, key)
session.verify = False
transport = Transport(session=session)
client = Client(wsdl=base_url, transport=transport)
xml_send = {}
xml_send = {
"nfseDadosMsg": kwargs["xml"],
"nfseCabecMsg": """<?xml version="1.0"?>
<cabecalho xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" versao="1" xmlns="http://www.abrasf.org.br/ABRASF/arquivos/nfse.xsd">
<versaoDados>1</versaoDados>
</cabecalho>""",
}
response = client.service[method](**xml_send)
response, obj = sanitize_response(response)
return {"sent_xml": xml_send, "received_xml": response, "object": obj}
def xml_recepcionar_lote_rps(certificado, **kwargs):
return _render(certificado, "RecepcionarLoteRps", **kwargs)
def recepcionar_lote_rps(certificado, **kwargs):
if "xml" not in kwargs:
kwargs["xml"] = xml_recepcionar_lote_rps(certificado, **kwargs)
return _send(certificado, "RecepcionarLoteRps", **kwargs)
def xml_consultar_lote_rps(certificado, **kwargs):
return _render(certificado, "ConsultarLoteRps", **kwargs)
def consultar_lote_rps(certificado, **kwargs):
if "xml" not in kwargs:
kwargs["xml"] = xml_consultar_lote_rps(certificado, **kwargs)
return _send(certificado, "ConsultarLoteRps", **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)

8
pytrustnfe/nfse/natal/templates/EnvelopeSoap.xml

@ -0,0 +1,8 @@
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<RecepcionarLoteRpsRequest xmlns="http://nfse.abrasf.org.br">
<nfseCabecMsg><![CDATA[{% include 'cabecalho.xml' %}]]></nfseCabecMsg>
<nfseDadosMsg><![CDATA[{% include 'RecepcionarLoteRps.xml' %}]]></nfseDadosMsg>
</RecepcionarLoteRpsRequest>
</soap:Body>
</soap:Envelope>

111
pytrustnfe/nfse/natal/templates/Exemplo_LoteRPS.xml

@ -0,0 +1,111 @@
<EnviarLoteRpsEnvio xmlns="http://www.abrasf.org.br/ABRASF/arquivos/nfse.xsd">
<LoteRps Id="lote">
<NumeroLote>1</NumeroLote>
<Cnpj>27596568000505</Cnpj>
<InscricaoMunicipal>1000047</InscricaoMunicipal>
<QuantidadeRps>1</QuantidadeRps>
<ListaRps>
<Rps>
<InfRps Id="rps:1ABCDZ">
<IdentificacaoRps>
<Numero>1</Numero>
<Serie>ABCDH</Serie>
<Tipo>1</Tipo>
</IdentificacaoRps>
<DataEmissao>2010-06-16T21:00:00</DataEmissao>
<NaturezaOperacao>1</NaturezaOperacao>
<RegimeEspecialTributacao>6</RegimeEspecialTributacao>
<OptanteSimplesNacional>1</OptanteSimplesNacional>
<IncentivadorCultural>2</IncentivadorCultural>
<Status>1</Status>
<Servico>
<Valores>
<ValorServicos>1000</ValorServicos>
<ValorPis>10</ValorPis>
<ValorCofins>10</ValorCofins>
<ValorInss>10</ValorInss>
<ValorIr>10</ValorIr>
<ValorCsll>10</ValorCsll>
<IssRetido>1</IssRetido>
<ValorIss>50</ValorIss>
<OutrasRetencoes>10</OutrasRetencoes>
<BaseCalculo>1000</BaseCalculo>
<Aliquota>0.05</Aliquota>
</Valores>
<ItemListaServico>11.01</ItemListaServico>
<CodigoCnae>4520005</CodigoCnae>
<Discriminacao>Teste.</Discriminacao>
<CodigoMunicipio>3106200</CodigoMunicipio>
</Servico>
<Prestador>
<Cnpj>27596568000505</Cnpj>
<InscricaoMunicipal>1000047</InscricaoMunicipal>
</Prestador>
<Tomador>
<IdentificacaoTomador>
<CpfCnpj>
<Cnpj>24533572000102</Cnpj>
</CpfCnpj>
<InscricaoMunicipal>1000039</InscricaoMunicipal>
</IdentificacaoTomador>
<RazaoSocial>INSCRICAO DE TESTE SIATU - DAGUA -PAULINOS</RazaoSocial>
<Endereco>
<Endereco>DA BAHIA</Endereco>
<Numero>200</Numero>
<Complemento>ANDAR 14</Complemento>
<Bairro>CENTRO</Bairro>
<CodigoMunicipio>2408102</CodigoMunicipio>
<Uf>RN</Uf>
<Cep>30160010</Cep>
</Endereco>
<Contato>
<Email>marcelo@teste.com.br</Email>
</Contato>
</Tomador>
<ConstrucaoCivil>
<CodigoObra>1234</CodigoObra>
<Art>1234</Art>
</ConstrucaoCivil>
</InfRps>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="#rps:1ABCDZ">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>mMyQLAm4psxx52kaD8Jlta3ouPM=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>qBKfaNz6RbsYUxCOrjGZ9zrdgiGL7QSBxjlhYRlKDNlDERlDWvM8gi28yus8FoUb0v2CTKKIBz0tzfqxgk60rke4YCMkTzdWfpm7ofMIhYC9VHqbWdInC20znOKygJy5hyIx6JBoyXbejnw/0KF+2E1P1ZehqXJWZqY+KPaIGAY=</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIE7DCCA9SgAwIBAgIQZMlLC9ZEsHWsnvJNdMI2yzANBgkqhkiG9w0BAQUFADBqMQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDEsMCoGA1UECxMjU2VjcmV0YXJpYSBkYSBSZWNlaXRhIEZlZGVyYWwgLSBTUkYxGDAWBgNVBAMTD0FDIFBST0RFTUdFIFNSRjAeFw0wNzEwMzEwMDAwMDBaFw0xMDEwMzAyMzU5NTlaMIGQMQswCQYDVQQGEwJCUjETMBEGA1UEChQKSUNQLUJyYXNpbDEqMCgGA1UECxQhU2VjcmV0YXJpYSBkYSBSZWNlaXRhIEZlZGVyYWwtU1JGMRIwEAYDVQQLFAlTUkYgZS1DUEYxLDAqBgNVBAMTI0VER0FSIERPIENBUk1PIEZFUlJFSVJBOjQzMjYwMTUyNjg3MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6M+9XT5KLQN3IH8mAr+S6vxiochY/EwF8EhtNoxPTYl+zr0Dh+eZsRG31bN410nr2OrwncrRorMK8Ngq+j3FnNF0nIMigaaa5NAEfIk3Yy4kuqrTUZBpqUJvCqc3mkF3C3XD0MTmtbVTWCvYIk+qn3t5ShHyMnQcuah5Q0ItSbQIDAQABo4IB6TCCAeUwgZUGA1UdEQSBjTCBiqA9BgVgTAEDAaA0BDIyMTAzMTk2NDQzMjYwMTUyNjg3MDAwMDAwMDAwMDAwMDAwMDBNLTI4ODQwODVTU1BNR6AXBgVgTAEDBqAOBAwwMDAwMDAwMDAwMDCgHgYFYEwBAwWgFQQTMDAwMDAwMDAwMDAwMDAwMDAwMIEQZWRnYXJAcGJoLmdvdi5icjAJBgNVHRMEAjAAMF8GA1UdHwRYMFYwVKBSoFCGTmh0dHA6Ly9pY3AtYnJhc2lsLmNlcnRpc2lnbi5jb20uYnIvcmVwb3NpdG9yaW8vbGNyL0FDUFJPREVNR0VTUkYvTGF0ZXN0Q1JMLmNybDAfBgNVHSMEGDAWgBTdO9vtjZcRRUMBQ020Ev0O7niacDAOBgNVHQ8BAf8EBAMCBeAwVQYDVR0gBE4wTDBKBgZgTAECAxQwQDA+BggrBgEFBQcCARYyaHR0cDovL2ljcC1icmFzaWwuY2VydGlzaWduLmNvbS5ici9yZXBvc2l0b3Jpby9kcGMwHQYDVR0lBBYwFAYIKwYBBQUHAwQGCCsGAQUFBwMCMDgGCCsGAQUFBwEBBCwwKjAoBggrBgEFBQcwAYYcaHR0cDovL29jc3AuY2VydGlzaWduLmNvbS5icjANBgkqhkiG9w0BAQUFAAOCAQEAYFcjZj4lGVEREHBaHtcRletWS6/mvpkxmodwj3ele5yXsxuqSZd7ebHbKewXx7gkyaWFkFAxFanQhls2tYKjg6haqt2b0AO1FsitVIHkMcxRwkU9G+1ec8yfdxymra2VdXazkxuvqKABgxkqKnaFdHjje7cjWDgwparymH64mTlHkSQz59GutJW0xfwBHcMGx0/9/iIug6pfMQivWf0NMVpFNzxO5ZNPEuOeBhVDxQr4+KB+4B9xDai/3J6f42UNbSy+z3xuB0K8/7V7BsFUYOYFSNnBrXhvbvXtZOtteX65V0r1+RJJX5OK+PAPhZ57T1LEmHMggdo5kli3Nr1KFQ==</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</Rps>
</ListaRps>
</LoteRps>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#" Id="Ass_lote">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="#lote">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>n42EhtzDSnZ071g+44ZMBCc74UQ=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>pQyeXnJ2S9KyUJ1BE3k3PZuDpk7WkD2nMPLoELSLJeNBe9TwmLhImsIUS4inAUreuTsjfrs2BUmChN6jPA0/1cSR0GbblLsHFN+IwPE2dnPN/u0vIOmsan4MuW1OnlH6KexmDHRj/uFwjoXfSJ0JJE1u9bYdbsp5LGlFuc//CCQ=</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIE7DCCA9SgAwIBAgIQZMlLC9ZEsHWsnvJNdMI2yzANBgkqhkiG9w0BAQUFADBqMQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDEsMCoGA1UECxMjU2VjcmV0YXJpYSBkYSBSZWNlaXRhIEZlZGVyYWwgLSBTUkYxGDAWBgNVBAMTD0FDIFBST0RFTUdFIFNSRjAeFw0wNzEwMzEwMDAwMDBaFw0xMDEwMzAyMzU5NTlaMIGQMQswCQYDVQQGEwJCUjETMBEGA1UEChQKSUNQLUJyYXNpbDEqMCgGA1UECxQhU2VjcmV0YXJpYSBkYSBSZWNlaXRhIEZlZGVyYWwtU1JGMRIwEAYDVQQLFAlTUkYgZS1DUEYxLDAqBgNVBAMTI0VER0FSIERPIENBUk1PIEZFUlJFSVJBOjQzMjYwMTUyNjg3MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6M+9XT5KLQN3IH8mAr+S6vxiochY/EwF8EhtNoxPTYl+zr0Dh+eZsRG31bN410nr2OrwncrRorMK8Ngq+j3FnNF0nIMigaaa5NAEfIk3Yy4kuqrTUZBpqUJvCqc3mkF3C3XD0MTmtbVTWCvYIk+qn3t5ShHyMnQcuah5Q0ItSbQIDAQABo4IB6TCCAeUwgZUGA1UdEQSBjTCBiqA9BgVgTAEDAaA0BDIyMTAzMTk2NDQzMjYwMTUyNjg3MDAwMDAwMDAwMDAwMDAwMDBNLTI4ODQwODVTU1BNR6AXBgVgTAEDBqAOBAwwMDAwMDAwMDAwMDCgHgYFYEwBAwWgFQQTMDAwMDAwMDAwMDAwMDAwMDAwMIEQZWRnYXJAcGJoLmdvdi5icjAJBgNVHRMEAjAAMF8GA1UdHwRYMFYwVKBSoFCGTmh0dHA6Ly9pY3AtYnJhc2lsLmNlcnRpc2lnbi5jb20uYnIvcmVwb3NpdG9yaW8vbGNyL0FDUFJPREVNR0VTUkYvTGF0ZXN0Q1JMLmNybDAfBgNVHSMEGDAWgBTdO9vtjZcRRUMBQ020Ev0O7niacDAOBgNVHQ8BAf8EBAMCBeAwVQYDVR0gBE4wTDBKBgZgTAECAxQwQDA+BggrBgEFBQcCARYyaHR0cDovL2ljcC1icmFzaWwuY2VydGlzaWduLmNvbS5ici9yZXBvc2l0b3Jpby9kcGMwHQYDVR0lBBYwFAYIKwYBBQUHAwQGCCsGAQUFBwMCMDgGCCsGAQUFBwEBBCwwKjAoBggrBgEFBQcwAYYcaHR0cDovL29jc3AuY2VydGlzaWduLmNvbS5icjANBgkqhkiG9w0BAQUFAAOCAQEAYFcjZj4lGVEREHBaHtcRletWS6/mvpkxmodwj3ele5yXsxuqSZd7ebHbKewXx7gkyaWFkFAxFanQhls2tYKjg6haqt2b0AO1FsitVIHkMcxRwkU9G+1ec8yfdxymra2VdXazkxuvqKABgxkqKnaFdHjje7cjWDgwparymH64mTlHkSQz59GutJW0xfwBHcMGx0/9/iIug6pfMQivWf0NMVpFNzxO5ZNPEuOeBhVDxQr4+KB+4B9xDai/3J6f42UNbSy+z3xuB0K8/7V7BsFUYOYFSNnBrXhvbvXtZOtteX65V0r1+RJJX5OK+PAPhZ57T1LEmHMggdo5kli3Nr1KFQ==</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</EnviarLoteRpsEnvio>

112
pytrustnfe/nfse/natal/templates/Rps.xml

@ -0,0 +1,112 @@
<Rps>
<InfRps Id="rps:{{ rps.numero }}{{ rps.serie }}">
<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>
<Servico>
<Valores>
<ValorServicos>{{ rps.servico.valor_servico }}</ValorServicos>
<ValorPis>{{ rps.servico.pis }}</ValorPis>
<ValorCofins>{{ rps.servico.cofins }}</ValorCofins>
<ValorInss>{{ rps.servico.inss }}</ValorInss>
<ValorIr>{{ rps.servico.ir }}</ValorIr>
<ValorCsll>{{ rps.servico.csll }}</ValorCsll>
<IssRetido>{{ rps.servico.iss_retido }}</IssRetido>
<ValorIss>{{ rps.servico.iss }}</ValorIss>
<OutrasRetencoes>{{ rps.servico.retencoes }}</OutrasRetencoes>
<BaseCalculo>{{ rps.servico.base_calculo }}</BaseCalculo>
<Aliquota>{{ rps.servico.aliquota }}</Aliquota>
</Valores>
<ItemListaServico>{{ rps.servico.codigo_servico }}</ItemListaServico>
<CodigoCnae>{{ rps.servico.cnae_servico }}</CodigoCnae>
<Discriminacao>{{ rps.servico.discriminacao }}</Discriminacao>
<CodigoMunicipio>{{ rps.servico.codigo_municipio }}</CodigoMunicipio>
</Servico>
<Prestador>
<Cnpj>{{ rps.prestador.cnpj }}</Cnpj>
<InscricaoMunicipal>{{ rps.prestador.inscricao_municipal }}</InscricaoMunicipal>
<RazaoSocial>{{ rps.prestador.razaosocial }}</RazaoSocial>
<NomeFantasia>{{ rps.prestador.fantasia }}</NomeFantasia>
<Endereco>
<Endereco>{{ rps.prestador.endereco }}</Endereco>
<Numero>{{ rps.prestador.numero }}</Numero>
<Complemento>{{ rps.prestador.complemento }}</Complemento>
<Bairro>{{ rps.prestador.bairro }}</Bairro>
<CodigoMunicipio>{{ rps.prestador.codigomunicipal }}</CodigoMunicipio>
<Uf>{{ rps.prestador.uf }}</Uf>
<Cep>{{ rps.prestador.cep }}</Cep>
</Endereco>
<Contato>
{% if rps.prestador.telefone is defined -%}
<Telefone>{{ rps.prestador.telefone }}</Telefone>
{% endif %}
{% if rps.prestador.email is defined -%}
<Email>{{ rps.prestador.email }}</Email>
{% endif %}
</Contato>
</Prestador>
<Tomador>
<IdentificacaoTomador>
<CpfCnpj>
{% if rps.tomador.cpf_cnpj|length == 14 %}
<Cnpj>{{ rps.tomador.cpf_cnpj }}</Cnpj>
{% endif %}
{% if rps.tomador.cpf_cnpj|length == 11 %}
<Cpf>{{ rps.tomador.cpf_cnpj }}</Cpf>
{% endif %}
</CpfCnpj>
{% if rps.tomador.inscricao_municipal is defined -%}
<InscricaoMunicipal>{{ rps.tomador.inscricao_municipal }}</InscricaoMunicipal>
{% endif %}
</IdentificacaoTomador>
<RazaoSocial>{{ rps.tomador.razao_social }}</RazaoSocial>
<Endereco>
<Endereco>{{ rps.tomador.endereco }}</Endereco>
<Numero>{{ rps.tomador.numero }}</Numero>
<Complemento>{{ rps.tomador.complemento }}</Complemento>
<Bairro>{{ rps.tomador.bairro }}</Bairro>
<CodigoMunicipio>{{ rps.tomador.codigo_municipio }}</CodigoMunicipio>
<Uf>{{ rps.tomador.uf }}</Uf>
<Cep>{{ rps.tomador.cep }}</Cep>
</Endereco>
<Contato>
{% if rps.tomador.telefone is defined -%}
<Telefone>{{ rps.tomador.telefone }}</Telefone>
{% endif %}
{% if rps.tomador.email is defined -%}
<Email>{{ rps.tomador.email }}</Email>
{% endif %}
</Contato>
{% if rps.tomador.orgao_gerador is defined -%}
<OrgaoGerador>
<CodigoMunicipio>{{ rps.tomador.orgao_gerador.codigo_municipio }}</CodigoMunicipio>
<Uf>{{ rps.tomador.orgao_gerador.uf }}</Uf>
</OrgaoGerador>
{% endif %}
</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>
<Signature Id="placeholder"></Signature>
</Rps>

3
pytrustnfe/nfse/natal/templates/cabecalho.xml

@ -0,0 +1,3 @@
<cabecalho versao="2.01" xmlns="http://www.abrasf.org.br/nfse.xsd">
<versaoDados>2.01</versaoDados>
</cabecalho>

15
pytrustnfe/nfse/natal/templates/cancelarNfse.xml

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

7
pytrustnfe/nfse/natal/templates/consultarLoteRps.xml

@ -0,0 +1,7 @@
<ConsultarLoteRpsEnvio xmlns="http://www.abrasf.org.br/ABRASF/arquivos/nfse.xsd">
<Prestador>
<Cnpj>{{ consulta.cnpj_prestador }}</Cnpj>
<InscricaoMunicipal>{{ consulta.inscricao_municipal }}</InscricaoMunicipal>
</Prestador>
<Protocolo>{{ consulta.protocolo }}</Protocolo>
</ConsultarLoteRpsEnvio>

11
pytrustnfe/nfse/natal/templates/recepcionarLoteRps.xml

@ -0,0 +1,11 @@
<EnviarLoteRpsEnvio xmlns="http://www.abrasf.org.br/ABRASF/arquivos/nfse.xsd">
<LoteRps Id="lote">
<NumeroLote>{{ nfse.numero_lote }}</NumeroLote>
<Cnpj>{{ nfse.cnpj_prestador }}</Cnpj>
<InscricaoMunicipal>{{ nfse.inscricao_municipal }}</InscricaoMunicipal>
<QuantidadeRps>{{ nfse.lista_rps|length }}</QuantidadeRps>
<ListaRps>
{{lote}}
</ListaRps>
</LoteRps>
</EnviarLoteRpsEnvio>

73
pytrustnfe/nfse/paulistana/__init__.py

@ -15,97 +15,94 @@ from pytrustnfe.nfse.assinatura import Assinatura
def sign_tag(certificado, **kwargs):
pkcs12 = crypto.load_pkcs12(certificado.pfx, certificado.password)
key = pkcs12.get_privatekey()
if 'nfse' in kwargs:
for item in kwargs['nfse']['lista_rps']:
signed = crypto.sign(key, item['assinatura'], 'SHA1')
item['assinatura'] = b64encode(signed)
if 'cancelamento' in kwargs:
signed = crypto.sign(key, kwargs['cancelamento']['assinatura'], 'SHA1')
kwargs['cancelamento']['assinatura'] = b64encode(signed)
if "nfse" in kwargs:
for item in kwargs["nfse"]["lista_rps"]:
signed = crypto.sign(key, item["assinatura"], "SHA1")
item["assinatura"] = b64encode(signed).decode()
if "cancelamento" in kwargs:
signed = crypto.sign(key, kwargs["cancelamento"]["assinatura"], "SHA1")
kwargs["cancelamento"]["assinatura"] = b64encode(signed).decode()
def _send(certificado, method, **kwargs):
# A little hack to test
path = os.path.join(os.path.dirname(__file__), 'templates')
if method == 'TesteEnvioLoteRPS' or method == 'EnvioLoteRPS' \
or method == 'CancelamentoNFe':
path = os.path.join(os.path.dirname(__file__), "templates")
if (
method == "TesteEnvioLoteRPS"
or method == "EnvioLoteRPS"
or method == "CancelamentoNFe"
):
sign_tag(certificado, **kwargs)
if method == 'TesteEnvioLoteRPS':
xml_send = render_xml(path, 'EnvioLoteRPS.xml', False, **kwargs)
if method == "TesteEnvioLoteRPS":
xml_send = render_xml(path, "EnvioLoteRPS.xml", False, **kwargs)
else:
xml_send = render_xml(path, '%s.xml' % method, False, **kwargs)
base_url = 'https://nfe.prefeitura.sp.gov.br/ws/lotenfe.asmx?wsdl'
xml_send = render_xml(path, "%s.xml" % method, False, **kwargs)
base_url = "https://nfe.prefeitura.sp.gov.br/ws/lotenfe.asmx?wsdl"
cert, key = extract_cert_and_key_from_pfx(
certificado.pfx, certificado.password)
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)
pfx_path = certificado.save_pfx()
signer = Assinatura(pfx_path, certificado.password)
xml_send = signer.assina_xml(xml_send, '')
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,
'object': None
"sent_xml": xml_send,
"received_xml": e.fault.faultstring,
"object": None,
}
response, obj = sanitize_response(response)
return {
'sent_xml': xml_send,
'received_xml': response,
'object': obj
}
return {"sent_xml": xml_send, "received_xml": response, "object": obj}
def envio_rps(certificado, **kwargs):
return _send(certificado, 'EnvioRPS', **kwargs)
return _send(certificado, "EnvioRPS", **kwargs)
# Testado pois usa o mesmo xml que o teste_envio_lote_rps
def envio_lote_rps(certificado, **kwargs):
return _send(certificado, 'EnvioLoteRPS', **kwargs)
return _send(certificado, "EnvioLoteRPS", **kwargs)
# Testado
def teste_envio_lote_rps(certificado, **kwargs):
return _send(certificado, 'TesteEnvioLoteRPS', **kwargs)
return _send(certificado, "TesteEnvioLoteRPS", **kwargs)
def cancelamento_nfe(certificado, **kwargs):
return _send(certificado, 'CancelamentoNFe', **kwargs)
return _send(certificado, "CancelamentoNFe", **kwargs)
# Testado
def consulta_nfe(certificado, **kwargs):
return _send(certificado, 'ConsultaNFe', **kwargs)
return _send(certificado, "ConsultaNFe", **kwargs)
# Testado
def consulta_nfe_recebidas(certificado, **kwargs):
return _send(certificado, 'ConsultaNFeRecebidas', **kwargs)
return _send(certificado, "ConsultaNFeRecebidas", **kwargs)
# Testado
def consulta_nfe_emitidas(certificado, **kwargs):
return _send(certificado, 'ConsultaNFeEmitidas', **kwargs)
return _send(certificado, "ConsultaNFeEmitidas", **kwargs)
# Testado
def consulta_lote(certificado, **kwargs):
return _send(certificado, 'ConsultaLote', **kwargs)
return _send(certificado, "ConsultaLote", **kwargs)
# Testado
def consulta_informacoes_lote(certificado, **kwargs):
return _send(certificado, 'ConsultaInformacoesLote', **kwargs)
return _send(certificado, "ConsultaInformacoesLote", **kwargs)
# Testado
def consulta_cnpj(certificado, **kwargs):
return _send(certificado, 'ConsultaCNPJ', **kwargs)
return _send(certificado, "ConsultaCNPJ", **kwargs)

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

@ -6,7 +6,7 @@
<transacao>false</transacao>
<dtInicio>{{ nfse.data_inicio }}</dtInicio>
<dtFim>{{ nfse.data_fim }}</dtFim>
<QtdRPS>1</QtdRPS>
<QtdRPS>{{ nfse.lista_rps|length }}</QtdRPS>
<ValorTotalServicos>{{ nfse.total_servicos }}</ValorTotalServicos>
<ValorTotalDeducoes>{{ nfse.total_deducoes }}</ValorTotalDeducoes>
</Cabecalho>
@ -32,6 +32,7 @@
<CodigoServico>{{ rps.codigo_atividade }}</CodigoServico>
<AliquotaServicos>{{ rps.aliquota_atividade }}</AliquotaServicos>
<ISSRetido>{{ rps.iss_retido | default('false') }}</ISSRetido>
{% if rps.tomador.tipo_cpfcnpj != 3 %}
<CPFCNPJTomador>
{% if rps.tomador.tipo_cpfcnpj == 1 -%}
<CPF>{{ rps.tomador.cpf_cnpj }}</CPF>
@ -40,21 +41,39 @@
<CNPJ>{{ rps.tomador.cpf_cnpj }}</CNPJ>
{% endif %}
</CPFCNPJTomador>
{% endif %}
{% if rps.tomador.inscricao_municipal -%}
<InscricaoMunicipalTomador>{{ rps.tomador.inscricao_municipal }}</InscricaoMunicipalTomador>
{% endif %}
<RazaoSocialTomador>{{ rps.tomador.razao_social|normalize|escape }}</RazaoSocialTomador>
{% if rps.tomador.cidade %}
<EnderecoTomador>
{% if rps.tomador.tipo_logradouro %}
<TipoLogradouro>{{ rps.tomador.tipo_logradouro }}</TipoLogradouro>
{% endif %}
{% if rps.tomador.logradouro %}
<Logradouro>{{ rps.tomador.logradouro|normalize|escape }}</Logradouro>
{% endif %}
{% if rps.tomador.numero %}
<NumeroEndereco>{{ rps.tomador.numero }}</NumeroEndereco>
{% endif %}
{% if rps.tomador.complemento %}
<ComplementoEndereco>{{ rps.tomador.complemento|normalize|escape }}</ComplementoEndereco>
{% endif %}
{% if rps.tomador.bairro %}
<Bairro>{{ rps.tomador.bairro }}</Bairro>
{% endif %}
<Cidade>{{ rps.tomador.cidade }}</Cidade>
<UF>{{ rps.tomador.uf }}</UF>
<CEP>{{ rps.tomador.cep }}</CEP>
{% if rps.tomador.cep %}<CEP>{{ rps.tomador.cep }}</CEP>{% endif %}
</EnderecoTomador>
{% endif %}
{% if rps.tomador.email %}<EmailTomador>{{ rps.tomador.email }}</EmailTomador>{% endif %}
<Discriminacao>{{ rps.descricao|normalize|escape }}</Discriminacao>
{% if rps.valor_carga_tributarial %}
<ValorCargaTributaria>{{ rps.valor_carga_tributaria }}</ValorCargaTributaria>
<FonteCargaTributaria>{{ rps.fonte_carga_tributaria }}</FonteCargaTributaria>
{% endif %}
</RPS>
{% endfor %}
</PedidoEnvioLoteRPS>

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

@ -12,20 +12,21 @@
<SerieRPS>{{ rps.serie }}</SerieRPS>
<NumeroRPS>{{ rps.numero }}</NumeroRPS>
</ChaveRPS>
<TipoRPS>RPS-M</TipoRPS>
<TipoRPS>{{ rps.tipo_rps | default('RPS') }}</TipoRPS>
<DataEmissao>{{ rps.data_emissao }}</DataEmissao>
<StatusRPS>N</StatusRPS>
<TributacaoRPS>T</TributacaoRPS>
<ValorServicos>{{ nfse.total_servicos }}</ValorServicos>
<ValorDeducoes>{{ nfse.total_deducoes }}</ValorDeducoes>
<ValorPIS>{{ rps.valor_pis }}</ValorPIS>
<ValorCOFINS>{{ rps.valor_cofins }}</ValorCOFINS>
<ValorINSS>{{ rps.valor_inss }}</ValorINSS>
<ValorIR>{{ rps.valor_pis }}</ValorIR>
<ValorCSLL>{{ rps.valor_csll }}</ValorCSLL>
<TributacaoRPS>{{ rps.tributacao_rps | default('T') }}</TributacaoRPS>
<ValorServicos>{{ rps.valor_servico }}</ValorServicos>
<ValorDeducoes>{{ rps.valor_deducao }}</ValorDeducoes>
<ValorPIS>{{ rps.valor_pis | default('0.00') }}</ValorPIS>
<ValorCOFINS>{{ rps.valor_cofins | default('0.00') }}</ValorCOFINS>
<ValorINSS>{{ rps.valor_inss | default('0.00') }}</ValorINSS>
<ValorIR>{{ rps.valor_ir | default('0.00') }}</ValorIR>
<ValorCSLL>{{ rps.valor_csll | default('0.00') }}</ValorCSLL>
<CodigoServico>{{ rps.codigo_atividade }}</CodigoServico>
<AliquotaServicos>{{ rps.aliquota_atividade }}</AliquotaServicos>
<ISSRetido>false</ISSRetido>
<ISSRetido>{{ rps.iss_retido | default('false') }}</ISSRetido>
{% if rps.tomador.tipo_cpfcnpj != 3 %}
<CPFCNPJTomador>
{% if rps.tomador.tipo_cpfcnpj == 1 -%}
<CPF>{{ rps.tomador.cpf_cnpj }}</CPF>
@ -34,18 +35,37 @@
<CNPJ>{{ rps.tomador.cpf_cnpj }}</CNPJ>
{% endif %}
</CPFCNPJTomador>
<RazaoSocialTomador>{{ rps.tomador.razao_social }}</RazaoSocialTomador>
{% endif %}
{% if rps.tomador.inscricao_municipal -%}
<InscricaoMunicipalTomador>{{ rps.tomador.inscricao_municipal }}</InscricaoMunicipalTomador>
{% endif %}
<RazaoSocialTomador>{{ rps.tomador.razao_social|normalize|escape }}</RazaoSocialTomador>
{% if rps.tomador.cidade %}
<EnderecoTomador>
<TipoLogradouro>{{ rps.tomador.tipo_logradouro }}</TipoLogradouro>
<Logradouro>{{ rps.tomador.logradouro }}</Logradouro>
<NumeroEndereco>{{ rps.tomador.numero }}</NumeroEndereco>
<ComplementoEndereco>{{ rps.tomador.complemento }}</ComplementoEndereco>
<Bairro>{{ rps.tomador.bairro }}</Bairro>
<Cidade>{{ rps.tomador.cidade }}</Cidade>
<UF>{{ rps.tomador.uf }}</UF>
<CEP>{{ rps.tomador.cep }}</CEP>
{% if rps.tomador.tipo_logradouro %}
<TipoLogradouro>{{ rps.tomador.tipo_logradouro }}</TipoLogradouro>
{% endif %}
{% if rps.tomador.logradouro %}
<Logradouro>{{ rps.tomador.logradouro|normalize|escape }}</Logradouro>
{% endif %}
{% if rps.tomador.numero %}
<NumeroEndereco>{{ rps.tomador.numero }}</NumeroEndereco>
{% endif %}
{% if rps.tomador.complemento %}
<ComplementoEndereco>{{ rps.tomador.complemento|normalize|escape }}</ComplementoEndereco>
{% endif %}
{% if rps.tomador.bairro %}
<Bairro>{{ rps.tomador.bairro }}</Bairro>
{% endif %}
<Cidade>{{ rps.tomador.cidade }}</Cidade>
<UF>{{ rps.tomador.uf }}</UF>
{% if rps.tomador.cep %}<CEP>{{ rps.tomador.cep }}</CEP>{% endif %}
</EnderecoTomador>
<Discriminacao>{{ rps.descricao }}</Discriminacao>
{% endfor %}
{% endif %}
{% if rps.tomador.email %}<EmailTomador>{{ rps.tomador.email }}</EmailTomador>{% endif %}
<Discriminacao>{{ rps.descricao|normalize|escape }}</Discriminacao>
<ValorCargaTributaria>{{ rps.valor_carga_tributaria }}</ValorCargaTributaria>
<FonteCargaTributaria>{{ rps.fonte_carga_tributaria }}</FonteCargaTributaria>
</RPS>
{% endfor %}
</PedidoEnvioRPS>

66
pytrustnfe/nfse/simpliss/__init__.py

@ -15,16 +15,16 @@ from pytrustnfe.xml import render_xml, sanitize_response
def _render_xml(certificado, method, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates')
xml_send = render_xml(path, '%s.xml' % method, True, **kwargs)
path = os.path.join(os.path.dirname(__file__), "templates")
xml_send = render_xml(path, "%s.xml" % method, True, **kwargs)
xml_send = etree.tostring(xml_send)
return xml_send
def _validate(method, xml):
path = os.path.join(os.path.dirname(__file__), 'templates')
schema = os.path.join(path, '%s.xsd' % method)
path = os.path.join(os.path.dirname(__file__), "templates")
schema = os.path.join(path, "%s.xsd" % method)
nfe = etree.fromstring(xml)
esquema = etree.XMLSchema(etree.parse(schema))
@ -34,86 +34,82 @@ def _validate(method, xml):
def _send(method, **kwargs):
if kwargs['ambiente'] == 'producao':
base_url = 'http://sistemas.pmp.sp.gov.br/semfi/simpliss/ws_nfse/nfseservice.svc' # noqa
if kwargs["ambiente"] == "producao":
base_url = "http://sistemas.pmp.sp.gov.br/semfi/simpliss/ws_nfse/nfseservice.svc" # noqa
else:
base_url = 'http://wshomologacao.simplissweb.com.br/nfseservice.svc' # noqa
base_url = "http://wshomologacao.simplissweb.com.br/nfseservice.svc" # noqa
base_url = 'http://wshomologacao.simplissweb.com.br/nfseservice.svc'
xml_send = kwargs["xml"].replace('<?xml version="1.0"?>', '')
path = os.path.join(os.path.dirname(__file__), 'templates')
soap = render_xml(path, 'SoapRequest.xml', False, soap_body=xml_send)
base_url = "http://wshomologacao.simplissweb.com.br/nfseservice.svc"
xml_send = kwargs["xml"]
path = os.path.join(os.path.dirname(__file__), "templates")
soap = render_xml(path, "SoapRequest.xml", False, soap_body=xml_send)
act = 'http://www.sistema.com.br/Sistema.Ws.Nfse/INfseService/%s' % method
act = "http://www.sistema.com.br/Sistema.Ws.Nfse/INfseService/%s" % method
client = HttpClient(base_url)
response = client.post_soap(soap, act)
response, obj = sanitize_response(response)
return {
'sent_xml': xml_send,
'received_xml': response,
'object': obj
}
return {"sent_xml": xml_send, "received_xml": response, "object": obj}
def xml_recepcionar_lote_rps(certificado, **kwargs):
return _render_xml(certificado, 'RecepcionarLoteRps', **kwargs)
return _render_xml(certificado, "RecepcionarLoteRps", **kwargs)
def recepcionar_lote_rps(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_recepcionar_lote_rps(certificado, **kwargs)
return _send('RecepcionarLoteRps', **kwargs)
kwargs["xml"] = xml_recepcionar_lote_rps(certificado, **kwargs)
return _send("RecepcionarLoteRps", **kwargs)
def xml_consultar_situacao_lote(certificado, **kwargs):
return _render_xml(certificado, 'ConsultarSituacaoLoteRps', **kwargs)
return _render_xml(certificado, "ConsultarSituacaoLoteRps", **kwargs)
def consultar_situacao_lote(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_consultar_situacao_lote(certificado, **kwargs)
return _send('ConsultarSituacaoLoteRps', **kwargs)
kwargs["xml"] = xml_consultar_situacao_lote(certificado, **kwargs)
return _send("ConsultarSituacaoLoteRps", **kwargs)
def consultar_nfse_por_rps(certificado, **kwargs):
return _send('ConsultarNfsePorRps', **kwargs)
return _send("ConsultarNfsePorRps", **kwargs)
def xml_consultar_lote_rps(certificado, **kwargs):
return _render_xml(certificado, 'ConsultarLoteRps', **kwargs)
return _render_xml(certificado, "ConsultarLoteRps", **kwargs)
def consultar_lote_rps(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_consultar_lote_rps(certificado, **kwargs)
return _send('ConsultarLoteRps', **kwargs)
kwargs["xml"] = xml_consultar_lote_rps(certificado, **kwargs)
return _send("ConsultarLoteRps", **kwargs)
def xml_consultar_nfse(certificado, **kwargs):
return _render_xml(certificado, 'ConsultarNfse', **kwargs)
return _render_xml(certificado, "ConsultarNfse", **kwargs)
def consultar_nfse(certificado, **kwargs):
return _send('ConsultarNfse', **kwargs)
return _send("ConsultarNfse", **kwargs)
def xml_cancelar_nfse(certificado, **kwargs):
return _render_xml(certificado, 'CancelarNfse', **kwargs)
return _render_xml(certificado, "CancelarNfse", **kwargs)
def cancelar_nfse(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_cancelar_nfse(certificado, **kwargs)
return _send('CancelarNfse', **kwargs)
kwargs["xml"] = xml_cancelar_nfse(certificado, **kwargs)
return _send("CancelarNfse", **kwargs)
def xml_gerar_nfse(certificado, **kwargs):
return _render_xml(certificado, 'GerarNfse', **kwargs)
return _render_xml(certificado, "GerarNfse", **kwargs)
def gerar_nfse(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_recepcionar_lote_rps(certificado, **kwargs)
return _send('GerarNfse', **kwargs)
kwargs["xml"] = xml_recepcionar_lote_rps(certificado, **kwargs)
return _send("GerarNfse", **kwargs)

36
pytrustnfe/nfse/susesu/__init__.py

@ -9,49 +9,49 @@ from pytrustnfe.client import get_client
def _render_xml(method, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates')
xml_send = render_xml(path, '%s.xml' % method, False, **kwargs)
path = os.path.join(os.path.dirname(__file__), "templates")
xml_send = render_xml(path, "%s.xml" % method, False, **kwargs)
return xml_send
def _send(method, **kwargs):
if kwargs['ambiente'] == 'producao':
base_url = 'http://www.susesu.com.br/wsnfd/serviconfd.asmx?WSDL'
if kwargs["ambiente"] == "producao":
base_url = "http://www.susesu.com.br/wsnfd/serviconfd.asmx?WSDL"
else:
base_url = 'http://pira.comunix.net:5002/gestaopublica/wsnfd/ServicoNfd.asmx?WSDL' # noqa
base_url = "http://pira.comunix.net:5002/gestaopublica/wsnfd/ServicoNfd.asmx?WSDL" # noqa
client = get_client(base_url)
try:
xml_send = kwargs["xml"]
result = getattr(client.service, method)(__inject={'msg': xml_send})
result = getattr(client.service, method)(__inject={"msg": xml_send})
except Exception as e:
return {
'sent_xml': xml_send,
'received_xml': e.fault.faultstring,
"sent_xml": xml_send,
"received_xml": e.fault.faultstring,
}
result = unicode(result)
result = unicodedata.normalize('NFKD', result).encode('ascii', 'ignore')
result = str(result)
result = unicodedata.normalize("NFKD", result).encode("ascii", "ignore")
return {
'sent_xml': xml_send,
'received_xml': result,
"sent_xml": xml_send,
"received_xml": result,
}
def xml_enviar_nota(**kwargs):
return _render_xml('EnviarNota', **kwargs)
return _render_xml("EnviarNota", **kwargs)
def enviar_nota(**kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_enviar_nota(**kwargs)
return _send('EnviarNota', **kwargs)
kwargs["xml"] = xml_enviar_nota(**kwargs)
return _send("EnviarNota", **kwargs)
def xml_enviar_nota_retorna_url(**kwargs):
return _render_xml('EnviarNotaRetornaurlNota', **kwargs)
return _render_xml("EnviarNotaRetornaurlNota", **kwargs)
def enviar_nota_retorna_url(**kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_enviar_nota_retorna_url(**kwargs)
return _send('EnviarNotaRetornaurlNota', **kwargs)
kwargs["xml"] = xml_enviar_nota_retorna_url(**kwargs)
return _send("EnviarNotaRetornaurlNota", **kwargs)

9
pytrustnfe/nfse/susesu/templates/EnviarNota.xml

@ -1,5 +1,6 @@
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<x:Envelope xmlns:x="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.nfse">
<x:Header/>
<x:Body>
<EnviarNota xmlns="http://susesu.com.br/">
<codPrefeitura>{{ codigo_prefeitura }}</codPrefeitura>
<notaFiscal>
@ -10,5 +11,5 @@
<cnpjPrestador>{{ cnpj_prestador }}</cnpjPrestador>
<senhaNFD>{{ senha_nfd }}</senhaNFD>
</EnviarNota>
</soap:Body>
</soap:Envelope>
</x:Body>
</x:Envelope>

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())

57
pytrustnfe/test/test_assinatura.py

@ -1,57 +0,0 @@
# coding=utf-8
'''
Created on Jun 14, 2015
@author: danimar
'''
import os
import os.path
import unittest
from lxml import etree
from pytrustnfe.nfe.assinatura import Assinatura
XML_ASSINAR = '<?xml version="1.0" encoding="UTF-8"?>' \
'<Envelope xmlns="urn:envelope">' \
' <Data Id="NFe43150602261542000143550010000000761792265342">'\
' Hello, World!' \
' </Data>' \
'</Envelope>'
XML_ERRADO = '<?xml version="1.0" encoding="UTF-8"?>' \
'<Envelope xmlns="urn:envelope">' \
' <Data Id="NFe">' \
' Hello, World!' \
' </Data>' \
'</Envelope>'
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()
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()
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()
signer = Assinatura(pfx, '123456')
xml = signer.assina_xml(
etree.fromstring(XML_ASSINAR),
'NFe43150602261542000143550010000000761792265342')
xml_assinado = open(os.path.join(self.caminho,
'xml_valido_assinado.xml'),
'r').read()
self.assertEqual(xml_assinado, xml, 'Xml assinado é inválido')

82
pytrustnfe/test/test_certificado.py

@ -1,82 +0,0 @@
# coding=utf-8
'''
Created on Jun 14, 2015
@author: danimar
'''
import unittest
import os
import os.path
from pytrustnfe.certificado import Certificado
from pytrustnfe.certificado import save_cert_key
from pytrustnfe.certificado import extract_cert_and_key_from_pfx
CHAVE = '-----BEGIN PRIVATE KEY-----\n' \
'MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJONRp6l1y2ojgv8\n' \
'tP3AOLW0vjWQqiPseBLM7YAxbzz5R7LYlWHC0ZJ4uIvd4Cvc6AuoNJoeuhzFcwHx\n' \
'PL0TcFuW+5up1ktUohwaJ+/zKrMODCKt0gvif302yqasMnwLh9mGZQIkLkHPOX8p\n' \
'ZQDC4dlqwOyYDi0f+bRd5C7aWx3RAgMBAAECgYADqASP+dwTLZIXifOSNikxl4D/\n' \
'Is6UhU+UZ6+a9Z6kDClSrTtGaOV4k7U/AgiEDb1STKDBEPHbtKjc63Vt2gV2teem\n' \
'ohU0Giv+gD42uuwy2DM31OfYrpR46mzOK9JrpQc78b36ealL3AWJ1gyBbbcOWbAb\n' \
'KmP742V7pcD07EEp4QJBAM/e7M8VdLgOyaQzH9KHekU6fJlI4vy1UwgRUwx3/1W6\n' \
'zlBYo1qXfc7NSVG8ZaSrJwW4rPn393u31CpXv+oc/OMCQQC1txS6nxM9+p/641HX\n' \
'CHXiWJRn0Wv7rT1FyF2dHO+OQOkCCnHCsGDMf3bacTNb7iyaPbXEDac8od5uF/3h\n' \
'aUy7AkBDPGoAeYItXqseL2Mlp6iG5+oRcp/o+YWH4IKqT84JHslI98KutL1+vKvw\n'\
'gi2mW63djeR1Xh1wqP85SvTKduHdAkAIJLlIF8Lr/yRWQQO06EsoJqIX+Pmm4L+j\n'\
'NfSECvztWhlXHxK0D+V2pKu15GbR0t2q1+Micx4wiGyIcIjPJkHrAkAvlbXGFcGT\n'\
'pk9bQ8nl7EYqlvVn1TejzTLfBhBYOse/xT/NI4Kwjkan9R+EJ1cOc9EE8gm1W3jv\n'\
'fMw/Bh2wC5kj\n'\
'-----END PRIVATE KEY-----\n'
CERTIFICADO = '-----BEGIN CERTIFICATE-----\n'\
'MIICMTCCAZqgAwIBAgIQfYOsIEVuAJ1FwwcTrY0t1DANBgkqhkiG9w0BAQUFADBX\n'\
'MVUwUwYDVQQDHkwAewA1ADkARgAxAEUANAA2ADEALQBEAEQARQA1AC0ANABEADIA\n'\
'RgAtAEEAMAAxAEEALQA4ADMAMwAyADIAQQA5AEUAQgA4ADMAOAB9MB4XDTE1MDYx\n'\
'NTA1NDc1N1oXDTE2MDYxNDExNDc1N1owVzFVMFMGA1UEAx5MAHsANQA5AEYAMQBF\n'\
'ADQANgAxAC0ARABEAEUANQAtADQARAAyAEYALQBBADAAMQBBAC0AOAAzADMAMgAy\n'\
'AEEAOQBFAEIAOAAzADgAfTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAk41G\n'\
'nqXXLaiOC/y0/cA4tbS+NZCqI+x4EsztgDFvPPlHstiVYcLRkni4i93gK9zoC6g0\n'\
'mh66HMVzAfE8vRNwW5b7m6nWS1SiHBon7/Mqsw4MIq3SC+J/fTbKpqwyfAuH2YZl\n'\
'AiQuQc85fyllAMLh2WrA7JgOLR/5tF3kLtpbHdECAwEAATANBgkqhkiG9w0BAQUF\n'\
'AAOBgQArdh+RyT6VxKGsXk1zhHsgwXfToe6GpTF4W8PHI1+T0WIsNForDhvst6nm\n'\
'QtgAhuZM9rxpOJuNKc+pM29EixpAiZZiRMCSWEItNyEVdUIi+YnKBcAHd88TwO86\n'\
'd126MWQ2O8cu5W1VoDp7hYBYKOnLbYi11/StO+0rzK+oPYAvIw==\n'\
'-----END CERTIFICATE-----\n'
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()
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 = Certificado(pfx_source, '123')
path = pfx.save_pfx()
saved = open(path, 'r').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()
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()
key_saved = open(key_path, 'r').read()
self.assertEqual(
cert, cert_saved, 'Certificado não corresponde ao original')
self.assertEqual(key, key_saved, 'Chave não corresponde ao original')
def test_pfx_nao_existe(self):
self.assertRaises(Exception, extract_cert_and_key_from_pfx,
'file.pfx', '123456')
def test_pfx_senha_invalida(self):
dir_pfx = os.path.join(self.caminho, 'teste.pfx')
self.assertRaises(Exception, extract_cert_and_key_from_pfx,
dir_pfx, '123')

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

143
pytrustnfe/test/test_nfse_paulistana.py

@ -1,143 +0,0 @@
# coding=utf-8
import mock
import os.path
import unittest
from pytrustnfe.certificado import Certificado
from pytrustnfe.nfse.paulistana import envio_lote_rps
from pytrustnfe.nfse.paulistana import cancelamento_nfe
from pytrustnfe.nfse.assinatura import Assinatura
from pytrustnfe.nfse.paulistana import sign_tag
class test_nfse_paulistana(unittest.TestCase):
caminho = os.path.dirname(__file__)
def _get_nfse(self):
rps = [
{
'assinatura': '123',
'serie': '1',
'numero': '1',
'data_emissao': '2016-08-29',
'codigo_atividade': '07498',
'total_servicos': '2.00',
'total_deducoes': '3.00',
'prestador': {
'inscricao_municipal': '123456'
},
'tomador': {
'tipo_cpfcnpj': '1',
'cpf_cnpj': '12345678923256',
'inscricao_municipal': '123456',
'razao_social': 'Trustcode',
'tipo_logradouro': '1',
'logradouro': 'Vinicius de Moraes, 42',
'numero': '42',
'bairro': 'Corrego',
'cidade': 'Floripa',
'uf': 'SC',
'cep': '88037240',
},
'codigo_atividade': '07498',
'aliquota_atividade': '5.00',
'descricao': 'Venda de servico'
}
]
nfse = {
'cpf_cnpj': '12345678901234',
'data_inicio': '2016-08-29',
'data_fim': '2016-08-29',
'lista_rps': rps
}
return nfse
def test_envio_nfse(self):
pfx_source = open(os.path.join(self.caminho, 'teste.pfx'), 'r').read()
pfx = Certificado(pfx_source, '123456')
nfse = self._get_nfse()
path = os.path.join(os.path.dirname(__file__), 'XMLs')
xml_return = open(os.path.join(
path, 'paulistana_resultado.xml'), 'r').read()
with mock.patch('pytrustnfe.nfse.paulistana.get_authenticated_client') as client:
retorno = mock.MagicMock()
client.return_value = retorno
retorno.service.EnvioLoteRPS.return_value = xml_return
retorno = envio_lote_rps(pfx, nfse=nfse)
self.assertEqual(retorno['received_xml'], xml_return)
self.assertEqual(retorno['object'].Cabecalho.Sucesso, True)
self.assertEqual(
retorno['object'].ChaveNFeRPS.ChaveNFe.NumeroNFe, 446)
self.assertEqual(
retorno['object'].ChaveNFeRPS.ChaveRPS.NumeroRPS, 6)
def test_nfse_signature(self):
pfx_source = open(os.path.join(self.caminho, 'teste.pfx'), 'r').read()
pfx = Certificado(pfx_source, '123456')
nfse = self._get_nfse()
path = os.path.join(os.path.dirname(__file__), 'XMLs')
xml_sent = open(os.path.join(
path, 'paulistana_signature.xml'), 'r').read()
with mock.patch('pytrustnfe.nfse.paulistana.get_authenticated_client') as client:
retorno = mock.MagicMock()
client.return_value = retorno
retorno.service.EnvioLoteRPS.return_value = '<xml></xml>'
retorno = envio_lote_rps(pfx, nfse=nfse)
self.assertEqual(retorno['sent_xml'], xml_sent)
def _get_cancelamento(self):
return {
'cnpj_remetente': '123',
'assinatura': 'assinatura',
'numero_nfse': '456',
'inscricao_municipal': '654',
'codigo_verificacao': '789',
}
def test_cancelamento_nfse_ok(self):
pfx_source = open(os.path.join(self.caminho, 'teste.pfx'), 'r').read()
pfx = Certificado(pfx_source, '123456')
cancelamento = self._get_cancelamento()
path = os.path.join(os.path.dirname(__file__), 'XMLs')
xml_return = open(os.path.join(
path, 'paulistana_canc_ok.xml'), 'r').read()
with mock.patch('pytrustnfe.nfse.paulistana.get_authenticated_client') as client:
retorno = mock.MagicMock()
client.return_value = retorno
retorno.service.CancelamentoNFe.return_value = xml_return
retorno = cancelamento_nfe(pfx, cancelamento=cancelamento)
self.assertEqual(retorno['received_xml'], xml_return)
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 = Certificado(pfx_source, '123456')
cancelamento = self._get_cancelamento()
path = os.path.join(os.path.dirname(__file__), 'XMLs')
xml_return = open(os.path.join(
path, 'paulistana_canc_errado.xml'), 'r').read()
with mock.patch('pytrustnfe.nfse.paulistana.get_authenticated_client') as client:
retorno = mock.MagicMock()
client.return_value = retorno
retorno.service.CancelamentoNFe.return_value = xml_return
retorno = cancelamento_nfe(pfx, cancelamento=cancelamento)
self.assertEqual(retorno['received_xml'], xml_return)
self.assertEqual(retorno['object'].Cabecalho.Sucesso, False)
self.assertEqual(
retorno['object'].Erro.ChaveNFe.NumeroNFe, 446)

48
pytrustnfe/test/test_servidores.py

@ -1,48 +0,0 @@
# coding=utf-8
'''
Created on Jun 14, 2015
@author: danimar
'''
import unittest
from pytrustnfe.Servidores import localizar_url, localizar_qrcode
url_ba = 'https://nfe.sefaz.ba.gov.br/webservices/NfeAutorizacao/NfeAutoriza\
cao.asmx'
url_sp = 'https://nfe.fazenda.sp.gov.br/ws/nfeautorizacao.asmx'
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_rs = 'https://nfe.sefazrs.rs.gov.br/ws/NfeAutorizacao/NFeAutorizacao.asmx'
url_cad_rs = 'https://cad.sefazrs.rs.gov.br/ws/cadconsultacadastro/cadcon\
sultacadastro2.asmx'
url_cad_sc = 'https://cad.svrs.rs.gov.br/ws/CadConsultaCadastro/CadConsult\
aCadastro2.asmx'
class test_servidores(unittest.TestCase):
def test_localizar_url(self):
url = localizar_url('NfeAutorizacao', '29', ambiente=1)
self.assertEqual(url, url_ba)
url = localizar_url('NfeAutorizacao', '35', ambiente=1)
self.assertEqual(url, url_sp)
url = localizar_url('NfeAutorizacao', '42', ambiente=1)
self.assertEqual(url, url_sc)
url = localizar_url('NfeAutorizacao', '43', ambiente=1)
self.assertEqual(url, url_rs)
url = localizar_url('NfeConsultaCadastro', '43', ambiente=2)
self.assertEqual(url, url_cad_rs)
url = localizar_url('NfeConsultaCadastro', '42', ambiente=2)
self.assertEqual(url, url_cad_sc)
def test_localizar_qrcode(self):
url = localizar_qrcode('35')
self.assertEqual(url, url_qrcode_homologacao_sp)

119
pytrustnfe/test/test_utils.py

@ -1,119 +0,0 @@
# coding=utf-8
'''
Created on Jun 16, 2015
@author: danimar
'''
import unittest
import datetime
from pytrustnfe.utils import date_tostring, datetime_tostring, \
gerar_chave
from pytrustnfe.utils import ChaveNFe, CabecalhoSoap
class test_utils(unittest.TestCase):
kwargs = {
'cnpj': '33009911002506', 'estado': '52', 'emissao': '0604',
'modelo': '55', 'serie': '012', 'numero': 780,
'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)
self.assertEqual(data, hoje.strftime("%d-%m-%y"),
"Não convertido corretamente")
self.assertRaises(Exception, date_tostring, "Not a date")
def test_datetime_tostring(self):
hoje = datetime.datetime.now()
data = datetime_tostring(hoje)
self.assertEqual(data, hoje.strftime("%d-%m-%y %H:%M:%S"),
"Não convertido corretamente")
self.assertRaises(Exception, datetime_tostring, "Not a date")
def test_geracao_chave(self):
chave = ChaveNFe(**self.kwargs)
str_chave = gerar_chave(chave)
chave_correta = '52060433009911002506550120000007800267301615'
self.assertEqual(str_chave, chave_correta,
"Geração de chave nf-e incorreta")
str_chave = gerar_chave(chave, prefix='NFe')
chave_correta = 'NFe52060433009911002506550120000007800267301615'
self.assertEqual(str_chave, chave_correta,
"Geração de chave nf-e com prefixo incorreta")
self.assertRaises(Exception, gerar_chave, "Not a ChaveNFe object")
self.assertRaises(Exception, gerar_chave, "Not a ChaveNFe object")
def test_chave_nfe(self):
chave = ChaveNFe(**self.kwargs)
with self.assertRaises(AssertionError) as cm:
chave.cnpj = ''
chave.validar()
chave.cnpj = '1234567891011'
self.assertEqual('CNPJ necessário para criar chave NF-e',
cm.exception.message,
'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm:
chave.estado = ''
chave.validar()
chave.estado = '42'
self.assertEqual('Estado necessário para criar chave NF-e',
cm.exception.message,
'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm:
chave.emissao = ''
chave.validar()
chave.emissao = '0'
self.assertEqual('Emissão necessário para criar chave NF-e',
cm.exception.message,
'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm:
chave.modelo = ''
chave.validar()
chave.modelo = '55'
self.assertEqual('Modelo necessário para criar chave NF-e',
cm.exception.message,
'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm:
chave.serie = ''
chave.validar()
chave.serie = '012'
self.assertEqual('Série necessária para criar chave NF-e',
cm.exception.message,
'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm:
chave.numero = ''
chave.validar()
chave.numero = '000000780'
self.assertEqual('Número necessário para criar chave NF-e',
cm.exception.message,
'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm:
chave.tipo = ''
chave.validar()
chave.tipo = '42'
self.assertEqual('Tipo necessário para criar chave NF-e',
cm.exception.message,
'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,
'Validação da chave nf-e incorreta')

37
pytrustnfe/test/test_xml_serializacao.py

@ -1,37 +0,0 @@
# coding=utf-8
import os.path
import unittest
from lxml import etree
from pytrustnfe.xml import render_xml
from pytrustnfe.xml import sanitize_response
class test_xml_serializacao(unittest.TestCase):
def test_serializacao_default(self):
path = os.path.join(os.path.dirname(__file__), 'XMLs')
xml = render_xml(path, 'jinja_template.xml', False, tag1='oi',
tag2='ola', tag3='comovai')
result = open(os.path.join(path, 'jinja_result.xml'), 'r').read()
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)
result = open(os.path.join(path, 'jinja_remove_empty.xml'), 'r').read()
self.assertEqual(xml + '\n', result)
def test_sanitize_response(self):
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')
self.assertEqual(obj.indNFe, '')
self.assertEqual(obj.indEmi, 'comovai')

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>

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

Loading…
Cancel
Save