Compare commits

...

42 Commits

Author SHA1 Message Date
Danimar Ribeiro 296afb4897 [WIP] - Envio das notas do provedor DSF 8 years ago
carcaroff 6117a8b63f 'Incrementar versão ;-;' 8 years ago
carcaroff 6b00132c4b [FIX]Normalize e escape em campos de texto 8 years ago
Danimar Ribeiro e1754c8e16 Incremento da versão do suds requests 8 years ago
Danimar Ribeiro 954dfcfecf Incremento de versão 8 years ago
Danimar Ribeiro baca382a0f
Merge pull request #98 from danimaribeiro/master-fix 8 years ago
Danimar Ribeiro f73aadada9
Merge branch 'master3' into master-fix 8 years ago
Danimar Ribeiro 57997f9164 Merge branch 'master3' into master-fix 8 years ago
Danimar Ribeiro 24be4815e1 Incremento de versão 8 years ago
Danimar Ribeiro 55031fe78d Implementação do cancelamento de NFSe - Floripa 8 years ago
Danimar Ribeiro 1f56dd5fa7 Retorna uma exceção correta quando não for possível obter o token 8 years ago
Danimar Ribeiro 7d6aacb655 Incremento de versões das dependencias 8 years ago
Danimar Ribeiro d0ce9460b1
Merge pull request #86 from danimaribeiro/feature/nfse-floripa 8 years ago
Danimar Ribeiro d53a0bb833
Merge branch 'master3' into feature/nfse-floripa 8 years ago
Danimar Ribeiro 78b0e47dfb Finalizado a parte de emissão de NFSe Floripa 8 years ago
pal0schi 7d4f9079c8 * correção da url de campo grande-ms 8 years ago
Felipe 36611c8f81 alteração nfse campinas para dsf 8 years ago
Felipe d2a0c26b1a mudança nfse campinas para dsf, adicionadas outras cidades também emitidas pela dsf 8 years ago
Felipe 4fc6b9dc89 adicionados urls de cidades do dsf 8 years ago
carcaroff f42937ae5f [FIX] 8 years ago
carcaroff 4915472aa7 [FIX] 8 years ago
carcaroff 0a21cfaad8 [FIX]Campo CPF 8 years ago
Fábio Luna 22ac348b8b Implementa NFse de Campinas 8 years ago
Fábio Luna c2e2d1ed46 [WIP] Implementa NFS-e Campinas 8 years ago
Danimar Ribeiro 5a85580ba4 [WIP] - NFSE Floripa 8 years ago
Felipe 2138bd3ee2 adicionado string.strip no recursive_normalize 8 years ago
Danimar Ribeiro 7cff7bb5f1 [WIP] Implementação da nfse de florianópolis 8 years ago
Fábio Luna 9219ec4243 [FIX] Corrige erro 225 na NF-e, devido a caracteres especiais. 8 years ago
Fábio Luna fa3f44b0de [FIX]Corrige envios de eventos 8 years ago
Danimar Ribeiro 914912ec7c Nova versão para publicar 8 years ago
Fábio Luna 582742ecca [FIX] Corrige consulta de distribuíção de NF-e 8 years ago
Danimar Ribeiro 9b21368d25 Nova versão, atualização de badges 8 years ago
Danimar Ribeiro 0b47eba7b5 Refactor - Permitir criar o xml de envio separadamente 8 years ago
Danimar Ribeiro b05bd19354 Build para windows 8 years ago
Danimar Ribeiro a1c65663bc Updating setup.py to include font files 8 years ago
Fábio Luna aa4fd2bb42 Inclui fonte NimbusSanL. 8 years ago
Danimar Ribeiro c44cd90103 Fixing build for windows 8 years ago
Danimar Ribeiro 7b31a2f78f Removendo print - Ajustando encodings 8 years ago
Danimar Ribeiro fabe0e0561 Preparando pypi package para versão python >= 3.4 8 years ago
Danimar Ribeiro 0c89b3e957 Modificação na estrutura do projeto - Movendo testes para fora do pacote 8 years ago
Danimar Ribeiro 5c5602acf9 Migração para python 3 - Correção de testes 8 years ago
Danimar Ribeiro 4581ea6dbe Ajuste de travis e dependencias da versão 3 8 years ago
  1. 32
      .appveyor.yml
  2. 2
      .gitignore
  3. 10
      .travis.yml
  4. 15
      README.md
  5. 4
      pytrustnfe/Servidores.py
  6. 8
      pytrustnfe/__init__.py
  7. 13
      pytrustnfe/certificado.py
  8. 16
      pytrustnfe/client.py
  9. 126
      pytrustnfe/nfe/__init__.py
  10. 4
      pytrustnfe/nfe/assinatura.py
  11. 4
      pytrustnfe/nfe/comunicacao.py
  12. 26
      pytrustnfe/nfe/danfe.py
  13. BIN
      pytrustnfe/nfe/fonts/NimbusSanL Bold.ttf
  14. BIN
      pytrustnfe/nfe/fonts/NimbusSanL Regular.ttf
  15. 90
      pytrustnfe/nfse/assinatura.py
  16. 2
      pytrustnfe/nfse/betha/__init__.py
  17. 113
      pytrustnfe/nfse/dsf/__init__.py
  18. 18
      pytrustnfe/nfse/dsf/templates/cancelar.xml
  19. 11
      pytrustnfe/nfse/dsf/templates/consulta_notas.xml
  20. 10
      pytrustnfe/nfse/dsf/templates/consultarLote.xml
  21. 22
      pytrustnfe/nfse/dsf/templates/consultarNFSeRps.xml
  22. 108
      pytrustnfe/nfse/dsf/templates/enviar.xml
  23. 12
      pytrustnfe/nfse/dsf/templates/soap_header.xml
  24. 119
      pytrustnfe/nfse/floripa/__init__.py
  25. 7
      pytrustnfe/nfse/floripa/templates/cancelar_nota.xml
  26. 40
      pytrustnfe/nfse/floripa/templates/processar_nota.xml
  27. 2
      pytrustnfe/nfse/ginfes/__init__.py
  28. 9
      pytrustnfe/nfse/paulistana/__init__.py
  29. 2
      pytrustnfe/nfse/susesu/__init__.py
  30. 1
      pytrustnfe/test/XMLs/paulistana_resultado.xml
  31. 26
      pytrustnfe/xml/__init__.py
  32. 18
      pytrustnfe/xml/filters.py
  33. 0
      pytrustnfe/xml/schemas/enviNFe_v3.10.xsd
  34. 0
      pytrustnfe/xml/schemas/leiauteNFe_v3.10.xsd
  35. 0
      pytrustnfe/xml/schemas/nfe_v3.10.xsd
  36. 2
      pytrustnfe/xml/schemas/tiposBasico_v3.10.xsd
  37. 0
      pytrustnfe/xml/schemas/xmldsig-core-schema_v1.01.xsd
  38. 31
      pytrustnfe/xml/validate.py
  39. 22
      requirements.txt
  40. 24
      setup.py
  41. 0
      tests/XMLs/NFe00000857.xml
  42. 0
      tests/XMLs/jinja_remove_empty.xml
  43. 0
      tests/XMLs/jinja_result.xml
  44. 0
      tests/XMLs/jinja_template.xml
  45. 0
      tests/XMLs/paulistana_canc.xml
  46. 1
      tests/XMLs/paulistana_canc_errado.xml
  47. 1
      tests/XMLs/paulistana_canc_ok.xml
  48. 1
      tests/XMLs/paulistana_resultado.xml
  49. 16
      tests/XMLs/paulistana_signature.xml
  50. 0
      tests/XMLs/recibo_envio_1.xml
  51. 0
      tests/XMLs/recibo_envio_2.xml
  52. 0
      tests/XMLs/recibo_protocolo_sucesso_1.xml
  53. 0
      tests/XMLs/recibo_protocolo_sucesso_2.xml
  54. 0
      tests/__init__.py
  55. 6
      tests/test_add_qr_code.py
  56. 12
      tests/test_assinatura.py
  57. 8
      tests/test_certificado.py
  58. 0
      tests/test_comunicacao.py
  59. 2
      tests/test_consulta_cadastro.py
  60. 2
      tests/test_danfe.py
  61. 2
      tests/test_ginfes.py
  62. 8
      tests/test_nfse_paulistana.py
  63. 0
      tests/test_servidores.py
  64. 16
      tests/test_utils.py
  65. 4
      tests/test_xml.py
  66. 5
      tests/test_xml_serializacao.py
  67. 0
      tests/teste.pfx
  68. 0
      tests/xml_assinado.xml
  69. 6
      tests/xml_com_qrcode.xml
  70. 6
      tests/xml_sem_qrcode.xml
  71. 0
      tests/xml_valido_assinado.xml

32
.appveyor.yml

@ -0,0 +1,32 @@
version: 1.3.{build}
environment:
matrix:
- python: 35
- python: 35-x64
- python: 36
- python: 36-x64
install:
- SET PATH=C:\\Python%PYTHON%;c:\\Python%PYTHON%\\scripts;%PATH%
- python -m pip.__main__ install -U pip wheel setuptools
- pip install -r requirements.txt
build: off
build_script:
# configure version
- ps: >-
If ($env:APPVEYOR_REPO_TAG -Eq "true" ) {
$version = "$env:APPVEYOR_REPO_TAG_NAME"
} Else {
$version = "$env:APPVEYOR_BUILD_VERSION.dev0"
}
$version | Set-Content version.txt
- python setup.py build bdist_wheel
- ps: Get-ChildItem dist\*.whl | % { pip install $_.FullName }
test: off
test_script:
- pip list
- py.test -v tests
- ps: Get-ChildItem dist\*.whl | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }

2
.gitignore

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

10
.travis.yml

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

15
README.md

@ -1,17 +1,17 @@
# PyTrustNFe # PyTrustNFe
Biblioteca Python que tem por objetivo enviar NFe, NFCe e NFSe no Brasil Biblioteca Python que tem por objetivo enviar NFe, NFCe e NFSe no Brasil
[![Coverage Status](https://coveralls.io/repos/danimaribeiro/PyTrustNFe/badge.svg?branch=master)](https://coveralls.io/r/danimaribeiro/PyTrustNFe?branch=master)
[![Code Health](https://landscape.io/github/danimaribeiro/PyTrustNFe/master/landscape.svg?style=flat)](https://landscape.io/github/danimaribeiro/PyTrustNFe/master)
[![Build Status](https://travis-ci.org/danimaribeiro/PyTrustNFe.svg?branch=master)](https://travis-ci.org/danimaribeiro/PyTrustNFe)
[![PyPI version](https://badge.fury.io/py/PyTrustNFe.svg)](https://badge.fury.io/py/PyTrustNFe)
[![Coverage Status](https://coveralls.io/repos/danimaribeiro/PyTrustNFe/badge.svg?branch=master3)](https://coveralls.io/r/danimaribeiro/PyTrustNFe?branch=master3)
[![Code Health](https://landscape.io/github/danimaribeiro/PyTrustNFe/master3/landscape.svg?style=flat)](https://landscape.io/github/danimaribeiro/PyTrustNFe/master3)
[![Build Status](https://travis-ci.org/danimaribeiro/PyTrustNFe.svg?branch=master3)](https://travis-ci.org/danimaribeiro/PyTrustNFe)
[![PyPI version](https://badge.fury.io/py/PyTrustNFe3.svg)](https://badge.fury.io/py/PyTrustNFe3)
Dependências: Dependências:
* PyXmlSec * PyXmlSec
* lxml * lxml
* signxml * signxml
* suds
* suds_requests
* suds-jurko
* suds-jurko-requests
* reportlab * reportlab
* Jinja2 * Jinja2
@ -19,7 +19,7 @@ NFSe - Cidades atendidas
-------------- --------------
* [Ariss](cidades/ariss.md) - 4 cidades atendidas * [Ariss](cidades/ariss.md) - 4 cidades atendidas
* [Simpliss](cidades/simpliss.md) - 18 cidade atendidas * [Simpliss](cidades/simpliss.md) - 18 cidade atendidas
* [GINFES](cidades/ginfes.md) - 79 cidades atendidas
Roadmap Roadmap
-------------- --------------
@ -31,7 +31,6 @@ Compatibilidade [python 2 e 3](https://github.com/danimaribeiro/PyTrustNFe/pull/
Implementar novos provedores de NFSe Implementar novos provedores de NFSe
* [Betha](cidades/betha.md) - 81 cidades atendidas WIP * [Betha](cidades/betha.md) - 81 cidades atendidas WIP
* [GINFES](cidades/ginfes.md) - 79 cidades atendidas
* [WebISS](cidades/webiss.md) - 51 cidades atendidas * [WebISS](cidades/webiss.md) - 51 cidades atendidas
* [ISSIntel](cidades/issintel.md) - 32 cidades atendidas * [ISSIntel](cidades/issintel.md) - 32 cidades atendidas
* [ISSNET](cidades/issnet.md) - 32 cidades atendidas * [ISSNET](cidades/issnet.md) - 32 cidades atendidas

4
pytrustnfe/Servidores.py

@ -37,8 +37,8 @@ NFE_AMBIENTE_HOMOLOGACAO = 2
NFCE_AMBIENTE_PRODUCAO = 1 NFCE_AMBIENTE_PRODUCAO = 1
NFCE_AMBIENTE_HOMOLOGACAO = 2 NFCE_AMBIENTE_HOMOLOGACAO = 2
NFE_MODELO = u'55'
NFCE_MODELO = u'65'
NFE_MODELO = '55'
NFCE_MODELO = '65'
SIGLA_ESTADO = { SIGLA_ESTADO = {
'12': 'AC', '12': 'AC',

8
pytrustnfe/__init__.py

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

13
pytrustnfe/certificado.py

@ -2,8 +2,7 @@
# © 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).
from uuid import uuid4
import tempfile
from OpenSSL import crypto from OpenSSL import crypto
@ -13,8 +12,8 @@ class Certificado(object):
self.password = password self.password = password
def save_pfx(self): def save_pfx(self):
pfx_temp = '/tmp/' + uuid4().hex
arq_temp = open(pfx_temp, 'w')
pfx_temp = tempfile.mkstemp()[1]
arq_temp = open(pfx_temp, 'wb')
arq_temp.write(self.pfx) arq_temp.write(self.pfx)
arq_temp.close() arq_temp.close()
return pfx_temp return pfx_temp
@ -28,12 +27,12 @@ def extract_cert_and_key_from_pfx(pfx, password):
# PEM formatted certificate # PEM formatted certificate
cert = crypto.dump_certificate(crypto.FILETYPE_PEM, cert = crypto.dump_certificate(crypto.FILETYPE_PEM,
pfx.get_certificate()) pfx.get_certificate())
return cert, key
return cert.decode(), key.decode()
def save_cert_key(cert, key): def save_cert_key(cert, key):
cert_temp = '/tmp/' + uuid4().hex
key_temp = '/tmp/' + uuid4().hex
cert_temp = tempfile.mkstemp()[1]
key_temp = tempfile.mkstemp()[1]
arq_temp = open(cert_temp, 'w') arq_temp = open(cert_temp, 'w')
arq_temp.write(cert) arq_temp.write(cert)

16
pytrustnfe/client.py

@ -42,14 +42,20 @@ class HttpClient(object):
self.cert_path = cert_path self.cert_path = cert_path
self.key_path = key_path self.key_path = key_path
def _headers(self, action):
def _headers(self, action, send_raw):
if send_raw:
return {
'Content-type': 'text/xml; charset=utf-8; action="http://www.portalfiscal.inf.br/nfe/wsdl/%s"' % action,
'Accept': 'application/soap+xml; charset=utf-8',
}
return { return {
u'Content-type': u'application/soap+xml; charset=utf-8; action="http://www.portalfiscal.inf.br/nfe/wsdl/%s"' % action,
u'Accept': u'application/soap+xml; charset=utf-8',
'Content-type': 'application/soap+xml; charset=utf-8; action="http://www.portalfiscal.inf.br/nfe/wsdl/%s"' % action,
'Accept': 'application/soap+xml; charset=utf-8',
} }
def post_soap(self, xml_soap, cabecalho):
header = self._headers(cabecalho.soap_action)
def post_soap(self, xml_soap, cabecalho, send_raw):
header = self._headers(cabecalho.soap_action, send_raw)
urllib3.disable_warnings(category=InsecureRequestWarning) urllib3.disable_warnings(category=InsecureRequestWarning)
res = requests.post(self.url, data=xml_soap, res = requests.post(self.url, data=xml_soap,
cert=(self.cert_path, self.key_path), cert=(self.cert_path, self.key_path),

126
pytrustnfe/nfe/__init__.py

@ -5,6 +5,7 @@
import os import os
import hashlib import hashlib
import binascii
from lxml import etree from lxml import etree
from .comunicacao import executar_consulta from .comunicacao import executar_consulta
from .assinatura import Assinatura from .assinatura import Assinatura
@ -80,7 +81,7 @@ def _add_qrCode(xml, **kwargs):
infnfesupl = etree.Element('infNFeSupl') infnfesupl = etree.Element('infNFeSupl')
qrcode = etree.Element('qrCode') qrcode = etree.Element('qrCode')
chave_nfe = inf_nfe['Id'][3:] chave_nfe = inf_nfe['Id'][3:]
dh_emissao = inf_nfe['ide']['dhEmi'].encode('hex')
dh_emissao = binascii.hexlify(inf_nfe['ide']['dhEmi'].encode()).decode()
versao = '100' versao = '100'
ambiente = kwargs['ambiente'] ambiente = kwargs['ambiente']
valor_total = inf_nfe['total']['vNF'] valor_total = inf_nfe['total']['vNF']
@ -98,9 +99,8 @@ def _add_qrCode(xml, **kwargs):
dest.append(cpf) dest.append(cpf)
dest_parent.append(dest) dest_parent.append(dest)
icms_total = inf_nfe['total']['vICMS'] icms_total = inf_nfe['total']['vICMS']
dig_val = xml.find(
".//{http://www.w3.org/2000/09/xmldsig#}DigestValue")\
.text.encode('hex')
dig_val = binascii.hexlify(xml.find(
".//{http://www.w3.org/2000/09/xmldsig#}DigestValue").text.encode()).decode()
cid_token = kwargs['NFes'][0]['infNFe']['codigo_seguranca']['cid_token'] cid_token = kwargs['NFes'][0]['infNFe']['codigo_seguranca']['cid_token']
csc = kwargs['NFes'][0]['infNFe']['codigo_seguranca']['csc'] csc = kwargs['NFes'][0]['infNFe']['codigo_seguranca']['csc']
@ -108,7 +108,7 @@ def _add_qrCode(xml, **kwargs):
={5}&vICMS={6}&digVal={7}&cIdToken={8}{9}".\ ={5}&vICMS={6}&digVal={7}&cIdToken={8}{9}".\
format(chave_nfe, versao, ambiente, dest_cpf, dh_emissao, format(chave_nfe, versao, ambiente, dest_cpf, dh_emissao,
valor_total, icms_total, dig_val, cid_token, csc) valor_total, icms_total, dig_val, cid_token, csc)
c_hash_QR_code = hashlib.sha1(c_hash_QR_code).hexdigest()
c_hash_QR_code = hashlib.sha1(c_hash_QR_code.encode()).hexdigest()
QR_code_url = "?chNFe={0}&nVersao={1}&tpAmb={2}&{3}dhEmi={4}&vNF={5}&vICMS\ QR_code_url = "?chNFe={0}&nVersao={1}&tpAmb={2}&{3}dhEmi={4}&vNF={5}&vICMS\
={6}&digVal={7}&cIdToken={8}&cHashQRCode={9}".\ ={6}&digVal={7}&cIdToken={8}&cHashQRCode={9}".\
@ -121,12 +121,13 @@ def _add_qrCode(xml, **kwargs):
qrcode.text = etree.CDATA(qrcode_text) qrcode.text = etree.CDATA(qrcode_text)
infnfesupl.append(qrcode) infnfesupl.append(qrcode)
nfe.insert(1, infnfesupl) nfe.insert(1, infnfesupl)
return etree.tostring(xml)
return etree.tostring(xml, encoding=str)
def _send(certificado, method, sign, **kwargs):
def _render(certificado, method, sign, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates') path = os.path.join(os.path.dirname(__file__), 'templates')
xmlElem_send = render_xml(path, '%s.xml' % method, True, **kwargs) 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' modelo = modelo.text if modelo is not None else '55'
if modelo == '65': if modelo == '65':
@ -175,9 +176,13 @@ def _send(certificado, method, sign, **kwargs):
xml_send = _add_qrCode(xml_send, **kwargs) xml_send = _add_qrCode(xml_send, **kwargs)
else: else:
xml_send = etree.tostring(xmlElem_send)
xml_send = etree.tostring(xmlElem_send, encoding=str)
return xml_send
url = localizar_url(method, kwargs['estado'], modelo,
def _send(certificado, method, **kwargs):
xml_send = kwargs["xml"]
url = localizar_url(method, kwargs['estado'], '55',
kwargs['ambiente']) kwargs['ambiente'])
cabecalho = _build_header(method, **kwargs) cabecalho = _build_header(method, **kwargs)
@ -189,55 +194,128 @@ def _send(certificado, method, sign, **kwargs):
send_raw=send_raw) send_raw=send_raw)
return { return {
'sent_xml': xml_send, 'sent_xml': xml_send,
'received_xml': response,
'received_xml': response.decode(),
'object': obj 'object': obj
} }
def autorizar_nfe(certificado, **kwargs): # Assinar
def xml_autorizar_nfe(certificado, **kwargs):
_generate_nfe_id(**kwargs) _generate_nfe_id(**kwargs)
return _send(certificado, 'NfeAutorizacao', True, **kwargs)
return _render(certificado, 'NfeAutorizacao', True, **kwargs)
def autorizar_nfe(certificado, **kwargs): # Assinar
if "xml" not in kwargs:
kwargs['xml'] = xml_autorizar_nfe(certificado, **kwargs)
return _send(certificado, 'NfeAutorizacao', **kwargs)
def xml_retorno_autorizar_nfe(certificado, **kwargs):
return _render(certificado, 'NfeRetAutorizacao', False, **kwargs)
def retorno_autorizar_nfe(certificado, **kwargs): 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, 'RecepcaoEventoCancelamento', 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, 'RecepcaoEventoCancelamento', **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', True, **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)
return _send(certificado, 'NfeConsultaCadastro', **kwargs)
def xml_recepcao_evento_carta_correcao(certificado, **kwargs): # Assinar
return _render(certificado, 'RecepcaoEventoCarta', 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, 'RecepcaoEventoCarta', **kwargs)
def xml_recepcao_evento_manifesto(certificado, **kwargs): # Assinar
return _render(certificado, 'RecepcaoEventoManifesto', **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, 'RecepcaoEventoManifesto', **kwargs)
def xml_recepcao_evento_epec(certificado, **kwargs): # Assinar
return _render(certificado, 'RecepcaoEventoEPEC', True, **kwargs)
def recepcao_evento_epec(certificado, **kwargs): # Assinar def recepcao_evento_epec(certificado, **kwargs): # Assinar
return _send(certificado, 'RecepcaoEventoEPEC', True, **kwargs)
if "xml" not in kwargs:
kwargs['xml'] = xml_recepcao_evento_epec(certificado, **kwargs)
return _send(certificado, 'RecepcaoEventoEPEC', **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(certificado, 'NFeDistribuicaoDFe', **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(certificado, 'NFeDistribuicaoDFe', **kwargs)

4
pytrustnfe/nfe/assinatura.py

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

4
pytrustnfe/nfe/comunicacao.py

@ -30,5 +30,5 @@ def executar_consulta(certificado, url, cabecalho, xmlEnviar, send_raw=False):
if send_raw: if send_raw:
xml = '<?xml version="1.0" encoding="utf-8"?>' + xmlEnviar.rstrip('\n') xml = '<?xml version="1.0" encoding="utf-8"?>' + xmlEnviar.rstrip('\n')
xml_enviar = xml xml_enviar = xml
xml_retorno = client.post_soap(xml_enviar, cabecalho)
return sanitize_response(xml_retorno)
xml_retorno = client.post_soap(xml_enviar, cabecalho, send_raw)
return sanitize_response(xml_retorno.encode())

26
pytrustnfe/nfe/danfe.py

@ -3,8 +3,8 @@
# 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).
# Classe para geração de PDF da DANFE a partir de xml etree.fromstring # Classe para geração de PDF da DANFE a partir de xml etree.fromstring
from cStringIO import StringIO as IO
import os
from io import BytesIO
from textwrap import wrap from textwrap import wrap
from reportlab.lib import utils from reportlab.lib import utils
@ -17,6 +17,8 @@ from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.enums import TA_CENTER from reportlab.lib.enums import TA_CENTER
from reportlab.platypus import Paragraph, Image from reportlab.platypus import Paragraph, Image
from reportlab.lib.styles import ParagraphStyle from reportlab.lib.styles import ParagraphStyle
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
def chunks(cString, nLen): def chunks(cString, nLen):
@ -73,6 +75,14 @@ class danfe(object):
def __init__(self, sizepage=A4, list_xml=None, recibo=True, def __init__(self, sizepage=A4, list_xml=None, recibo=True,
orientation='portrait', logo=None, cce_xml=None): orientation='portrait', logo=None, cce_xml=None):
path = os.path.join(os.path.dirname(__file__), 'fonts')
pdfmetrics.registerFont(
TTFont('NimbusSanL-Regu',
os.path.join(path, 'NimbusSanL Regular.ttf')))
pdfmetrics.registerFont(
TTFont('NimbusSanL-Bold',
os.path.join(path, 'NimbusSanL Bold.ttf')))
self.width = 210 # 21 x 29,7cm self.width = 210 # 21 x 29,7cm
self.height = 297 self.height = 297
self.nLeft = 10 self.nLeft = 10
@ -86,7 +96,7 @@ class danfe(object):
'2': '2 - Terceiros', '2': '2 - Terceiros',
'9': '9 - Sem Frete'} '9': '9 - Sem Frete'}
self.oPDF_IO = IO()
self.oPDF_IO = BytesIO()
if orientation == 'landscape': if orientation == 'landscape':
raise NameError('Rotina não implementada') raise NameError('Rotina não implementada')
else: else:
@ -212,7 +222,7 @@ class danfe(object):
cNF = '{0:011,}'.format(int(cNF)).replace(",", ".") cNF = '{0:011,}'.format(int(cNF)).replace(",", ".")
self.stringcenter(self.nLeft + 100, self.nlin + 25, "%s" % (cNF)) self.stringcenter(self.nLeft + 100, self.nlin + 25, "%s" % (cNF))
self.stringcenter(self.nLeft + 100, self.nlin + 29, u"SÉRIE %s" % (
self.stringcenter(self.nLeft + 100, self.nlin + 29, "SÉRIE %s" % (
tagtext(oNode=elem_ide, cTag='serie'))) tagtext(oNode=elem_ide, cTag='serie')))
cPag = "Página %s de %s" % (str(self.Page), str(self.NrPages)) cPag = "Página %s de %s" % (str(self.Page), str(self.NrPages))
self.stringcenter(self.nLeft + 100, self.nlin + 32, cPag) self.stringcenter(self.nLeft + 100, self.nlin + 32, cPag)
@ -289,7 +299,7 @@ class danfe(object):
oNode=elem_emit, cTag='CEP') oNode=elem_emit, cTag='CEP')
regime = tagtext(oNode=elem_emit, cTag='CRT') regime = tagtext(oNode=elem_emit, cTag='CRT')
cEnd += u'<br />Regime Tributário: %s' % (REGIME_TRIBUTACAO[regime])
cEnd += '<br />Regime Tributário: %s' % (REGIME_TRIBUTACAO[regime])
styleN.fontName = 'NimbusSanL-Regu' styleN.fontName = 'NimbusSanL-Regu'
styleN.fontSize = 7 styleN.fontSize = 7
@ -646,7 +656,7 @@ obsCont[@xCampo='NomeVendedor']")
self.canvas.setFont('NimbusSanL-Regu', 5) self.canvas.setFont('NimbusSanL-Regu', 5)
nLin = self.nlin + 10.5 nLin = self.nlin + 10.5
for id in xrange(oPaginator[0], oPaginator[1]):
for id in range(oPaginator[0], oPaginator[1]):
item = el_det[id] item = el_det[id]
el_prod = item.find(".//{http://www.portalfiscal.inf.br/nfe}prod") el_prod = item.find(".//{http://www.portalfiscal.inf.br/nfe}prod")
el_imp = item.find( el_imp = item.find(
@ -767,7 +777,7 @@ obsCont[@xCampo='NomeVendedor']")
self.string(self.width - self.nRight - nW + self.string(self.width - self.nRight - nW +
2, self.nlin + 8, "%s" % (cNF)) 2, self.nlin + 8, "%s" % (cNF))
self.string(self.width - self.nRight - nW + 2, self.nlin + 14, self.string(self.width - self.nRight - nW + 2, self.nlin + 14,
u"SÉRIE %s" % (tagtext(oNode=el_ide, cTag='serie')))
"SÉRIE %s" % (tagtext(oNode=el_ide, cTag='serie')))
cDt, cHr = getdateUTC(tagtext(oNode=el_ide, cTag='dhEmi')) cDt, cHr = getdateUTC(tagtext(oNode=el_ide, cTag='dhEmi'))
cTotal = format_number(tagtext(oNode=el_total, cTag='vNF')) cTotal = format_number(tagtext(oNode=el_total, cTag='vNF'))
@ -779,7 +789,7 @@ obsCont[@xCampo='NomeVendedor']")
oNode=el_dest, cTag='xMun') + ' - ' oNode=el_dest, cTag='xMun') + ' - '
cEnd += tagtext(oNode=el_dest, cTag='UF') cEnd += tagtext(oNode=el_dest, cTag='UF')
cString = u"""
cString = """
RECEBEMOS DE %s OS PRODUTOS/SERVIÇOS CONSTANTES DA NOTA FISCAL INDICADA RECEBEMOS DE %s OS PRODUTOS/SERVIÇOS CONSTANTES DA NOTA FISCAL INDICADA
ABAIXO. EMISSÃO: %s VALOR TOTAL: %s ABAIXO. EMISSÃO: %s VALOR TOTAL: %s
DESTINATARIO: %s""" % (tagtext(oNode=el_emit, cTag='xNome'), DESTINATARIO: %s""" % (tagtext(oNode=el_emit, cTag='xNome'),

BIN
pytrustnfe/nfe/fonts/NimbusSanL Bold.ttf

BIN
pytrustnfe/nfe/fonts/NimbusSanL Regular.ttf

90
pytrustnfe/nfse/assinatura.py

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

2
pytrustnfe/nfse/betha/__init__.py

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

113
pytrustnfe/nfse/dsf/__init__.py

@ -0,0 +1,113 @@
# -*- encoding: utf-8 -*-
# © 2017 Fábio Luna, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import os
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
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)
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_url(**kwargs):
urls = {
# Belém - PA
'2715': 'http://www.issdigitalbel.com.br/WsNFe2/LoteRps.jws',
# Sorocaba - SP
'7145': 'http://issdigital.sorocaba.sp.gov.br/WsNFe2/LoteRps.jws',
# Teresina - PI
'1219': 'http://www.issdigitalthe.com.br/WsNFe2/LoteRps.jws',
# 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',
# São Luis - MA
'0921': 'https://stm.semfaz.saoluis.ma.gov.br/WsNFe2/LoteRps?wsdl',
# Campo Grande - MS
'9051': 'http://issdigital.pmcg.ms.gov.br/WsNFe2/LoteRps.jws?wsdl',
}
return urls[kwargs['siafi_code']]
def _send(certificado, method, **kwargs):
url = _get_url(**kwargs)
xml_send = '<?xml version="1.0" encoding="utf-8"?>' +\
kwargs['xml'].decode('utf-8')
response = False
soap = '<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">'\
'<Body>'\
'<enviarSincrono xmlns="http://issdigital.pmcg.ms.gov.br/WsNFe2/LoteRps.jws">'\
'<mensagemXml><![CDATA[' + xml_send + ']]></mensagemXml>'\
'</enviarSincrono>'\
'</Body>'\
'</Envelope>'
headers = {
"Content-Type": 'text/xml; charset="utf-8"',
"SOAPAction": "",
}
http_response = requests.post(url, data=soap, headers=headers)
response, obj = sanitize_response(http_response.text.encode('utf-8'))
return {
'status_code': http_response.status_code,
'sent_xml': xml_send,
'received_xml': response,
'object': obj
}
def xml_enviar(certificado, **kwargs):
return _render(certificado, 'enviar', **kwargs)
def enviar(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_enviar(certificado, **kwargs)
return _send(certificado, 'enviar', **kwargs)
def xml_teste_enviar(certificado, **kwargs):
return _render(certificado, 'testeEnviar', **kwargs)
def teste_enviar(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_teste_enviar(certificado, **kwargs)
return _send(certificado, 'testeEnviar', **kwargs)
def cancelar(certificado, ** kwargs):
return _send(certificado, 'cancelar', **kwargs)
def consulta_lote(**kwargs):
return _send(False, 'consultarLote', **kwargs)
def xml_consultar_nfse_rps(certificado, **kwargs):
return _render(certificado, 'consultarNFSeRps', **kwargs)
def consultar_nfse_rps(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_consultar_nfse_rps(certificado, **kwargs)
return _send(certificado, 'consultarNFSeRps', **kwargs)

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

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

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

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

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

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

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

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

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

@ -0,0 +1,108 @@
<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_descricao_prestacao }}
</MunicipioPrestacaoDescricao>
<Operacao>{{ rps.operacao }}</Operacao>
<Tributacao>{{ rps.tributacao }}</Tributacao>
<ValorPIS>{{ rps.valor_pis }}</ValorPIS>
<ValorCOFINS>{{ rps.valor_cofins }}</ValorCOFINS>
<ValorINSS>{{ rps.valor_inss }}</ValorINSS>
<ValorIR>{{ rps.valor_ir }}</ValorIR>
<ValorCSLL>{{ rps.valor_csll }}</ValorCSLL>
<AliquotaPIS>{{ rps.aliquota_pis }}</AliquotaPIS>
<AliquotaCOFINS>{{ rps.aliquota_cofins }}</AliquotaCOFINS>
<AliquotaINSS>{{ rps.aliquota_inss }}</AliquotaINSS>
<AliquotaIR>{{ rps.aliquota_ir }}</AliquotaIR>
<AliquotaCSLL>{{ rps.aliquota_csll }}</AliquotaCSLL>
<DescricaoRPS>{{ rps.descricao }}</DescricaoRPS>
<DDDPrestador>{{ rps.prestador.ddd }}</DDDPrestador>
<TelefonePrestador>{{ rps.prestador.telefone }}</TelefonePrestador>
<DDDTomador>{{ rps.tomador.ddd }}</DDDTomador>
<TelefoneTomador>{{ rps.tomador.telefone }}</TelefoneTomador>
<MotCancelamento>{{ rps.motivo_cancelamento }}</MotCancelamento>
{% if rps.deducoes|count > 0 %}
<Deducoes>
{% for deducao in rps.deducoes -%}
<Deducao>
<DeducaoPor>{{ deducao.por }}</DeducaoPor>
<TipoDeducao>{{ deducao.tipo }}</TipoDeducao>
<CPFCNPJReferencia>{{ deducao.cnpj_referencia }}</CPFCNPJReferencia>
<NumeroNFReferencia>{{ deducao.nf_referencia }}</NumeroNFReferencia>
<ValorTotalReferencia>{{ deducao.valor_referencia }}</ValorTotalReferencia>
<PercentualDeduzir>{{ deducao.percentual_deduzir }}</PercentualDeduzir>
<ValorDeduzir>{{ deducao.valor_deduzir }}</ValorDeduzir>
</Deducao>
{% endfor %}
</Deducoes>
{% endif %}
{% if rps.deducoes|count == 0 %}
<Deducoes />
{% endif %}
<Itens>
{% for item in rps.itens -%}
<Item>
<DiscriminacaoServico>{{ item.descricao }}</DiscriminacaoServico>
<Quantidade>{{ item.quantidade }}</Quantidade>
<ValorUnitario>{{ item.valor_unitario }}</ValorUnitario>
<ValorTotal>{{ item.valor_total }}</ValorTotal>
<Tributavel>S</Tributavel>
</Item>
{% endfor %}
</Itens>
</RPS>
{% endfor %}
</Lote>
</ns1:ReqEnvioLoteRPS>

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

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

119
pytrustnfe/nfse/floripa/__init__.py

@ -0,0 +1,119 @@
# -*- 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().encode('utf-8'))
return {
'sent_xml': xml_send,
'received_xml': response,
'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)
print(r.status_code)
if r.status_code == 200:
return r.text
else:
return r.text

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

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

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

@ -0,0 +1,40 @@
<?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>
<cst>{{ item.cst_servico }}</cst>
<descricaoServico>{{ item.descricao|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>

2
pytrustnfe/nfse/ginfes/__init__.py

@ -38,7 +38,7 @@ def _send(certificado, method, **kwargs):
xml_send = kwargs['xml'] xml_send = kwargs['xml']
header = '<ns2:cabecalho xmlns:ns2="http://www.ginfes.com.br/cabecalho_v03.xsd" versao="3"><versaoDados>3</versaoDados></ns2:cabecalho>' #noqa header = '<ns2:cabecalho xmlns:ns2="http://www.ginfes.com.br/cabecalho_v03.xsd" versao="3"><versaoDados>3</versaoDados></ns2:cabecalho>' #noqa
response = getattr(client.service, method)(header, xml_send) response = getattr(client.service, method)(header, xml_send)
except suds.WebFault, e:
except suds.WebFault as e:
return { return {
'sent_xml': xml_send, 'sent_xml': xml_send,
'received_xml': e.fault.faultstring, 'received_xml': e.fault.faultstring,

9
pytrustnfe/nfse/paulistana/__init__.py

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

2
pytrustnfe/nfse/susesu/__init__.py

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

1
pytrustnfe/test/XMLs/paulistana_resultado.xml

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

26
pytrustnfe/xml/__init__.py

@ -2,7 +2,6 @@
# © 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 unicodedata
from lxml import etree from lxml import etree
from lxml import objectify from lxml import objectify
@ -41,7 +40,6 @@ def render_xml(path, template_name, remove_empty, **nfe):
env.filters["comma"] = filters.format_with_comma env.filters["comma"] = filters.format_with_comma
template = env.get_template(template_name) template = env.get_template(template_name)
xml = template.render(**nfe) xml = template.render(**nfe)
parser = etree.XMLParser(remove_blank_text=True, remove_comments=True, parser = etree.XMLParser(remove_blank_text=True, remove_comments=True,
strip_cdata=False) strip_cdata=False)
@ -53,14 +51,13 @@ def render_xml(path, template_name, remove_empty, **nfe):
if recursively_empty(elem): if recursively_empty(elem):
parent.remove(elem) parent.remove(elem)
return root return root
return etree.tostring(root)
for element in root.iter("*"): # remove espaços em branco
if element.text is not None and not element.text.strip():
element.text = None
return etree.tostring(root, encoding=str)
def sanitize_response(response): def sanitize_response(response):
response = unicode(response)
response = unicodedata.normalize('NFKD', response).encode('ascii',
'ignore')
tree = etree.fromstring(response) tree = etree.fromstring(response)
# Remove namespaces inuteis na resposta # Remove namespaces inuteis na resposta
for elem in tree.getiterator(): for elem in tree.getiterator():
@ -68,6 +65,19 @@ def sanitize_response(response):
continue continue
i = elem.tag.find('}') i = elem.tag.find('}')
if i >= 0: if i >= 0:
elem.tag = elem.tag[i+1:]
elem.tag = elem.tag[i + 1:]
objectify.deannotate(tree, cleanup_namespaces=True) objectify.deannotate(tree, cleanup_namespaces=True)
return response, objectify.fromstring(etree.tostring(tree)) return response, objectify.fromstring(etree.tostring(tree))
def recursively_normalize(vals):
for item in vals:
if type(vals[item]) is str:
vals[item] = vals[item].strip()
vals[item] = filters.normalize_str(vals[item])
elif type(vals[item]) is dict:
recursively_normalize(vals[item])
elif type(vals[item]) is list:
for a in vals[item]:
recursively_normalize(a)
return vals

18
pytrustnfe/xml/filters.py

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

0
pytrustnfe/xml/schemas/enviNFe_v3.10.xsd

0
pytrustnfe/xml/schemas/leiauteNFe_v3.10.xsd

0
pytrustnfe/xml/schemas/nfe_v3.10.xsd

2
pytrustnfe/xml/schemas/tiposBasico_v3.10.xsd

@ -494,7 +494,7 @@
</xs:annotation> </xs:annotation>
<xs:restriction base="xs:string"> <xs:restriction base="xs:string">
<xs:whiteSpace value="preserve"/> <xs:whiteSpace value="preserve"/>
<xs:pattern value="[!-ÿ]{1}[ -ÿ]{0,}[!-ÿ]{1}|[!-ÿ]{1}"/>
<xs:pattern value="[!-ÿ]{1}[ -ÿ]{0,}[!-ÿ]{1}|[!-ÿ]{1}|[!-ÿ]{2}"/>
</xs:restriction> </xs:restriction>
</xs:simpleType> </xs:simpleType>
<xs:simpleType name="TData"> <xs:simpleType name="TData">

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

31
pytrustnfe/xml/validate.py

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

22
requirements.txt

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

24
setup.py

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

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

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

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

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

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

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

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

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

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

1
tests/XMLs/paulistana_resultado.xml

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

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

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

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

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

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

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

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

6
pytrustnfe/test/test_add_qr_code.py → tests/test_add_qr_code.py

@ -9,10 +9,10 @@ from pytrustnfe.nfe import _add_qrCode
class TestAddQRCode(unittest.TestCase): class TestAddQRCode(unittest.TestCase):
def setUp(self): 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')
self.xml_sem_qrcode = open('tests/xml_sem_qrcode.xml', 'r')
self.xml_com_qrcode = open('tests/xml_com_qrcode.xml', 'r')
dhEmi = '2016-11-09T16:03:25-00:00' dhEmi = '2016-11-09T16:03:25-00:00'
chave_nfe = u'NFe35161121332917000163650010000000011448875034'
chave_nfe = 'NFe35161121332917000163650010000000011448875034'
ambiente = 2 ambiente = 2
valor_total = '324.00' valor_total = '324.00'
icms_total = '61.56' icms_total = '61.56'

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

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

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

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

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

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

@ -12,7 +12,7 @@ class test_consulta_cadastro(unittest.TestCase):
caminho = os.path.dirname(__file__) caminho = os.path.dirname(__file__)
def test_conta_de_cadastro(self): def test_conta_de_cadastro(self):
pfx_source = open(os.path.join(self.caminho, 'teste.pfx'), 'r').read()
pfx_source = open(os.path.join(self.caminho, 'teste.pfx'), 'rb').read()
pfx = Certificado(pfx_source, '123456') pfx = Certificado(pfx_source, '123456')
obj = {'cnpj': '12345678901234', 'estado': '42'} obj = {'cnpj': '12345678901234', 'estado': '42'}

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

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

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

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

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

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

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

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

@ -60,7 +60,7 @@ class test_utils(unittest.TestCase):
chave.validar() chave.validar()
chave.cnpj = '1234567891011' chave.cnpj = '1234567891011'
self.assertEqual('CNPJ necessário para criar chave NF-e', self.assertEqual('CNPJ necessário para criar chave NF-e',
cm.exception.message,
str(cm.exception),
'Validação da chave nf-e incorreta') 'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm: with self.assertRaises(AssertionError) as cm:
@ -68,7 +68,7 @@ class test_utils(unittest.TestCase):
chave.validar() chave.validar()
chave.estado = '42' chave.estado = '42'
self.assertEqual('Estado necessário para criar chave NF-e', self.assertEqual('Estado necessário para criar chave NF-e',
cm.exception.message,
str(cm.exception),
'Validação da chave nf-e incorreta') 'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm: with self.assertRaises(AssertionError) as cm:
@ -76,7 +76,7 @@ class test_utils(unittest.TestCase):
chave.validar() chave.validar()
chave.emissao = '0' chave.emissao = '0'
self.assertEqual('Emissão necessário para criar chave NF-e', self.assertEqual('Emissão necessário para criar chave NF-e',
cm.exception.message,
str(cm.exception),
'Validação da chave nf-e incorreta') 'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm: with self.assertRaises(AssertionError) as cm:
@ -84,7 +84,7 @@ class test_utils(unittest.TestCase):
chave.validar() chave.validar()
chave.modelo = '55' chave.modelo = '55'
self.assertEqual('Modelo necessário para criar chave NF-e', self.assertEqual('Modelo necessário para criar chave NF-e',
cm.exception.message,
str(cm.exception),
'Validação da chave nf-e incorreta') 'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm: with self.assertRaises(AssertionError) as cm:
@ -92,7 +92,7 @@ class test_utils(unittest.TestCase):
chave.validar() chave.validar()
chave.serie = '012' chave.serie = '012'
self.assertEqual('Série necessária para criar chave NF-e', self.assertEqual('Série necessária para criar chave NF-e',
cm.exception.message,
str(cm.exception),
'Validação da chave nf-e incorreta') 'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm: with self.assertRaises(AssertionError) as cm:
@ -100,7 +100,7 @@ class test_utils(unittest.TestCase):
chave.validar() chave.validar()
chave.numero = '000000780' chave.numero = '000000780'
self.assertEqual('Número necessário para criar chave NF-e', self.assertEqual('Número necessário para criar chave NF-e',
cm.exception.message,
str(cm.exception),
'Validação da chave nf-e incorreta') 'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm: with self.assertRaises(AssertionError) as cm:
@ -108,12 +108,12 @@ class test_utils(unittest.TestCase):
chave.validar() chave.validar()
chave.tipo = '42' chave.tipo = '42'
self.assertEqual('Tipo necessário para criar chave NF-e', self.assertEqual('Tipo necessário para criar chave NF-e',
cm.exception.message,
str(cm.exception),
'Validação da chave nf-e incorreta') 'Validação da chave nf-e incorreta')
with self.assertRaises(AssertionError) as cm: with self.assertRaises(AssertionError) as cm:
chave.codigo = '' chave.codigo = ''
chave.validar() chave.validar()
self.assertEqual('Código necessário para criar chave NF-e', self.assertEqual('Código necessário para criar chave NF-e',
cm.exception.message,
str(cm.exception),
'Validação da chave nf-e incorreta') 'Validação da chave nf-e incorreta')

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

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

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

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

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

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

6
pytrustnfe/test/xml_com_qrcode.xml → tests/xml_com_qrcode.xml

@ -30,11 +30,11 @@
<xNome>LEL AMBIENTAL LTDA - EPP</xNome> <xNome>LEL AMBIENTAL LTDA - EPP</xNome>
<xFant>Zell Ambiental</xFant> <xFant>Zell Ambiental</xFant>
<enderEmit> <enderEmit>
<xLgr>Rua Padre Jo&#227;o</xLgr>
<xLgr>Rua Padre João</xLgr>
<nro>444</nro> <nro>444</nro>
<xBairro>Penha de Fran&#231;a</xBairro>
<xBairro>Penha de França</xBairro>
<cMun>3550308</cMun> <cMun>3550308</cMun>
<xMun>S&#227;o Paulo</xMun>
<xMun>São Paulo</xMun>
<UF>SP</UF> <UF>SP</UF>
<CEP>03637000</CEP> <CEP>03637000</CEP>
<cPais>1058</cPais> <cPais>1058</cPais>

6
pytrustnfe/test/xml_sem_qrcode.xml → tests/xml_sem_qrcode.xml

@ -31,11 +31,11 @@
<xNome>LEL AMBIENTAL LTDA - EPP</xNome> <xNome>LEL AMBIENTAL LTDA - EPP</xNome>
<xFant>Zell Ambiental</xFant> <xFant>Zell Ambiental</xFant>
<enderEmit> <enderEmit>
<xLgr>Rua Padre Jo&#xE3;o</xLgr>
<xLgr>Rua Padre João</xLgr>
<nro>444</nro> <nro>444</nro>
<xBairro>Penha de Fran&#xE7;a</xBairro>
<xBairro>Penha de França</xBairro>
<cMun>3550308</cMun> <cMun>3550308</cMun>
<xMun>S&#xE3;o Paulo</xMun>
<xMun>São Paulo</xMun>
<UF>SP</UF> <UF>SP</UF>
<CEP>03637000</CEP> <CEP>03637000</CEP>
<cPais>1058</cPais> <cPais>1058</cPais>

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

Loading…
Cancel
Save