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*/
docs/_build
.vscode/tags
.cache/
.cache

10
.travis.yml

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

15
README.md

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

4
pytrustnfe/Servidores.py

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

8
pytrustnfe/__init__.py

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

13
pytrustnfe/certificado.py

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

16
pytrustnfe/client.py

@ -42,14 +42,20 @@ class HttpClient(object):
self.cert_path = cert_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 {
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)
res = requests.post(self.url, data=xml_soap,
cert=(self.cert_path, self.key_path),

126
pytrustnfe/nfe/__init__.py

@ -5,6 +5,7 @@
import os
import hashlib
import binascii
from lxml import etree
from .comunicacao import executar_consulta
from .assinatura import Assinatura
@ -80,7 +81,7 @@ def _add_qrCode(xml, **kwargs):
infnfesupl = etree.Element('infNFeSupl')
qrcode = etree.Element('qrCode')
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'
ambiente = kwargs['ambiente']
valor_total = inf_nfe['total']['vNF']
@ -98,9 +99,8 @@ def _add_qrCode(xml, **kwargs):
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')
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']
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}".\
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()
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\
={6}&digVal={7}&cIdToken={8}&cHashQRCode={9}".\
@ -121,12 +121,13 @@ def _add_qrCode(xml, **kwargs):
qrcode.text = etree.CDATA(qrcode_text)
infnfesupl.append(qrcode)
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')
xmlElem_send = render_xml(path, '%s.xml' % method, True, **kwargs)
modelo = xmlElem_send.find(".//{http://www.portalfiscal.inf.br/nfe}mod")
modelo = modelo.text if modelo is not None else '55'
if modelo == '65':
@ -175,9 +176,13 @@ def _send(certificado, method, sign, **kwargs):
xml_send = _add_qrCode(xml_send, **kwargs)
else:
xml_send = etree.tostring(xmlElem_send)
xml_send = etree.tostring(xmlElem_send, encoding=str)
return xml_send
url = localizar_url(method, kwargs['estado'], modelo,
def _send(certificado, method, **kwargs):
xml_send = kwargs["xml"]
url = localizar_url(method, kwargs['estado'], '55',
kwargs['ambiente'])
cabecalho = _build_header(method, **kwargs)
@ -189,55 +194,128 @@ def _send(certificado, method, sign, **kwargs):
send_raw=send_raw)
return {
'sent_xml': xml_send,
'received_xml': response,
'received_xml': response.decode(),
'object': obj
}
def autorizar_nfe(certificado, **kwargs): # Assinar
def xml_autorizar_nfe(certificado, **kwargs):
_generate_nfe_id(**kwargs)
return _send(certificado, 'NfeAutorizacao', True, **kwargs)
return _render(certificado, 'NfeAutorizacao', True, **kwargs)
def autorizar_nfe(certificado, **kwargs): # Assinar
if "xml" not in kwargs:
kwargs['xml'] = xml_autorizar_nfe(certificado, **kwargs)
return _send(certificado, 'NfeAutorizacao', **kwargs)
def xml_retorno_autorizar_nfe(certificado, **kwargs):
return _render(certificado, 'NfeRetAutorizacao', False, **kwargs)
def retorno_autorizar_nfe(certificado, **kwargs):
return _send(certificado, 'NfeRetAutorizacao', False, **kwargs)
if "xml" not in kwargs:
kwargs['xml'] = xml_retorno_autorizar_nfe(certificado, **kwargs)
return _send(certificado, 'NfeRetAutorizacao', **kwargs)
def xml_recepcao_evento_cancelamento(certificado, **kwargs): # Assinar
return _render(certificado, 'RecepcaoEventoCancelamento', True, **kwargs)
def recepcao_evento_cancelamento(certificado, **kwargs): # Assinar
return _send(certificado, 'RecepcaoEventoCancelamento', True, **kwargs)
if "xml" not in kwargs:
kwargs['xml'] = xml_recepcao_evento_cancelamento(certificado, **kwargs)
return _send(certificado, '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):
return _send(certificado, 'NfeConsultaProtocolo', True, **kwargs)
if "xml" not in kwargs:
kwargs['xml'] = xml_consultar_protocolo_nfe(certificado, **kwargs)
return _send(certificado, 'NfeConsultaProtocolo', **kwargs)
def xml_nfe_status_servico(certificado, **kwargs):
return _render(certificado, 'NfeStatusServico', False, **kwargs)
def nfe_status_servico(certificado, **kwargs):
return _send(certificado, 'NfeStatusServico', False, **kwargs)
if "xml" not in kwargs:
kwargs['xml'] = xml_nfe_status_servico(certificado, **kwargs)
return _send(certificado, 'NfeStatusServico', **kwargs)
def xml_consulta_cadastro(certificado, **kwargs):
return _render(certificado, 'NfeConsultaCadastro', False, **kwargs)
def consulta_cadastro(certificado, **kwargs):
return _send(certificado, 'NfeConsultaCadastro', False, **kwargs)
if "xml" not in kwargs:
kwargs['xml'] = xml_consulta_cadastro(certificado, **kwargs)
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
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
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
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):
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):
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
signed_root = signer.sign(
xml_element, key=key, cert=cert,
xml_element, key=key.encode(), cert=cert.encode(),
reference_uri=ref_uri)
if reference:
element_signed = signed_root.find(".//*[@Id='%s']" % reference)
@ -42,4 +42,4 @@ class Assinatura(object):
if element_signed is not None and signature is not None:
parent = element_signed.getparent()
parent.append(signature)
return etree.tostring(signed_root)
return etree.tostring(signed_root, encoding=str)

4
pytrustnfe/nfe/comunicacao.py

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

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

2
pytrustnfe/nfse/betha/__init__.py

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

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']
header = '<ns2:cabecalho xmlns:ns2="http://www.ginfes.com.br/cabecalho_v03.xsd" versao="3"><versaoDados>3</versaoDados></ns2:cabecalho>' #noqa
response = getattr(client.service, method)(header, xml_send)
except suds.WebFault, e:
except suds.WebFault as e:
return {
'sent_xml': xml_send,
'received_xml': e.fault.faultstring,

9
pytrustnfe/nfse/paulistana/__init__.py

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

2
pytrustnfe/nfse/susesu/__init__.py

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

1
pytrustnfe/test/XMLs/paulistana_resultado.xml

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

26
pytrustnfe/xml/__init__.py

@ -2,7 +2,6 @@
# © 2016 Danimar Ribeiro, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import unicodedata
from lxml import etree
from lxml import objectify
@ -41,7 +40,6 @@ def render_xml(path, template_name, remove_empty, **nfe):
env.filters["comma"] = filters.format_with_comma
template = env.get_template(template_name)
xml = template.render(**nfe)
parser = etree.XMLParser(remove_blank_text=True, remove_comments=True,
strip_cdata=False)
@ -53,14 +51,13 @@ def render_xml(path, template_name, remove_empty, **nfe):
if recursively_empty(elem):
parent.remove(elem)
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):
response = unicode(response)
response = unicodedata.normalize('NFKD', response).encode('ascii',
'ignore')
tree = etree.fromstring(response)
# Remove namespaces inuteis na resposta
for elem in tree.getiterator():
@ -68,6 +65,19 @@ def sanitize_response(response):
continue
i = elem.tag.find('}')
if i >= 0:
elem.tag = elem.tag[i+1:]
elem.tag = elem.tag[i + 1:]
objectify.deannotate(tree, cleanup_namespaces=True)
return response, objectify.fromstring(etree.tostring(tree))
def recursively_normalize(vals):
for item in vals:
if type(vals[item]) is str:
vals[item] = vals[item].strip()
vals[item] = filters.normalize_str(vals[item])
elif type(vals[item]) is dict:
recursively_normalize(vals[item])
elif type(vals[item]) is list:
for a in vals[item]:
recursively_normalize(a)
return vals

18
pytrustnfe/xml/filters.py

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

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:restriction base="xs:string">
<xs:whiteSpace value="preserve"/>
<xs:pattern value="[!-ÿ]{1}[ -ÿ]{0,}[!-ÿ]{1}|[!-ÿ]{1}"/>
<xs:pattern value="[!-ÿ]{1}[ -ÿ]{0,}[!-ÿ]{1}|[!-ÿ]{1}|[!-ÿ]{2}"/>
</xs:restriction>
</xs:simpleType>
<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).
import os
import re
from lxml import etree
PATH = os.path.dirname(os.path.abspath(__file__))
SCHEMA = os.path.join(PATH, 'schemas/nfe_v3.10.xsd')
SCHEMA = os.path.join(PATH, 'schemas/enviNFe_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.validate(nfe)
erros = [x.message for x in esquema.error_log]
error_msg = '{field} inválido: {valor}.'
unexpected = '{unexpected} não é esperado. O valor esperado é {expected}'
namespace = '{http://www.portalfiscal.inf.br/nfe}'
mensagens = []
for erro in erros:
campo = re.findall(r"'([^']*)'", erro)[0]
nome = campo[campo.find('}') + 1: ]
valor = nfe.find('.//' + campo).text
if 'Expected is' in erro:
expected_name = re.findall('\(.*?\)', erro)
valor = unexpected.format(unexpected=nome, expected=expected_name)
mensagem = error_msg.format(field=campo.replace(namespace, ''),
valor=valor)
mensagens.append(mensagem)
return "\n".join(mensagens)
return "\n".join(erros)

22
requirements.txt

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

24
setup.py

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

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

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

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

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

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

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

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

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

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

1
tests/XMLs/paulistana_resultado.xml

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

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

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

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

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

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

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

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

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):
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'
chave_nfe = u'NFe35161121332917000163650010000000011448875034'
chave_nfe = 'NFe35161121332917000163650010000000011448875034'
ambiente = 2
valor_total = '324.00'
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
XML_ASSINAR = '<?xml version="1.0" encoding="UTF-8"?>' \
'<Envelope xmlns="urn:envelope">' \
XML_ASSINAR = '<Envelope xmlns="urn:envelope">' \
' <Data Id="NFe43150602261542000143550010000000761792265342">'\
' Hello, World!' \
' </Data>' \
'</Envelope>'
XML_ERRADO = '<?xml version="1.0" encoding="UTF-8"?>' \
'<Envelope xmlns="urn:envelope">' \
XML_ERRADO = '<Envelope xmlns="urn:envelope">' \
' <Data Id="NFe">' \
' Hello, World!' \
' </Data>' \
@ -32,21 +30,21 @@ class test_assinatura(unittest.TestCase):
caminho = os.path.dirname(__file__)
def test_assinar_xml_senha_invalida(self):
pfx = open(os.path.join(self.caminho, 'teste.pfx')).read()
pfx = open(os.path.join(self.caminho, 'teste.pfx'), 'rb').read()
signer = Assinatura(pfx, '123')
self.assertRaises(Exception, signer.assina_xml, signer,
etree.fromstring(XML_ASSINAR),
'NFe43150602261542000143550010000000761792265342')
def test_assinar_xml_invalido(self):
pfx = open(os.path.join(self.caminho, 'teste.pfx')).read()
pfx = open(os.path.join(self.caminho, 'teste.pfx'), 'rb').read()
signer = Assinatura(pfx, '123456')
self.assertRaises(Exception, signer.assina_xml, signer,
etree.fromstring(XML_ERRADO),
'NFe43150602261542000143550010000000761792265342')
def test_assinar_xml_valido(self):
pfx = open(os.path.join(self.caminho, 'teste.pfx')).read()
pfx = open(os.path.join(self.caminho, 'teste.pfx'), 'rb').read()
signer = Assinatura(pfx, '123456')
xml = signer.assina_xml(
etree.fromstring(XML_ASSINAR),

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

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

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__)
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')
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
# with open('/home/danimar/danfe.pdf', 'w') as oFile:
with tempfile.TemporaryFile(mode='w') as oFile:
with tempfile.TemporaryFile(mode='wb') as oFile:
oDanfe.writeto_pdf(oFile)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading…
Cancel
Save