Compare commits
merge into: felipe:master
felipe:feature/betha
felipe:feature/cte
felipe:feature/nfe-4.0
felipe:feature/nfe-mde
felipe:feature/nota-dsf
felipe:fix-endereco
felipe:fix-invalid-token-bug
felipe:fix-url-df
felipe:fix/warning
felipe:iss-net
felipe:master
felipe:master3
felipe:merge-master3
pull from: felipe:master3
felipe:feature/betha
felipe:feature/cte
felipe:feature/nfe-4.0
felipe:feature/nfe-mde
felipe:feature/nota-dsf
felipe:fix-endereco
felipe:fix-invalid-token-bug
felipe:fix-url-df
felipe:fix/warning
felipe:iss-net
felipe:master
felipe:master3
felipe:merge-master3
231 Commits
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
6eec69795c
|
Merge pull request #301 from danimaribeiro/order-exporta
Corrige a ordem da tag exportacao |
5 years ago |
|
|
75c2759196 |
Corrige a ordem da tag exportacao
|
5 years ago |
|
|
010939f062
|
Merge pull request #300 from fabioluna/feature/NT2020-0006
[IMP] NT2020-006 |
5 years ago |
|
|
19fcda59e7
|
[IMP] NT2020-006
Adiciona nova tag para adequar a NT2020-006 https://www.nfe.fazenda.gov.br/portal/exibirArquivo.aspx?conteudo=olkYgvQ3E7o= |
5 years ago |
|
|
98215954f8
|
Update README.md
|
5 years ago |
|
|
e05f972fee
|
Update README.md
|
5 years ago |
|
|
f052a5ae3f
|
Merge pull request #299 from filipealft/patch-3
Update setup.py |
5 years ago |
|
|
9a1cf24839
|
Merge pull request #298 from filipealft/patch-2
Update leiauteNFe_v4.00.xsd |
5 years ago |
|
|
41a0d028c1
|
Update setup.py
increment version |
5 years ago |
|
|
b552caa7ac
|
Update leiauteNFe_v4.00.xsd
fix leiauteNFe_v4 |
5 years ago |
|
|
092c467370
|
Merge pull request #297 from felipepaloschi/increment_version
Incrementing version |
5 years ago |
|
|
b71dee280c |
Incrementing version
|
5 years ago |
|
|
5e136c027a
|
Merge pull request #296 from filipealft/patch-1
Update NfeAutorizacao.xml |
5 years ago |
|
|
ae4ca15813
|
Update NfeAutorizacao.xml
Add indicativo do intermediador |
5 years ago |
|
|
969701912a
|
Merge pull request #292 from faio/master3
Geração NFSe prefeitura de Goiânia |
5 years ago |
|
|
922b26b7f4
|
Melhorias GINFES (#293) - Registrar nota assincronamente, Cancelar nota
* Fix ginfes cities link * Fix Cnpj in xml CancelarNfseV3 template * Update CancelarNfseV3.xml * Update CancelarNfseV3.xml * Update CancelarNfseV3.xml * Update CancelarNfseV3.xml * Update CancelarNfseV3.xml * Update CancelarNfseV3.xml * Update CancelarNfseV3.xml * Update CancelarNfseV3.xml * Update CancelarNfseV3.xml * Update CancelarNfseV3.xml * Update CancelarNfseV3.xml * Update CancelarNfseV3.xml * Update CancelarNfseV3.xml * Update CancelarNfseV3.xml * Update CancelarNfseV3.xml * Update CancelarNfseV3.xml * Update __init__.py * Update CancelarNfseV3.xml * Update CancelarNfseV3.xml * Update CancelarNfseV3.xml * Update CancelarNfseV3.xml * Update __init__.py * Create RecepcionarLoteRpsV3 * Update __init__.py * Update __init__.py * Implement xml_consultar_nfse_por_rps * Fix layout ConsultarNfseRpsEnvio * Add namespace to xml properties * Fix name of service in servico_consultar_nfse_rps_envio_v03 * Fix ConsultarNfseV3 * Fix xmlns ConsultarNfseEnvio * Fix property ConsultarNfseEnvio * Add PeriodoEmissao to ConsultarNfseEnvio * Change number to numero_nfse instead on ConsultarNfseEnvio * Remove periodo on ConsultarNfse |
5 years ago |
|
|
a008f99434 |
Retirado métodos não padrões na emissão de NFSe para a prefeitura de Goiânia/GO.
|
5 years ago |
|
|
79024b4747 |
Adicionado método para gerar o xml da NFSe para a prefeitura de Goiânia.
|
5 years ago |
|
|
17ee875c18 |
- Colocado como opcional tags de preenchimento de endereço do tomador conforme manual.
|
5 years ago |
|
|
54bf90863e |
- Colocado como opcional tags de preenchimento de impostos do serviço conforme manual da prefeitura.
|
5 years ago |
|
|
486d2ad1d9 |
- Adicionado consulta do RPS para a prefeitura de Goiânia/GO.
- Colocado como opcional a tag endereço na geração do rps conforme manual da prefeitura. |
5 years ago |
|
|
27c9ce902b |
- Correção no nome do método.
- Atualização do README.md |
5 years ago |
|
|
0b2c7259af
|
Merge pull request #1 from danimaribeiro/master3
Merge |
5 years ago |
|
|
ab57f6c4e0 |
Adicionado geração do RPS para a prefeitura de Goiânia/GO.
|
5 years ago |
|
|
719e15474b
|
Merge pull request #287 from danimaribeiro/fix-url-df
Corrige as urls do DF |
5 years ago |
|
|
f358b33d2f
|
Merge branch 'master3' into fix-url-df
|
5 years ago |
|
|
08c3bd31e3 |
Corrige as urls do DF
|
5 years ago |
|
|
c5b4d9e4e8 |
Fix unit tests
|
5 years ago |
|
|
7729c1df50 |
Carga tributaria media como opcional
|
5 years ago |
|
|
325bb78234 |
New urls for nfce RS
|
5 years ago |
|
|
ae1d57b5c6 |
Fixed overflow of address text
|
6 years ago |
|
|
05472e59a2 |
Melhoria na DANFE
Implementa blocos para impressão do endereço de entrega e endereço de retirada, conforme a NT2018 005. |
6 years ago |
|
|
6357ea46a0
|
Update XSD schemas for NFe (#272)
|
6 years ago |
|
|
4e6da7011e
|
Novos campos para icms retido por substituição tributária (#270)
* Fix the xml template for sending nfe * Novos campos para icms retido por substituição tributária |
6 years ago |
|
|
36c9b6c9ae |
Fix the xml template for sending nfe
|
6 years ago |
|
|
57672b4d9e
|
Bump version (#266)
|
6 years ago |
|
|
09a0ff15be
|
Criando parametros para contingência (#233)
* Criando parametros para contingência
Foi criado no template de autorização de NF-e o parametros:
dhCont = Data que o sistema entrou em contingência;
xJust = O motivo do sistema ter entrado em contingência.
E foi alterado versão
* Geração do QRCode
Foi alterado para gerar o QRCode pelo pytrustnfe ou manter da forma antiga sem afetar os usuarios que prefere gerar manualmente.
Foi alterado a função de assinatura para que possa retornar o etree ou string.
corrigindo o retorno do assina_xml para manter codigo
criado nova função para gerar qrcode com a possiblidade de assinatura tbm com parametro de entrada minimos;
edição do xml para gerar as tags de Informações Suplementares mesmo que não seja repassada a informação, gerando com informação de um ponto.
Co-authored-by: Michael Douglas <maiconkkl@users.noreply.github.com>
Co-authored-by: Danimar Ribeiro <danimaribeiro@gmail.com>
|
6 years ago |
|
|
09428c65a3
|
Merge pull request #265 from danimaribeiro/gabicavalcante-master3
Adicionando NFSe de Natal |
6 years ago |
|
|
944c7145bf |
Revert a few things before for nfe
|
6 years ago |
|
|
66127b4b1e
|
Merge branch 'master3' into gabicavalcante-master3
|
6 years ago |
|
|
4eaaa83016
|
Merge pull request #264 from danimaribeiro/loggi/master3
Loggi/master3 |
6 years ago |
|
|
79e192c9f8 |
Increment version
|
6 years ago |
|
|
3d2bd59f98 |
update gitignore
|
6 years ago |
|
|
9c4650d5da
|
Merge branch 'master3' into master3
|
6 years ago |
|
|
80950950d7
|
Merge pull request #7 from loggi/resolve-fork
[FINPD-69] Usar PyTrustNFe para emitar as notas fiscais |
6 years ago |
|
|
13900c782b |
test batch size
|
6 years ago |
|
|
7eb152da65 |
updates
|
6 years ago |
|
|
547af9d609 |
adding tests for rps batch
|
6 years ago |
|
|
77b4bfbfa9 |
fix tests
|
6 years ago |
|
|
404c9dd743 |
fix natal
|
6 years ago |
|
|
a14a06c2e9 |
remove
|
6 years ago |
|
|
0b5ffd82af |
base url
|
6 years ago |
|
|
9b125f513e |
fix nfse natal
|
6 years ago |
|
|
ecea3543ad |
add tests natal
|
6 years ago |
|
|
9574a48d8d |
flask8
|
6 years ago |
|
|
8c311d4caf |
black - python code formater
|
6 years ago |
|
|
c7beb73a6b |
add natal nfse
|
6 years ago |
|
|
100d6aa909
|
(Pytrust) fixing templete and bump version to 1.0.33.post3 (#6)
|
6 years ago |
|
|
09362d0f48
|
feat(pytrust) changing amount of rps sent. (#3)
* feat(pytrust) changing amount of rps sent. * Bump to v1.0.33.post2 |
6 years ago |
|
|
0b2e3ce204 |
New release
|
6 years ago |
|
|
55c44c2beb |
Adicionado as URL's para emissão de NFC-e no estado de MG. close issue #255
|
6 years ago |
|
|
90544b7301 |
Alterado webservice de consulta CNPJ do Estado de Minas Gerais
|
6 years ago |
|
|
a94092d128 |
[FIX] Corrigindo valor vISSRet
|
6 years ago |
|
|
8f98065ffc |
Add value 0.00 when ICMS value is not provided
|
6 years ago |
|
|
8768b24202 |
[IMP] Implementação de danfce de contingência - mensagens no danfce
|
6 years ago |
|
|
8e2b326010 |
[FIX] Cancelamento pode também ser status 155
|
6 years ago |
|
|
4ebc848237 |
[VER] Increment version
|
6 years ago |
|
|
a9154ba93d |
[FIX] Utilizando o campo xPed diretamente do produto
|
6 years ago |
|
|
704cd29b42 |
[FIX] Corrige NFS-e Simpliss
|
7 years ago |
|
|
ad83071171 |
[WIP] Implementação de NFSe Aparecida SP.
|
7 years ago |
|
|
b9f567953b |
Correção no arquivo xsd referenciado
|
7 years ago |
|
|
e229bd8f53 |
[ADD] new version
|
7 years ago |
|
|
7f360d39a8 |
[FIX] Corrige Urls de NFC-e para SP
|
7 years ago |
|
|
2ada587c2a |
Melhorias no DFeDistribuicao (#230)
* Corrigindo as URL de manaus * Alterando função da validação de esquema para não ser feito repetições e poder reutilizar a função só mudando o xsd; Foi alterado o template da função de consultra distribuição do DFe, permitindo consulta via NSU. * Remoção da classe de validação do esquema e deixando somente como função * Atualização do Readme para facilitar o uso das funções xml_consulta_distribuicao_nfe e valida_nfe; |
7 years ago |
|
|
cd9b87da94 |
Corrigindo as URL de manaus
Foi corrigido os webservices da nfce de manaus |
7 years ago |
|
|
9b8a37ce41 |
New version
|
7 years ago |
|
|
fd165c103b |
Correçao do erro gerado na mudança de versao 1.0.37
File "/usr/local/lib/python3.5/dist-packages/PyTrustNFe3-1.0.37-py3.5.egg/pytrustnfe/nfe/templates/NfeAutorizacao.xml", line 262, in top-level template code
<xPed>{{ NFe.infNFe.compra.xPed }}</xPed>
File "/usr/lib/python3/dist-packages/jinja2/environment.py", line 408, in getattr
return getattr(obj, attribute)
jinja2.exceptions.UndefinedError: 'dict object' has no attribute 'compra'
|
7 years ago |
|
|
a01421febd |
[FIX] servidor consulta svrs
|
7 years ago |
|
|
ba2ca969c8 |
[FIX] Add markdown type to readme
|
7 years ago |
|
|
d8d6b947a6 |
[ADD] Adiciona tag xped no item da nfe
* [FIX] xped path * [version] upgrade |
7 years ago |
|
|
0d3dd0693f |
[FIX] Ajusta a nota de petropolis ao novo formato
|
7 years ago |
|
|
710490dc1d |
[FIX] Product description overwriting NCM (#223)
* [FIX] Product description overwriting ncm * [VERSION] Increments |
7 years ago |
|
|
391dd1ae7d |
[IMP] NT 2018_005 v1.20 (#222)
Implementa novas tags incluídas na NT 2018_005 v1.20, que pode ser localizada em https://bit.ly/2FpEjp4. |
7 years ago |
|
|
eadc7b7306 |
[VER] New version
* [FIX] Dont duplicate items * [VERSION] Add |
7 years ago |
|
|
aea7429327 |
Merge branch 'master3' of github.com:danimaribeiro/PyTrustNFe into master3
|
7 years ago |
|
|
051f3b8f12 |
[VERSION] Increments version
|
7 years ago |
|
|
b5bcf0cd88 |
[FIX] Last item missed when doesnt fit in current page
|
7 years ago |
|
|
ba9e05538e |
Fix 'bytes' object has no attribute 'encode'
|
7 years ago |
|
|
c16a31469e |
Incrementa versão.
|
7 years ago |
|
|
452af1ecb3 |
Corrige url de consulta NFC-e Paraná.
|
7 years ago |
|
|
945dd77bea |
[FIX] nfeInutilizacaoCE url (#211)
* fix CE url in patch * flake8 * verifiy environment before choose URI |
7 years ago |
|
|
b01cde145e
|
Merge pull request #213 from danimaribeiro/Implanti-Solucoes-master3
Implanti solucoes master3 |
7 years ago |
|
|
bf03d54107 |
[VERSION] Increment pytest version to fix build
|
7 years ago |
|
|
197f36b9ed |
Erro na url de produção
Correção na url de produção, falha boba |
7 years ago |
|
|
628c3a20bf |
Correção do p=
Retirada do p= |
7 years ago |
|
|
9c4f76446b |
urlChave e Webservices
Foi adicionado uma função para retorno da urlChave do NFC-e que é diferente da url do QRCode; Foi corrigido os webservices da sefaz BA que estava as antigas; |
7 years ago |
|
|
b0a9e5370e |
[FIX] Fix patch method for inutilizacao - wrong merge
|
7 years ago |
|
|
c74893b658 |
[FIX] Inutilização NFe CE - Add a patch method
|
7 years ago |
|
|
59d4cf94b4 |
[FIX] Ajuste de cancelamento NFSe BH
|
7 years ago |
|
|
8de6521e8d |
[FIX] Adicionando complemento de endereço na danfe
|
7 years ago |
|
|
8c2de797aa
|
* [FIX] Ajuste na URL do parana
|
7 years ago |
|
|
a81c869a56 |
[FIX] Fix https warnings
|
7 years ago |
|
|
2723aece89 |
[ADD] Adiciona urls do qrcode para NFCe
|
7 years ago |
|
|
f01405affa |
[FIX] Sempre mostra o valor zerado de IPI e aliquota caso não existir
|
7 years ago |
|
|
ee047ef0cd |
[FIX] Corrige o encoding do retorno da nfse
|
7 years ago |
|
|
c7c380ce36 |
Webservices NFC-e Mato Grosso. (#194)
Inclui webservices para emissão de NFC-e no Mato Grosso. |
7 years ago |
|
|
b645b8ba78 |
Inclui Webservices para NFC-e do Mato Grosso do Sul. (#195)
|
7 years ago |
|
|
8b54bfa081 |
[IMP] Habilita enviar apenas o CPF no destinatário
|
7 years ago |
|
|
a25dd8a9d3 |
Webservices NFC-e Paraná
Incluí webservices da NFC-e do Paraná. |
7 years ago |
|
|
e233abfb04 |
[FIX] Corrige envio de NFS-e Ginfes
|
7 years ago |
|
|
ff75a6c77a |
[FIX] Calcula previamente o tamanho do proximo item
se ultrapassar o tamanho disponível finaliza e continua na próxima iteração |
7 years ago |
|
|
d4c3a8a0a7 |
[VER] New version
|
7 years ago |
|
|
36142aaf58 |
add troco and valor pago with the added values
|
7 years ago |
|
|
b81f3ddbc6 |
[FIX] first page overwriting (#188)
* change number of lines in first page * increments version |
7 years ago |
|
|
0ce7c3045f |
[FIX] Voltando as urls antigas, a principio DF não atualizaram ainda
|
7 years ago |
|
|
dbe2ab2ec5 |
[FIX] Ajusta a consulta de download de xml
|
7 years ago |
|
|
790543d36a |
[FIX] Ajustando as urls de consulta da danfce
|
7 years ago |
|
|
a252d86523 |
[FIX] Removing useless code for NFCe, adjusting xml template for NFCe
|
7 years ago |
|
|
4f3da1a075 |
[IMP] Fixing the fields for service invoices and danfe
|
7 years ago |
|
|
0c12982197 |
[FIX] Assign the correct variable when addin thousand separator
|
7 years ago |
|
|
7f5fe48999 |
thousandseparator added to monetary fields
|
7 years ago |
|
|
21994c73e8 |
[FIX] Removendo previamente os espaços resolve o problema
|
7 years ago |
|
|
43289e84c1 |
[FIX] Percentuais de tributação para nota paulistana
|
7 years ago |
|
|
3c5e8cd24c |
[FIX] Try to fix strange with spaces in some eletronic documents
|
7 years ago |
|
|
95e3e5710c |
Corrige webservice de retorno de autorização do Paraná.
|
8 years ago |
|
|
13a59b61b6 |
[FIX] Compare the operation name in lower case
|
8 years ago |
|
|
260a7e5add |
[11.0][IMP]different method to compute lines per page (#172)
* Adicionando mais espaço para as linhas de produtos |
8 years ago |
|
|
aafb2cf92a |
DSF - xml decode and url fix (#171)
* [WIP]NFS-e DSF * [WIP]NFS-e url * Update setup.py |
8 years ago |
|
|
0a1539ce1a |
[FIX] Corrige urls do MT
|
8 years ago |
|
|
59457090e9 |
[FIX] Ajusta os namespaces para envio de NFe no estado do PR
A biblioteca ZEEP remove namespaces duplicadas. O estado do PR exige a mesma namespace em duas tags, devemos adicionar a mesma na tag NFe antes do envio ao sefaz Paraná. |
8 years ago |
|
|
eea7fd74f4 |
nfse_mga minor changes in singnature, removed encode for byte type response and id on cancel template is now correct
|
8 years ago |
|
|
17770982cc |
[IMP] Migrando restante das urls da nfe 4.0
|
8 years ago |
|
|
de06defe5f |
[FIX] Realiza umas conversões de texto para evitar erros no Odoo
|
8 years ago |
|
|
2e9d83568e |
[FIX] Ajusta o danfe nos campos de frete e transportadora.
|
8 years ago |
|
|
8c132a3bbb |
[FIX] Certifica que vamos usar a operação correta ao enviar a NFE
|
8 years ago |
|
|
37506bd3ca |
Incrementa a versao da biblioteca
|
8 years ago |
|
|
92c1191d8f |
Revisado as urls da sefaz
|
8 years ago |
|
|
79abb1798c |
Atualizada as urls dos servidores sefaz ce
|
8 years ago |
|
|
bb5509c781 |
Correção do número de versão da tag nfeProc
Corrigido: número de versão da tag nfeProc de 3.10 para 4.00. A versão incorreta gerava erro de validação da nota fiscal enviada aos clientes, conforme testes realizados no site "https://www.sefaz.rs.gov.br/nfe/NFE-VAL.aspx" Alterado: número de versão do pytrustnfe da versão 0.10.1 para 0.10.2 |
8 years ago |
|
|
c9f71c9ea1 |
New zeep version uses settings instead of options
|
8 years ago |
|
|
4691c28623 |
[IMP] Novo release da pytrusnfe3
|
8 years ago |
|
|
bcbaf1474d |
[IMP] Atualizado schema da NFe
|
8 years ago |
|
|
f63ad7abbf |
[FIX] Corrige o build para o deploy
|
8 years ago |
|
|
40c3068447
|
Merge pull request #147 from danimaribeiro/feature/zeep
Feature/zeep |
8 years ago |
|
|
06764a249c |
[IMP] Fix unit tests and fix some urls
|
8 years ago |
|
|
16602cfa5b
|
Merge branch 'master3' into feature/zeep
|
8 years ago |
|
|
c95cba8ae7 |
Corrige webservice de recepção de eventos do Paraná
|
8 years ago |
|
|
9817897d30 |
update library version
|
8 years ago |
|
|
f0f38237f2 |
corrected issues in layout of danfe
|
8 years ago |
|
|
70816097e9 |
[IMP] Implementing NFe 4.0 - Cleaning up code and improve readability
|
8 years ago |
|
|
4572b99fcb |
[WIP]NFS-e Maringa
|
8 years ago |
|
|
5cbdd146f5 |
[WIP] Cleanup of useless code, using zeep for NFe soap, adjust xml files
|
8 years ago |
|
|
f4bcbb071c |
[WIP] Finalização do layout do xml, usando zeep para fazer chamada SOAP
|
8 years ago |
|
|
3180759fd9 |
Ajustando conforme validação.
|
8 years ago |
|
|
cbf17ee87d |
Adicionado campos novos da Nfe 4.0
|
8 years ago |
|
|
d4fdda4dab
|
Merge pull request #1 from loggi/frp-better-templates-for-nfse-paulistana-py3
Copy template from py2 version, fix setup.py |
8 years ago |
|
|
7fa4f4602f |
Add missing deps, bump version to 0.9.18.post2
|
8 years ago |
|
|
966ba5f795 |
[FIX] XML NFSe maringa and percent difal
|
8 years ago |
|
|
d616b3b9a6 |
Fix case name, bump version to 0.9.18.post1
|
8 years ago |
|
|
49113d7dab |
Better templates, for real.
|
8 years ago |
|
|
44bcedc33b |
Realiza a conversão do retorno corretamente com decode - BH
|
8 years ago |
|
|
8f4cf56a29 |
Adição das tags do veículo de transporte no danfe
|
8 years ago |
|
|
b474ec9913 |
Pequenos ajustes na impressão do danfce
|
8 years ago |
|
|
4f25b76ec8 |
Ajuste na url do qrcode amazonas
|
8 years ago |
|
|
c0dd5d02dd |
Implementação da danfe para NFC-e
|
8 years ago |
|
|
5893a0324b |
Correção de testes.
|
8 years ago |
|
|
c7bc21de2a |
Retira lixo código.
|
8 years ago |
|
|
5c9908da57 |
Insere servidor de cancelamento de NFC-e.
# Conflicts: # pytrustnfe/Servidores.py |
8 years ago |
|
|
cab13549cc |
Correções para envio de NFC-e
|
8 years ago |
|
|
4f8b752626 |
[WIP] NFC-e Corrige servidores.
Passa modelo do documento para localizar a url. |
8 years ago |
|
|
1d9f4b0776 |
[WIP] Implementando NFC-e SVRS
# Conflicts: # pytrustnfe/Servidores.py |
8 years ago |
|
|
e712bf0d6a |
Correção de envio Ginfes - Mudança para o zeep para webservice SOAP
|
8 years ago |
|
|
3db71b7b9a |
Nota de BH utiliza duas assinaturas
Correçõs nos templates |
8 years ago |
|
|
236515b12d |
Ajuste ao imprimir nfe cancelada, correção de encoding
|
8 years ago |
|
|
4f423e28f5 |
Implementa timezone ao imprimir a danfe
|
8 years ago |
|
|
9a1f406c62 |
Correção no envio de NFe para o estado do Ceará
|
8 years ago |
|
|
38874665b9 |
WIP - Implementação da nota de BH, tentativa de usar o zeep
|
8 years ago |
|
|
eb1db219b8 |
Nova tag de base de cálculo para nfse-floripa
|
8 years ago |
|
|
7a3f6275b8 |
Incrementando versão do pyopenssl
|
8 years ago |
|
|
1b2e206fcc |
Versão 0.9.15
|
8 years ago |
|
|
2aea7bae5d |
Update utils.py
|
8 years ago |
|
|
1f5645cf8e |
[FEAT][10.0]Cancelado no DANFE
# Conflicts: # setup.py |
8 years ago |
|
|
92a2505726 |
Correcao NFS-e Floripa - Descrição dos serviços
* decode no sent_xml nfse imperial * |
8 years ago |
|
|
ed322847ed |
decode no sent_xml nfse imperial
|
8 years ago |
|
|
6760152ed5 |
Ajuste no readme com novas implementações, remoção do appveyor build
Não vai ser desta vez que vamos atender o windows :/ |
8 years ago |
|
|
42d2a8d1e3 |
[DONE] Finalização do layout Nota Carioca - Geração e Cancelamento
|
8 years ago |
|
|
2bbd78c295 |
Implementação da nota carioca
|
8 years ago |
|
|
141fecb6a0 |
FIX - Corrige eventos do manifesto e geração da nfe processada
|
8 years ago |
|
|
9c2344c15e |
Master3 nfse imperial (#110)
* inserida danfse imperial * adicionado encode nfse imperial * correcao nfse imperial |
8 years ago |
|
|
105843d719 |
Master3 bug unicode (#109)
* correçao de bug unicode * bug encode nfse paulistana * incremento versao |
8 years ago |
|
|
6117a8b63f |
'Incrementar versão ;-;'
|
8 years ago |
|
|
6b00132c4b |
[FIX]Normalize e escape em campos de texto
|
8 years ago |
|
|
e1754c8e16 |
Incremento da versão do suds requests
|
8 years ago |
|
|
954dfcfecf |
Incremento de versão
|
8 years ago |
|
|
baca382a0f
|
Merge pull request #98 from danimaribeiro/master-fix
Master fix |
8 years ago |
|
|
f73aadada9
|
Merge branch 'master3' into master-fix
|
8 years ago |
|
|
57997f9164 |
Merge branch 'master3' into master-fix
# Conflicts: # .gitignore # pytrustnfe/nfe/danfe.py # setup.py |
8 years ago |
|
|
24be4815e1 |
Incremento de versão
|
8 years ago |
|
|
55031fe78d |
Implementação do cancelamento de NFSe - Floripa
|
8 years ago |
|
|
1f56dd5fa7 |
Retorna uma exceção correta quando não for possível obter o token
|
8 years ago |
|
|
7d6aacb655 |
Incremento de versões das dependencias
|
8 years ago |
|
|
d0ce9460b1
|
Merge pull request #86 from danimaribeiro/feature/nfse-floripa
Feature NFS-e Floripa |
8 years ago |
|
|
d53a0bb833
|
Merge branch 'master3' into feature/nfse-floripa
|
8 years ago |
|
|
78b0e47dfb |
Finalizado a parte de emissão de NFSe Floripa
|
8 years ago |
|
|
7d4f9079c8 |
* correção da url de campo grande-ms
|
8 years ago |
|
|
36611c8f81 |
alteração nfse campinas para dsf
|
8 years ago |
|
|
d2a0c26b1a |
mudança nfse campinas para dsf, adicionadas outras cidades também emitidas pela dsf
|
8 years ago |
|
|
4fc6b9dc89 |
adicionados urls de cidades do dsf
|
8 years ago |
|
|
f42937ae5f |
[FIX]
|
8 years ago |
|
|
4915472aa7 |
[FIX]
|
8 years ago |
|
|
0a21cfaad8 |
[FIX]Campo CPF
|
8 years ago |
|
|
22ac348b8b |
Implementa NFse de Campinas
|
8 years ago |
|
|
c2e2d1ed46 |
[WIP] Implementa NFS-e Campinas
|
8 years ago |
|
|
5a85580ba4 |
[WIP] - NFSE Floripa
|
8 years ago |
|
|
2138bd3ee2 |
adicionado string.strip no recursive_normalize
|
8 years ago |
|
|
7cff7bb5f1 |
[WIP] Implementação da nfse de florianópolis
|
8 years ago |
|
|
9219ec4243 |
[FIX] Corrige erro 225 na NF-e, devido a caracteres especiais.
|
8 years ago |
|
|
fa3f44b0de |
[FIX]Corrige envios de eventos
|
8 years ago |
|
|
914912ec7c |
Nova versão para publicar
|
8 years ago |
|
|
582742ecca |
[FIX] Corrige consulta de distribuíção de NF-e
|
8 years ago |
|
|
9b21368d25 |
Nova versão, atualização de badges
|
8 years ago |
|
|
0b47eba7b5 |
Refactor - Permitir criar o xml de envio separadamente
Ajuste na função de validação do xml |
8 years ago |
|
|
b05bd19354 |
Build para windows
|
9 years ago |
|
|
a1c65663bc |
Updating setup.py to include font files
|
9 years ago |
|
|
aa4fd2bb42 |
Inclui fonte NimbusSanL.
|
9 years ago |
|
|
c44cd90103 |
Fixing build for windows
|
9 years ago |
|
|
7b31a2f78f |
Removendo print - Ajustando encodings
|
9 years ago |
|
|
fabe0e0561 |
Preparando pypi package para versão python >= 3.4
|
9 years ago |
|
|
0c89b3e957 |
Modificação na estrutura do projeto - Movendo testes para fora do pacote
|
9 years ago |
|
|
5c5602acf9 |
Migração para python 3 - Correção de testes
|
9 years ago |
|
|
4581ea6dbe |
Ajuste de travis e dependencias da versão 3
|
9 years ago |
175 changed files with 14383 additions and 10120 deletions
-
6.flake8
-
10.gitignore
-
10.travis.yml
-
130README.md
-
7cidades/dsf.md
-
165docs/conf.py
-
1222pytrustnfe/Servidores.py
-
8pytrustnfe/__init__.py
-
23pytrustnfe/certificado.py
-
36pytrustnfe/client.py
-
4pytrustnfe/exceptions.py
-
501pytrustnfe/nfe/__init__.py
-
29pytrustnfe/nfe/assinatura.py
-
34pytrustnfe/nfe/comunicacao.py
-
529pytrustnfe/nfe/danfce.py
-
1268pytrustnfe/nfe/danfe.py
-
BINpytrustnfe/nfe/fonts/NimbusSanL Bold.ttf
-
BINpytrustnfe/nfe/fonts/NimbusSanL Regular.ttf
-
45pytrustnfe/nfe/patch.py
-
33pytrustnfe/nfe/templates/NFeDistribuicaoDFe.xml
-
286pytrustnfe/nfe/templates/NfeAutorizacao.xml
-
5pytrustnfe/nfe/templates/NfeConsultaProtocolo.xml
-
2pytrustnfe/nfe/templates/NfeInutilizacao.xml
-
2pytrustnfe/nfe/templates/NfeRetAutorizacao.xml
-
4pytrustnfe/nfe/templates/NfeStatusServico.xml
-
6pytrustnfe/nfe/templates/RecepcaoEvento.xml
-
21pytrustnfe/nfe/templates/RecepcaoEventoCarta.xml
-
31pytrustnfe/nfe/templates/RecepcaoEventoEPEC.xml
-
2pytrustnfe/nfe/templates/RecepcaoEventoManifesto.xml
-
76pytrustnfe/nfse/aparecida/__init__.py
-
91pytrustnfe/nfse/aparecida/templates/Rps.xml
-
15pytrustnfe/nfse/aparecida/templates/cancelarNfse.xml
-
7pytrustnfe/nfse/aparecida/templates/consultarLoteRps.xml
-
13pytrustnfe/nfse/aparecida/templates/recepcionarLoteRps.xml
-
99pytrustnfe/nfse/assinatura.py
-
78pytrustnfe/nfse/betha/__init__.py
-
79pytrustnfe/nfse/bh/__init__.py
-
47pytrustnfe/nfse/bh/assinatura.py
-
13pytrustnfe/nfse/bh/templates/CancelarNfse.xml
-
11pytrustnfe/nfse/bh/templates/GerarNfse.xml
-
91pytrustnfe/nfse/bh/templates/Rps.xml
-
69pytrustnfe/nfse/carioca/__init__.py
-
13pytrustnfe/nfse/carioca/templates/CancelarNfse.xml
-
3pytrustnfe/nfse/carioca/templates/GerarNfse.xml
-
91pytrustnfe/nfse/carioca/templates/Rps.xml
-
124pytrustnfe/nfse/dsf/__init__.py
-
18pytrustnfe/nfse/dsf/templates/cancelar.xml
-
11pytrustnfe/nfse/dsf/templates/consulta_notas.xml
-
10pytrustnfe/nfse/dsf/templates/consultarLote.xml
-
22pytrustnfe/nfse/dsf/templates/consultarNFSeRps.xml
-
100pytrustnfe/nfse/dsf/templates/enviar.xml
-
12pytrustnfe/nfse/dsf/templates/soap_header.xml
-
128pytrustnfe/nfse/floripa/__init__.py
-
7pytrustnfe/nfse/floripa/templates/cancelar_nota.xml
-
41pytrustnfe/nfse/floripa/templates/processar_nota.xml
-
110pytrustnfe/nfse/ginfes/__init__.py
-
25pytrustnfe/nfse/ginfes/templates/CancelarNfseV3.xml
-
15pytrustnfe/nfse/ginfes/templates/ConsultarNfsePorRpsV3.xml
-
12pytrustnfe/nfse/ginfes/templates/ConsultarNfseV3.xml
-
61pytrustnfe/nfse/goiania/__init__.py
-
38pytrustnfe/nfse/goiania/assinatura.py
-
19pytrustnfe/nfse/goiania/templates/ConsultarNfseRps.xml
-
4pytrustnfe/nfse/goiania/templates/GerarNfse.xml
-
63pytrustnfe/nfse/goiania/templates/Rps.xml
-
74pytrustnfe/nfse/imperial/__init__.py
-
17pytrustnfe/nfse/imperial/templates/CANCELANOTAELETRONICA.xml
-
9pytrustnfe/nfse/imperial/templates/CONSULTANOTASPROTOCOLO.xml
-
9pytrustnfe/nfse/imperial/templates/CONSULTAPROTOCOLO.xml
-
14pytrustnfe/nfse/imperial/templates/CancelarNota.xml
-
43pytrustnfe/nfse/imperial/templates/GerarNota.xml
-
81pytrustnfe/nfse/imperial/templates/PROCESSARPS.xml
-
5pytrustnfe/nfse/imperial/templates/SoapRequest.xml
-
70pytrustnfe/nfse/mga/__init__.py
-
48pytrustnfe/nfse/mga/assinatura.py
-
15pytrustnfe/nfse/mga/templates/CancelarNfse.xml
-
3pytrustnfe/nfse/mga/templates/GerarNfse.xml
-
81pytrustnfe/nfse/mga/templates/Rps.xml
-
116pytrustnfe/nfse/natal/__init__.py
-
8pytrustnfe/nfse/natal/templates/EnvelopeSoap.xml
-
111pytrustnfe/nfse/natal/templates/Exemplo_LoteRPS.xml
-
112pytrustnfe/nfse/natal/templates/Rps.xml
-
3pytrustnfe/nfse/natal/templates/cabecalho.xml
-
15pytrustnfe/nfse/natal/templates/cancelarNfse.xml
-
7pytrustnfe/nfse/natal/templates/consultarLoteRps.xml
-
11pytrustnfe/nfse/natal/templates/recepcionarLoteRps.xml
-
73pytrustnfe/nfse/paulistana/__init__.py
-
23pytrustnfe/nfse/paulistana/templates/EnvioLoteRPS.xml
-
62pytrustnfe/nfse/paulistana/templates/EnvioRPS.xml
-
66pytrustnfe/nfse/simpliss/__init__.py
-
36pytrustnfe/nfse/susesu/__init__.py
-
9pytrustnfe/nfse/susesu/templates/EnviarNota.xml
-
1pytrustnfe/test/XMLs/paulistana_resultado.xml
-
34pytrustnfe/test/test_add_qr_code.py
-
57pytrustnfe/test/test_assinatura.py
-
82pytrustnfe/test/test_certificado.py
-
20pytrustnfe/test/test_comunicacao.py
-
19pytrustnfe/test/test_consulta_cadastro.py
-
143pytrustnfe/test/test_nfse_paulistana.py
-
48pytrustnfe/test/test_servidores.py
-
119pytrustnfe/test/test_utils.py
@ -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/ |
||||
@ -0,0 +1,7 @@ |
|||||
|
* Belém - PA |
||||
|
* Sorocaba - SP |
||||
|
* Teresina - PI |
||||
|
* Campinas - SP |
||||
|
* Uberlandia - MG |
||||
|
* São Luis - MA |
||||
|
* Campo Grande - MS |
||||
1222
pytrustnfe/Servidores.py
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,243 +1,378 @@ |
|||||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||||
# © 2016 Danimar Ribeiro, Trustcode |
# © 2016 Danimar Ribeiro, Trustcode |
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
|
||||
|
|
||||
import os |
|
||||
import hashlib |
import hashlib |
||||
|
import os |
||||
|
import requests |
||||
from lxml import etree |
from lxml import etree |
||||
from .comunicacao import executar_consulta |
|
||||
|
from .patch import has_patch |
||||
from .assinatura import Assinatura |
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.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): |
def _generate_nfe_id(**kwargs): |
||||
for item in kwargs['NFes']: |
|
||||
|
for item in kwargs["NFes"]: |
||||
vals = { |
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 = 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 = 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: |
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) |
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( |
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( |
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: |
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 |
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): |
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 |
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): |
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): |
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): |
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 |
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 |
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): |
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): |
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, |
||||
|
} |
||||
@ -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) |
|
||||
@ -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
File diff suppressed because it is too large
View File
@ -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 |
||||
@ -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> |
||||
@ -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> |
<tpAmb>{{ obj.ambiente }}</tpAmb> |
||||
<xServ>CONSULTAR</xServ> |
<xServ>CONSULTAR</xServ> |
||||
<chNFe>{{ obj.chave_nfe }}</chNFe> |
<chNFe>{{ obj.chave_nfe }}</chNFe> |
||||
</consSitNFe> |
|
||||
|
</consSitNFe> |
||||
@ -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> |
<tpAmb>{{ obj.ambiente }}</tpAmb> |
||||
<nRec>{{ obj.numero_recibo }}</nRec> |
<nRec>{{ obj.numero_recibo }}</nRec> |
||||
</consReciNFe> |
</consReciNFe> |
||||
@ -1,6 +1,6 @@ |
|||||
<?xml version="1.0" encoding="UTF-8" ?> |
<?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> |
<tpAmb>{{ obj.ambiente }}</tpAmb> |
||||
<cUF>{{ obj.estado }}</cUF> |
<cUF>{{ obj.estado }}</cUF> |
||||
<xServ>STATUS</xServ> |
<xServ>STATUS</xServ> |
||||
</consStatServ> |
|
||||
|
</consStatServ> |
||||
@ -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> |
|
||||
@ -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> |
|
||||
@ -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) |
||||
@ -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> |
||||
@ -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> |
||||
@ -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> |
||||
@ -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> |
||||
@ -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) |
||||
@ -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) |
||||
@ -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> |
||||
@ -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> |
||||
@ -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> |
||||
@ -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) |
||||
@ -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> |
||||
@ -0,0 +1,3 @@ |
|||||
|
<GerarNfseEnvio xmlns="http://notacarioca.rio.gov.br/WSNacional/XSD/1/nfse_pcrj_v01.xsd"> |
||||
|
{% include 'Rps.xml' %} |
||||
|
</GerarNfseEnvio> |
||||
@ -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> |
||||
@ -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) |
||||
@ -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> |
||||
@ -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> |
||||
@ -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> |
||||
@ -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> |
||||
@ -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> |
||||
@ -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> |
||||
@ -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 |
||||
@ -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> |
||||
@ -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> |
||||
@ -1,15 +1,14 @@ |
|||||
<CancelarNfseEnvio xmlns="http://www.betha.com.br/e-nota-contribuinte-ws"> |
|
||||
<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> |
|
||||
|
<CancelarNfseEnvio xmlns="http://www.ginfes.com.br/servico_cancelar_nfse_envio_v03.xsd" |
||||
|
xmlns:tipos="http://www.ginfes.com.br/tipos_v03.xsd"> |
||||
|
<Pedido xmlns=""> |
||||
|
<tipos:InfPedidoCancelamento Id="C{{ cancelamento.numero_nfse }}"> |
||||
|
<tipos:IdentificacaoNfse> |
||||
|
<tipos:Numero>{{ cancelamento.numero_nfse }}</tipos:Numero> |
||||
|
<tipos:Cnpj>{{ cancelamento.cnpj_prestador }}</tipos:Cnpj> |
||||
|
<tipos:InscricaoMunicipal>{{ cancelamento.inscricao_municipal }}</tipos:InscricaoMunicipal> |
||||
|
<tipos:CodigoMunicipio>{{ cancelamento.cidade }}</tipos:CodigoMunicipio> |
||||
|
</tipos:IdentificacaoNfse> |
||||
|
<tipos:CodigoCancelamento>{{ cancelamento.codigo_cancelamento }}</tipos:CodigoCancelamento> |
||||
|
</tipos:InfPedidoCancelamento> |
||||
</Pedido> |
</Pedido> |
||||
</CancelarNfseEnvio> |
</CancelarNfseEnvio> |
||||
@ -1,13 +1,12 @@ |
|||||
<ConsultarNfseRpsEnvio xmlns="http://www.betha.com.br/e-nota-contribuinte-ws"> |
|
||||
|
<ConsultarNfseRpsEnvio xmlns="http://www.ginfes.com.br/servico_consultar_nfse_rps_envio_v03.xsd" |
||||
|
xmlns:tipos="http://www.ginfes.com.br/tipos_v03.xsd"> |
||||
<IdentificacaoRps> |
<IdentificacaoRps> |
||||
<Numero>24</Numero> |
|
||||
<Serie>A1</Serie> |
|
||||
<Tipo>1</Tipo> |
|
||||
|
<tipos:Numero>{{ consulta.numero }}</tipos:Numero> |
||||
|
<tipos:Serie>{{ consulta.serie }}</tipos:Serie> |
||||
|
<tipos:Tipo>{{ consulta.tipo }}</tipos:Tipo> |
||||
</IdentificacaoRps> |
</IdentificacaoRps> |
||||
<Prestador> |
<Prestador> |
||||
<CpfCnpj> |
|
||||
<Cnpj>45111111111100</Cnpj> |
|
||||
</CpfCnpj> |
|
||||
<InscricaoMunicipal>123498</InscricaoMunicipal> |
|
||||
|
<tipos:Cnpj>{{ consulta.cnpj_prestador }}</tipos:Cnpj> |
||||
|
<tipos:InscricaoMunicipal>{{ consulta.inscricao_municipal }}</tipos:InscricaoMunicipal> |
||||
</Prestador> |
</Prestador> |
||||
</ConsultarNfseRpsEnvio> |
</ConsultarNfseRpsEnvio> |
||||
@ -1,8 +1,8 @@ |
|||||
<ConsultarLoteRpsEnvio xmlns="http://www.betha.com.br/e-nota-contribuinte-ws"> |
|
||||
|
<ConsultarNfseEnvio xmlns="http://www.ginfes.com.br/servico_consultar_nfse_envio_v03.xsd" |
||||
|
xmlns:tipos="http://www.ginfes.com.br/tipos_v03.xsd"> |
||||
<Prestador> |
<Prestador> |
||||
<CpfCnpj> |
|
||||
<Cnpj>45111111111100</Cnpj> |
|
||||
</CpfCnpj> |
|
||||
|
<tipos:Cnpj>{{ consulta.cnpj_prestador }}</tipos:Cnpj> |
||||
|
<tipos:InscricaoMunicipal>{{ consulta.inscricao_municipal }}</tipos:InscricaoMunicipal> |
||||
</Prestador> |
</Prestador> |
||||
<Protocolo>141542179222170</Protocolo> |
|
||||
</ConsultarLoteRpsEnvio> |
|
||||
|
<NumeroNfse>{{ consulta.numero_nfse }}</NumeroNfse> |
||||
|
</ConsultarNfseEnvio> |
||||
@ -0,0 +1,61 @@ |
|||||
|
import os |
||||
|
import suds |
||||
|
|
||||
|
from lxml import etree |
||||
|
|
||||
|
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 .assinatura import Assinatura |
||||
|
|
||||
|
|
||||
|
def _render(certificado, method, **kwargs): |
||||
|
path = os.path.join(os.path.dirname(__file__), "templates") |
||||
|
xml_send = render_xml(path, f"{method}.xml", False, **kwargs) |
||||
|
signer = Assinatura(certificado.pfx, certificado.password) |
||||
|
xml_send = etree.fromstring(xml_send) |
||||
|
xml_send = signer.assina_xml(xml_send) |
||||
|
return xml_send |
||||
|
|
||||
|
|
||||
|
def _send(certificado, method, **kwargs): |
||||
|
base_url = "https://nfse.goiania.go.gov.br/ws/nfse.asmx?wsdl" |
||||
|
xml_send = kwargs["xml"] |
||||
|
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 { |
||||
|
"send_xml": str(xml_send), |
||||
|
"received_xml": str(e.fault.faultstring), |
||||
|
"object": None, |
||||
|
} |
||||
|
|
||||
|
response, obj = sanitize_response(response) |
||||
|
return {"send_xml": str(xml_send), "received_xml": str(response), "object": obj} |
||||
|
|
||||
|
|
||||
|
def xml_gerar_nfse(certificado, **kwargs): |
||||
|
""" Retorna o XML montado para ser enviado para o Webservice """ |
||||
|
|
||||
|
return _render(certificado, "GerarNfse", **kwargs) |
||||
|
|
||||
|
|
||||
|
def gerar_nfse(certificado, **kwargs): |
||||
|
"""" Gera uma NFSe de saída """ |
||||
|
|
||||
|
if "xml" not in kwargs: |
||||
|
kwargs["xml"] = xml_gerar_nfse |
||||
|
return _send(certificado, "GerarNfse", **kwargs) |
||||
|
|
||||
|
|
||||
|
def consulta_nfse_por_rps(certificado, **kwargs): |
||||
|
""" Consulta os dados de um NFSe já emitida """ |
||||
|
|
||||
|
if "xml" not in kwargs: |
||||
|
kwargs["xml"] = _render(certificado, "ConsultarNfseRps", **kwargs) |
||||
|
return _send(certificado, "ConsultarNfseRps", **kwargs) |
||||
@ -0,0 +1,38 @@ |
|||||
|
from lxml import etree |
||||
|
from pytrustnfe.certificado import extract_cert_and_key_from_pfx |
||||
|
from signxml import XMLSigner, methods |
||||
|
from pytrustnfe.nfe.assinatura import Assinatura as _Assinatura |
||||
|
|
||||
|
|
||||
|
class Assinatura(_Assinatura): |
||||
|
|
||||
|
def assina_xml(self, xml_element): |
||||
|
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=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_signed = xml_element.find(".//{http://nfse.goiania.go.gov.br/xsd/nfse_gyn_v02.xsd}Rps") |
||||
|
signed_root = signer.sign( |
||||
|
xml_element, key=key.encode(), cert=cert.encode() |
||||
|
) |
||||
|
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) |
||||
@ -0,0 +1,19 @@ |
|||||
|
<?xml version="1.0"?> |
||||
|
<ConsultarNfseRpsEnvio xmlns="http://nfse.goiania.go.gov.br/xsd/nfse_gyn_v02.xsd"> |
||||
|
<IdentificacaoRps> |
||||
|
<Numero>{{ numero }}</Numero> |
||||
|
<Serie>{{ serie }}</Serie> |
||||
|
<Tipo>{{ tipo }}</Tipo> |
||||
|
</IdentificacaoRps> |
||||
|
<Prestador> |
||||
|
<CpfCnpj> |
||||
|
{% if cnpj_cpf|length == 14 %} |
||||
|
<Cnpj>{{ cnpj_cpf }}</Cnpj> |
||||
|
{% endif %} |
||||
|
{% if cnpj_cpf|length == 11 %} |
||||
|
<Cpf>{{ cnpj_cpf }}</Cpf> |
||||
|
{% endif %} |
||||
|
</CpfCnpj> |
||||
|
<InscricaoMunicipal>{{ inscricao_municipal }}</InscricaoMunicipal> |
||||
|
</Prestador> |
||||
|
</ConsultarNfseRpsEnvio> |
||||
@ -0,0 +1,4 @@ |
|||||
|
<?xml version="1.0"?> |
||||
|
<GerarNfseEnvio xmlns="http://nfse.goiania.go.gov.br/xsd/nfse_gyn_v02.xsd"> |
||||
|
{% include 'Rps.xml' %} |
||||
|
</GerarNfseEnvio> |
||||
@ -0,0 +1,63 @@ |
|||||
|
<Rps> |
||||
|
<InfDeclaracaoPrestacaoServico> |
||||
|
<Rps> |
||||
|
<IdentificacaoRps> |
||||
|
<Numero>{{ numero }}</Numero> |
||||
|
<Serie>{{ serie }}</Serie> |
||||
|
<Tipo>{{ tipo }}</Tipo> |
||||
|
</IdentificacaoRps> |
||||
|
<DataEmissao>{{ data_emissao }}</DataEmissao> |
||||
|
<Status>{{ status }}</Status> |
||||
|
</Rps> |
||||
|
<Servico> |
||||
|
<Valores> |
||||
|
<ValorServicos>{{ servico.valor_servicos }}</ValorServicos> |
||||
|
{% if servico.valor_pis %}<ValorPis>{{ servico.valor_pis }}</ValorPis>{% endif %} |
||||
|
{% if servico.valor_confins %}<ValorCofins>{{ servico.valor_confins }}</ValorCofins>{% endif %} |
||||
|
{% if servico.valor_inss %}<ValorInss>{{ servico.valor_inss }}</ValorInss>{% endif %} |
||||
|
{% if servico.valor_csll %}<ValorCsll>{{ servico.valor_csll }}</ValorCsll>{% endif %} |
||||
|
{% if servico.aliquota %}<Aliquota>{{ servico.aliquota }}</Aliquota>{% endif %} |
||||
|
</Valores> |
||||
|
<CodigoTributacaoMunicipio>{{ servico.codigo_tributacao_municipio }}</CodigoTributacaoMunicipio> |
||||
|
<Discriminacao>{{ servico.discriminacao }}</Discriminacao> |
||||
|
<CodigoMunicipio>{{ servico.codigo_municipio }}</CodigoMunicipio> |
||||
|
</Servico> |
||||
|
<Prestador> |
||||
|
<CpfCnpj> |
||||
|
{% if prestador.cnpj_cpf|length == 14 %} |
||||
|
<Cnpj>{{ prestador.cnpj_cpf }}</Cnpj> |
||||
|
{% endif %} |
||||
|
{% if prestador.cnpj_cpf|length == 11 %} |
||||
|
<Cpf>{{ prestador.cnpj_cpf }}</Cpf> |
||||
|
{% endif %} |
||||
|
</CpfCnpj> |
||||
|
<InscricaoMunicipal>{{ prestador.inscricao_municipal }}</InscricaoMunicipal> |
||||
|
</Prestador> |
||||
|
<Tomador> |
||||
|
<IdentificacaoTomador> |
||||
|
<CpfCnpj> |
||||
|
{% if tomador.cnpj_cpf|length == 14 %} |
||||
|
<Cnpj>{{ tomador.cnpj_cpf }}</Cnpj> |
||||
|
{% endif %} |
||||
|
{% if prestador.cnpj_cpf|length == 11 %} |
||||
|
<Cpf>{{ prestador.cnpj_cpf }}</Cpf> |
||||
|
{% endif %} |
||||
|
</CpfCnpj> |
||||
|
{% if tomador.inscricao_municipal %} |
||||
|
<InscricaoMunicipal>{{ tomador.inscricao_municipal }}</InscricaoMunicipal> |
||||
|
{% endif %} |
||||
|
</IdentificacaoTomador> |
||||
|
<RazaoSocial>{{ tomador.razao_social }}</RazaoSocial> |
||||
|
{% if tomador.endereco %} |
||||
|
<Endereco> |
||||
|
{% if tomador.endereco.rua %}<Endereco>{{ tomador.endereco.rua }}</Endereco>{%endif %} |
||||
|
{% if tomador.endereco.numero %}<Numero>{{ tomador.endereco.numero }}</Numero>{%endif %} |
||||
|
{% if tomador.endereco.complemento %}<Complemento>{{ tomador.endereco.complemento }}</Complemento>{%endif %} |
||||
|
{% if tomador.endereco.bairro %}<Bairro>{{ tomador.endereco.bairro }}</Bairro>{%endif %} |
||||
|
{% if tomador.endereco.codigo_municipio %}<CodigoMunicipio>{{ tomador.endereco.codigo_municipio }}</CodigoMunicipio>{%endif %} |
||||
|
{% if tomador.endereco.uf %}<Uf>{{ tomador.endereco.uf }}</Uf>{%endif %} |
||||
|
</Endereco> |
||||
|
{% endif %} |
||||
|
</Tomador> |
||||
|
</InfDeclaracaoPrestacaoServico> |
||||
|
</Rps> |
||||
@ -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> |
|
||||
@ -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> |
|
||||
@ -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> |
|
||||
@ -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> |
||||
@ -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> |
||||
@ -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> |
|
||||
@ -1,5 +0,0 @@ |
|||||
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"> |
|
||||
<Body> |
|
||||
{{ soap_body }} |
|
||||
</Body> |
|
||||
</Envelope> |
|
||||
@ -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) |
||||
@ -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) |
||||
@ -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> |
||||
@ -0,0 +1,3 @@ |
|||||
|
<GerarNfseEnvio xmlns="http://www.abrasf.org.br/nfse.xsd"> |
||||
|
{% include 'Rps.xml' %} |
||||
|
</GerarNfseEnvio> |
||||
@ -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> |
||||
@ -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) |
||||
@ -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> |
||||
@ -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> |
||||
@ -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> |
||||
@ -0,0 +1,3 @@ |
|||||
|
<cabecalho versao="2.01" xmlns="http://www.abrasf.org.br/nfse.xsd"> |
||||
|
<versaoDados>2.01</versaoDados> |
||||
|
</cabecalho> |
||||
@ -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> |
||||
@ -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> |
||||
@ -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> |
||||
@ -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> |
|
||||
@ -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()) |
|
||||
@ -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') |
|
||||
@ -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') |
|
||||
@ -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 |
|
||||
@ -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') |
|
||||
@ -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) |
|
||||
@ -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) |
|
||||
@ -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') |
|
||||
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue