diff --git a/.appveyor.yml b/.appveyor.yml
new file mode 100644
index 0000000..886484b
--- /dev/null
+++ b/.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 }
diff --git a/.gitignore b/.gitignore
index f171cd6..eff572b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,4 +11,4 @@ dist/
*egg*/
docs/_build
.vscode/tags
-.cache/
+.cache
diff --git a/.travis.yml b/.travis.yml
index c8bec07..73a0c70 100644
--- a/.travis.yml
+++ b/.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"
diff --git a/README.md b/README.md
index d16772c..98ddd7b 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,17 @@
# PyTrustNFe
Biblioteca Python que tem por objetivo enviar NFe, NFCe e NFSe no Brasil
-[](https://coveralls.io/r/danimaribeiro/PyTrustNFe?branch=master)
-[](https://landscape.io/github/danimaribeiro/PyTrustNFe/master)
-[](https://travis-ci.org/danimaribeiro/PyTrustNFe)
-[](https://badge.fury.io/py/PyTrustNFe)
+[](https://coveralls.io/r/danimaribeiro/PyTrustNFe?branch=master3)
+[](https://landscape.io/github/danimaribeiro/PyTrustNFe/master3)
+[](https://travis-ci.org/danimaribeiro/PyTrustNFe)
+[](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
diff --git a/pytrustnfe/Servidores.py b/pytrustnfe/Servidores.py
index 8ff98cd..0b3e28d 100644
--- a/pytrustnfe/Servidores.py
+++ b/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',
diff --git a/pytrustnfe/__init__.py b/pytrustnfe/__init__.py
index 252e121..093c5c3 100644
--- a/pytrustnfe/__init__.py
+++ b/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):
diff --git a/pytrustnfe/certificado.py b/pytrustnfe/certificado.py
index 4e44ae9..d24732c 100644
--- a/pytrustnfe/certificado.py
+++ b/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)
diff --git a/pytrustnfe/client.py b/pytrustnfe/client.py
index bf76c7b..03adbd2 100644
--- a/pytrustnfe/client.py
+++ b/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),
diff --git a/pytrustnfe/nfe/__init__.py b/pytrustnfe/nfe/__init__.py
index f00ee9f..62b6932 100644
--- a/pytrustnfe/nfe/__init__.py
+++ b/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)
diff --git a/pytrustnfe/nfe/assinatura.py b/pytrustnfe/nfe/assinatura.py
index e14ec5d..34df54a 100644
--- a/pytrustnfe/nfe/assinatura.py
+++ b/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)
diff --git a/pytrustnfe/nfe/comunicacao.py b/pytrustnfe/nfe/comunicacao.py
index 2e3f4d9..df6ba53 100644
--- a/pytrustnfe/nfe/comunicacao.py
+++ b/pytrustnfe/nfe/comunicacao.py
@@ -30,5 +30,5 @@ def executar_consulta(certificado, url, cabecalho, xmlEnviar, send_raw=False):
if send_raw:
xml = '' + 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())
diff --git a/pytrustnfe/nfe/danfe.py b/pytrustnfe/nfe/danfe.py
index c1fb83c..001810a 100644
--- a/pytrustnfe/nfe/danfe.py
+++ b/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, "Nº %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'
Regime Tributário: %s' % (REGIME_TRIBUTACAO[regime])
+ cEnd += '
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, "Nº %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'),
diff --git a/pytrustnfe/nfe/fonts/NimbusSanL Bold.ttf b/pytrustnfe/nfe/fonts/NimbusSanL Bold.ttf
new file mode 100644
index 0000000..0e29c13
Binary files /dev/null and b/pytrustnfe/nfe/fonts/NimbusSanL Bold.ttf differ
diff --git a/pytrustnfe/nfe/fonts/NimbusSanL Regular.ttf b/pytrustnfe/nfe/fonts/NimbusSanL Regular.ttf
new file mode 100644
index 0000000..ac601af
Binary files /dev/null and b/pytrustnfe/nfe/fonts/NimbusSanL Regular.ttf differ
diff --git a/pytrustnfe/nfse/assinatura.py b/pytrustnfe/nfse/assinatura.py
index b2a8d28..0c65cee 100644
--- a/pytrustnfe/nfse/assinatura.py
+++ b/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)
diff --git a/pytrustnfe/nfse/betha/__init__.py b/pytrustnfe/nfse/betha/__init__.py
index f2517fa..67ae42d 100644
--- a/pytrustnfe/nfse/betha/__init__.py
+++ b/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,
diff --git a/pytrustnfe/nfse/dsf/__init__.py b/pytrustnfe/nfse/dsf/__init__.py
new file mode 100644
index 0000000..df4ce4a
--- /dev/null
+++ b/pytrustnfe/nfse/dsf/__init__.py
@@ -0,0 +1,131 @@
+# -*- encoding: utf-8 -*-
+# © 2017 Fábio Luna, Trustcode
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+import os
+import suds
+from lxml import etree
+from pytrustnfe.xml import render_xml, sanitize_response
+from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key
+from pytrustnfe.nfse.assinatura import Assinatura
+from pytrustnfe.client import get_client
+
+
+def _render(certificado, method, **kwargs):
+ path = os.path.join(os.path.dirname(__file__), 'templates')
+ if method == "testeEnviar":
+ xml_send = render_xml(path, 'enviar.xml', True, **kwargs)
+ else:
+ xml_send = render_xml(path, '%s.xml' % method, False, **kwargs)
+
+ if type(xml_send) != str:
+ xml_send = etree.tostring(xml_send)
+
+ return xml_send
+
+
+def _get_url(**kwargs):
+
+ try:
+ cod_cidade = kwargs['nfse']['cidade']
+ except (KeyError, TypeError):
+ raise KeyError("Código de cidade inválido!")
+
+ urls = {
+ # Belém - PA
+ '2715': 'http://www.issdigitalbel.com.br/WsNFe2/LoteRps.jws',
+ # 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
+ '2729': 'http://issdigital.pmcg.ms.gov.br/WsNFe2/LoteRps.jws?wsdl',
+ }
+
+ try:
+ return urls[str(cod_cidade)]
+ except KeyError:
+ raise KeyError("DSF não emite notas da cidade {}!".format(
+ cod_cidade))
+
+
+def _send(certificado, method, **kwargs):
+ url = _get_url(**kwargs)
+
+ path = os.path.join(os.path.dirname(__file__), 'templates')
+
+ xml_send = _render(path, method, **kwargs)
+ client = get_client(url)
+ response = False
+
+ if certificado:
+ cert, key = extract_cert_and_key_from_pfx(
+ certificado.pfx, certificado.password)
+ cert, key = save_cert_key(cert, key)
+ signer = Assinatura(cert, key, certificado.password)
+ xml_send = signer.assina_xml(xml_send, '')
+
+ try:
+ response = getattr(client.service, method)(xml_send)
+ response, obj = sanitize_response(response.encode())
+ except suds.WebFault as e:
+ return {
+ 'sent_xml': xml_send,
+ 'received_xml': e.fault.faultstring,
+ 'object': None
+ }
+ except Exception as e:
+ if response:
+ raise Exception(response)
+ else:
+ raise e
+
+ return {
+ 'sent_xml': xml_send,
+ 'received_xml': response,
+ 'object': obj
+ }
+
+
+def xml_enviar(certificado, **kwargs):
+ return _render(certificado, 'enviar', **kwargs)
+
+
+def enviar(certificado, **kwargs):
+ if "xml" not in kwargs:
+ kwargs['xml'] = xml_enviar(certificado, **kwargs)
+ return _send(certificado, 'enviar', **kwargs)
+
+
+def xml_teste_enviar(certificado, **kwargs):
+ return _render(certificado, 'testeEnviar', **kwargs)
+
+
+def teste_enviar(certificado, **kwargs):
+ if "xml" not in kwargs:
+ kwargs['xml'] = xml_teste_enviar(certificado, **kwargs)
+ return _send(certificado, 'testeEnviar', **kwargs)
+
+
+def cancelar(certificado, ** kwargs):
+ return _send(certificado, 'cancelar', **kwargs)
+
+
+def consulta_lote(**kwargs):
+ return _send(False, 'consultarLote', **kwargs)
+
+
+def xml_consultar_nfse_rps(certificado, **kwargs):
+ return _render(certificado, 'consultarNFSeRps', **kwargs)
+
+
+def consultar_nfse_rps(certificado, **kwargs):
+ if "xml" not in kwargs:
+ kwargs['xml'] = xml_consultar_nfse_rps(certificado, **kwargs)
+ return _send(certificado, 'consultarNFSeRps', **kwargs)
diff --git a/pytrustnfe/nfse/dsf/templates/cancelar.xml b/pytrustnfe/nfse/dsf/templates/cancelar.xml
new file mode 100644
index 0000000..d72086b
--- /dev/null
+++ b/pytrustnfe/nfse/dsf/templates/cancelar.xml
@@ -0,0 +1,18 @@
+
+
+ {{ cancelamento.cidade }}
+ {{ cancelamento.cpf_cnpj }}
+ true
+ 1
+
+
+
+ {{ cancelamento.inscricao_municipal }}
+ {{ cancelamento.nota_id }}
+ {{ cancelamento.assinatura }}
+ {{ cancelamento.motivo }}
+
+
+
diff --git a/pytrustnfe/nfse/dsf/templates/consulta_notas.xml b/pytrustnfe/nfse/dsf/templates/consulta_notas.xml
new file mode 100644
index 0000000..4a666d0
--- /dev/null
+++ b/pytrustnfe/nfse/dsf/templates/consulta_notas.xml
@@ -0,0 +1,11 @@
+
+
+{{ consulta.cidade }}
+{{ consulta.cpf_cnpj }}
+{{ consulta.inscricao_municipal }}
+{{ consulta.data_inicio }}
+{{ consulta.data_final }}
+{{ consulta.nota_inicial }}
+1
+
+
\ No newline at end of file
diff --git a/pytrustnfe/nfse/dsf/templates/consultarLote.xml b/pytrustnfe/nfse/dsf/templates/consultarLote.xml
new file mode 100644
index 0000000..24afc5d
--- /dev/null
+++ b/pytrustnfe/nfse/dsf/templates/consultarLote.xml
@@ -0,0 +1,10 @@
+
+
+ {{ consulta.cidade }}
+ {{ consulta.cpf_cnpj }}
+ 1
+ {{ consulta.lote }}
+
+
\ No newline at end of file
diff --git a/pytrustnfe/nfse/dsf/templates/consultarNFSeRps.xml b/pytrustnfe/nfse/dsf/templates/consultarNFSeRps.xml
new file mode 100644
index 0000000..a6a51bc
--- /dev/null
+++ b/pytrustnfe/nfse/dsf/templates/consultarNFSeRps.xml
@@ -0,0 +1,22 @@
+
+
+ {{ nfse.cidade }}
+ {{ nfse.cpf_cnpj }}
+ true
+ 1
+
+
+ {% for rps in nfse.lista_rps -%}
+
+
+ {{ rps.prestador.inscricao_municipal }}
+ {{ rps.numero }}
+ {{ rps.serie_prestacao }}
+
+
+ {% endfor %}
+
+
\ No newline at end of file
diff --git a/pytrustnfe/nfse/dsf/templates/enviar.xml b/pytrustnfe/nfse/dsf/templates/enviar.xml
new file mode 100644
index 0000000..7e4b178
--- /dev/null
+++ b/pytrustnfe/nfse/dsf/templates/enviar.xml
@@ -0,0 +1,108 @@
+
+
+ {{ nfse.cidade }}
+ {{ nfse.cpf_cnpj }}
+ {{ nfse.remetente }}
+ {{ nfse.transacao }}
+ {{ nfse.data_inicio|format_date }}
+ {{ nfse.data_fim|format_date }}
+ {{ nfse.total_rps }}
+ {{ nfse.total_servicos }}
+ {{ nfse.total_deducoes }}
+ 1
+ WS
+
+
+ {% for rps in nfse.lista_rps -%}
+
+ {{ rps.assinatura }}
+ {{ rps.prestador.inscricao_municipal }}
+
+ {{ rps.prestador.razao_social }}
+ RPS
+ {{ rps.serie }}
+ {{ rps.numero }}
+ {{ rps.data_emissao|format_datetime }}
+
+ {{ rps.situacao }}
+
+ 0
+ 0
+ 1900-01-01
+ {{ rps.serie_prestacao }}
+ {{ rps.tomador.inscricao_municipal }}
+ {{ rps.tomador.cpf_cnpj }}
+ {{ rps.tomador.razao_social }}
+
+ {{ rps.tomador.tipo_logradouro }}
+
+ {{ rps.tomador.logradouro }}
+ {{ rps.tomador.numero }}
+
+ {{ rps.tomador.tipo_bairro }}
+ {{ rps.tomador.bairro }}
+ {{ rps.tomador.cidade }}
+ {{ rps.tomador.cidade_descricao }}
+
+ {{ rps.tomador.cep }}
+ {{ rps.tomador.email }}
+ {{ rps.codigo_atividade }}
+ {{ rps.aliquota_atividade }}
+ {{ rps.tipo_recolhimento }}
+ {{ rps.municipio_prestacao }}
+
+ {{ rps.municipio_descricao_prestacao }}
+
+ {{ rps.operacao }}
+ {{ rps.tributacao }}
+ {{ rps.valor_pis }}
+ {{ rps.valor_cofins }}
+ {{ rps.valor_inss }}
+ {{ rps.valor_ir }}
+ {{ rps.valor_csll }}
+ {{ rps.aliquota_pis }}
+ {{ rps.aliquota_cofins }}
+ {{ rps.aliquota_inss }}
+ {{ rps.aliquota_ir }}
+ {{ rps.aliquota_csll }}
+ {{ rps.descricao }}
+ {{ rps.prestador.ddd }}
+ {{ rps.prestador.telefone }}
+ {{ rps.tomador.ddd }}
+ {{ rps.tomador.telefone }}
+ {{ rps.motivo_cancelamento }}
+ {% if rps.deducoes|count > 0 %}
+
+ {% for deducao in rps.deducoes -%}
+
+ {{ deducao.por }}
+ {{ deducao.tipo }}
+ {{ deducao.cnpj_referencia }}
+ {{ deducao.nf_referencia }}
+ {{ deducao.valor_referencia }}
+ {{ deducao.percentual_deduzir }}
+ {{ deducao.valor_deduzir }}
+
+ {% endfor %}
+
+ {% endif %}
+ {% if rps.deducoes|count == 0 %}
+
+ {% endif %}
+
+ {% for item in rps.itens -%}
+ -
+ {{ item.descricao }}
+ {{ item.quantidade }}
+ {{ item.valor_unitario }}
+ {{ item.valor_total }}
+ S
+
+ {% endfor %}
+
+
+ {% endfor %}
+
+
diff --git a/pytrustnfe/nfse/dsf/templates/soap_header.xml b/pytrustnfe/nfse/dsf/templates/soap_header.xml
new file mode 100644
index 0000000..e9d1dd2
--- /dev/null
+++ b/pytrustnfe/nfse/dsf/templates/soap_header.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pytrustnfe/nfse/floripa/__init__.py b/pytrustnfe/nfse/floripa/__init__.py
new file mode 100644
index 0000000..a8282d3
--- /dev/null
+++ b/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
diff --git a/pytrustnfe/nfse/floripa/templates/cancelar_nota.xml b/pytrustnfe/nfse/floripa/templates/cancelar_nota.xml
new file mode 100644
index 0000000..324d779
--- /dev/null
+++ b/pytrustnfe/nfse/floripa/templates/cancelar_nota.xml
@@ -0,0 +1,7 @@
+
+
+ {{ cancelamento.motivo }}
+ {{ cancelamento.aedf }}
+ {{ cancelamento.numero }}
+ {{ cancelamento.codigo_verificacao }}
+
diff --git a/pytrustnfe/nfse/floripa/templates/processar_nota.xml b/pytrustnfe/nfse/floripa/templates/processar_nota.xml
new file mode 100644
index 0000000..08a7a2e
--- /dev/null
+++ b/pytrustnfe/nfse/floripa/templates/processar_nota.xml
@@ -0,0 +1,40 @@
+
+
+ {{ rps.tomador.bairro }}
+ {{ rps.base_calculo }}
+ 0.0
+ {{ rps.cfps }}
+ {{ rps.tomador.cidade }}
+ {{ rps.tomador.cep }}
+ {{ rps.tomador.complemento }}
+ {{ rps.observacoes }}
+ {{ rps.data_emissao }}
+ {{ rps.tomador.email }}
+ {{ rps.numero }}
+ {{ rps.tomador.cnpj_cpf }}
+ {{ rps.tomador.inscricao_municipal }}
+
+ {% for item in rps.itens_servico -%}
+
+ {{ item.aliquota }}
+ {{ item.cst_servico }}
+ {{ item.descricao }}
+ {{ item.cnae }}
+ {{ item.quantidade }}
+ {{ item.valor_total }}
+ {{ item.valor_unitario }}
+
+ {% endfor %}
+
+ {{ rps.tomador.logradouro }}
+
+ {{ rps.aedf }}
+ {{ rps.tomador.numero }}
+ 1058
+ {{ rps.tomador.razao_social }}
+ {{ rps.tomador.telefone }}
+ {{ rps.tomador.uf }}
+ {{rps.valor_issqn }}
+ 0.0
+ {{ rps.valor_total }}
+
diff --git a/pytrustnfe/nfse/ginfes/__init__.py b/pytrustnfe/nfse/ginfes/__init__.py
index f69ffe1..80e969b 100644
--- a/pytrustnfe/nfse/ginfes/__init__.py
+++ b/pytrustnfe/nfse/ginfes/__init__.py
@@ -38,7 +38,7 @@ def _send(certificado, method, **kwargs):
xml_send = kwargs['xml']
header = '3' #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,
diff --git a/pytrustnfe/nfse/paulistana/__init__.py b/pytrustnfe/nfse/paulistana/__init__.py
index 48818e8..ed0611b 100644
--- a/pytrustnfe/nfse/paulistana/__init__.py
+++ b/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,
diff --git a/pytrustnfe/nfse/susesu/__init__.py b/pytrustnfe/nfse/susesu/__init__.py
index 2295bb2..557a168 100644
--- a/pytrustnfe/nfse/susesu/__init__.py
+++ b/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,
diff --git a/pytrustnfe/test/XMLs/paulistana_resultado.xml b/pytrustnfe/test/XMLs/paulistana_resultado.xml
deleted file mode 100644
index d4f6759..0000000
--- a/pytrustnfe/test/XMLs/paulistana_resultado.xml
+++ /dev/null
@@ -1 +0,0 @@
-true265436451212213329001632016-08-29T10:52:15101.3552382446APR9MJR5128216
diff --git a/pytrustnfe/xml/__init__.py b/pytrustnfe/xml/__init__.py
index ff1a6f3..5cf7454 100644
--- a/pytrustnfe/xml/__init__.py
+++ b/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
diff --git a/pytrustnfe/xml/filters.py b/pytrustnfe/xml/filters.py
index 8a33af4..c3f7cbf 100644
--- a/pytrustnfe/xml/filters.py
+++ b/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
diff --git a/pytrustnfe/xml/schemas/enviNFe_v3.10.xsd b/pytrustnfe/xml/schemas/enviNFe_v3.10.xsd
old mode 100644
new mode 100755
diff --git a/pytrustnfe/xml/schemas/leiauteNFe_v3.10.xsd b/pytrustnfe/xml/schemas/leiauteNFe_v3.10.xsd
old mode 100644
new mode 100755
diff --git a/pytrustnfe/xml/schemas/nfe_v3.10.xsd b/pytrustnfe/xml/schemas/nfe_v3.10.xsd
old mode 100644
new mode 100755
diff --git a/pytrustnfe/xml/schemas/tiposBasico_v3.10.xsd b/pytrustnfe/xml/schemas/tiposBasico_v3.10.xsd
old mode 100644
new mode 100755
index 70c9a4b..1dfe7d0
--- a/pytrustnfe/xml/schemas/tiposBasico_v3.10.xsd
+++ b/pytrustnfe/xml/schemas/tiposBasico_v3.10.xsd
@@ -494,7 +494,7 @@
-
+
diff --git a/pytrustnfe/xml/schemas/xmldsig-core-schema_v1.01.xsd b/pytrustnfe/xml/schemas/xmldsig-core-schema_v1.01.xsd
old mode 100644
new mode 100755
diff --git a/pytrustnfe/xml/validate.py b/pytrustnfe/xml/validate.py
index 4314d2c..3306b57 100644
--- a/pytrustnfe/xml/validate.py
+++ b/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)
diff --git a/requirements.txt b/requirements.txt
index 9a94f0c..abb0836 100644
--- a/requirements.txt
+++ b/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
diff --git a/setup.py b/setup.py
index 2b186be..bbb800c 100644
--- a/setup.py
+++ b/setup.py
@@ -1,33 +1,41 @@
# coding=utf-8
from setuptools import setup, find_packages
-VERSION = "0.1.44"
+
+VERSION = "0.9.7"
+
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.1',
'reportlab'
],
- test_suite='nose.collector',
tests_require=[
- 'nose',
- 'mock',
+ 'pytest',
],
)
diff --git a/pytrustnfe/test/XMLs/NFe00000857.xml b/tests/XMLs/NFe00000857.xml
similarity index 100%
rename from pytrustnfe/test/XMLs/NFe00000857.xml
rename to tests/XMLs/NFe00000857.xml
diff --git a/pytrustnfe/test/XMLs/jinja_remove_empty.xml b/tests/XMLs/jinja_remove_empty.xml
similarity index 100%
rename from pytrustnfe/test/XMLs/jinja_remove_empty.xml
rename to tests/XMLs/jinja_remove_empty.xml
diff --git a/pytrustnfe/test/XMLs/jinja_result.xml b/tests/XMLs/jinja_result.xml
similarity index 100%
rename from pytrustnfe/test/XMLs/jinja_result.xml
rename to tests/XMLs/jinja_result.xml
diff --git a/pytrustnfe/test/XMLs/jinja_template.xml b/tests/XMLs/jinja_template.xml
similarity index 100%
rename from pytrustnfe/test/XMLs/jinja_template.xml
rename to tests/XMLs/jinja_template.xml
diff --git a/pytrustnfe/test/XMLs/paulistana_canc.xml b/tests/XMLs/paulistana_canc.xml
similarity index 100%
rename from pytrustnfe/test/XMLs/paulistana_canc.xml
rename to tests/XMLs/paulistana_canc.xml
diff --git a/pytrustnfe/test/XMLs/paulistana_canc_errado.xml b/tests/XMLs/paulistana_canc_errado.xml
similarity index 93%
rename from pytrustnfe/test/XMLs/paulistana_canc_errado.xml
rename to tests/XMLs/paulistana_canc_errado.xml
index fc1aeae..4fd39b4 100644
--- a/pytrustnfe/test/XMLs/paulistana_canc_errado.xml
+++ b/tests/XMLs/paulistana_canc_errado.xml
@@ -1,4 +1,3 @@
-
true265436451212213329001632016-08-29T10:52:15101.3552382446APR9MJR5128216
diff --git a/pytrustnfe/test/XMLs/paulistana_signature.xml b/tests/XMLs/paulistana_signature.xml
similarity index 73%
rename from pytrustnfe/test/XMLs/paulistana_signature.xml
rename to tests/XMLs/paulistana_signature.xml
index 013053e..c4f8cbc 100644
--- a/pytrustnfe/test/XMLs/paulistana_signature.xml
+++ b/tests/XMLs/paulistana_signature.xml
@@ -1,8 +1,4 @@
-
-12345678901234false2016-08-292016-08-291E4fpHYkQa7Naxn6IKGb7NwwZu5tPk/KXJ9hCwtZgq0xvKS450aQqqBL+7Iv46lTgqrSMu7+gLrl+LC1qs/8aT2mbHE8uaVFSbzwZ+sF/BkcT6nsFHLMswEiTAEs95Jb7hN1cC91xqQGRH4buw0TzxHKmhuLJ22WwtG/scxyKtjM=12345611RPS2016-08-29NT0.000.000.000.000.00074985.00false
-
-
- 123456Trustcode1Vinicius de Moraes, 4242CorregoFloripaSC88037240Venda de servico
+12345678901234false2016-08-292016-08-291E4fpHYkQa7Naxn6IKGb7NwwZu5tPk/KXJ9hCwtZgq0xvKS450aQqqBL+7Iv46lTgqrSMu7+gLrl+LC1qs/8aT2mbHE8uaVFSbzwZ+sF/BkcT6nsFHLMswEiTAEs95Jb7hN1cC91xqQGRH4buw0TzxHKmhuLJ22WwtG/scxyKtjM=12345611RPS2016-08-29NT0.000.000.000.000.00074985.00false123456Trustcode1Vinicius de Moraes, 4242CorregoFloripaSC88037240Venda de servico
@@ -12,12 +8,12 @@
-ivaOwkcrt0pfuMYsAdfyLaUAcIk=
+ePJnD6hyDvlJo08PFX8h2TXk0ZM=
-FjIHdfPavSEyaWYhAT0z0shPLuTsqBKyy78PUEZ8PUhTZ+iSV0MOvAIRq9MPPVK9
-jjXOw1TE903uSK8aJon52RNKPd68ORVJ3bKFSjTqQLxFRR9tiiAQFrWDETf7FF89
-EhG6dy6TGcgVbOyn0Jqm8MkqrE1XrJ44orN1X+Jt+7U=
+GbaQaTEtxuKdRRaadginWPFH5K65ywqEikkwChWO3xX5Kglq8RPm4+LjnpJmuTcE
+9I2BVon3GJFh+c/6RKzJPose6FXog2xnCpTOgwA/rks/gKsUAaRlXCPsLcKMKaOj
+3eH21RHEyrxBAbdpEUdlEgQWaWzmGq009EiQ544sD6c=
MIICMTCCAZqgAwIBAgIQfYOsIEVuAJ1FwwcTrY0t1DANBgkqhkiG9w0BAQUFADBX
@@ -34,4 +30,4 @@ QtgAhuZM9rxpOJuNKc+pM29EixpAiZZiRMCSWEItNyEVdUIi+YnKBcAHd88TwO86
d126MWQ2O8cu5W1VoDp7hYBYKOnLbYi11/StO+0rzK+oPYAvIw==
-
+
\ No newline at end of file
diff --git a/pytrustnfe/test/XMLs/recibo_envio_1.xml b/tests/XMLs/recibo_envio_1.xml
similarity index 100%
rename from pytrustnfe/test/XMLs/recibo_envio_1.xml
rename to tests/XMLs/recibo_envio_1.xml
diff --git a/pytrustnfe/test/XMLs/recibo_envio_2.xml b/tests/XMLs/recibo_envio_2.xml
similarity index 100%
rename from pytrustnfe/test/XMLs/recibo_envio_2.xml
rename to tests/XMLs/recibo_envio_2.xml
diff --git a/pytrustnfe/test/XMLs/recibo_protocolo_sucesso_1.xml b/tests/XMLs/recibo_protocolo_sucesso_1.xml
similarity index 100%
rename from pytrustnfe/test/XMLs/recibo_protocolo_sucesso_1.xml
rename to tests/XMLs/recibo_protocolo_sucesso_1.xml
diff --git a/pytrustnfe/test/XMLs/recibo_protocolo_sucesso_2.xml b/tests/XMLs/recibo_protocolo_sucesso_2.xml
similarity index 100%
rename from pytrustnfe/test/XMLs/recibo_protocolo_sucesso_2.xml
rename to tests/XMLs/recibo_protocolo_sucesso_2.xml
diff --git a/pytrustnfe/test/__init__.py b/tests/__init__.py
similarity index 100%
rename from pytrustnfe/test/__init__.py
rename to tests/__init__.py
diff --git a/pytrustnfe/test/test_add_qr_code.py b/tests/test_add_qr_code.py
similarity index 82%
rename from pytrustnfe/test/test_add_qr_code.py
rename to tests/test_add_qr_code.py
index 17965c9..77a24f4 100644
--- a/pytrustnfe/test/test_add_qr_code.py
+++ b/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'
diff --git a/pytrustnfe/test/test_assinatura.py b/tests/test_assinatura.py
similarity index 79%
rename from pytrustnfe/test/test_assinatura.py
rename to tests/test_assinatura.py
index 380e1fd..f13b851 100644
--- a/pytrustnfe/test/test_assinatura.py
+++ b/tests/test_assinatura.py
@@ -11,16 +11,14 @@ from lxml import etree
from pytrustnfe.nfe.assinatura import Assinatura
-XML_ASSINAR = '' \
- '' \
+XML_ASSINAR = '' \
' '\
' Hello, World!' \
' ' \
''
-XML_ERRADO = '' \
- '' \
+XML_ERRADO = '' \
' ' \
' Hello, World!' \
' ' \
@@ -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),
diff --git a/pytrustnfe/test/test_certificado.py b/tests/test_certificado.py
similarity index 97%
rename from pytrustnfe/test/test_certificado.py
rename to tests/test_certificado.py
index 2e7f248..e05a8f0 100644
--- a/pytrustnfe/test/test_certificado.py
+++ b/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()
diff --git a/pytrustnfe/test/test_comunicacao.py b/tests/test_comunicacao.py
similarity index 100%
rename from pytrustnfe/test/test_comunicacao.py
rename to tests/test_comunicacao.py
diff --git a/pytrustnfe/test/test_consulta_cadastro.py b/tests/test_consulta_cadastro.py
similarity index 96%
rename from pytrustnfe/test/test_consulta_cadastro.py
rename to tests/test_consulta_cadastro.py
index 9c63f03..a284b56 100644
--- a/pytrustnfe/test/test_consulta_cadastro.py
+++ b/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'}
diff --git a/pytrustnfe/test/test_danfe.py b/tests/test_danfe.py
similarity index 92%
rename from pytrustnfe/test/test_danfe.py
rename to tests/test_danfe.py
index e7385c7..a854858 100644
--- a/pytrustnfe/test/test_danfe.py
+++ b/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)
diff --git a/pytrustnfe/test/test_ginfes.py b/tests/test_ginfes.py
similarity index 98%
rename from pytrustnfe/test/test_ginfes.py
rename to tests/test_ginfes.py
index d2895f2..6d23e5e 100644
--- a/pytrustnfe/test/test_ginfes.py
+++ b/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'}
diff --git a/pytrustnfe/test/test_nfse_paulistana.py b/tests/test_nfse_paulistana.py
similarity index 98%
rename from pytrustnfe/test/test_nfse_paulistana.py
rename to tests/test_nfse_paulistana.py
index 0ed4ded..06d4200 100644
--- a/pytrustnfe/test/test_nfse_paulistana.py
+++ b/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()
diff --git a/pytrustnfe/test/test_servidores.py b/tests/test_servidores.py
similarity index 100%
rename from pytrustnfe/test/test_servidores.py
rename to tests/test_servidores.py
diff --git a/pytrustnfe/test/test_utils.py b/tests/test_utils.py
similarity index 91%
rename from pytrustnfe/test/test_utils.py
rename to tests/test_utils.py
index 64cf1fa..247327d 100644
--- a/pytrustnfe/test/test_utils.py
+++ b/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')
diff --git a/pytrustnfe/test/test_xml.py b/tests/test_xml.py
similarity index 88%
rename from pytrustnfe/test/test_xml.py
rename to tests/test_xml.py
index ad5162f..45b70b7 100644
--- a/pytrustnfe/test/test_xml.py
+++ b/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")
diff --git a/pytrustnfe/test/test_xml_serializacao.py b/tests/test_xml_serializacao.py
similarity index 93%
rename from pytrustnfe/test/test_xml_serializacao.py
rename to tests/test_xml_serializacao.py
index 64d0a2a..3f06e14 100644
--- a/pytrustnfe/test/test_xml_serializacao.py
+++ b/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')
diff --git a/pytrustnfe/test/teste.pfx b/tests/teste.pfx
similarity index 100%
rename from pytrustnfe/test/teste.pfx
rename to tests/teste.pfx
diff --git a/pytrustnfe/test/xml_assinado.xml b/tests/xml_assinado.xml
similarity index 100%
rename from pytrustnfe/test/xml_assinado.xml
rename to tests/xml_assinado.xml
diff --git a/pytrustnfe/test/xml_com_qrcode.xml b/tests/xml_com_qrcode.xml
similarity index 98%
rename from pytrustnfe/test/xml_com_qrcode.xml
rename to tests/xml_com_qrcode.xml
index 5731c5f..22cf1ef 100644
--- a/pytrustnfe/test/xml_com_qrcode.xml
+++ b/tests/xml_com_qrcode.xml
@@ -30,11 +30,11 @@
LEL AMBIENTAL LTDA - EPP
Zell Ambiental
- Rua Padre João
+ Rua Padre João
444
- Penha de França
+ Penha de França
3550308
- São Paulo
+ São Paulo
SP
03637000
1058
diff --git a/pytrustnfe/test/xml_sem_qrcode.xml b/tests/xml_sem_qrcode.xml
similarity index 98%
rename from pytrustnfe/test/xml_sem_qrcode.xml
rename to tests/xml_sem_qrcode.xml
index 74b8c78..3352a22 100644
--- a/pytrustnfe/test/xml_sem_qrcode.xml
+++ b/tests/xml_sem_qrcode.xml
@@ -31,11 +31,11 @@
LEL AMBIENTAL LTDA - EPP
Zell Ambiental
- Rua Padre João
+ Rua Padre João
444
- Penha de França
+ Penha de França
3550308
- São Paulo
+ São Paulo
SP
03637000
1058
diff --git a/pytrustnfe/test/xml_valido_assinado.xml b/tests/xml_valido_assinado.xml
similarity index 100%
rename from pytrustnfe/test/xml_valido_assinado.xml
rename to tests/xml_valido_assinado.xml