Browse Source

Merge branch 'master3' into master3

pull/233/head
Danimar Ribeiro 6 years ago
committed by GitHub
parent
commit
f104f65a62
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      .flake8
  2. 4
      .gitignore
  3. 2
      .travis.yml
  4. 61
      docs/conf.py
  5. 1030
      pytrustnfe/Servidores.py
  6. 8
      pytrustnfe/__init__.py
  7. 12
      pytrustnfe/certificado.py
  8. 12
      pytrustnfe/client.py
  9. 4
      pytrustnfe/exceptions.py
  10. 165
      pytrustnfe/nfe/__init__.py
  11. 27
      pytrustnfe/nfe/assinatura.py
  12. 352
      pytrustnfe/nfe/danfce.py
  13. 1054
      pytrustnfe/nfe/danfe.py
  14. 34
      pytrustnfe/nfe/patch.py
  15. 43
      pytrustnfe/nfse/aparecida/__init__.py
  16. 23
      pytrustnfe/nfse/assinatura.py
  17. 76
      pytrustnfe/nfse/betha/__init__.py
  18. 51
      pytrustnfe/nfse/bh/__init__.py
  19. 21
      pytrustnfe/nfse/bh/assinatura.py
  20. 53
      pytrustnfe/nfse/carioca/__init__.py
  21. 67
      pytrustnfe/nfse/dsf/__init__.py
  22. 94
      pytrustnfe/nfse/floripa/__init__.py
  23. 57
      pytrustnfe/nfse/ginfes/__init__.py
  24. 40
      pytrustnfe/nfse/imperial/__init__.py
  25. 47
      pytrustnfe/nfse/mga/__init__.py
  26. 17
      pytrustnfe/nfse/mga/assinatura.py
  27. 116
      pytrustnfe/nfse/natal/__init__.py
  28. 8
      pytrustnfe/nfse/natal/templates/EnvelopeSoap.xml
  29. 111
      pytrustnfe/nfse/natal/templates/Exemplo_LoteRPS.xml
  30. 112
      pytrustnfe/nfse/natal/templates/Rps.xml
  31. 3
      pytrustnfe/nfse/natal/templates/cabecalho.xml
  32. 15
      pytrustnfe/nfse/natal/templates/cancelarNfse.xml
  33. 7
      pytrustnfe/nfse/natal/templates/consultarLoteRps.xml
  34. 11
      pytrustnfe/nfse/natal/templates/recepcionarLoteRps.xml
  35. 68
      pytrustnfe/nfse/paulistana/__init__.py
  36. 19
      pytrustnfe/nfse/paulistana/templates/EnvioLoteRPS.xml
  37. 50
      pytrustnfe/nfse/paulistana/templates/EnvioRPS.xml
  38. 64
      pytrustnfe/nfse/simpliss/__init__.py
  39. 34
      pytrustnfe/nfse/susesu/__init__.py
  40. 9
      pytrustnfe/nfse/susesu/templates/EnviarNota.xml
  41. 262
      pytrustnfe/urls.py
  42. 60
      pytrustnfe/utils.py
  43. 19
      pytrustnfe/xml/__init__.py
  44. 27
      pytrustnfe/xml/filters.py
  45. 4
      pytrustnfe/xml/validate.py
  46. 4
      requirements-dev.txt
  47. 82
      setup.py
  48. 154
      tests/XMLs/natal_sent_xml.xml
  49. 10
      tests/XMLs/paulistana_signature.xml
  50. 1
      tests/XMLs/xml_send_rps_batch_to_paulistana.xml
  51. 56
      tests/const.py
  52. 68
      tests/test_assinatura.py
  53. 110
      tests/test_certificado.py
  54. 6
      tests/test_danfe.py
  55. 13
      tests/test_ginfes.py
  56. 145
      tests/test_nfse_paulistana.py
  57. 35
      tests/test_nfse_paulistana_email_tomador.py
  58. 54
      tests/test_nfse_paulistana_endereco_tomador.py
  59. 29
      tests/test_nfse_paulistana_para_lote.py
  60. 60
      tests/test_nfse_paulistana_tipo_cpfcnpj.py
  61. 47
      tests/test_nfse_paulistana_valores_default.py
  62. 37
      tests/test_servidores.py
  63. 118
      tests/test_utils.py
  64. 17
      tests/test_xml.py
  65. 33
      tests/test_xml_serializacao.py

6
.flake8

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

4
.gitignore

@ -14,3 +14,7 @@ docs/_build
.cache
.pytest_cache
.vscode/
*.pfx
sent_xml.xml
received_xml.xml
nfse-exemplo.py

2
.travis.yml

@ -1,8 +1,6 @@
language: python
python:
- "3.6"
virtual_env:
system_site_packages: true
install:
- pip install --upgrade pip
- pip install -r requirements.txt

61
docs/conf.py

@ -30,44 +30,44 @@ import shlex
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.coverage',
"sphinx.ext.autodoc",
"sphinx.ext.coverage",
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
source_suffix = ".rst"
# The encoding of source files.
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
master_doc = "index"
# General information about the project.
project = u'PyTrustNFe'
copyright = u'2015, Danimar Ribeiro'
author = u'Danimar Ribeiro'
project = u"PyTrustNFe"
copyright = u"2015, Danimar Ribeiro"
author = u"Danimar Ribeiro"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '1.0'
version = "1.0"
# The full version, including alpha/beta/rc tags.
release = '1.0'
release = "1.0"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'pt_BR'
language = "pt_BR"
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
@ -77,7 +77,7 @@ language = 'pt_BR'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all
# documents.
@ -95,7 +95,7 @@ exclude_patterns = ['_build']
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
@ -111,7 +111,7 @@ todo_include_todos = False
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'nature'
html_theme = "nature"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@ -140,7 +140,7 @@ html_theme = 'nature'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = ["_static"]
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
@ -203,20 +203,17 @@ html_static_path = ['_static']
# html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder.
htmlhelp_basename = 'PyTrustNFedoc'
htmlhelp_basename = "PyTrustNFedoc"
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
# Latex figure (float) alignment
#'figure_align': 'htbp',
}
@ -225,8 +222,13 @@ latex_elements = {
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'PyTrustNFe.tex', u'PyTrustNFe Documentation',
u'Danimar Ribeiro', 'manual'),
(
master_doc,
"PyTrustNFe.tex",
u"PyTrustNFe Documentation",
u"Danimar Ribeiro",
"manual",
),
]
# The name of an image file (relative to this directory) to place at the top of
@ -254,10 +256,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'pytrustnfe', u'PyTrustNFe Documentation',
[author], 1)
]
man_pages = [(master_doc, "pytrustnfe", u"PyTrustNFe Documentation", [author], 1)]
# If true, show URL addresses after external links.
# man_show_urls = False
@ -269,9 +268,15 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'PyTrustNFe', u'PyTrustNFe Documentation',
author, 'PyTrustNFe', 'One line description of project.',
'Miscellaneous'),
(
master_doc,
"PyTrustNFe",
u"PyTrustNFe Documentation",
author,
"PyTrustNFe",
"One line description of project.",
"Miscellaneous",
),
]
# Documents to append as an appendix to all manuals.

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

8
pytrustnfe/__init__.py

@ -6,16 +6,14 @@ import requests
class HttpClient(object):
def __init__(self, url):
self.url = url
def _headers(self, action):
return {
'Content-type':
'text/xml; charset=utf-8;',
'Accept': 'application/soap+xml; charset=utf-8',
'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):

12
pytrustnfe/certificado.py

@ -13,7 +13,7 @@ class Certificado(object):
def save_pfx(self):
pfx_temp = tempfile.mkstemp()[1]
arq_temp = open(pfx_temp, 'wb')
arq_temp = open(pfx_temp, "wb")
arq_temp.write(self.pfx)
arq_temp.close()
return pfx_temp
@ -22,11 +22,9 @@ class Certificado(object):
def extract_cert_and_key_from_pfx(pfx, password):
pfx = crypto.load_pkcs12(pfx, password)
# PEM formatted private key
key = crypto.dump_privatekey(crypto.FILETYPE_PEM,
pfx.get_privatekey())
key = crypto.dump_privatekey(crypto.FILETYPE_PEM, pfx.get_privatekey())
# PEM formatted certificate
cert = crypto.dump_certificate(crypto.FILETYPE_PEM,
pfx.get_certificate())
cert = crypto.dump_certificate(crypto.FILETYPE_PEM, pfx.get_certificate())
return cert.decode(), key.decode()
@ -34,11 +32,11 @@ def save_cert_key(cert, key):
cert_temp = tempfile.mkstemp()[1]
key_temp = tempfile.mkstemp()[1]
arq_temp = open(cert_temp, 'w')
arq_temp = open(cert_temp, "w")
arq_temp.write(cert)
arq_temp.close()
arq_temp = open(key_temp, 'w')
arq_temp = open(key_temp, "w")
arq_temp.write(key)
arq_temp.close()

12
pytrustnfe/client.py

@ -8,26 +8,22 @@ import suds_requests
def get_authenticated_client(base_url, cert, key):
cache_location = '/tmp/suds'
cache_location = "/tmp/suds"
cache = suds.cache.DocumentCache(location=cache_location)
session = requests.Session()
session.cert = (cert, key)
return suds.client.Client(
base_url,
cache=cache,
transport=suds_requests.RequestsTransport(session)
base_url, cache=cache, transport=suds_requests.RequestsTransport(session)
)
def get_client(base_url):
cache_location = '/tmp/suds'
cache_location = "/tmp/suds"
cache = suds.cache.DocumentCache(location=cache_location)
session = requests.Session()
return suds.client.Client(
base_url,
cache=cache,
transport=suds_requests.RequestsTransport(session)
base_url, cache=cache, transport=suds_requests.RequestsTransport(session)
)

4
pytrustnfe/exceptions.py

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

165
pytrustnfe/nfe/__init__.py

@ -20,44 +20,48 @@ from zeep.transports import Transport
def _generate_nfe_id(**kwargs):
for item in kwargs['NFes']:
for item in kwargs["NFes"]:
vals = {
'cnpj': item['infNFe']['emit']['cnpj_cpf'],
'estado': item['infNFe']['ide']['cUF'],
'emissao': '%s%s' % (item['infNFe']['ide']['dhEmi'][2:4],
item['infNFe']['ide']['dhEmi'][5:7]),
'modelo': item['infNFe']['ide']['mod'],
'serie': item['infNFe']['ide']['serie'],
'numero': item['infNFe']['ide']['nNF'],
'tipo': item['infNFe']['ide']['tpEmis'],
'codigo': item['infNFe']['ide']['cNF'],
"cnpj": item["infNFe"]["emit"]["cnpj_cpf"],
"estado": item["infNFe"]["ide"]["cUF"],
"emissao": "%s%s"
% (
item["infNFe"]["ide"]["dhEmi"][2:4],
item["infNFe"]["ide"]["dhEmi"][5:7],
),
"modelo": item["infNFe"]["ide"]["mod"],
"serie": item["infNFe"]["ide"]["serie"],
"numero": item["infNFe"]["ide"]["nNF"],
"tipo": item["infNFe"]["ide"]["tpEmis"],
"codigo": item["infNFe"]["ide"]["cNF"],
}
chave_nfe = ChaveNFe(**vals)
chave_nfe = gerar_chave(chave_nfe, 'NFe')
item['infNFe']['Id'] = chave_nfe
item['infNFe']['ide']['cDV'] = chave_nfe[len(chave_nfe) - 1:]
chave_nfe = gerar_chave(chave_nfe, "NFe")
item["infNFe"]["Id"] = chave_nfe
item["infNFe"]["ide"]["cDV"] = chave_nfe[len(chave_nfe) - 1 :]
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)
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'
modelo = modelo.text if modelo is not None else "55"
if sign:
signer = Assinatura(certificado.pfx, certificado.password)
if method == 'NfeInutilizacao':
xml_send = signer.assina_xml(xmlElem_send, kwargs['obj']['id'])
if method == 'NfeAutorizacao':
if method == "NfeInutilizacao":
xml_send = signer.assina_xml(xmlElem_send, kwargs["obj"]["id"])
if method == "NfeAutorizacao":
xml_send = signer.assina_xml(
xmlElem_send, kwargs['NFes'][0]['infNFe']['Id'])
elif method == 'RecepcaoEvento':
xml_send = signer.assina_xml(
xmlElem_send, kwargs['eventos'][0]['Id'])
elif method == 'RecepcaoEventoManifesto':
xmlElem_send, kwargs["NFes"][0]["infNFe"]["Id"]
)
elif method == "RecepcaoEvento":
xml_send = signer.assina_xml(xmlElem_send, kwargs["eventos"][0]["Id"])
elif method == "RecepcaoEventoManifesto":
xml_send = signer.assina_xml(
xmlElem_send, kwargs['manifesto']['identificador'])
xmlElem_send, kwargs["manifesto"]["identificador"]
)
else:
xml_send = etree.tostring(xmlElem_send, encoding=str)
@ -175,8 +179,7 @@ def gerar_qrcode(id_csc: int, csc: str, xml_send: str, cert = False) -> str:
return etree.tostring(xml)
def _get_session(certificado):
cert, key = extract_cert_and_key_from_pfx(
certificado.pfx, certificado.password)
cert, key = extract_cert_and_key_from_pfx(certificado.pfx, certificado.password)
cert, key = save_cert_key(cert, key)
session = Session()
@ -188,19 +191,23 @@ def _get_session(certificado):
def _get_client(base_url, transport):
client = Client(base_url, transport=transport)
port = next(iter(client.wsdl.port_types))
first_operation = [x for x in iter(
client.wsdl.port_types[port].operations) if "zip" not in x.lower()][0]
first_operation = [
x
for x in iter(client.wsdl.port_types[port].operations)
if "zip" not in x.lower()
][0]
return first_operation, client
def _send(certificado, method, **kwargs):
xml_send = kwargs["xml"]
base_url = localizar_url(
method, kwargs['estado'], kwargs['modelo'], kwargs['ambiente'])
method, kwargs["estado"], kwargs["modelo"], kwargs["ambiente"]
)
session = _get_session(certificado)
patch = has_patch(kwargs['estado'], method)
patch = has_patch(kwargs["estado"], method)
if patch:
return patch(session, xml_send, kwargs['ambiente'])
return patch(session, xml_send, kwargs["ambiente"])
transport = Transport(session=session)
first_op, client = _get_client(base_url, transport)
return _send_zeep(first_op, client, xml_send)
@ -212,140 +219,138 @@ def _send_zeep(first_operation, client, xml_send):
namespaceNFe = xml.find(".//{http://www.portalfiscal.inf.br/nfe}NFe")
if namespaceNFe is not None:
namespaceNFe.set('xmlns', 'http://www.portalfiscal.inf.br/nfe')
namespaceNFe.set("xmlns", "http://www.portalfiscal.inf.br/nfe")
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
with client.settings(raw_response=True):
response = client.service[first_operation](xml)
response, obj = sanitize_response(response.text)
return {
'sent_xml': xml_send,
'received_xml': response,
'object': obj.Body.getchildren()[0]
"sent_xml": xml_send,
"received_xml": response,
"object": obj.Body.getchildren()[0],
}
def xml_autorizar_nfe(certificado, **kwargs):
_generate_nfe_id(**kwargs)
return _render(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)
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)
return _render(certificado, "NfeRetAutorizacao", False, **kwargs)
def retorno_autorizar_nfe(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_retorno_autorizar_nfe(certificado, **kwargs)
return _send(certificado, 'NfeRetAutorizacao', **kwargs)
kwargs["xml"] = xml_retorno_autorizar_nfe(certificado, **kwargs)
return _send(certificado, "NfeRetAutorizacao", **kwargs)
def xml_recepcao_evento_cancelamento(certificado, **kwargs): # Assinar
return _render(certificado, 'RecepcaoEvento', True, **kwargs)
return _render(certificado, "RecepcaoEvento", True, **kwargs)
def recepcao_evento_cancelamento(certificado, **kwargs): # Assinar
if "xml" not in kwargs:
kwargs['xml'] = xml_recepcao_evento_cancelamento(certificado, **kwargs)
return _send(certificado, 'RecepcaoEvento', **kwargs)
kwargs["xml"] = xml_recepcao_evento_cancelamento(certificado, **kwargs)
return _send(certificado, "RecepcaoEvento", **kwargs)
def xml_inutilizar_nfe(certificado, **kwargs):
return _render(certificado, 'NfeInutilizacao', True, **kwargs)
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)
kwargs["xml"] = xml_inutilizar_nfe(certificado, **kwargs)
return _send(certificado, "NfeInutilizacao", **kwargs)
def xml_consultar_protocolo_nfe(certificado, **kwargs):
return _render(certificado, 'NfeConsultaProtocolo', False, **kwargs)
return _render(certificado, "NfeConsultaProtocolo", False, **kwargs)
def consultar_protocolo_nfe(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_consultar_protocolo_nfe(certificado, **kwargs)
return _send(certificado, 'NfeConsultaProtocolo', **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)
return _render(certificado, "NfeStatusServico", False, **kwargs)
def nfe_status_servico(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_nfe_status_servico(certificado, **kwargs)
return _send(certificado, 'NfeStatusServico', **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)
return _render(certificado, "NfeConsultaCadastro", False, **kwargs)
def consulta_cadastro(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_consulta_cadastro(certificado, **kwargs)
kwargs['modelo'] = '55'
return _send(certificado, 'NfeConsultaCadastro', **kwargs)
kwargs["xml"] = xml_consulta_cadastro(certificado, **kwargs)
kwargs["modelo"] = "55"
return _send(certificado, "NfeConsultaCadastro", **kwargs)
def xml_recepcao_evento_carta_correcao(certificado, **kwargs): # Assinar
return _render(certificado, 'RecepcaoEvento', True, **kwargs)
return _render(certificado, "RecepcaoEvento", True, **kwargs)
def recepcao_evento_carta_correcao(certificado, **kwargs): # Assinar
if "xml" not in kwargs:
kwargs['xml'] = xml_recepcao_evento_carta_correcao(
certificado, **kwargs)
return _send(certificado, 'RecepcaoEvento', **kwargs)
kwargs["xml"] = xml_recepcao_evento_carta_correcao(certificado, **kwargs)
return _send(certificado, "RecepcaoEvento", **kwargs)
def xml_recepcao_evento_manifesto(certificado, **kwargs): # Assinar
return _render(certificado, 'RecepcaoEvento', True, **kwargs)
return _render(certificado, "RecepcaoEvento", True, **kwargs)
def recepcao_evento_manifesto(certificado, **kwargs): # Assinar
if "xml" not in kwargs:
kwargs['xml'] = xml_recepcao_evento_manifesto(certificado, **kwargs)
return _send(certificado, 'RecepcaoEvento', **kwargs)
kwargs["xml"] = xml_recepcao_evento_manifesto(certificado, **kwargs)
return _send(certificado, "RecepcaoEvento", **kwargs)
def xml_consulta_distribuicao_nfe(certificado, **kwargs): # Assinar
return _render(certificado, 'NFeDistribuicaoDFe', False, **kwargs)
return _render(certificado, "NFeDistribuicaoDFe", False, **kwargs)
def consulta_distribuicao_nfe(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_consulta_distribuicao_nfe(certificado, **kwargs)
kwargs["xml"] = xml_consulta_distribuicao_nfe(certificado, **kwargs)
return _send_v310(certificado, **kwargs)
def xml_download_nfe(certificado, **kwargs): # Assinar
return _render(certificado, 'NFeDistribuicaoDFe', False, **kwargs)
return _render(certificado, "NFeDistribuicaoDFe", False, **kwargs)
def download_nfe(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_download_nfe(certificado, **kwargs)
kwargs["xml"] = xml_download_nfe(certificado, **kwargs)
return _send_v310(certificado, **kwargs)
def _send_v310(certificado, **kwargs):
xml_send = kwargs["xml"]
base_url = localizar_url(
'NFeDistribuicaoDFe', kwargs['estado'], kwargs['modelo'],
kwargs['ambiente'])
"NFeDistribuicaoDFe", kwargs["estado"], kwargs["modelo"], kwargs["ambiente"]
)
cert, key = extract_cert_and_key_from_pfx(
certificado.pfx, certificado.password)
cert, key = extract_cert_and_key_from_pfx(certificado.pfx, certificado.password)
cert, key = save_cert_key(cert, key)
session = Session()
@ -354,16 +359,20 @@ def _send_v310(certificado, **kwargs):
transport = Transport(session=session)
xml = etree.fromstring(xml_send)
xml_um = etree.fromstring('<nfeCabecMsg xmlns="http://www.portalfiscal.inf.br/nfe/wsdl/"><cUF>AN</cUF><versaoDados>1.00</versaoDados></nfeCabecMsg>')
xml_um = etree.fromstring(
'<nfeCabecMsg xmlns="http://www.portalfiscal.inf.br/nfe/wsdl/"><cUF>AN</cUF><versaoDados>1.00</versaoDados></nfeCabecMsg>'
)
client = Client(base_url, transport=transport)
port = next(iter(client.wsdl.port_types))
first_operation = next(iter(client.wsdl.port_types[port].operations))
with client.settings(raw_response=True):
response = client.service[first_operation](nfeDadosMsg=xml, _soapheaders=[xml_um])
response = client.service[first_operation](
nfeDadosMsg=xml, _soapheaders=[xml_um]
)
response, obj = sanitize_response(response.text)
return {
'sent_xml': xml_send,
'received_xml': response,
'object': obj.Body.nfeDistDFeInteresseResponse.nfeDistDFeInteresseResult
"sent_xml": xml_send,
"received_xml": response,
"object": obj.Body.nfeDistDFeInteresseResponse.nfeDistDFeInteresseResult,
}

27
pytrustnfe/nfe/assinatura.py

@ -9,12 +9,11 @@ from signxml import XMLSigner
class Assinatura(object):
def __init__(self, arquivo, senha):
self.arquivo = arquivo
self.senha = senha
def assina_xml(self, xml_element, reference):
def assina_xml(self, xml_element, reference, getchildren=False):
cert, key = extract_cert_and_key_from_pfx(self.arquivo, self.senha)
for element in xml_element.iter("*"):
@ -22,24 +21,30 @@ class Assinatura(object):
element.text = None
signer = XMLSigner(
method=signxml.methods.enveloped, signature_algorithm="rsa-sha1",
digest_algorithm='sha1',
c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315')
method=signxml.methods.enveloped,
signature_algorithm="rsa-sha1",
digest_algorithm="sha1",
c14n_algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
)
ns = {}
ns[None] = signer.namespaces['ds']
ns[None] = signer.namespaces["ds"]
signer.namespaces = ns
ref_uri = ('#%s' % reference) if reference else None
ref_uri = ("#%s" % reference) if reference else None
signed_root = signer.sign(
xml_element, key=key.encode(), cert=cert.encode(),
reference_uri=ref_uri)
xml_element, key=key.encode(), cert=cert.encode(), reference_uri=ref_uri
)
if reference:
element_signed = signed_root.find(".//*[@Id='%s']" % reference)
signature = signed_root.find(
".//{http://www.w3.org/2000/09/xmldsig#}Signature")
".//{http://www.w3.org/2000/09/xmldsig#}Signature"
)
if element_signed is not None and signature is not None:
if getchildren and element_signed is not None and signature is not None:
child = element_signed.getchildren()
child.append(signature)
elif element_signed is not None and signature is not None:
parent = element_signed.getparent()
parent.append(signature)
return etree.tostring(signed_root, encoding=str)

352
pytrustnfe/nfe/danfce.py

@ -21,25 +21,34 @@ from reportlab.lib.styles import ParagraphStyle
def format_cnpj_cpf(value):
if len(value) < 12: # CPF
cValue = '%s.%s.%s-%s' % (value[:-8], value[-8:-5],
value[-5:-2], value[-2:])
cValue = "%s.%s.%s-%s" % (value[:-8], value[-8:-5], value[-5:-2], value[-2:])
else:
cValue = '%s.%s.%s/%s-%s' % (value[:-12], value[-12:-9],
value[-9:-6], value[-6:-2], value[-2:])
cValue = "%s.%s.%s/%s-%s" % (
value[:-12],
value[-12:-9],
value[-9:-6],
value[-6:-2],
value[-2:],
)
return cValue
def getdateUTC(cDateUTC):
cDt = cDateUTC[0:10].split('-')
cDt = cDateUTC[0:10].split("-")
cDt.reverse()
return '/'.join(cDt), cDateUTC[11:16]
return "/".join(cDt), cDateUTC[11:16]
def format_number(cNumber, precision=0, group_sep='.', decimal_sep=','):
def format_number(cNumber, precision=0, group_sep=".", decimal_sep=","):
if cNumber:
number = float(cNumber)
return ("{:,." + str(precision) + "f}").format(number).\
replace(",", "X").replace(".", ",").replace("X", ".")
return (
("{:,." + str(precision) + "f}")
.format(number)
.replace(",", "X")
.replace(".", ",")
.replace("X", ".")
)
return ""
@ -48,7 +57,7 @@ def tagtext(oNode=None, cTag=None):
xpath = ".//{http://www.portalfiscal.inf.br/nfe}%s" % (cTag)
cText = oNode.find(xpath).text
except:
cText = ''
cText = ""
return cText
@ -60,20 +69,15 @@ def get_image(path, width=1 * cm):
def format_telefone(telefone):
telefone = re.sub('[^0-9]', '', telefone)
telefone = re.sub("[^0-9]", "", telefone)
if len(telefone) == 10:
telefone = '(%s) %s-%s' % (telefone[0:2],
telefone[2:6],
telefone[6:])
telefone = "(%s) %s-%s" % (telefone[0:2], telefone[2:6], telefone[6:])
elif len(telefone) == 11:
telefone = '(%s) %s-%s' % (telefone[0:2],
telefone[2:7],
telefone[7:])
telefone = "(%s) %s-%s" % (telefone[0:2], telefone[2:7], telefone[7:])
return telefone
class danfce(object):
def __init__(self, list_xml, logo=None, timezone=None):
dir_fonts = os.path.dirname(os.path.realpath(__file__))
pdfmetrics.registerFont(
@ -85,7 +89,7 @@ class danfce(object):
'NimbusSanL-Regu',
os.path.join(dir_fonts, 'fonts/NimbusSanL Regular.ttf')))
self.current_font_size = 7
self.current_font_name = 'NimbusSanL-Regu'
self.current_font_name = "NimbusSanL-Regu"
self.max_height = 840
self.min_height = 1
@ -95,8 +99,8 @@ class danfce(object):
self.oPDF_IO = BytesIO()
self.canvas = canvas.Canvas(self.oPDF_IO, pagesize=(7.2 * cm, 30 * cm))
self.canvas.setTitle('DANFCE')
self.canvas.setLineWidth(.5)
self.canvas.setTitle("DANFCE")
self.canvas.setLineWidth(0.5)
self.canvas.setFont(self.current_font_name, self.current_font_size)
self.list_xml = list_xml
@ -109,50 +113,69 @@ class danfce(object):
elem_emit = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}emit")
# Razão Social emitente
nomeEmpresa = tagtext(oNode=elem_emit, cTag='xFant')
nomeEmpresa = tagtext(oNode=elem_emit, cTag="xFant")
self.drawTitle(nomeEmpresa, 10)
if self.logo:
img = get_image(self.logo, width=10 * mm)
img.drawOn(self.canvas, 5, 830)
cEnd = tagtext(oNode=elem_emit, cTag="xNome") + '<br />'
cEnd += "CNPJ: %s " % (format_cnpj_cpf(
tagtext(oNode=elem_emit, cTag='CNPJ')))
cEnd += "IE: %s" % (tagtext(oNode=elem_emit, cTag="IE")) + '<br />'
cEnd += tagtext(oNode=elem_emit, cTag='xLgr') + ', ' + tagtext(
oNode=elem_emit, cTag='nro') + ' - '
cEnd += tagtext(oNode=elem_emit, cTag='xBairro') + '<br />' + tagtext(
oNode=elem_emit, cTag='xMun') + ' - '
cEnd += tagtext(oNode=elem_emit, cTag='UF') + ' - ' + tagtext(
oNode=elem_emit, cTag='CEP') + '<br />'
cEnd += 'Fone: ' + format_telefone(tagtext(
oNode=elem_emit, cTag='fone'))
cEnd = tagtext(oNode=elem_emit, cTag="xNome") + "<br />"
cEnd += "CNPJ: %s " % (format_cnpj_cpf(tagtext(oNode=elem_emit, cTag="CNPJ")))
cEnd += "IE: %s" % (tagtext(oNode=elem_emit, cTag="IE")) + "<br />"
cEnd += (
tagtext(oNode=elem_emit, cTag="xLgr")
+ ", "
+ tagtext(oNode=elem_emit, cTag="nro")
+ " - "
)
cEnd += (
tagtext(oNode=elem_emit, cTag="xBairro")
+ "<br />"
+ tagtext(oNode=elem_emit, cTag="xMun")
+ " - "
)
cEnd += (
tagtext(oNode=elem_emit, cTag="UF")
+ " - "
+ tagtext(oNode=elem_emit, cTag="CEP")
+ "<br />"
)
cEnd += "Fone: " + format_telefone(tagtext(oNode=elem_emit, cTag="fone"))
self._drawCenteredParagraph(cEnd)
self.drawLine()
def danfce_information(self, oXML=None):
el_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide")
tipo_emissao = tagtext(oNode=el_ide, cTag='tpEmis')
tipo_emissao = tagtext(oNode=el_ide, cTag="tpEmis")
if tipo_emissao in ("5", "9"):
self.current_height -= 5
self.drawTitle("EMITIDA EM CONTINGÊNCIA", 9, "NimbusSanL-Bold")
self.drawTitle("Pendente de autorização", 7, "NimbusSanL-Bold")
self.drawLine()
else:
self.drawTitle(
"DANFE NFC-e - Documento Auxiliar da Nota Fiscal de",
7, 'NimbusSanL-Bold')
7,
"NimbusSanL-Bold",
)
self.drawTitle("Consumidor Eletrônica", 7, 'NimbusSanL-Bold')
self.drawTitle("Consumidor Eletrônica", 7, "NimbusSanL-Bold")
self.drawString(
"NFC-e não permite aproveitamento de crédito de ICMS", True)
if tipo_emissao in ('5', '9'):
self.current_height -= 5
self.drawTitle("EMITIDA EM CONTINGÊNCIA",9, 'NimbusSanL-Bold')
self.drawTitle("Pendente de autorização", 7, 'NimbusSanL-Bold')
self.drawString("NFC-e não permite aproveitamento de crédito de ICMS", True)
self.drawLine()
def produtos(self, oXML=None, el_det=None, oPaginator=None,
list_desc=None, list_cod_prod=None):
def produtos(
self,
oXML=None,
el_det=None,
oPaginator=None,
list_desc=None,
list_cod_prod=None,
):
rows = [['Cód', 'Descrição', 'Qtde', 'Un', 'Unit.', 'Total']]
rows = [["Cód", "Descrição", "Qtde", "Un", "Unit.", "Total"]]
colWidths = (25, 90, 15, 15, 25, 25)
rowHeights = [7]
@ -161,17 +184,14 @@ class danfce(object):
item = el_det[id]
el_prod = item.find(".//{http://www.portalfiscal.inf.br/nfe}prod")
cod = tagtext(oNode=el_prod, cTag='cProd')
descricao = tagtext(oNode=el_prod, cTag='xProd')
descricao = (descricao[:20] + '..') if len(descricao) > 20 else descricao
Un = tagtext(oNode=el_prod, cTag='uCom')
cod = tagtext(oNode=el_prod, cTag="cProd")
descricao = tagtext(oNode=el_prod, cTag="xProd")
descricao = (descricao[:20] + "..") if len(descricao) > 20 else descricao
Un = tagtext(oNode=el_prod, cTag="uCom")
Un = (Un[:2]) if len(Un) > 2 else Un
qtde = format_number(tagtext(oNode=el_prod, cTag='qCom'),
precision=2)
vl_unit = format_number(tagtext(oNode=el_prod, cTag='vUnCom'),
precision=2)
vl_total = format_number(
tagtext(oNode=el_prod, cTag='vProd'), precision=2)
qtde = format_number(tagtext(oNode=el_prod, cTag="qCom"), precision=2)
vl_unit = format_number(tagtext(oNode=el_prod, cTag="vUnCom"), precision=2)
vl_total = format_number(tagtext(oNode=el_prod, cTag="vProd"), precision=2)
new_row = [cod, descricao, qtde, Un, vl_unit, vl_total]
@ -182,54 +202,57 @@ class danfce(object):
def _draw_product_table(self, rows, colWidths, rowHeights):
table = Table(rows, colWidths, tuple(rowHeights))
table.setStyle(TableStyle([
('FONTSIZE', (0, 0), (-1, -1), 7),
('FONT', (0, 1), (-1, -1), 'NimbusSanL-Regu'),
('FONT', (0, 0), (-1, 0), 'NimbusSanL-Bold'),
('ALIGN', (0, 0), (-1, 0), "LEFT"),
('ALIGN', (1, 0), (-1, 0), "LEFT"),
('ALIGN', (2, 0), (-1, 0), "CENTER"),
('ALIGN', (3, 0), (-1, 0), "CENTER"),
('ALIGN', (0, 1), (-1, -1), "LEFT"),
('ALIGN', (1, 1), (-1, -1), "LEFT"),
('ALIGN', (2, 1), (-1, -1), "CENTER"),
('ALIGN', (3, 1), (-1, -1), "CENTER"),
]))
table.setStyle(
TableStyle(
[
("FONTSIZE", (0, 0), (-1, -1), 7),
("FONT", (0, 1), (-1, -1), "NimbusSanL-Regu"),
("FONT", (0, 0), (-1, 0), "NimbusSanL-Bold"),
("ALIGN", (0, 0), (-1, 0), "LEFT"),
("ALIGN", (1, 0), (-1, 0), "LEFT"),
("ALIGN", (2, 0), (-1, 0), "CENTER"),
("ALIGN", (3, 0), (-1, 0), "CENTER"),
("ALIGN", (0, 1), (-1, -1), "LEFT"),
("ALIGN", (1, 1), (-1, -1), "LEFT"),
("ALIGN", (2, 1), (-1, -1), "CENTER"),
("ALIGN", (3, 1), (-1, -1), "CENTER"),
]
)
)
w, h = table.wrapOn(self.canvas, 200, 450)
table.drawOn(self.canvas, 0, self.current_height - (h * 1.2))
self.current_height -= (h * 1.1)
self.current_height -= h * 1.1
def totais(self, oXML=None):
# Impostos
el_total = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}total")
total_tributo = format_number(
tagtext(oNode=el_total, cTag='vTotTrib'), precision=2)
valor_total = format_number(
tagtext(oNode=el_total, cTag='vProd'), precision=2)
desconto = format_number(
tagtext(oNode=el_total, cTag='vDesc'), precision=2)
valor_a_pagar = format_number(
tagtext(oNode=el_total, cTag='vNF'), precision=2)
tagtext(oNode=el_total, cTag="vTotTrib"), precision=2
)
valor_total = format_number(tagtext(oNode=el_total, cTag="vProd"), precision=2)
desconto = format_number(tagtext(oNode=el_total, cTag="vDesc"), precision=2)
valor_a_pagar = format_number(tagtext(oNode=el_total, cTag="vNF"), precision=2)
el_pag = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}pag")
troco = tagtext(oNode=el_pag, cTag="vTroco")
payment_method_list = {'01': 'Dinheiro',
'02': 'Cheque',
'03': 'Cartão de Crédito',
'04': 'Cartão de Débito',
payment_method_list = {
"01": "Dinheiro",
"02": "Cheque",
"03": "Cartão de Crédito",
"04": "Cartão de Débito",
"05": "Crédito Loja",
'10': 'Vale Alimentação',
'11': 'Vale Refeição',
'12': 'Vale Presente',
'13': 'Vale Combustível',
'14': 'Duplicata Mercantil',
'15': 'Boleto Bancario',
'90': 'Sem Pagamento',
'99': 'Outros'}
quant_produtos = len(oXML.findall(
".//{http://www.portalfiscal.inf.br/nfe}det"))
"10": "Vale Alimentação",
"11": "Vale Refeição",
"12": "Vale Presente",
"13": "Vale Combustível",
"14": "Duplicata Mercantil",
"15": "Boleto Bancario",
"90": "Sem Pagamento",
"99": "Outros",
}
quant_produtos = len(oXML.findall(".//{http://www.portalfiscal.inf.br/nfe}det"))
payment_methods = []
for pagId, item in enumerate(el_pag):
@ -244,13 +267,13 @@ class danfce(object):
payment_methods.append(payment)
values = {
'quantidade_itens': quant_produtos,
'total_tributo': total_tributo,
'valor_total': valor_total,
'desconto': desconto,
'valor_a_pagar': valor_a_pagar,
'formas_de_pagamento': payment_methods,
'troco': troco,
"quantidade_itens": quant_produtos,
"total_tributo": total_tributo,
"valor_total": valor_total,
"desconto": desconto,
"valor_a_pagar": valor_a_pagar,
"formas_de_pagamento": payment_methods,
"troco": troco,
}
self.draw_totals_table(values)
@ -260,26 +283,30 @@ class danfce(object):
def draw_totals_table(self, values):
rowHeights = [7, 7, 7, 7, 7]
data = [
['QTD.TOTAL DE ITENS', values['quantidade_itens']],
['VALOR TOTAL R$', values['valor_total']],
['DESCONTO R$', values['desconto']],
['VALOR A PAGAR R$', values['valor_a_pagar']],
['FORMA DE PAGAMENTO', 'VALOR PAGO R$'],
["QTD.TOTAL DE ITENS", values["quantidade_itens"]],
["VALOR TOTAL R$", values["valor_total"]],
["DESCONTO R$", values["desconto"]],
["VALOR A PAGAR R$", values["valor_a_pagar"]],
["FORMA DE PAGAMENTO", "VALOR PAGO R$"],
]
for item in values['formas_de_pagamento']:
for item in values["formas_de_pagamento"]:
data.append([item[0], item[1]])
rowHeights.append(7)
data.append(['TROCO', format_number(values['troco'], precision=2)])
data.append(["TROCO", format_number(values["troco"], precision=2)])
rowHeights.append(7)
table2 = Table(data, colWidths=(150, 50), rowHeights=tuple(rowHeights))
table2.setStyle(TableStyle([
('FONTSIZE', (0, 0), (-1, -1), 7),
('FONT', (0, 0), (1, -1), 'NimbusSanL-Regu'),
('FONT', (0, 4), (1, 4), 'NimbusSanL-Bold'),
('ALIGN', (1, 0), (1, -1), "RIGHT")
]))
table2.setStyle(
TableStyle(
[
("FONTSIZE", (0, 0), (-1, -1), 7),
("FONT", (0, 0), (1, -1), "NimbusSanL-Regu"),
("FONT", (0, 4), (1, 4), "NimbusSanL-Bold"),
("ALIGN", (1, 0), (1, -1), "RIGHT"),
]
)
)
w, h = table2.wrapOn(self.canvas, 200, 450)
table2.drawOn(self.canvas, 0, self.current_height - (h * 1.1))
self.current_height -= h
@ -289,27 +316,27 @@ class danfce(object):
# n nfce, serie e data de solicitacao
el_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide")
el_NFeSupl = oXML.find(
".//{http://www.portalfiscal.inf.br/nfe}infNFeSupl")
el_NFeSupl = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}infNFeSupl")
el_dest = el_infNFe.find(".//{http://www.portalfiscal.inf.br/nfe}dest")
# chave, n protocolo, data autorizacao
el_prot_nfe = oXML.find(
".//{http://www.portalfiscal.inf.br/nfe}protNFe")
el_prot_nfe = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}protNFe")
el_infAdic = oXML.find(
".//{http://www.portalfiscal.inf.br/nfe}infAdic")
el_infAdic = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}infAdic")
url_chave = tagtext(oNode=el_NFeSupl, cTag='urlChave')
url_chave = tagtext(oNode=el_NFeSupl, cTag="urlChave")
access_key = tagtext(oNode=el_prot_nfe, cTag="chNFe")
frase_chave_acesso = 'Consulte pela Chave de Acesso em:<br />\
%s<br />%s' % (url_chave, access_key)
frase_chave_acesso = (
"Consulte pela Chave de Acesso em:<br />\
%s<br />%s"
% (url_chave, access_key)
)
qrcode = tagtext(oNode=el_NFeSupl, cTag='qrCode')
qrcode = tagtext(oNode=el_NFeSupl, cTag="qrCode")
cnpj = tagtext(oNode=el_dest, cTag='CNPJ')
cpf = tagtext(oNode=el_dest, cTag='CPF')
cnpj = tagtext(oNode=el_dest, cTag="CNPJ")
cpf = tagtext(oNode=el_dest, cTag="CPF")
if cnpj:
cnpj_cpf = format_cnpj_cpf(cnpj)
cnpj_cpf = "CONSUMIDOR CNPJ: %s" % (cnpj)
@ -320,45 +347,53 @@ class danfce(object):
cnpj_cpf = u"CONSUMIDOR NÃO IDENTIFICADO"
nNFC = tagtext(oNode=el_ide, cTag="nNF")
serie = tagtext(oNode=el_ide, cTag='serie')
serie = tagtext(oNode=el_ide, cTag="serie")
dataSolicitacao = getdateUTC(tagtext(oNode=el_ide, cTag="dhEmi"))
dataSolicitacao = dataSolicitacao[0] + " " + dataSolicitacao[1]
text = u"%s <br />%s <br />NFC-e nº%s Série %s %s<br />" % (
frase_chave_acesso, cnpj_cpf, nNFC, serie, dataSolicitacao)
frase_chave_acesso,
cnpj_cpf,
nNFC,
serie,
dataSolicitacao,
)
self._drawCenteredParagraph(text)
tipo_emissao = tagtext(oNode=el_ide, cTag='tpEmis')
if tipo_emissao in ('5', '9'):
tipo_emissao = tagtext(oNode=el_ide, cTag="tpEmis")
if tipo_emissao in ("5", "9"):
self.current_height -= 8
self.drawTitle("EMITIDA EM CONTINGÊNCIA",9, 'NimbusSanL-Bold')
self.drawTitle("Pendente de autorização - Via Consumidor", 7, 'NimbusSanL-Bold')
self.drawTitle("EMITIDA EM CONTINGÊNCIA", 9, "NimbusSanL-Bold")
self.drawTitle(
"Pendente de autorização - Via Consumidor", 7, "NimbusSanL-Bold"
)
else:
numProtocolo = tagtext(oNode=el_prot_nfe, cTag="nProt")
dataAutorizacao = getdateUTC(tagtext(oNode=el_prot_nfe,
cTag='dhRecbto'))
dataAutorizacao = getdateUTC(tagtext(oNode=el_prot_nfe, cTag="dhRecbto"))
dataAutorizacao = dataAutorizacao[0] + " " + dataAutorizacao[1]
text = "Protocolo de autorização: %s<br />Data de autorização %s<br />" % (
numProtocolo, dataAutorizacao)
numProtocolo,
dataAutorizacao,
)
self._drawCenteredParagraph(text)
self.draw_qr_code(qrcode)
infAdFisco = tagtext(oNode=el_infAdic, cTag='infAdFisco')
infAdFisco = tagtext(oNode=el_infAdic, cTag="infAdFisco")
self._drawCenteredParagraph(infAdFisco)
infCpl = tagtext(oNode=el_infAdic, cTag='infCpl')
infCpl = tagtext(oNode=el_infAdic, cTag="infCpl")
self._drawCenteredParagraph(infCpl)
def _drawCenteredParagraph(self, text):
style = ParagraphStyle(
name='Normal',
fontName='NimbusSanL-Regu',
name="Normal",
fontName="NimbusSanL-Regu",
fontSize=7,
alignment=TA_CENTER,
leading=7,
@ -367,27 +402,28 @@ class danfce(object):
paragraph = Paragraph(text, style=style)
w, h = paragraph.wrapOn(self.canvas, 180, 300)
paragraph.drawOn(self.canvas, 10, self.current_height - h)
self.current_height -= (h*1.1)
self.current_height -= h * 1.1
def drawString(self, string, centered=False):
if centered:
self.canvas.drawCentredString(
self.max_width / 2, self.current_height, string)
self.max_width / 2, self.current_height, string
)
self.current_height -= self.current_font_size
else:
self.canvas.drawString(self.min_width, self.current_height, string)
self.current_height -= self.current_font_size
def drawTitle(self, string, size, font='NimbusSanL-Regu'):
def drawTitle(self, string, size, font="NimbusSanL-Regu"):
self.canvas.setFont(font, size)
self.canvas.drawCentredString(
self.max_width / 2, self.current_height, string)
self.canvas.drawCentredString(self.max_width / 2, self.current_height, string)
self.current_height -= self.current_font_size
self.canvas.setFont(self.current_font_name, self.current_font_size)
def drawLine(self):
self.canvas.line(self.min_width, self.current_height,
self.max_width, self.current_height)
self.canvas.line(
self.min_width, self.current_height, self.max_width, self.current_height
)
self.current_height -= self.current_font_size
def draw_qr_code(self, string):
@ -404,8 +440,7 @@ class danfce(object):
def nfce_generate(self):
for oXML in self.list_xml:
oXML_cobr = oXML.find(
".//{http://www.portalfiscal.inf.br/nfe}cobr")
oXML_cobr = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}cobr")
self.NrPages = 1
self.Page = 1
@ -421,17 +456,17 @@ class danfce(object):
list_cod_prod = []
nPg = 0
for nId, item in enumerate(el_det):
el_prod = item.find(
".//{http://www.portalfiscal.inf.br/nfe}prod")
el_prod = item.find(".//{http://www.portalfiscal.inf.br/nfe}prod")
infAdProd = item.find(
".//{http://www.portalfiscal.inf.br/nfe}infAdProd")
".//{http://www.portalfiscal.inf.br/nfe}infAdProd"
)
list_ = wrap(tagtext(oNode=el_prod, cTag='xProd'), 56)
list_ = wrap(tagtext(oNode=el_prod, cTag="xProd"), 56)
if infAdProd is not None:
list_.extend(wrap(infAdProd.text, 56))
list_desc.append(list_)
list_cProd = wrap(tagtext(oNode=el_prod, cTag='cProd'), 14)
list_cProd = wrap(tagtext(oNode=el_prod, cTag="cProd"), 14)
list_cod_prod.append(list_cProd)
# Nr linhas necessárias p/ descrição item
@ -454,8 +489,13 @@ class danfce(object):
# self.destinatario(oXML=oXML)
self.danfce_information(oXML=oXML)
self.produtos(oXML=oXML, el_det=el_det, oPaginator=oPaginator[0],
list_desc=list_desc, list_cod_prod=list_cod_prod)
self.produtos(
oXML=oXML,
el_det=el_det,
oPaginator=oPaginator[0],
list_desc=list_desc,
list_cod_prod=list_cod_prod,
)
self.drawLine()
@ -469,9 +509,13 @@ class danfce(object):
self.newpage()
self.ide_emit(oXML=oXML)
# self.destinatario(oXML=oXML)
self.produtos(oXML=oXML, el_det=el_det, oPaginator=oPag,
self.produtos(
oXML=oXML,
el_det=el_det,
oPaginator=oPag,
list_desc=list_desc,
list_cod_prod=list_cod_prod)
list_cod_prod=list_cod_prod,
)
self.totais(oXML=oXML)
self.inf_authentication(oXML=oXML)

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

34
pytrustnfe/nfe/patch.py

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

43
pytrustnfe/nfse/aparecida/__init__.py

@ -13,24 +13,23 @@ from pytrustnfe.nfe.assinatura import Assinatura
def _render(certificado, method, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates')
xml_send = render_xml(path, '%s.xml' % method, True, **kwargs)
path = os.path.join(os.path.dirname(__file__), "templates")
xml_send = render_xml(path, "%s.xml" % method, True, **kwargs)
reference = ''
reference = ""
signer = Assinatura(certificado.pfx, certificado.password)
xml_send = signer.assina_xml(xml_send, reference)
return xml_send
def _send(certificado, method, **kwargs):
base_url = ''
if kwargs['ambiente'] == 'producao':
base_url = 'https://aparecida.siltecnologia.com.br/tbw/services/Abrasf10?wsdl'
base_url = ""
if kwargs["ambiente"] == "producao":
base_url = "https://aparecida.siltecnologia.com.br/tbw/services/Abrasf10?wsdl"
else:
base_url = 'https://aparecida.siltecnologia.com.br/tbwhomologacao/services/Abrasf10?wsdl'
base_url = "https://aparecida.siltecnologia.com.br/tbwhomologacao/services/Abrasf10?wsdl"
cert, key = extract_cert_and_key_from_pfx(
certificado.pfx, certificado.password)
cert, key = extract_cert_and_key_from_pfx(certificado.pfx, certificado.password)
cert, key = save_cert_key(cert, key)
disable_warnings()
@ -41,41 +40,37 @@ def _send(certificado, method, **kwargs):
client = Client(base_url, transport=transport)
xml_send = kwargs['xml']
xml_send = kwargs["xml"]
response = client.service[method](xml_send)
response, obj = sanitize_response(response)
return {
'sent_xml': xml_send,
'received_xml': response,
'object': obj
}
return {"sent_xml": xml_send, "received_xml": response, "object": obj}
def xml_recepcionar_lote_rps(certificado, **kwargs):
return _render(certificado, 'recepcionarLoteRps', **kwargs)
return _render(certificado, "recepcionarLoteRps", **kwargs)
def recepcionar_lote_rps(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_recepcionar_lote_rps(certificado, **kwargs)
return _send(certificado, 'recepcionarLoteRps', **kwargs)
kwargs["xml"] = xml_recepcionar_lote_rps(certificado, **kwargs)
return _send(certificado, "recepcionarLoteRps", **kwargs)
def xml_consultar_lote_rps(certificado, **kwargs):
return _render(certificado, 'consultarLoteRps', **kwargs)
return _render(certificado, "consultarLoteRps", **kwargs)
def consultar_lote_rps(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_consultar_lote_rps(certificado, **kwargs)
return _send(certificado, 'consultarLoteRps', **kwargs)
kwargs["xml"] = xml_consultar_lote_rps(certificado, **kwargs)
return _send(certificado, "consultarLoteRps", **kwargs)
def xml_cancelar_nfse(certificado, **kwargs):
return _render(certificado, 'cancelarNfse', **kwargs)
return _render(certificado, "cancelarNfse", **kwargs)
def cancelar_nfse(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_cancelar_nfse(certificado, **kwargs)
return _send(certificado, 'cancelarNfse', **kwargs)
kwargs["xml"] = xml_cancelar_nfse(certificado, **kwargs)
return _send(certificado, "cancelarNfse", **kwargs)

23
pytrustnfe/nfse/assinatura.py

@ -8,11 +8,10 @@ import os.path
consts = xmlsec.constants
NAMESPACE_SIG = 'http://www.w3.org/2000/09/xmldsig#'
NAMESPACE_SIG = "http://www.w3.org/2000/09/xmldsig#"
class Assinatura(object):
def __init__(self, cert_pem, private_key, password):
self.cert_pem = cert_pem
self.private_key = private_key
@ -20,22 +19,27 @@ class Assinatura(object):
def _checar_certificado(self):
if not os.path.isfile(self.private_key):
raise Exception('Caminho do certificado não existe.')
raise Exception("Caminho do certificado não existe.")
def assina_xml(self, xml, reference):
self._checar_certificado()
template = etree.fromstring(xml)
key = xmlsec.Key.from_file(
self.private_key, format=xmlsec.constants.KeyDataFormatPem,
password=self.password)
self.private_key,
format=xmlsec.constants.KeyDataFormatPem,
password=self.password,
)
signature_node = xmlsec.template.create(
template, c14n_method=consts.TransformInclC14N,
sign_method=consts.TransformRsaSha1)
template,
c14n_method=consts.TransformInclC14N,
sign_method=consts.TransformRsaSha1,
)
template.append(signature_node)
ref = xmlsec.template.add_reference(
signature_node, consts.TransformSha1, uri='')
signature_node, consts.TransformSha1, uri=""
)
xmlsec.template.add_transform(ref, consts.TransformEnveloped)
xmlsec.template.add_transform(ref, consts.TransformInclC14N)
@ -46,8 +50,7 @@ class Assinatura(object):
ctx = xmlsec.SignatureContext()
ctx.key = key
ctx.key.load_cert_from_file(
self.cert_pem, consts.KeyDataFormatPem)
ctx.key.load_cert_from_file(self.cert_pem, consts.KeyDataFormatPem)
ctx.sign(signature_node)
return etree.tostring(template, encoding=str)

76
pytrustnfe/nfse/betha/__init__.py

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

51
pytrustnfe/nfse/bh/__init__.py

@ -13,38 +13,37 @@ from pytrustnfe.nfse.bh.assinatura import Assinatura
def _render(certificado, method, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates')
xml_send = render_xml(path, '%s.xml' % method, True, **kwargs)
path = os.path.join(os.path.dirname(__file__), "templates")
xml_send = render_xml(path, "%s.xml" % method, True, **kwargs)
reference = ''
ref_lote = ''
if method == 'GerarNfse':
reference = 'rps:%s' % kwargs['rps']['numero']
ref_lote = 'lote%s' % kwargs['rps']['numero_lote']
elif method == 'CancelarNfse':
reference = 'pedidoCancelamento_%s' % kwargs['cancelamento']['numero_nfse']
reference = ""
ref_lote = ""
if method == "GerarNfse":
reference = "rps:%s" % kwargs["rps"]["numero"]
ref_lote = "lote%s" % kwargs["rps"]["numero_lote"]
elif method == "CancelarNfse":
reference = "pedidoCancelamento_%s" % kwargs["cancelamento"]["numero_nfse"]
signer = Assinatura(certificado.pfx, certificado.password)
xml_send = signer.assina_xml(xml_send, reference)
if ref_lote:
xml_send = signer.assina_xml(etree.fromstring(xml_send), ref_lote)
return xml_send.encode('utf-8')
return xml_send.encode("utf-8")
def _send(certificado, method, **kwargs):
base_url = ''
if kwargs['ambiente'] == 'producao':
base_url = 'https://bhissdigital.pbh.gov.br/bhiss-ws/nfse?wsdl'
base_url = ""
if kwargs["ambiente"] == "producao":
base_url = "https://bhissdigital.pbh.gov.br/bhiss-ws/nfse?wsdl"
else:
base_url = 'https://bhisshomologa.pbh.gov.br/bhiss-ws/nfse?wsdl'
base_url = "https://bhisshomologa.pbh.gov.br/bhiss-ws/nfse?wsdl"
xml_send = kwargs["xml"].decode('utf-8')
xml_send = kwargs["xml"].decode("utf-8")
xml_cabecalho = '<?xml version="1.0" encoding="UTF-8"?>\
<cabecalho xmlns="http://www.abrasf.org.br/nfse.xsd" versao="1.00">\
<versaoDados>1.00</versaoDados></cabecalho>'
cert, key = extract_cert_and_key_from_pfx(
certificado.pfx, certificado.password)
cert, key = extract_cert_and_key_from_pfx(certificado.pfx, certificado.password)
cert, key = save_cert_key(cert, key)
session = Session()
@ -57,28 +56,24 @@ def _send(certificado, method, **kwargs):
response = client.service[method](xml_cabecalho, xml_send)
response, obj = sanitize_response(response)
return {
'sent_xml': xml_send,
'received_xml': response,
'object': obj
}
return {"sent_xml": xml_send, "received_xml": response, "object": obj}
def xml_gerar_nfse(certificado, **kwargs):
return _render(certificado, 'GerarNfse', **kwargs)
return _render(certificado, "GerarNfse", **kwargs)
def gerar_nfse(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_gerar_nfse(certificado, **kwargs)
return _send(certificado, 'GerarNfse', **kwargs)
kwargs["xml"] = xml_gerar_nfse(certificado, **kwargs)
return _send(certificado, "GerarNfse", **kwargs)
def xml_cancelar_nfse(certificado, **kwargs):
return _render(certificado, 'CancelarNfse', **kwargs)
return _render(certificado, "CancelarNfse", **kwargs)
def cancelar_nfse(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_cancelar_nfse(certificado, **kwargs)
return _send(certificado, 'CancelarNfse', **kwargs)
kwargs["xml"] = xml_cancelar_nfse(certificado, **kwargs)
return _send(certificado, "CancelarNfse", **kwargs)

21
pytrustnfe/nfse/bh/assinatura.py

@ -9,7 +9,6 @@ from signxml import XMLSigner
class Assinatura(object):
def __init__(self, arquivo, senha):
self.arquivo = arquivo
self.senha = senha
@ -22,21 +21,25 @@ class Assinatura(object):
element.text = None
signer = XMLSigner(
method=signxml.methods.enveloped, signature_algorithm="rsa-sha1",
digest_algorithm='sha1',
c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315')
method=signxml.methods.enveloped,
signature_algorithm="rsa-sha1",
digest_algorithm="sha1",
c14n_algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
)
ns = {}
ns[None] = signer.namespaces['ds']
ns[None] = signer.namespaces["ds"]
signer.namespaces = ns
ref_uri = ('#%s' % reference) if reference else None
ref_uri = ("#%s" % reference) if reference else None
signed_root = signer.sign(
xml_element, key=key.encode(), cert=cert.encode(),
reference_uri=ref_uri)
xml_element, key=key.encode(), cert=cert.encode(), reference_uri=ref_uri
)
if reference:
element_signed = signed_root.find(".//*[@Id='%s']" % reference)
signature = signed_root.find(".//*[@URI='#%s']" % reference).getparent().getparent()
signature = (
signed_root.find(".//*[@URI='#%s']" % reference).getparent().getparent()
)
if element_signed is not None and signature is not None:
parent = element_signed.getparent()

53
pytrustnfe/nfse/carioca/__init__.py

@ -10,30 +10,29 @@ from pytrustnfe.nfe.assinatura import Assinatura
def _render(certificado, method, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates')
xml_send = render_xml(path, '%s.xml' % method, True, **kwargs)
path = os.path.join(os.path.dirname(__file__), "templates")
xml_send = render_xml(path, "%s.xml" % method, True, **kwargs)
reference = ''
if method == 'GerarNfse':
reference = 'r%s' % kwargs['rps']['numero']
elif method == 'CancelarNfse':
reference = 'Cancelamento_NF%s' % kwargs['cancelamento']['numero_nfse']
reference = ""
if method == "GerarNfse":
reference = "r%s" % kwargs["rps"]["numero"]
elif method == "CancelarNfse":
reference = "Cancelamento_NF%s" % kwargs["cancelamento"]["numero_nfse"]
signer = Assinatura(certificado.pfx, certificado.password)
xml_send = signer.assina_xml(xml_send, reference)
return xml_send.encode('utf-8')
return xml_send.encode("utf-8")
def _send(certificado, method, **kwargs):
base_url = ''
if kwargs['ambiente'] == 'producao':
base_url = 'https://notacarioca.rio.gov.br/WSNacional/nfse.asmx?wsdl'
base_url = ""
if kwargs["ambiente"] == "producao":
base_url = "https://notacarioca.rio.gov.br/WSNacional/nfse.asmx?wsdl"
else:
base_url = 'https://homologacao.notacarioca.rio.gov.br/WSNacional/nfse.asmx?wsdl' # noqa
base_url = "https://homologacao.notacarioca.rio.gov.br/WSNacional/nfse.asmx?wsdl" # noqa
xml_send = kwargs["xml"].decode('utf-8')
cert, key = extract_cert_and_key_from_pfx(
certificado.pfx, certificado.password)
xml_send = kwargs["xml"].decode("utf-8")
cert, key = extract_cert_and_key_from_pfx(certificado.pfx, certificado.password)
cert, key = save_cert_key(cert, key)
client = get_authenticated_client(base_url, cert, key)
@ -41,34 +40,30 @@ def _send(certificado, method, **kwargs):
response = getattr(client.service, method)(xml_send)
except suds.WebFault as e:
return {
'sent_xml': str(xml_send),
'received_xml': str(e.fault.faultstring),
'object': None
"sent_xml": str(xml_send),
"received_xml": str(e.fault.faultstring),
"object": None,
}
response, obj = sanitize_response(response)
return {
'sent_xml': str(xml_send),
'received_xml': str(response),
'object': obj
}
return {"sent_xml": str(xml_send), "received_xml": str(response), "object": obj}
def xml_gerar_nfse(certificado, **kwargs):
return _render(certificado, 'GerarNfse', **kwargs)
return _render(certificado, "GerarNfse", **kwargs)
def gerar_nfse(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_gerar_nfse(certificado, **kwargs)
return _send(certificado, 'GerarNfse', **kwargs)
kwargs["xml"] = xml_gerar_nfse(certificado, **kwargs)
return _send(certificado, "GerarNfse", **kwargs)
def xml_cancelar_nfse(certificado, **kwargs):
return _render(certificado, 'CancelarNfse', **kwargs)
return _render(certificado, "CancelarNfse", **kwargs)
def cancelar_nfse(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_cancelar_nfse(certificado, **kwargs)
return _send(certificado, 'CancelarNfse', **kwargs)
kwargs["xml"] = xml_cancelar_nfse(certificado, **kwargs)
return _send(certificado, "CancelarNfse", **kwargs)

67
pytrustnfe/nfse/dsf/__init__.py

@ -12,11 +12,11 @@ from pytrustnfe.client import get_client
def _render(certificado, method, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates')
path = os.path.join(os.path.dirname(__file__), "templates")
if method == "testeEnviar":
xml_send = render_xml(path, 'enviar.xml', True, **kwargs)
xml_send = render_xml(path, "enviar.xml", True, **kwargs)
else:
xml_send = render_xml(path, '%s.xml' % method, False, **kwargs)
xml_send = render_xml(path, "%s.xml" % method, False, **kwargs)
if type(xml_send) != str:
xml_send = etree.tostring(xml_send)
@ -26,59 +26,56 @@ def _render(certificado, method, **kwargs):
def _get_url(**kwargs):
try:
cod_cidade = kwargs['nfse']['cidade']
cod_cidade = kwargs["nfse"]["cidade"]
except (KeyError, TypeError):
raise KeyError("Código de cidade inválido!")
urls = {
# Belém - PA
'2715': 'http://www.issdigitalbel.com.br/WsNFe2/LoteRps.jws?wsdl',
"2715": "http://www.issdigitalbel.com.br/WsNFe2/LoteRps.jws?wsdl",
# Sorocaba - SP
'7145': 'http://issdigital.sorocaba.sp.gov.br/WsNFe2/LoteRps.jws?wsdl',
"7145": "http://issdigital.sorocaba.sp.gov.br/WsNFe2/LoteRps.jws?wsdl",
# Teresina - PI
'1219': 'http://www.issdigitalthe.com.br/WsNFe2/LoteRps.jws?wsdl',
"1219": "http://www.issdigitalthe.com.br/WsNFe2/LoteRps.jws?wsdl",
# Campinas - SP
'6291': 'http://issdigital.campinas.sp.gov.br/WsNFe2/LoteRps.jws?wsdl',
"6291": "http://issdigital.campinas.sp.gov.br/WsNFe2/LoteRps.jws?wsdl",
# Uberlandia - MG
'5403': 'http://udigital.uberlandia.mg.gov.br/WsNFe2/LoteRps.jws?wsdl',
"5403": "http://udigital.uberlandia.mg.gov.br/WsNFe2/LoteRps.jws?wsdl",
# São Luis - MA
'0921':
'http://sistemas.semfaz.saoluis.ma.gov.br/WsNFe2/LoteRps.jws?wsdl',
"0921": "http://sistemas.semfaz.saoluis.ma.gov.br/WsNFe2/LoteRps.jws?wsdl",
# Campo Grande - MS
'2729': 'http://issdigital.pmcg.ms.gov.br/WsNFe2/LoteRps.jws?wsdl',
"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))
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')
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 = 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, '')
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
"sent_xml": xml_send,
"received_xml": e.fault.faultstring,
"object": None,
}
except Exception as e:
if response:
@ -86,46 +83,42 @@ def _send(certificado, method, **kwargs):
else:
raise e
return {
'sent_xml': xml_send,
'received_xml': response,
'object': obj
}
return {"sent_xml": xml_send, "received_xml": response, "object": obj}
def xml_enviar(certificado, **kwargs):
return _render(certificado, 'enviar', **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)
kwargs["xml"] = xml_enviar(certificado, **kwargs)
return _send(certificado, "enviar", **kwargs)
def xml_teste_enviar(certificado, **kwargs):
return _render(certificado, 'testeEnviar', **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)
kwargs["xml"] = xml_teste_enviar(certificado, **kwargs)
return _send(certificado, "testeEnviar", **kwargs)
def cancelar(certificado, **kwargs):
return _send(certificado, 'cancelar', **kwargs)
return _send(certificado, "cancelar", **kwargs)
def consulta_lote(**kwargs):
return _send(False, 'consultarLote', **kwargs)
return _send(False, "consultarLote", **kwargs)
def xml_consultar_nfse_rps(certificado, **kwargs):
return _render(certificado, 'consultarNFSeRps', **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)
kwargs["xml"] = xml_consultar_nfse_rps(certificado, **kwargs)
return _send(certificado, "consultarNFSeRps", **kwargs)

94
pytrustnfe/nfse/floripa/__init__.py

@ -11,47 +11,48 @@ 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'
"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",
},
'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)
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 = 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, '')
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'
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'
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'))
auth = base64.b64encode(secret.encode("utf-8"))
headers = {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": "Basic %s" % auth.decode('utf-8').replace('\n', '')
"Authorization": "Basic %s" % auth.decode("utf-8").replace("\n", ""),
}
m.update(kwargs["password"].encode('utf-8'))
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"])
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()
@ -60,57 +61,66 @@ def _get_oauth_token(**kwargs):
def _send(certificado, method, **kwargs):
url = URLS[kwargs['ambiente']][method]
xml_send = kwargs['xml']
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"]})
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",
headers = {
"Accept": "application/xml;charset=UTF-8",
"Content-Type": "application/xml",
"Authorization": "Bearer %s" % kwargs['access_token']}
"Authorization": "Bearer %s" % kwargs["access_token"],
}
r = requests.post(url, headers=headers, data=xml_send)
response, obj = sanitize_response(r.text.strip())
return {
'sent_xml': xml_send,
'received_xml': response.encode('utf-8'),
'object': obj,
'status_code': r.status_code,
"sent_xml": xml_send,
"received_xml": response.encode("utf-8"),
"object": obj,
"status_code": r.status_code,
}
def xml_processar_nota(certificado, **kwargs):
return _render(certificado, 'processar_nota', **kwargs)
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)
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)
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)
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"])
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"])
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']}
headers = {
"Accept": "application/json",
"Authorization": "Bearer %s" % kwargs["access_token"],
}
r = requests.get(url, headers=headers)
if r.status_code == 200:
return r.text

57
pytrustnfe/nfse/ginfes/__init__.py

@ -14,12 +14,12 @@ from pytrustnfe.nfe.assinatura import Assinatura
def _render(certificado, method, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates')
xml_send = render_xml(path, '%s.xml' % method, True, **kwargs)
path = os.path.join(os.path.dirname(__file__), "templates")
xml_send = render_xml(path, "%s.xml" % method, True, **kwargs)
reference = ''
if method == 'RecepcionarLoteRpsV3':
reference = 'rps%s' % kwargs['nfse']['lista_rps'][0]['numero']
reference = ""
if method == "RecepcionarLoteRpsV3":
reference = "rps%s" % kwargs["nfse"]["lista_rps"][0]["numero"]
signer = Assinatura(certificado.pfx, certificado.password)
xml_send = signer.assina_xml(xml_send, reference)
@ -27,14 +27,13 @@ def _render(certificado, method, **kwargs):
def _send(certificado, method, **kwargs):
base_url = ''
if kwargs['ambiente'] == 'producao':
base_url = 'https://producao.ginfes.com.br/ServiceGinfesImpl?wsdl'
base_url = ""
if kwargs["ambiente"] == "producao":
base_url = "https://producao.ginfes.com.br/ServiceGinfesImpl?wsdl"
else:
base_url = 'https://homologacao.ginfes.com.br/ServiceGinfesImpl?wsdl'
base_url = "https://homologacao.ginfes.com.br/ServiceGinfesImpl?wsdl"
cert, key = extract_cert_and_key_from_pfx(
certificado.pfx, certificado.password)
cert, key = extract_cert_and_key_from_pfx(certificado.pfx, certificado.password)
cert, key = save_cert_key(cert, key)
header = '<ns2:cabecalho xmlns:ns2="http://www.ginfes.com.br/cabecalho_v03.xsd" versao="3"><versaoDados>3</versaoDados></ns2:cabecalho>' # noqa
@ -47,60 +46,56 @@ def _send(certificado, method, **kwargs):
client = Client(base_url, transport=transport)
xml_send = kwargs['xml']
xml_send = kwargs["xml"]
response = client.service[method](header, xml_send)
response, obj = sanitize_response(response)
return {
'sent_xml': xml_send,
'received_xml': response,
'object': obj
}
return {"sent_xml": xml_send, "received_xml": response, "object": obj}
def xml_recepcionar_lote_rps(certificado, **kwargs):
return _render(certificado, 'RecepcionarLoteRpsV3', **kwargs)
return _render(certificado, "RecepcionarLoteRpsV3", **kwargs)
def recepcionar_lote_rps(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_recepcionar_lote_rps(certificado, **kwargs)
return _send(certificado, 'RecepcionarLoteRpsV3', **kwargs)
kwargs["xml"] = xml_recepcionar_lote_rps(certificado, **kwargs)
return _send(certificado, "RecepcionarLoteRpsV3", **kwargs)
def xml_consultar_situacao_lote(certificado, **kwargs):
return _render(certificado, 'ConsultarSituacaoLoteRpsV3', **kwargs)
return _render(certificado, "ConsultarSituacaoLoteRpsV3", **kwargs)
def consultar_situacao_lote(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_consultar_situacao_lote(certificado, **kwargs)
return _send(certificado, 'ConsultarSituacaoLoteRpsV3', **kwargs)
kwargs["xml"] = xml_consultar_situacao_lote(certificado, **kwargs)
return _send(certificado, "ConsultarSituacaoLoteRpsV3", **kwargs)
def consultar_nfse_por_rps(certificado, **kwargs):
return _send(certificado, 'ConsultarNfsePorRpsV3', **kwargs)
return _send(certificado, "ConsultarNfsePorRpsV3", **kwargs)
def xml_consultar_lote_rps(certificado, **kwargs):
return _render(certificado, 'ConsultarLoteRpsV3', **kwargs)
return _render(certificado, "ConsultarLoteRpsV3", **kwargs)
def consultar_lote_rps(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_consultar_lote_rps(certificado, **kwargs)
return _send(certificado, 'ConsultarLoteRpsV3', **kwargs)
kwargs["xml"] = xml_consultar_lote_rps(certificado, **kwargs)
return _send(certificado, "ConsultarLoteRpsV3", **kwargs)
def consultar_nfse(certificado, **kwargs):
return _send(certificado, 'ConsultarNfseV3', **kwargs)
return _send(certificado, "ConsultarNfseV3", **kwargs)
def xml_cancelar_nfse(certificado, **kwargs):
return _render(certificado, 'CancelarNfseV3', **kwargs)
return _render(certificado, "CancelarNfseV3", **kwargs)
def cancelar_nfse(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_cancelar_nfse(certificado, **kwargs)
return _send(certificado, 'CancelarNfseV3', **kwargs)
kwargs["xml"] = xml_cancelar_nfse(certificado, **kwargs)
return _send(certificado, "CancelarNfseV3", **kwargs)

40
pytrustnfe/nfse/imperial/__init__.py

@ -5,56 +5,52 @@
import os
import requests
from lxml import etree
from requests import Session
from zeep import Client
from zeep.transports import Transport
from pytrustnfe.xml import render_xml, sanitize_response
def _render(certificado, method, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates')
xml_send = render_xml(path, '%s.xml' % method, True, **kwargs)
path = os.path.join(os.path.dirname(__file__), "templates")
xml_send = render_xml(path, "%s.xml" % method, True, **kwargs)
return etree.tostring(xml_send)
def _send(certificado, method, **kwargs):
base_url = ''
if kwargs['ambiente'] == 'producao':
base_url = 'https://petropolis.sigiss.com.br/petropolis/ws/sigiss_ws.php' # noqa
base_url = ""
if kwargs["ambiente"] == "producao":
base_url = (
"https://petropolis.sigiss.com.br/petropolis/ws/sigiss_ws.php" # noqa
)
else:
raise Exception('Não existe ambiente de homologação!')
raise Exception("Não existe ambiente de homologação!")
xml_send = kwargs["xml"].decode('utf-8')
xml_send = kwargs["xml"].decode("utf-8")
headers = {
'SOAPAction': "urn:sigiss_ws#%s" % method,
'Content-Type': 'text/xml; charset="utf-8"'
"SOAPAction": "urn:sigiss_ws#%s" % method,
"Content-Type": 'text/xml; charset="utf-8"',
}
r = requests.post(base_url, data=xml_send, headers=headers)
response, obj = sanitize_response(r.text.strip())
return {
'sent_xml': xml_send,
'received_xml': response,
'object': obj.Body
}
return {"sent_xml": xml_send, "received_xml": response, "object": obj.Body}
def xml_gerar_nota(certificado, **kwargs):
return _render(certificado, 'GerarNota', **kwargs)
return _render(certificado, "GerarNota", **kwargs)
def gerar_nota(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_gerar_nota(certificado, **kwargs)
return _send(certificado, 'GerarNota', **kwargs)
kwargs["xml"] = xml_gerar_nota(certificado, **kwargs)
return _send(certificado, "GerarNota", **kwargs)
def xml_cancelar_nota(certificado, **kwargs):
return _render(certificado, 'CancelarNota', **kwargs)
return _render(certificado, "CancelarNota", **kwargs)
def cancelar_nota(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_cancelar_nota(certificado, **kwargs)
return _send(certificado, 'CancelarNota', **kwargs)
kwargs["xml"] = xml_cancelar_nota(certificado, **kwargs)
return _send(certificado, "CancelarNota", **kwargs)

47
pytrustnfe/nfse/mga/__init__.py

@ -12,31 +12,30 @@ from pytrustnfe.nfse.mga.assinatura import Assinatura
def _render(certificado, method, **kwargs):
path = os.path.join(os.path.dirname(__file__), 'templates')
xml_send = render_xml(path, '%s.xml' % method, True, **kwargs)
path = os.path.join(os.path.dirname(__file__), "templates")
xml_send = render_xml(path, "%s.xml" % method, True, **kwargs)
reference = ''
if method == 'GerarNfse':
reference = 'rps:%s' % kwargs['rps']['numero']
elif method == 'CancelarNfse':
reference = 'Cancelamento_NF%s' % kwargs['cancelamento']['numero_nfse']
reference = ""
if method == "GerarNfse":
reference = "rps:%s" % kwargs["rps"]["numero"]
elif method == "CancelarNfse":
reference = "Cancelamento_NF%s" % kwargs["cancelamento"]["numero_nfse"]
signer = Assinatura(certificado.pfx, certificado.password)
xml_send = signer.assina_xml(xml_send, reference)
return xml_send.encode('utf-8')
return xml_send.encode("utf-8")
def _send(certificado, method, **kwargs):
base_url = ''
if kwargs['ambiente'] == 'producao':
base_url = 'https://isse.maringa.pr.gov.br/ws/?wsdl'
base_url = ""
if kwargs["ambiente"] == "producao":
base_url = "https://isse.maringa.pr.gov.br/ws/?wsdl"
else:
base_url = 'https://isseteste.maringa.pr.gov.br/ws/?wsdl'
base_url = "https://isseteste.maringa.pr.gov.br/ws/?wsdl"
xml_send = kwargs["xml"].decode('utf-8')
xml_send = kwargs["xml"].decode("utf-8")
cert, key = extract_cert_and_key_from_pfx(
certificado.pfx, certificado.password)
cert, key = extract_cert_and_key_from_pfx(certificado.pfx, certificado.password)
cert, key = save_cert_key(cert, key)
session = Session()
@ -48,28 +47,24 @@ def _send(certificado, method, **kwargs):
response = client.service[method](xml_send)
response, obj = sanitize_response(response)
return {
'sent_xml': str(xml_send),
'received_xml': str(response),
'object': obj
}
return {"sent_xml": str(xml_send), "received_xml": str(response), "object": obj}
def xml_gerar_nfse(certificado, **kwargs):
return _render(certificado, 'GerarNfse', **kwargs)
return _render(certificado, "GerarNfse", **kwargs)
def gerar_nfse(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_gerar_nfse(certificado, **kwargs)
return _send(certificado, 'GerarNfse', **kwargs)
kwargs["xml"] = xml_gerar_nfse(certificado, **kwargs)
return _send(certificado, "GerarNfse", **kwargs)
def xml_cancelar_nfse(certificado, **kwargs):
return _render(certificado, 'CancelarNfse', **kwargs)
return _render(certificado, "CancelarNfse", **kwargs)
def cancelar_nfse(certificado, **kwargs):
if "xml" not in kwargs:
kwargs['xml'] = xml_cancelar_nfse(certificado, **kwargs)
return _send(certificado, 'CancelarNfse', **kwargs)
kwargs["xml"] = xml_cancelar_nfse(certificado, **kwargs)
return _send(certificado, "CancelarNfse", **kwargs)

17
pytrustnfe/nfse/mga/assinatura.py

@ -9,7 +9,6 @@ from signxml import XMLSigner
class Assinatura(object):
def __init__(self, arquivo, senha):
self.arquivo = arquivo
self.senha = senha
@ -22,22 +21,26 @@ class Assinatura(object):
element.text = None
signer = XMLSigner(
method=signxml.methods.enveloped, signature_algorithm=u"rsa-sha1",
digest_algorithm=u'sha1',
c14n_algorithm=u'http://www.w3.org/TR/2001/REC-xml-c14n-20010315')
method=signxml.methods.enveloped,
signature_algorithm=u"rsa-sha1",
digest_algorithm=u"sha1",
c14n_algorithm=u"http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
)
ns = {}
ns[None] = signer.namespaces['ds']
ns[None] = signer.namespaces["ds"]
signer.namespaces = ns
element_to_be_signed = xml_element.getchildren()[0].getchildren()[0]
signed_root = signer.sign(
element_to_be_signed, key=key.encode(), cert=cert.encode())
element_to_be_signed, key=key.encode(), cert=cert.encode()
)
if reference:
element_signed = xml_element.find(".//*[@Id='%s']" % reference)
signature = signed_root.find(
".//{http://www.w3.org/2000/09/xmldsig#}Signature")
".//{http://www.w3.org/2000/09/xmldsig#}Signature"
)
if element_signed is not None and signature is not None:
parent = xml_element.getchildren()[0]

116
pytrustnfe/nfse/natal/__init__.py

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

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

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

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

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

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

@ -0,0 +1,112 @@
<Rps>
<InfRps Id="rps:{{ rps.numero }}{{ rps.serie }}">
<IdentificacaoRps>
<Numero>{{ rps.numero }}</Numero>
<Serie>{{ rps.serie }}</Serie>
<Tipo>{{ rps.tipo_rps }}</Tipo>
</IdentificacaoRps>
<DataEmissao>{{ rps.data_emissao }}</DataEmissao>
<NaturezaOperacao>{{ rps.natureza_operacao }}</NaturezaOperacao>
<RegimeEspecialTributacao>{{ rps.regime_tributacao }}</RegimeEspecialTributacao>
<OptanteSimplesNacional>{{ rps.optante_simples }}</OptanteSimplesNacional>
<IncentivadorCultural>{{ rps.incentivador_cultural }}</IncentivadorCultural>
<Status>{{ rps.status }}</Status>
<Servico>
<Valores>
<ValorServicos>{{ rps.servico.valor_servico }}</ValorServicos>
<ValorPis>{{ rps.servico.pis }}</ValorPis>
<ValorCofins>{{ rps.servico.cofins }}</ValorCofins>
<ValorInss>{{ rps.servico.inss }}</ValorInss>
<ValorIr>{{ rps.servico.ir }}</ValorIr>
<ValorCsll>{{ rps.servico.csll }}</ValorCsll>
<IssRetido>{{ rps.servico.iss_retido }}</IssRetido>
<ValorIss>{{ rps.servico.iss }}</ValorIss>
<OutrasRetencoes>{{ rps.servico.retencoes }}</OutrasRetencoes>
<BaseCalculo>{{ rps.servico.base_calculo }}</BaseCalculo>
<Aliquota>{{ rps.servico.aliquota }}</Aliquota>
</Valores>
<ItemListaServico>{{ rps.servico.codigo_servico }}</ItemListaServico>
<CodigoCnae>{{ rps.servico.cnae_servico }}</CodigoCnae>
<Discriminacao>{{ rps.servico.discriminacao }}</Discriminacao>
<CodigoMunicipio>{{ rps.servico.codigo_municipio }}</CodigoMunicipio>
</Servico>
<Prestador>
<Cnpj>{{ rps.prestador.cnpj }}</Cnpj>
<InscricaoMunicipal>{{ rps.prestador.inscricao_municipal }}</InscricaoMunicipal>
<RazaoSocial>{{ rps.prestador.razaosocial }}</RazaoSocial>
<NomeFantasia>{{ rps.prestador.fantasia }}</NomeFantasia>
<Endereco>
<Endereco>{{ rps.prestador.endereco }}</Endereco>
<Numero>{{ rps.prestador.numero }}</Numero>
<Complemento>{{ rps.prestador.complemento }}</Complemento>
<Bairro>{{ rps.prestador.bairro }}</Bairro>
<CodigoMunicipio>{{ rps.prestador.codigomunicipal }}</CodigoMunicipio>
<Uf>{{ rps.prestador.uf }}</Uf>
<Cep>{{ rps.prestador.cep }}</Cep>
</Endereco>
<Contato>
{% if rps.prestador.telefone is defined -%}
<Telefone>{{ rps.prestador.telefone }}</Telefone>
{% endif %}
{% if rps.prestador.email is defined -%}
<Email>{{ rps.prestador.email }}</Email>
{% endif %}
</Contato>
</Prestador>
<Tomador>
<IdentificacaoTomador>
<CpfCnpj>
{% if rps.tomador.cpf_cnpj|length == 14 %}
<Cnpj>{{ rps.tomador.cpf_cnpj }}</Cnpj>
{% endif %}
{% if rps.tomador.cpf_cnpj|length == 11 %}
<Cpf>{{ rps.tomador.cpf_cnpj }}</Cpf>
{% endif %}
</CpfCnpj>
{% if rps.tomador.inscricao_municipal is defined -%}
<InscricaoMunicipal>{{ rps.tomador.inscricao_municipal }}</InscricaoMunicipal>
{% endif %}
</IdentificacaoTomador>
<RazaoSocial>{{ rps.tomador.razao_social }}</RazaoSocial>
<Endereco>
<Endereco>{{ rps.tomador.endereco }}</Endereco>
<Numero>{{ rps.tomador.numero }}</Numero>
<Complemento>{{ rps.tomador.complemento }}</Complemento>
<Bairro>{{ rps.tomador.bairro }}</Bairro>
<CodigoMunicipio>{{ rps.tomador.codigo_municipio }}</CodigoMunicipio>
<Uf>{{ rps.tomador.uf }}</Uf>
<Cep>{{ rps.tomador.cep }}</Cep>
</Endereco>
<Contato>
{% if rps.tomador.telefone is defined -%}
<Telefone>{{ rps.tomador.telefone }}</Telefone>
{% endif %}
{% if rps.tomador.email is defined -%}
<Email>{{ rps.tomador.email }}</Email>
{% endif %}
</Contato>
{% if rps.tomador.orgao_gerador is defined -%}
<OrgaoGerador>
<CodigoMunicipio>{{ rps.tomador.orgao_gerador.codigo_municipio }}</CodigoMunicipio>
<Uf>{{ rps.tomador.orgao_gerador.uf }}</Uf>
</OrgaoGerador>
{% endif %}
</Tomador>
{% if rps.intermediario is defined -%}
<IntermediarioServico>
<RazaoSocial>{{ rps.intermediario.razao_social }}</RazaoSocial>
<CpfCnpj>
<Cnpj>{{ rps.intermediario.cnpj }}</Cnpj>
</CpfCnpj>
<InscricaoMunicipal>{{ rps.intermediario.inscricao_municipal }}</InscricaoMunicipal>
</IntermediarioServico>
{% endif %}
{% if rps.construcao_civil is defined -%}
<ContrucaoCivil>
<CodigoObra>{{ rps.construcao_civil.codigo_obra }}</CodigoObra>
<Art>{{ rps.construcao_civil.art }}</Art>
</ContrucaoCivil>
{% endif %}
</InfRps>
<Signature Id="placeholder"></Signature>
</Rps>

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

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

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

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

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

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

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

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

68
pytrustnfe/nfse/paulistana/__init__.py

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

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

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

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

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

64
pytrustnfe/nfse/simpliss/__init__.py

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

34
pytrustnfe/nfse/susesu/__init__.py

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

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

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

262
pytrustnfe/urls.py

@ -1,146 +1,146 @@
AC = '12'
AL = '27'
AM = '13'
AP = '16'
BA = '29'
CE = '23'
DF = '53'
ES = '32'
GO = '52'
MA = '21'
MG = '31'
MS = '50'
MT = '51'
PA = '15'
PB = '25'
PE = '26'
PI = '22'
PR = '41'
RJ = '33'
RN = '24'
RO = '11'
RR = '14'
RS = '43'
SC = '42'
SE = '28'
SP = '35'
TO = '17'
AC = "12"
AL = "27"
AM = "13"
AP = "16"
BA = "29"
CE = "23"
DF = "53"
ES = "32"
GO = "52"
MA = "21"
MG = "31"
MS = "50"
MT = "51"
PA = "15"
PB = "25"
PE = "26"
PI = "22"
PR = "41"
RJ = "33"
RN = "24"
RO = "11"
RR = "14"
RS = "43"
SC = "42"
SE = "28"
SP = "35"
TO = "17"
PRODUCAO = '1'
HOMOLOGACAO = '2'
PRODUCAO = "1"
HOMOLOGACAO = "2"
URLS = {
PRODUCAO: {
AC: 'http://www.sefaznet.ac.gov.br/nfce/qrcode?',
AL: 'http://nfce.sefaz.al.gov.br/QRCode/consultarNFCe.jsp?',
AM: 'http://sistemas.sefaz.am.gov.br/nfceweb/consultarNFCe.jsp?',
AP: 'https://www.sefaz.ap.gov.br/nfce/nfcep.php?',
BA: 'http://nfe.sefaz.ba.gov.br/servicos/nfce/qrcode.aspx?',
DF: 'http://www.fazenda.df.gov.br/nfce/qrcode?',
GO: 'http://nfe.sefaz.go.gov.br/nfeweb/sites/nfce/danfeNFCe?',
MA: 'http://nfce.sefaz.ma.gov.br/portal/consultarNFCe.jsp?',
MG: 'https://nfce.fazenda.mg.gov.br/portalnfce/sistema/qrcode.xhtml?',
MS: 'http://www.dfe.ms.gov.br/nfce/qrcode?',
MT: 'http://www.sefaz.mt.gov.br/nfce/consultanfce?',
PA: 'https://appnfc.sefa.pa.gov.br/portal/view/consultas/nfce/nfceForm.seam?', # noqa
PB: 'http://www.receita.pb.gov.br/nfce?',
PE: 'http://nfce.sefaz.pe.gov.br/nfce/consulta?',
PI: 'http://www.sefaz.pi.gov.br/nfce/qrcode?',
PR: 'http://www.fazenda.pr.gov.br/nfce/consulta?',
RJ: 'http://www4.fazenda.rj.gov.br/consultaNFCe/QRCode?',
RN: 'http://nfce.set.rn.gov.br/consultarNFCe.aspx?',
RO: 'http://www.nfce.sefin.ro.gov.br/consultanfce/consulta.jsp?',
RR: 'https://www.sefaz.rr.gov.br/nfce/servlet/qrcode?',
RS: 'https://www.sefaz.rs.gov.br/NFCE/NFCE-COM.aspx?',
SE: 'http://www.nfce.se.gov.br/nfce/qrcode?',
SP: 'https://www.nfce.fazenda.sp.gov.br/qrcode?',
TO: 'http://www.sefaz.to.gov.br/nfce/qrcode?',
AC: "http://www.sefaznet.ac.gov.br/nfce/qrcode?",
AL: "http://nfce.sefaz.al.gov.br/QRCode/consultarNFCe.jsp?",
AM: "http://sistemas.sefaz.am.gov.br/nfceweb/consultarNFCe.jsp?",
AP: "https://www.sefaz.ap.gov.br/nfce/nfcep.php?",
BA: "http://nfe.sefaz.ba.gov.br/servicos/nfce/qrcode.aspx?",
DF: "http://www.fazenda.df.gov.br/nfce/qrcode?",
GO: "http://nfe.sefaz.go.gov.br/nfeweb/sites/nfce/danfeNFCe?",
MA: "http://nfce.sefaz.ma.gov.br/portal/consultarNFCe.jsp?",
MG: "https://nfce.fazenda.mg.gov.br/portalnfce/sistema/qrcode.xhtml?",
MS: "http://www.dfe.ms.gov.br/nfce/qrcode?",
MT: "http://www.sefaz.mt.gov.br/nfce/consultanfce?",
PA: "https://appnfc.sefa.pa.gov.br/portal/view/consultas/nfce/nfceForm.seam?", # noqa
PB: "http://www.receita.pb.gov.br/nfce?",
PE: "http://nfce.sefaz.pe.gov.br/nfce/consulta?",
PI: "http://www.sefaz.pi.gov.br/nfce/qrcode?",
PR: "http://www.fazenda.pr.gov.br/nfce/consulta?",
RJ: "http://www4.fazenda.rj.gov.br/consultaNFCe/QRCode?",
RN: "http://nfce.set.rn.gov.br/consultarNFCe.aspx?",
RO: "http://www.nfce.sefin.ro.gov.br/consultanfce/consulta.jsp?",
RR: "https://www.sefaz.rr.gov.br/nfce/servlet/qrcode?",
RS: "https://www.sefaz.rs.gov.br/NFCE/NFCE-COM.aspx?",
SE: "http://www.nfce.se.gov.br/nfce/qrcode?",
SP: "https://www.nfce.fazenda.sp.gov.br/qrcode?",
TO: "http://www.sefaz.to.gov.br/nfce/qrcode?",
},
HOMOLOGACAO: {
AC: 'http://www.hml.sefaznet.ac.gov.br/nfce/qrcode?',
AL: 'http://nfce.sefaz.al.gov.br/QRCode/consultarNFCe.jsp?',
AM: 'http://homnfce.sefaz.am.gov.br/nfceweb/consultarNFCe.jsp?',
AP: 'https://www.sefaz.ap.gov.br/nfcehml/nfce.php?',
BA: 'http://hnfe.sefaz.ba.gov.br/servicos/nfce/qrcode.aspx?',
DF: 'http://www.fazenda.df.gov.br/nfce/qrcode?',
GO: 'http://homolog.sefaz.go.gov.br/nfeweb/sites/nfce/danfeNFCe?',
MA: 'http://homologacao.sefaz.ma.gov.br/portal/consultarNFCe.jsp?',
MS: 'http://www.dfe.ms.gov.br/nfce/qrcode?',
MG: 'https://nfce.fazenda.mg.gov.br/portalnfce/sistema/qrcode.xhtml?',
MT: 'http://homologacao.sefaz.mt.gov.br/nfce/consultanfce?',
PA: 'https://appnfc.sefa.pa.gov.br/portal-homologacao/view/consultas/nfce/nfceForm.seam?', # noqa
PB: 'http://www.receita.pb.gov.br/nfcehom?',
PE: 'http://nfcehomolog.sefaz.pe.gov.br/nfce/consulta?',
PI: 'http://www.sefaz.pi.gov.br/nfce/qrcode?',
PR: 'http://www.fazenda.pr.gov.br/nfce/consulta?',
RJ: 'http://www4.fazenda.rj.gov.br/consultaNFCe/QRCode?',
RN: 'http://hom.nfce.set.rn.gov.br/consultarNFCe.aspx?',
RO: 'http://200.174.88.103:8080/nfce/servlet/qrcode?',
RR: 'https://www.sefaz.rr.gov.br/nfce/servlet/qrcode?',
RS: 'https://www.sefaz.rs.gov.br/NFCE/NFCE-COM.aspx?',
SE: 'http://www.hom.nfe.se.gov.br/nfce/qrcode?',
SP: 'https://www.homologacao.nfce.fazenda.sp.gov.br/qrcode?',
TO: 'http://homologacao.sefaz.to.gov.br/nfce/qrcode?',
}
AC: "http://www.hml.sefaznet.ac.gov.br/nfce/qrcode?",
AL: "http://nfce.sefaz.al.gov.br/QRCode/consultarNFCe.jsp?",
AM: "http://homnfce.sefaz.am.gov.br/nfceweb/consultarNFCe.jsp?",
AP: "https://www.sefaz.ap.gov.br/nfcehml/nfce.php?",
BA: "http://hnfe.sefaz.ba.gov.br/servicos/nfce/qrcode.aspx?",
DF: "http://www.fazenda.df.gov.br/nfce/qrcode?",
GO: "http://homolog.sefaz.go.gov.br/nfeweb/sites/nfce/danfeNFCe?",
MA: "http://homologacao.sefaz.ma.gov.br/portal/consultarNFCe.jsp?",
MS: "http://www.dfe.ms.gov.br/nfce/qrcode?",
MG: "https://nfce.fazenda.mg.gov.br/portalnfce/sistema/qrcode.xhtml?",
MT: "http://homologacao.sefaz.mt.gov.br/nfce/consultanfce?",
PA: "https://appnfc.sefa.pa.gov.br/portal-homologacao/view/consultas/nfce/nfceForm.seam?", # noqa
PB: "http://www.receita.pb.gov.br/nfcehom?",
PE: "http://nfcehomolog.sefaz.pe.gov.br/nfce/consulta?",
PI: "http://www.sefaz.pi.gov.br/nfce/qrcode?",
PR: "http://www.fazenda.pr.gov.br/nfce/consulta?",
RJ: "http://www4.fazenda.rj.gov.br/consultaNFCe/QRCode?",
RN: "http://hom.nfce.set.rn.gov.br/consultarNFCe.aspx?",
RO: "http://200.174.88.103:8080/nfce/servlet/qrcode?",
RR: "https://www.sefaz.rr.gov.br/nfce/servlet/qrcode?",
RS: "https://www.sefaz.rs.gov.br/NFCE/NFCE-COM.aspx?",
SE: "http://www.hom.nfe.se.gov.br/nfce/qrcode?",
SP: "https://www.homologacao.nfce.fazenda.sp.gov.br/qrcode?",
TO: "http://homologacao.sefaz.to.gov.br/nfce/qrcode?",
},
}
URLS_EXIBICAO = {
PRODUCAO: {
AC: 'www.sefaznet.ac.gov.br/nfce/consulta',
AL: 'www.sefaz.al.gov.br/nfce/consulta',
AM: 'www.sefaz.am.gov.br/nfce/consulta',
AP: 'www.sefaz.ap.gov.br/nfce/consulta',
BA: 'http://www.sefaz.ba.gov.br/nfce/consulta',
CE: 'www.sefaz.ce.gov.br/nfce/consulta',
DF: 'www.fazenda.df.gov.br/nfce/consulta',
ES: 'www.sefaz.es.gov.br/nfce/consulta',
GO: 'www.sefaz.go.gov.br/nfce/consulta',
MA: 'www.sefaz.ma.gov.br/nfce/consulta',
MS: 'www.dfe.ms.gov.br/nfce/consulta',
MT: 'www.sefaz.mt.gov.br/nfce/consulta',
MG: 'http://nfce.fazenda.mg.gov.br/portalnfce',
PA: 'www.sefa.pa.gov.br/nfce/consulta',
PB: 'www.receita.pb.gov.br/nfce/consulta',
PE: 'nfce.sefaz.pe.gov.br/nfce/consulta',
PI: 'www.sefaz.pi.gov.br/nfce/consulta',
PR: 'http://www.fazenda.pr.gov.br/nfce/consulta',
RJ: 'www.fazenda.rj.gov.br/nfce/consulta',
RN: 'www.set.rn.gov.br/nfce/consulta',
RO: 'www.sefin.ro.gov.br/nfce/consulta',
RR: 'www.sefaz.rr.gov.br/nfce/consulta',
RS: 'www.sefaz.rs.gov.br/nfce/consulta',
SE: 'http://www.nfce.se.gov.br/nfce/consulta',
SP: 'https://www.nfce.fazenda.sp.gov.br/consulta',
TO: 'www.sefaz.to.gov.br/nfce/consulta',
AC: "www.sefaznet.ac.gov.br/nfce/consulta",
AL: "www.sefaz.al.gov.br/nfce/consulta",
AM: "www.sefaz.am.gov.br/nfce/consulta",
AP: "www.sefaz.ap.gov.br/nfce/consulta",
BA: "http://www.sefaz.ba.gov.br/nfce/consulta",
CE: "www.sefaz.ce.gov.br/nfce/consulta",
DF: "www.fazenda.df.gov.br/nfce/consulta",
ES: "www.sefaz.es.gov.br/nfce/consulta",
GO: "www.sefaz.go.gov.br/nfce/consulta",
MA: "www.sefaz.ma.gov.br/nfce/consulta",
MS: "www.dfe.ms.gov.br/nfce/consulta",
MT: "www.sefaz.mt.gov.br/nfce/consulta",
MG: "http://nfce.fazenda.mg.gov.br/portalnfce",
PA: "www.sefa.pa.gov.br/nfce/consulta",
PB: "www.receita.pb.gov.br/nfce/consulta",
PE: "nfce.sefaz.pe.gov.br/nfce/consulta",
PI: "www.sefaz.pi.gov.br/nfce/consulta",
PR: "http://www.fazenda.pr.gov.br/nfce/consulta",
RJ: "www.fazenda.rj.gov.br/nfce/consulta",
RN: "www.set.rn.gov.br/nfce/consulta",
RO: "www.sefin.ro.gov.br/nfce/consulta",
RR: "www.sefaz.rr.gov.br/nfce/consulta",
RS: "www.sefaz.rs.gov.br/nfce/consulta",
SE: "http://www.nfce.se.gov.br/nfce/consulta",
SP: "https://www.nfce.fazenda.sp.gov.br/consulta",
TO: "www.sefaz.to.gov.br/nfce/consulta",
},
HOMOLOGACAO: {
AC: 'www.sefaznet.ac.gov.br/nfce/consulta',
AL: 'www.sefaz.al.gov.br/nfce/consulta',
AM: 'www.sefaz.am.gov.br/nfce/consulta',
AP: 'www.sefaz.ap.gov.br/nfce/consulta',
BA: 'http://hinternet.sefaz.ba.gov.br/nfce/consulta',
CE: 'www.sefaz.ce.gov.br/nfce/consulta',
DF: 'www.fazenda.df.gov.br/nfce/consulta',
ES: 'www.sefaz.es.gov.br/nfce/consulta',
GO: 'www.sefaz.go.gov.br/nfce/consulta',
MA: 'www.sefaz.ma.gov.br/nfce/consulta',
MS: 'www.dfe.ms.gov.br/nfce/consulta',
MT: 'www.sefaz.mt.gov.br/nfce/consulta',
MG: 'http://hnfce.fazenda.mg.gov.br/portalnfce',
PA: 'www.sefa.pa.gov.br/nfce/consulta',
PB: 'www.receita.pb.gov.br/nfcehom',
PE: 'nfce.sefaz.pe.gov.br/nfce/consulta',
PI: 'www.sefaz.pi.gov.br/nfce/consulta',
PR: 'http://www.fazenda.pr.gov.br/nfce/consulta',
RJ: 'www.fazenda.rj.gov.br/nfce/consulta',
RN: 'www.set.rn.gov.br/nfce/consulta',
RO: 'www.sefin.ro.gov.br/nfce/consulta',
RR: 'www.sefaz.rr.gov.br/nfce/consulta',
RS: 'www.sefaz.rs.gov.br/nfce/consulta',
SE: 'http://www.hom.nfe.se.gov.br/nfce/consulta',
SP: 'https://www.homologacao.nfce.fazenda.sp.gov.br/consulta',
TO: 'www.sefaz.to.gov.br/nfce/consulta',
}
AC: "www.sefaznet.ac.gov.br/nfce/consulta",
AL: "www.sefaz.al.gov.br/nfce/consulta",
AM: "www.sefaz.am.gov.br/nfce/consulta",
AP: "www.sefaz.ap.gov.br/nfce/consulta",
BA: "http://hinternet.sefaz.ba.gov.br/nfce/consulta",
CE: "www.sefaz.ce.gov.br/nfce/consulta",
DF: "www.fazenda.df.gov.br/nfce/consulta",
ES: "www.sefaz.es.gov.br/nfce/consulta",
GO: "www.sefaz.go.gov.br/nfce/consulta",
MA: "www.sefaz.ma.gov.br/nfce/consulta",
MS: "www.dfe.ms.gov.br/nfce/consulta",
MT: "www.sefaz.mt.gov.br/nfce/consulta",
MG: "http://hnfce.fazenda.mg.gov.br/portalnfce",
PA: "www.sefa.pa.gov.br/nfce/consulta",
PB: "www.receita.pb.gov.br/nfcehom",
PE: "nfce.sefaz.pe.gov.br/nfce/consulta",
PI: "www.sefaz.pi.gov.br/nfce/consulta",
PR: "http://www.fazenda.pr.gov.br/nfce/consulta",
RJ: "www.fazenda.rj.gov.br/nfce/consulta",
RN: "www.set.rn.gov.br/nfce/consulta",
RO: "www.sefin.ro.gov.br/nfce/consulta",
RR: "www.sefaz.rr.gov.br/nfce/consulta",
RS: "www.sefaz.rs.gov.br/nfce/consulta",
SE: "http://www.hom.nfe.se.gov.br/nfce/consulta",
SP: "https://www.homologacao.nfce.fazenda.sp.gov.br/consulta",
TO: "www.sefaz.to.gov.br/nfce/consulta",
},
}

60
pytrustnfe/utils.py

@ -9,26 +9,25 @@ import lxml.etree as ET
class ChaveNFe(object):
def __init__(self, **kwargs):
self.cnpj = kwargs.pop('cnpj', '')
self.estado = kwargs.pop('estado', '')
self.emissao = kwargs.pop('emissao', '')
self.modelo = kwargs.pop('modelo', '')
self.serie = kwargs.pop('serie', '')
self.numero = kwargs.pop('numero', '')
self.tipo = kwargs.pop('tipo', '')
self.codigo = kwargs.pop('codigo', '')
self.cnpj = kwargs.pop("cnpj", "")
self.estado = kwargs.pop("estado", "")
self.emissao = kwargs.pop("emissao", "")
self.modelo = kwargs.pop("modelo", "")
self.serie = kwargs.pop("serie", "")
self.numero = kwargs.pop("numero", "")
self.tipo = kwargs.pop("tipo", "")
self.codigo = kwargs.pop("codigo", "")
def validar(self):
assert self.cnpj != '', 'CNPJ necessário para criar chave NF-e'
assert self.estado != '', 'Estado necessário para criar chave NF-e'
assert self.emissao != '', 'Emissão necessário para criar chave NF-e'
assert self.modelo != '', 'Modelo necessário para criar chave NF-e'
assert self.serie != '', 'Série necessária para criar chave NF-e'
assert self.numero != '', 'Número necessário para criar chave NF-e'
assert self.tipo != '', 'Tipo necessário para criar chave NF-e'
assert self.codigo != '', 'Código necessário para criar chave NF-e'
assert self.cnpj != "", "CNPJ necessário para criar chave NF-e"
assert self.estado != "", "Estado necessário para criar chave NF-e"
assert self.emissao != "", "Emissão necessário para criar chave NF-e"
assert self.modelo != "", "Modelo necessário para criar chave NF-e"
assert self.serie != "", "Série necessária para criar chave NF-e"
assert self.numero != "", "Número necessário para criar chave NF-e"
assert self.tipo != "", "Tipo necessário para criar chave NF-e"
assert self.codigo != "", "Código necessário para criar chave NF-e"
def date_tostring(data):
@ -45,11 +44,16 @@ def gerar_chave(obj_chave, prefix=None):
assert isinstance(obj_chave, ChaveNFe), "Objeto deve ser do tipo ChaveNFe"
obj_chave.validar()
chave_parcial = "%s%s%s%s%s%s%d%s" % (
obj_chave.estado, obj_chave.emissao,
obj_chave.cnpj, obj_chave.modelo,
obj_chave.serie.zfill(3), str(obj_chave.numero).zfill(9),
obj_chave.tipo, obj_chave.codigo)
chave_parcial = re.sub('[^0-9]', '', chave_parcial)
obj_chave.estado,
obj_chave.emissao,
obj_chave.cnpj,
obj_chave.modelo,
obj_chave.serie.zfill(3),
str(obj_chave.numero).zfill(9),
obj_chave.tipo,
obj_chave.codigo,
)
chave_parcial = re.sub("[^0-9]", "", chave_parcial)
soma = 0
contador = 2
for c in reversed(chave_parcial):
@ -75,16 +79,16 @@ def _find_node(xml, node):
def gerar_nfeproc(envio, recibo):
NSMAP = {None: 'http://www.portalfiscal.inf.br/nfe'}
NSMAP = {None: "http://www.portalfiscal.inf.br/nfe"}
root = ET.Element("nfeProc", versao="4.00", nsmap=NSMAP)
parser = ET.XMLParser(encoding='utf-8')
docEnvio = ET.fromstring(envio.encode('utf-8'), parser=parser)
docRecibo = ET.fromstring(recibo.encode('utf-8'), parser=parser)
parser = ET.XMLParser(encoding="utf-8")
docEnvio = ET.fromstring(envio.encode("utf-8"), parser=parser)
docRecibo = ET.fromstring(recibo.encode("utf-8"), parser=parser)
nfe = _find_node(docEnvio, "NFe")
protocolo = _find_node(docRecibo, "protNFe")
if nfe is None or protocolo is None:
return b''
return b""
root.append(nfe)
root.append(protocolo)
return ET.tostring(root)
@ -96,6 +100,6 @@ def gerar_nfeproc_cancel(nfe_proc, cancelamento):
ev_cancelamento = _find_node(docCancel, "retEvento")
if ev_cancelamento is None:
return b''
return b""
docEnvio.append(ev_cancelamento)
return ET.tostring(docEnvio)

19
pytrustnfe/xml/__init__.py

@ -17,8 +17,8 @@ def recursively_empty(e):
def render_xml(path, template_name, remove_empty, **nfe):
nfe = recursively_normalize(nfe)
env = Environment(
loader=FileSystemLoader(path), extensions=['jinja2.ext.with_'])
env = Environment(loader=FileSystemLoader(
path), extensions=["jinja2.ext.with_"])
env.filters["normalize"] = filters.strip_line_feed
env.filters["normalize_str"] = filters.normalize_str
env.filters["format_percent"] = filters.format_percent
@ -27,9 +27,10 @@ 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).replace('\n', '')
parser = etree.XMLParser(remove_blank_text=True, remove_comments=True,
strip_cdata=False)
xml = template.render(**nfe).replace("\n", "")
parser = etree.XMLParser(
remove_blank_text=True, remove_comments=True, strip_cdata=False
)
root = etree.fromstring(xml, parser=parser)
for element in root.iter("*"): # remove espaços em branco
if element.text is not None and not element.text.strip():
@ -45,13 +46,13 @@ def render_xml(path, template_name, remove_empty, **nfe):
def sanitize_response(response):
parser = etree.XMLParser(encoding='utf-8')
tree = etree.fromstring(response.encode('UTF-8'), parser=parser)
parser = etree.XMLParser(encoding="utf-8")
tree = etree.fromstring(response.encode("UTF-8"), parser=parser)
# Remove namespaces inuteis na resposta
for elem in tree.getiterator():
if not hasattr(elem.tag, 'find'):
if not hasattr(elem.tag, "find"):
continue
i = elem.tag.find('}')
i = elem.tag.find("}")
if i >= 0:
elem.tag = elem.tag[i + 1:]
objectify.deannotate(tree, cleanup_namespaces=True)

27
pytrustnfe/xml/filters.py

@ -14,23 +14,24 @@ def normalize_str(string):
"""
if string:
if not isinstance(string, str):
string = str(string, 'utf-8', 'replace')
string = str(string, "utf-8", "replace")
string = string.encode('utf-8')
return normalize(
'NFKD', string.decode('utf-8')).encode('ASCII', 'ignore').decode()
return ''
string = string.encode("utf-8")
return (
normalize("NFKD", string.decode("utf-8")).encode("ASCII", "ignore").decode()
)
return ""
def strip_line_feed(string):
if string:
if not isinstance(string, str):
string = str(string, 'utf-8', 'replace')
string = str(string, "utf-8", "replace")
remap = {
ord('\t'): ' ',
ord('\n'): ' ',
ord('\f'): ' ',
ord('\r'): None, # Delete
ord("\t"): " ",
ord("\n"): " ",
ord("\f"): " ",
ord("\r"): None, # Delete
}
return string.translate(remap).strip()
return string
@ -45,7 +46,7 @@ def format_datetime(value):
"""
Format datetime
"""
dt_format = '%Y-%m-%dT%H:%M:%I'
dt_format = "%Y-%m-%dT%H:%M:%I"
if isinstance(value, datetime):
return value.strftime(dt_format)
return value
@ -55,7 +56,7 @@ def format_date(value):
"""
Format date
"""
dt_format = '%Y-%m-%d'
dt_format = "%Y-%m-%d"
if isinstance(value, date):
return value.strftime(dt_format)
return value
@ -63,5 +64,5 @@ def format_date(value):
def format_with_comma(value):
if isinstance(value, float):
return ('%.2f' % value).replace('.', ',')
return ("%.2f" % value).replace(".", ",")
return value

4
pytrustnfe/xml/validate.py

@ -7,8 +7,8 @@ import os
from lxml import etree
PATH = os.path.dirname(os.path.abspath(__file__))
SCHEMA = os.path.join(PATH, 'schemas/enviNFe_v4.00.xsd')
SCHEMA_DFE = os.path.join(PATH, 'schemas/distDFeInt_v1.01.xsd')
SCHEMA = os.path.join(PATH, "schemas/enviNFe_v4.00.xsd")
SCHEMA_DFE = os.path.join(PATH, "schemas/distDFeInt_v1.01.xsd")
def valida_nfe(xml_nfe, schema=SCHEMA):

4
requirements-dev.txt

@ -0,0 +1,4 @@
pytest==5.3.4
pytest-cov==2.8.1
black==19.10b0
flake8==3.7.9

82
setup.py

@ -1,51 +1,57 @@
# coding=utf-8
from setuptools import setup, find_packages
VERSION = "1.0.45"
VERSION = "1.0.47"
setup(
name="PyTrustNFe3",
name="pytrustnfe3",
version=VERSION,
author="Danimar Ribeiro",
author_email='danimaribeiro@gmail.com',
keywords=['nfe', 'mdf-e'],
author_email="danimaribeiro@gmail.com",
keywords=["nfe", "mdf-e"],
classifiers=[
'Development Status :: 5 - Production/Stable',
'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',
"Development Status :: 5 - Production/Stable",
"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',
'nfse/carioca/templates/*xml',
'nfse/bh/templates/*xml',
'nfse/mga/templates/*xml',
'nfse/aparecida/templates/*xml',
'xml/schemas/*xsd',
]},
url='https://github.com/danimaribeiro/PyTrustNFe',
license='LGPL-v2.1+',
description='PyTrustNFe é uma biblioteca para envio de NF-e',
long_description=open('README.md', 'r').read(),
long_description_content_type='text/markdown',
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",
"nfse/carioca/templates/*xml",
"nfse/bh/templates/*xml",
"nfse/mga/templates/*xml",
"nfse/aparecida/templates/*xml",
"xml/schemas/*xsd",
]
},
url="https://github.com/danimaribeiro/PyTrustNFe",
license="LGPL-v2.1+",
description="PyTrustNFe é uma biblioteca para envio de NF-e",
long_description=open("README.md", "r").read(),
long_description_content_type="text/markdown",
install_requires=[
'urllib3',
'xmlsec==1.3.3', # apt update;apt install libxmlsec1-dev pkg-config -y
'Jinja2 >= 2.8',
'pyOpenSSL >= 16.0.0, < 18',
'signxml >= 2.4.0',

154
tests/XMLs/natal_sent_xml.xml

@ -0,0 +1,154 @@
<?xml version="1.0" ?>
<EnviarLoteRpsEnvio xmlns="http://www.abrasf.org.br/ABRASF/arquivos/nfse.xsd">
<LoteRps Id="lote">
<NumeroLote>1</NumeroLote>
<Cnpj>24533572000102</Cnpj>
<InscricaoMunicipal>2143992</InscricaoMunicipal>
<QuantidadeRps>1</QuantidadeRps>
<ListaRps>
<Rps>
<InfRps Id="rps:1UNICA">
<IdentificacaoRps>
<Numero>1</Numero>
<Serie>UNICA</Serie>
<Tipo>1</Tipo>
</IdentificacaoRps>
<DataEmissao>2010-06-16T21:00:00</DataEmissao>
<NaturezaOperacao>1</NaturezaOperacao>
<RegimeEspecialTributacao>1</RegimeEspecialTributacao>
<OptanteSimplesNacional>1</OptanteSimplesNacional>
<IncentivadorCultural>2</IncentivadorCultural>
<Status>1</Status>
<Servico>
<Valores>
<ValorServicos>1.00</ValorServicos>
<ValorPis>0</ValorPis>
<ValorCofins>0</ValorCofins>
<ValorInss>0</ValorInss>
<ValorIr>0</ValorIr>
<ValorCsll>0</ValorCsll>
<IssRetido>2</IssRetido>
<ValorIss>2</ValorIss>
<OutrasRetencoes>2</OutrasRetencoes>
<BaseCalculo>0.00</BaseCalculo>
<Aliquota>2</Aliquota>
</Valores>
<ItemListaServico>01.07</ItemListaServico>
<CodigoCnae>6209100</CodigoCnae>
<Discriminacao>Sistema NFSe</Discriminacao>
<CodigoMunicipio>2408102</CodigoMunicipio>
</Servico>
<Prestador>
<Cnpj>24533572000102</Cnpj>
<InscricaoMunicipal>2143992</InscricaoMunicipal>
</Prestador>
<Tomador>
<IdentificacaoTomador>
<CpfCnpj>
<Cnpj>01812418000166</Cnpj>
</CpfCnpj>
<InscricaoMunicipal>2143992</InscricaoMunicipal>
</IdentificacaoTomador>
<RazaoSocial>MARIO</RazaoSocial>
<Endereco>
<Endereco>RUA GROBEIRO</Endereco>
<Numero>128</Numero>
<Complemento>ANDAR 14</Complemento>
<Bairro>Lagoa Nova</Bairro>
<CodigoMunicipio>3159506</CodigoMunicipio>
<Uf>BH</Uf>
<Cep>30160010</Cep>
</Endereco>
<Contato>
<Email>mario@email.com.br</Email>
</Contato>
</Tomador>
</InfRps>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference URI="#rps:1UNICA">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>CqxPWMfRKwecg6TEoFNA82URG5I=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>DIzLfhkM5HaOEMqHZcOXgAnHahgLuvtRF/ArPPyYKT/Dg0sg+ia/MjNchc2pH/eTOiqgj5s5UkxXgYBYHKVSuIvMfDqE+IYEanTYxzDqUhVwBlq4Vv9b8hNuE41D/qWCiC8zJswuOS8bPm+jHntFM/Fr7b6LAZV2Zuc5ITaCrkmRvKZ7HHhsffPk6gkb/NdzwdsYf8nSEbiFhujTXUswqkDWzQYxnwvp0ElU1Ev2SjqDG/oU0EG7vWMhuOP3wyDqVxF75cnEKW9RY1joiTxeiSBsYnivYC8t44MuHk9LAKaB17edlIEta+4MC9bAAmj2mkKxdJJJXjYHJLfw4WCJmg==</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIIMTCCBhmgAwIBAgIIDBYcAk2MYI0wDQYJKoZIhvcNAQELBQAwcjELMAkGA1UEBhMCQlIxEzARBgNVBAoTCklDUC1CcmFzaWwxNjA0BgNVBAsTLVNlY3JldGFyaWEgZGEgUmVjZWl0YSBGZWRlcmFsIGRvIEJyYXNpbCAtIFJGQjEWMBQGA1UEAxMNQUMgT05MSU5FIFJGQjAeFw0xOTA5MTgwMDAwMjZaFw0yMDA5MTcwMDAwMjZaMIIBADELMAkGA1UEBhMCQlIxCzAJBgNVBAgMAlJOMQ4wDAYDVQQHDAVOQVRBTDETMBEGA1UECgwKSUNQLUJyYXNpbDE2MDQGA1UECwwtU2VjcmV0YXJpYSBkYSBSZWNlaXRhIEZlZGVyYWwgZG8gQnJhc2lsIC0gUkZCMRYwFAYDVQQLDA1SRkIgZS1DTlBKIEExMSIwIAYDVQQLDBlBUiBTT0xJTU9FUyBDRVJUSUZJQ0FET1JBMRcwFQYDVQQLDA4yMjc1OTUzMTAwMDEwMzEyMDAGA1UEAwwpVFNNWCBTRVJWSUNPUyBERSBUSSBFSVJFTEk6MjM4MDkwNzAwMDAxOTAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDJ9LXn3SvQ02Gx9+aGiQh3cJ9A7J7w2eOWRVcjYo147tTsVNbpbJM56D5rvQ4uYLYTGEg9gnWRs/xkg/WwKmbC/ax3j/tJG62Boj8DF1QoGkiVfSOQbCYXnO9nDZ7fKRwUKXkLnpjNGnsZr/FIt6U3nNv/HJ5uwfmHRi3NPeMIkyQR+HogOUWChu8ZJz2quQu4tpNePV3GhW4h0o0Ggoq2YTIRhAidobjq7FWCu2jYkTEZ08h5fLn2bMLhaUgO1iBoJjzE1nD1C5wat4arxtzIz25q8lytp21q1kFD/Lkyh+RMjbngqeS8IBBrnMi0zeartgxtOKSdQD7PXzHFcuzzAgMBAAGjggM5MIIDNTCBoQYIKwYBBQUHAQEEgZQwgZEwXAYIKwYBBQUHMAKGUGh0dHA6Ly9pY3AtYnJhc2lsLnZwa2kudmFsaWRjZXJ0aWZpY2Fkb3JhLmNvbS5ici9hYy1vbmxpbmVyZmIvYWMtb25saW5lcmZidjIucDdiMDEGCCsGAQUFBzABhiVodHRwOi8vb2NzcC52YWxpZGNlcnRpZmljYWRvcmEuY29tLmJyMAkGA1UdEwQCMAAwHwYDVR0jBBgwFoAUkZp2jCuokxiYmHoD5MvstbAZJ/8wdQYDVR0gBG4wbDBqBgZgTAECATcwYDBeBggrBgEFBQcCARZSaHR0cDovL2ljcC1icmFzaWwudnBraS52YWxpZGNlcnRpZmljYWRvcmEuY29tLmJyL2FjLW9ubGluZXJmYi9kcGMtYWMtb25saW5lcmZiLnBkZjCCAQYGA1UdHwSB/jCB+zBVoFOgUYZPaHR0cDovL2ljcC1icmFzaWwudmFsaWRjZXJ0aWZpY2Fkb3JhLmNvbS5ici9hYy1vbmxpbmVyZmIvbGNyLWFjLW9ubGluZXJmYnYyLmNybDBWoFSgUoZQaHR0cDovL2ljcC1icmFzaWwyLnZhbGlkY2VydGlmaWNhZG9yYS5jb20uYnIvYWMtb25saW5lcmZiL2xjci1hYy1vbmxpbmVyZmJ2Mi5jcmwwSqBIoEaGRGh0dHA6Ly9yZXBvc2l0b3Jpby5pY3BicmFzaWwuZ292LmJyL2xjci9WQUxJRC9sY3ItYWMtb25saW5lcmZidjIuY3JsMA4GA1UdDwEB/wQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwgbIGA1UdEQSBqjCBp4ERdGhpYWdvQHNncC5uZXQuYnKgOAYFYEwBAwSgLwQtMDMwNjE5ODQwNTQ5NzkyODQ4MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwoCQGBWBMAQMCoBsEGVRISUFHTyBTQU1QQUlPIE1PTlRFTkVHUk+gGQYFYEwBAwOgEAQOMjM4MDkwNzAwMDAxOTCgFwYFYEwBAwegDgQMMDAwMDAwMDAwMDAwMA0GCSqGSIb3DQEBCwUAA4ICAQC+cp3UMxJPgs5JYHJmCJrCAw4qq4BMH7JWDUufvhiYKkWfWfhLMRidqRcPez+6u0EML5riw7uH3NoF/Byw5v8RY5/8diI7d5TB/SsM8wl9De3BiL5n9cC2oK5yF+OfvTFdMYPx7VnlGpVjeEEDAcflrX2zM6u6D/Me2xu0g0YnbLhYKcjjXBlmkm2fZrn0fo8te4OanvYMDbCiP1MROQUk95hxzn5/dZ8OdLuZ2Q+GwLGADKV4p6CEy6U8U/1Hi2Dt+py2hphRbGHdP/QB59dxayQSYEq38HSsdbgNnWrRe0L8W2xkoAkq2a0U0rW3sKae5dlrHs9hIsXrgDXHFl58snjTatz5g17IJ704ztshroRiWCiyJOsP12yvc8ildHN/u9li2YBxBr/9F3O3k2MKHYdVe10VNZMtmlhBcUlrC/bECbrJF/uIfGk5UMC9Tx8ROYsI8Rsfs1fS8KnjlOPav6ikoZUnHsZiU75Dp/9i9wxf5txPzjCEqG7UwjXEATC2ldlF+F1ZUbmREydeLcAHZu5eMBoRiNvl41E5LHbAfs42cnpI7QXbiqN30EcNkCVtYlcy04q00fJ4CnI1WANuslfa6srLnaiWVUSCeYiDBK3FS+mwa8ixT7MjhKs/6BOBoYlaDzZm242gbvh1eG8/XcDjT4BAGr5aA/cHnjEiXw==</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</Rps>
</ListaRps>
</LoteRps>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference URI="#lote">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>3OyPtYPIjU7Vm5azBnCaQVBCBxM=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>psdrJW0dE9bnGbK5UJchL/h4vRm5HUWPzkkX4LbCLJopsOAdR0l0M787njoX7b4tWwrPysgdNWfhGngNoiD6iVqXyNhbuHZftUJIlDUudAHIxjhrMJdiNVXQDz8lBYzOta8HMMGQqHzzEg6DCVSZ1zeF5D2Nu2I7UL12P3jTDPtrapLC0YHxcRamGcG48I5tMo1qCKPxtkePkwx13PahkET+aExnb+5sexl+nhzfFJIKC0mfIRtoLyLEidNxalzzfMLbDxwe6WGgJS66+VKOeYNL89Gn2yp17uMZdnS08a6tUKy8t7sZt9W3ktrrjCofRlkwK8dq8jsh/GIw8hXLkg==</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIIMTCCBhmgAwIBAgIIDBYcAk2MYI0wDQYJKoZIhvcNAQELBQAwcjELMAkGA1UE
BhMCQlIxEzARBgNVBAoTCklDUC1CcmFzaWwxNjA0BgNVBAsTLVNlY3JldGFyaWEg
ZGEgUmVjZWl0YSBGZWRlcmFsIGRvIEJyYXNpbCAtIFJGQjEWMBQGA1UEAxMNQUMg
T05MSU5FIFJGQjAeFw0xOTA5MTgwMDAwMjZaFw0yMDA5MTcwMDAwMjZaMIIBADEL
MAkGA1UEBhMCQlIxCzAJBgNVBAgMAlJOMQ4wDAYDVQQHDAVOQVRBTDETMBEGA1UE
CgwKSUNQLUJyYXNpbDE2MDQGA1UECwwtU2VjcmV0YXJpYSBkYSBSZWNlaXRhIEZl
ZGVyYWwgZG8gQnJhc2lsIC0gUkZCMRYwFAYDVQQLDA1SRkIgZS1DTlBKIEExMSIw
IAYDVQQLDBlBUiBTT0xJTU9FUyBDRVJUSUZJQ0FET1JBMRcwFQYDVQQLDA4yMjc1
OTUzMTAwMDEwMzEyMDAGA1UEAwwpVFNNWCBTRVJWSUNPUyBERSBUSSBFSVJFTEk6
MjM4MDkwNzAwMDAxOTAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDJ
9LXn3SvQ02Gx9+aGiQh3cJ9A7J7w2eOWRVcjYo147tTsVNbpbJM56D5rvQ4uYLYT
GEg9gnWRs/xkg/WwKmbC/ax3j/tJG62Boj8DF1QoGkiVfSOQbCYXnO9nDZ7fKRwU
KXkLnpjNGnsZr/FIt6U3nNv/HJ5uwfmHRi3NPeMIkyQR+HogOUWChu8ZJz2quQu4
tpNePV3GhW4h0o0Ggoq2YTIRhAidobjq7FWCu2jYkTEZ08h5fLn2bMLhaUgO1iBo
JjzE1nD1C5wat4arxtzIz25q8lytp21q1kFD/Lkyh+RMjbngqeS8IBBrnMi0zear
tgxtOKSdQD7PXzHFcuzzAgMBAAGjggM5MIIDNTCBoQYIKwYBBQUHAQEEgZQwgZEw
XAYIKwYBBQUHMAKGUGh0dHA6Ly9pY3AtYnJhc2lsLnZwa2kudmFsaWRjZXJ0aWZp
Y2Fkb3JhLmNvbS5ici9hYy1vbmxpbmVyZmIvYWMtb25saW5lcmZidjIucDdiMDEG
CCsGAQUFBzABhiVodHRwOi8vb2NzcC52YWxpZGNlcnRpZmljYWRvcmEuY29tLmJy
MAkGA1UdEwQCMAAwHwYDVR0jBBgwFoAUkZp2jCuokxiYmHoD5MvstbAZJ/8wdQYD
VR0gBG4wbDBqBgZgTAECATcwYDBeBggrBgEFBQcCARZSaHR0cDovL2ljcC1icmFz
aWwudnBraS52YWxpZGNlcnRpZmljYWRvcmEuY29tLmJyL2FjLW9ubGluZXJmYi9k
cGMtYWMtb25saW5lcmZiLnBkZjCCAQYGA1UdHwSB/jCB+zBVoFOgUYZPaHR0cDov
L2ljcC1icmFzaWwudmFsaWRjZXJ0aWZpY2Fkb3JhLmNvbS5ici9hYy1vbmxpbmVy
ZmIvbGNyLWFjLW9ubGluZXJmYnYyLmNybDBWoFSgUoZQaHR0cDovL2ljcC1icmFz
aWwyLnZhbGlkY2VydGlmaWNhZG9yYS5jb20uYnIvYWMtb25saW5lcmZiL2xjci1h
Yy1vbmxpbmVyZmJ2Mi5jcmwwSqBIoEaGRGh0dHA6Ly9yZXBvc2l0b3Jpby5pY3Bi
cmFzaWwuZ292LmJyL2xjci9WQUxJRC9sY3ItYWMtb25saW5lcmZidjIuY3JsMA4G
A1UdDwEB/wQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwgbIG
A1UdEQSBqjCBp4ERdGhpYWdvQHNncC5uZXQuYnKgOAYFYEwBAwSgLwQtMDMwNjE5
ODQwNTQ5NzkyODQ4MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwoCQGBWBMAQMC
oBsEGVRISUFHTyBTQU1QQUlPIE1PTlRFTkVHUk+gGQYFYEwBAwOgEAQOMjM4MDkw
NzAwMDAxOTCgFwYFYEwBAwegDgQMMDAwMDAwMDAwMDAwMA0GCSqGSIb3DQEBCwUA
A4ICAQC+cp3UMxJPgs5JYHJmCJrCAw4qq4BMH7JWDUufvhiYKkWfWfhLMRidqRcP
ez+6u0EML5riw7uH3NoF/Byw5v8RY5/8diI7d5TB/SsM8wl9De3BiL5n9cC2oK5y
F+OfvTFdMYPx7VnlGpVjeEEDAcflrX2zM6u6D/Me2xu0g0YnbLhYKcjjXBlmkm2f
Zrn0fo8te4OanvYMDbCiP1MROQUk95hxzn5/dZ8OdLuZ2Q+GwLGADKV4p6CEy6U8
U/1Hi2Dt+py2hphRbGHdP/QB59dxayQSYEq38HSsdbgNnWrRe0L8W2xkoAkq2a0U
0rW3sKae5dlrHs9hIsXrgDXHFl58snjTatz5g17IJ704ztshroRiWCiyJOsP12yv
c8ildHN/u9li2YBxBr/9F3O3k2MKHYdVe10VNZMtmlhBcUlrC/bECbrJF/uIfGk5
UMC9Tx8ROYsI8Rsfs1fS8KnjlOPav6ikoZUnHsZiU75Dp/9i9wxf5txPzjCEqG7U
wjXEATC2ldlF+F1ZUbmREydeLcAHZu5eMBoRiNvl41E5LHbAfs42cnpI7QXbiqN3
0EcNkCVtYlcy04q00fJ4CnI1WANuslfa6srLnaiWVUSCeYiDBK3FS+mwa8ixT7Mj
hKs/6BOBoYlaDzZm242gbvh1eG8/XcDjT4BAGr5aA/cHnjEiXw==
</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</EnviarLoteRpsEnvio>

10
tests/XMLs/paulistana_signature.xml

@ -1,4 +1,4 @@
<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><ValorCargaTributaria/><FonteCargaTributaria/></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><Bairro>Corrego</Bairro><Cidade>Floripa</Cidade><UF>SC</UF><CEP>88037240</CEP></EnderecoTomador><Discriminacao>Venda de servico</Discriminacao><ValorCargaTributaria/><FonteCargaTributaria/></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"/>
@ -8,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>IAh8GGlbp/Tnqma+2RZ7UrGZhTc=</DigestValue>
<DigestValue>Thwvs++WdhRuXOVgMxXTY/9Zih0=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>gjkMTCq0uuaX8tkRBlLjgybn8a2O4Axl6HHq1MN8nnEMliERcziU3pa3r1jbghlE
EUyIO8bTZ0V7c05pQvHQgVUHcSo6vHld4ZQNk7JfMfmpez4uxrUeuSrSqSLCwT9W
NmTY9EJ16GyrQNELw+SkYuEFOvqZTU3qjDZkLddQ8bc=</SignatureValue>
<SignatureValue>fvJR0msutiLI9KpUY/8VDPqmDeGYpXt/JvY6LUQZlGjjGb71jM2cLEHotM4lwJLi
WKLvhSBbaLQQm/OFm1KbQ8TRrEJl8NMYv2bABNoH9OxIn5Ecnb4jxCCAaIDN3iXy
B7oYCq5nqtfsFGplU29enQ//1SrRTE4MDsOwoN8bX0c=</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIICMTCCAZqgAwIBAgIQfYOsIEVuAJ1FwwcTrY0t1DANBgkqhkiG9w0BAQUFADBX

1
tests/XMLs/xml_send_rps_batch_to_paulistana.xml
File diff suppressed because it is too large
View File

56
tests/const.py

@ -0,0 +1,56 @@
LOTE_RPS = [
{
'assinatura': '123',
'serie': '1',
'numero': str(i),
'data_emissao': '2016-08-29',
'codigo_atividade': '07498',
'total_servicos': '2.00',
'total_deducoes': '3.00',
'prestador': {
'inscricao_municipal': '123456'
},
'tomador': {
'tipo_cpfcnpj': 1,
'cpf_cnpj': '12345678923256',
'inscricao_municipal': '123456',
'razao_social': 'Trustcode',
'tipo_logradouro': '1',
'complemento': 'aaa',
'logradouro': 'Vinicius de Moraes, 42',
'numero': '42',
'bairro': 'Corrego',
'cidade': 'Floripa',
'uf': 'SC',
'cep': '88037240',
'email': 'user@user.com'
},
'codigo_atividade': '07498',
'aliquota_atividade': '5.00',
'descricao': 'Venda de servico'
} for i in range(5)
]
DEFAULT_RPS = [
{
'assinatura': '123',
'serie': '1',
'numero': '1',
'data_emissao': '2016-08-29',
'codigo_atividade': '07498',
'prestador': {
'inscricao_municipal': '123456'
},
'tomador': {
'tipo_cpfcnpj': 1,
'cpf_cnpj': '12345678923256',
},
}
]
NFSE = {
'cpf_cnpj': '12345678901234',
'data_inicio': '2016-08-29',
'data_fim': '2016-08-29',
'lista_rps': []
}

68
tests/test_assinatura.py

@ -1,9 +1,9 @@
# coding=utf-8
'''
"""
Created on Jun 14, 2015
@author: danimar
'''
"""
import os
import os.path
import unittest
@ -11,18 +11,22 @@ from lxml import etree
from pytrustnfe.nfe.assinatura import Assinatura
XML_ASSINAR = '<Envelope xmlns="urn:envelope">' \
' <Data Id="NFe43150602261542000143550010000000761792265342">'\
' Hello, World!' \
' </Data>' \
'</Envelope>'
XML_ASSINAR = (
'<Envelope xmlns="urn:envelope">'
' <Data Id="NFe43150602261542000143550010000000761792265342">'
" Hello, World!"
" </Data>"
"</Envelope>"
)
XML_ERRADO = '<Envelope xmlns="urn:envelope">' \
' <Data Id="NFe">' \
' Hello, World!' \
' </Data>' \
'</Envelope>'
XML_ERRADO = (
'<Envelope xmlns="urn:envelope">'
' <Data Id="NFe">'
" Hello, World!"
" </Data>"
"</Envelope>"
)
class test_assinatura(unittest.TestCase):
@ -30,26 +34,36 @@ 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'), 'rb').read()
signer = Assinatura(pfx, '123')
self.assertRaises(Exception, signer.assina_xml, signer,
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')
"NFe43150602261542000143550010000000761792265342",
)
def test_assinar_xml_invalido(self):
pfx = open(os.path.join(self.caminho, 'teste.pfx'), 'rb').read()
signer = Assinatura(pfx, '123456')
self.assertRaises(Exception, signer.assina_xml, signer,
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')
"NFe43150602261542000143550010000000761792265342",
)
@unittest.skip
def test_assinar_xml_valido(self):
pfx = open(os.path.join(self.caminho, 'teste.pfx'), 'rb').read()
signer = Assinatura(pfx, '123456')
pfx = open(os.path.join(self.caminho, "teste.pfx"), "rb").read()
signer = Assinatura(pfx, "123456")
xml = signer.assina_xml(
etree.fromstring(XML_ASSINAR),
'NFe43150602261542000143550010000000761792265342')
xml_assinado = open(os.path.join(self.caminho,
'xml_valido_assinado.xml'),
'r').read()
self.assertEqual(xml_assinado, xml, 'Xml assinado é inválido')
"NFe43150602261542000143550010000000761792265342",
)
xml_assinado = open(
os.path.join(self.caminho, "xml_valido_assinado.xml"), "r"
).read()
self.assertEqual(xml_assinado, xml, "Xml assinado é inválido")

110
tests/test_certificado.py

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

6
tests/test_danfe.py

@ -12,8 +12,8 @@ class test_danfe(unittest.TestCase):
caminho = os.path.dirname(__file__)
def test_can_generate_danfe(self):
path = os.path.join(os.path.dirname(__file__), 'XMLs')
xml_string = open(os.path.join(path, 'NFe00000857.xml'), "r").read()
path = os.path.join(os.path.dirname(__file__), "XMLs")
xml_string = open(os.path.join(path, "NFe00000857.xml"), "r").read()
# xml_string = open('/home/danimar/Downloads/NFe (5).xml', "r").read()
xml_element = etree.fromstring(xml_string)
@ -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='wb') as oFile:
with tempfile.TemporaryFile(mode="wb") as oFile:
oDanfe.writeto_pdf(oFile)

13
tests/test_ginfes.py

@ -13,12 +13,11 @@ class test_nfse_ginfes(unittest.TestCase):
@unittest.skip
def test_consulta_situacao_lote(self):
pfx_source = open('/home/danimar/Downloads/machado.pfx', 'rb').read()
pfx = Certificado(pfx_source, '123456789')
pfx_source = open("/home/danimar/Downloads/machado.pfx", "rb").read()
pfx = Certificado(pfx_source, "123456789")
dados = {'ambiente': 'homologacao'}
retorno = consultar_situacao_lote(
pfx, consulta=dados, ambiente='homologacao')
dados = {"ambiente": "homologacao"}
retorno = consultar_situacao_lote(pfx, consulta=dados, ambiente="homologacao")
self.assertNotEqual(retorno['received_xml'], '')
self.assertEqual(retorno['object'].Cabecalho.Sucesso, True)
self.assertNotEqual(retorno["received_xml"], "")
self.assertEqual(retorno["object"].Cabecalho.Sucesso, True)

145
tests/test_nfse_paulistana.py

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

35
tests/test_nfse_paulistana_email_tomador.py

@ -0,0 +1,35 @@
# coding=utf-8
import unittest
from pytrustnfe.xml import render_xml, sanitize_response
from tests.const import NFSE, DEFAULT_RPS
template_path = 'pytrustnfe/nfse/paulistana/templates'
def _get_nfse(lista_rps):
nfse = NFSE
nfse['lista_rps'] = lista_rps
return nfse
def get_objects(nfse):
xml_rps = render_xml(template_path, 'EnvioRPS.xml', False, nfse=nfse)
_, obj_rps = sanitize_response(xml_rps)
xml_lote_rps = render_xml(template_path, 'EnvioLoteRPS.xml', False, nfse=nfse)
_, obj_lote_rps = sanitize_response(xml_lote_rps)
return obj_rps, obj_lote_rps
class test_nfse_paulistana_email_tomador(unittest.TestCase):
def test_rps_sem_email(self):
nfse = _get_nfse(DEFAULT_RPS)
obj_rps, obj_lote_rps = get_objects(nfse)
self.assertFalse(hasattr(obj_rps.RPS, 'EmailTomador'))
self.assertFalse(hasattr(obj_lote_rps.RPS, 'EmailTomador'))

54
tests/test_nfse_paulistana_endereco_tomador.py

@ -0,0 +1,54 @@
# coding=utf-8
import unittest
from pytrustnfe.xml import render_xml, sanitize_response
from tests.const import NFSE, DEFAULT_RPS
attrs = ['TipoLogradouro', 'Logradouro', 'NumeroEndereco', 'ComplementoEndereco', 'Bairro', 'CEP']
template_path = 'pytrustnfe/nfse/paulistana/templates'
def _get_nfse(lista_rps):
nfse = NFSE
nfse['lista_rps'] = lista_rps
return nfse
def get_objects(nfse):
xml_rps = render_xml(template_path, 'EnvioRPS.xml', False, nfse=nfse)
_, obj_rps = sanitize_response(xml_rps)
xml_lote_rps = render_xml(template_path, 'EnvioLoteRPS.xml', False, nfse=nfse)
_, obj_lote_rps = sanitize_response(xml_lote_rps)
return obj_rps, obj_lote_rps
class test_nfse_paulistana_endereco_tomador(unittest.TestCase):
def test_rps_sem_cidade(self):
nfse = _get_nfse(DEFAULT_RPS)
obj_rps, obj_lote_rps = get_objects(nfse)
self.assertFalse(hasattr(obj_rps.RPS, 'EnderecoTomador'))
self.assertFalse(hasattr(obj_lote_rps.RPS, 'EnderecoTomador'))
def test_rps_sem_dados_endereco(self):
lista_rps = DEFAULT_RPS
for rps in lista_rps:
rps['tomador']['cidade'] = 'Florianópolis'
nfse = _get_nfse(lista_rps)
obj_rps, obj_lote_rps = get_objects(nfse)
self.assertTrue(hasattr(obj_rps.RPS, 'EnderecoTomador'))
self.assertTrue(hasattr(obj_lote_rps.RPS, 'EnderecoTomador'))
for attr in attrs:
self.assertFalse(hasattr(obj_rps.RPS.EnderecoTomador, attr))
self.assertFalse(hasattr(obj_lote_rps.RPS.EnderecoTomador, attr))

29
tests/test_nfse_paulistana_para_lote.py

@ -0,0 +1,29 @@
# coding=utf-8
import os.path
import unittest
from pytrustnfe.xml import render_xml, sanitize_response
from tests.const import LOTE_RPS, NFSE
def _get_nfse():
nfse = NFSE
nfse['lista_rps'] = LOTE_RPS
return nfse
class test_nfse_paulistana_para_lote(unittest.TestCase):
xml_path = os.path.join(os.path.dirname(__file__), 'XMLs')
template_path = 'pytrustnfe/nfse/paulistana/templates'
BATCH_SIZE = len(LOTE_RPS)
def test_envio_nfse(self):
nfse = _get_nfse()
xml_send = render_xml(self.template_path, 'EnvioLoteRPS.xml', False, nfse=nfse)
expected_xml = open(os.path.join(self.xml_path, 'xml_send_rps_batch_to_paulistana.xml'), 'r').read()
_, obj = sanitize_response(xml_send)
self.assertEqual(obj.Cabecalho.QtdRPS, self.BATCH_SIZE)
self.assertEqual(xml_send, expected_xml)

60
tests/test_nfse_paulistana_tipo_cpfcnpj.py

@ -0,0 +1,60 @@
# coding=utf-8
import os.path
import unittest
from pytrustnfe.xml import render_xml, sanitize_response
from tests.const import DEFAULT_RPS, NFSE
template_path = 'pytrustnfe/nfse/paulistana/templates'
def _get_nfse(tipo_cpfcnpj):
nfse = NFSE
lista_rps = DEFAULT_RPS
for rps in lista_rps:
rps['tomador']['tipo_cpfcnpj'] = tipo_cpfcnpj
rps['tomador']['cpf_cnpj'] = '12345678923256'
nfse['lista_rps'] = lista_rps
return nfse
def get_objects(nfse):
xml_rps = render_xml(template_path, 'EnvioRPS.xml', False, nfse=nfse)
_, obj_rps = sanitize_response(xml_rps)
xml_lote_rps = render_xml(template_path, 'EnvioLoteRPS.xml', False, nfse=nfse)
_, obj_lote_rps = sanitize_response(xml_lote_rps)
return obj_rps, obj_lote_rps
class test_nfse_paulistana_tipo_cpfcnpj(unittest.TestCase):
def test_tipo_cpfcnpj_1(self):
nfse = _get_nfse(tipo_cpfcnpj=1)
obj_rps, obj_lote_rps = get_objects(nfse)
self.assertTrue(hasattr(obj_rps.RPS, 'CPFCNPJTomador'))
self.assertTrue(hasattr(obj_rps.RPS.CPFCNPJTomador, 'CPF'))
self.assertTrue(hasattr(obj_lote_rps.RPS, 'CPFCNPJTomador'))
self.assertTrue(hasattr(obj_lote_rps.RPS.CPFCNPJTomador, 'CPF'))
def test_tipo_cpfcnpj_2(self):
nfse = _get_nfse(tipo_cpfcnpj=2)
obj_rps, obj_lote_rps = get_objects(nfse)
self.assertTrue(hasattr(obj_rps.RPS, 'CPFCNPJTomador'))
self.assertTrue(hasattr(obj_rps.RPS.CPFCNPJTomador, 'CNPJ'))
self.assertTrue(hasattr(obj_lote_rps.RPS, 'CPFCNPJTomador'))
self.assertTrue(hasattr(obj_lote_rps.RPS.CPFCNPJTomador, 'CNPJ'))
def test_tipo_cpfcnpj_3(self):
nfse = _get_nfse(tipo_cpfcnpj=3)
obj_rps, obj_lote_rps = get_objects(nfse)
self.assertFalse(hasattr(obj_rps.RPS, 'CPFCNPJTomador'))

47
tests/test_nfse_paulistana_valores_default.py

@ -0,0 +1,47 @@
# coding=utf-8
import os.path
import unittest
from pytrustnfe.xml import render_xml, sanitize_response
from tests.const import DEFAULT_RPS, NFSE
default_values = {
'TipoRPS': 'RPS',
'TributacaoRPS': 'T',
'ValorCOFINS': 0.0,
'ValorINSS': 0.0,
'ValorIR': 0.0,
'ValorPIS': 0.0,
'ValorCSLL': 0.0,
'ISSRetido': False
}
attrs = ['TipoRPS', 'TributacaoRPS', 'ValorPIS', 'ValorCOFINS', 'ValorINSS', 'ValorIR', 'ValorCSLL', 'ISSRetido']
def _get_nfse():
nfse = NFSE
nfse['lista_rps'] = DEFAULT_RPS
return nfse
class test_nfse_paulistana_valores_default(unittest.TestCase):
template_path = 'pytrustnfe/nfse/paulistana/templates'
xml_path = os.path.join(os.path.dirname(__file__), 'XMLs')
nfse = _get_nfse()
def test_rps_sem_valores(self):
xml_rps = render_xml(self.template_path, 'EnvioRPS.xml', False, nfse=self.nfse)
_, obj = sanitize_response(xml_rps)
for attr in attrs:
self.assertEqual(getattr(obj.RPS, attr), default_values[attr])
def test_lote_rps_sem_valores(self):
xml_lote_rps = render_xml(self.template_path, 'EnvioLoteRPS.xml', False, nfse=self.nfse)
_, obj = sanitize_response(xml_lote_rps)
for attr in attrs:
self.assertEqual(getattr(obj.RPS, attr), default_values[attr])

37
tests/test_servidores.py

@ -1,47 +1,46 @@
# coding=utf-8
'''
"""
Created on Jun 14, 2015
@author: danimar
'''
"""
import unittest
from pytrustnfe.Servidores import localizar_url, localizar_qrcode
url_ba = 'https://nfe.sefaz.ba.gov.br/webservices/NFeAutorizacao4/NFeAutoriza\
cao4.asmx?wsdl'
url_ba = "https://nfe.sefaz.ba.gov.br/webservices/NFeAutorizacao4/NFeAutoriza\
cao4.asmx?wsdl"
url_sp = 'https://nfe.fazenda.sp.gov.br/ws/nfeautorizacao4.asmx?wsdl'
url_sp = "https://nfe.fazenda.sp.gov.br/ws/nfeautorizacao4.asmx?wsdl"
url_qrcode_homologacao_sp = 'https://homologacao.nfce.fazenda.sp.gov.br/NFCEConsultaPublica/Paginas/ConstultaQRCode.aspx'
url_qrcode_homologacao_sp = "https://homologacao.nfce.fazenda.sp.gov.br/NFCEConsultaPublica/Paginas/ConstultaQRCode.aspx"
url_sc = 'https://nfe.svrs.rs.gov.br/ws/NfeAutorizacao/NFeAutorizacao4.asmx?wsdl'
url_sc = "https://nfe.svrs.rs.gov.br/ws/NfeAutorizacao/NFeAutorizacao4.asmx?wsdl"
url_rs = 'https://nfe.sefazrs.rs.gov.br/ws/NfeAutorizacao/NFeAutorizacao4.asmx?wsdl'
url_rs = "https://nfe.sefazrs.rs.gov.br/ws/NfeAutorizacao/NFeAutorizacao4.asmx?wsdl"
url_cad_rs = 'https://cad.sefazrs.rs.gov.br/ws/cadconsultacadastro/cadconsultacadastro4.asmx?wsdl'
url_cad_rs = "https://cad.sefazrs.rs.gov.br/ws/cadconsultacadastro/cadconsultacadastro4.asmx?wsdl"
url_cad_sc = 'https://cad.svrs.rs.gov.br/ws/cadconsultacadastro/cadconsulta\
cadastro4.asmx?wsdl'
url_cad_sc = "https://cad.svrs.rs.gov.br/ws/cadconsultacadastro/cadconsulta\
cadastro4.asmx?wsdl"
class test_servidores(unittest.TestCase):
def test_localizar_url(self):
url = localizar_url('NfeAutorizacao', '29', ambiente=1)
url = localizar_url("NfeAutorizacao", "29", ambiente=1)
self.assertEqual(url, url_ba)
url = localizar_url('NfeAutorizacao', '35', ambiente=1)
url = localizar_url("NfeAutorizacao", "35", ambiente=1)
self.assertEqual(url, url_sp)
url = localizar_url('NfeAutorizacao', '42', ambiente=1)
url = localizar_url("NfeAutorizacao", "42", ambiente=1)
self.assertEqual(url, url_sc)
url = localizar_url('NfeAutorizacao', '43', ambiente=1)
url = localizar_url("NfeAutorizacao", "43", ambiente=1)
self.assertEqual(url, url_rs)
url = localizar_url('NfeConsultaCadastro', '43', ambiente=2)
url = localizar_url("NfeConsultaCadastro", "43", ambiente=2)
self.assertEqual(url, url_cad_rs)
url = localizar_url('NfeConsultaCadastro', '42', ambiente=2)
url = localizar_url("NfeConsultaCadastro", "42", ambiente=2)
self.assertEqual(url, url_cad_sc)
def test_localizar_qrcode(self):
url = localizar_qrcode('35')
url = localizar_qrcode("35")
self.assertEqual(url, url_qrcode_homologacao_sp)

118
tests/test_utils.py

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

17
tests/test_xml.py

@ -1,9 +1,9 @@
# coding=utf-8
'''
"""
Created on Jun 14, 2015
@author: danimar
'''
"""
import unittest
from datetime import datetime
from pytrustnfe.xml.filters import normalize_str
@ -14,17 +14,16 @@ from pytrustnfe.xml.filters import format_datetime
class test_xmlfilters(unittest.TestCase):
def test_xmlfilters(self):
word = normalize_str('ação café pó pá veêm')
self.assertEqual(word, 'acao cafe po pa veem')
word = normalize_str("ação café pó pá veêm")
self.assertEqual(word, "acao cafe po pa veem")
self.assertEqual(1.5, format_percent(150))
self.assertEqual('aa', format_date('aa'))
self.assertEqual('aa', format_datetime('aa'))
self.assertEqual("aa", format_date("aa"))
self.assertEqual("aa", format_datetime("aa"))
dt = datetime(2016, 9, 17, 12, 12, 12)
self.assertEqual('2016-09-17', format_date(dt.date()))
self.assertEqual('2016-09-17T12:12:12', format_datetime(dt))
self.assertEqual("2016-09-17", format_date(dt.date()))
self.assertEqual("2016-09-17T12:12:12", format_datetime(dt))
word = strip_line_feed("olá\ncomo vai\r senhor ")
self.assertEqual(word, "olá como vai senhor")

33
tests/test_xml_serializacao.py

@ -8,29 +8,30 @@ from pytrustnfe.xml import sanitize_response
class test_xml_serializacao(unittest.TestCase):
def test_serializacao_default(self):
path = os.path.join(os.path.dirname(__file__), 'XMLs')
xml = render_xml(path, 'jinja_template.xml', False, tag1='oi',
tag2='ola', tag3='comovai')
path = os.path.join(os.path.dirname(__file__), "XMLs")
xml = render_xml(
path, "jinja_template.xml", False, tag1="oi", tag2="ola", tag3="comovai"
)
result = open(os.path.join(path, 'jinja_result.xml'), 'r').read()
result = open(os.path.join(path, "jinja_result.xml"), "r").read()
self.assertEqual(xml + "\n", result)
def test_serializacao_remove_empty(self):
path = os.path.join(os.path.dirname(__file__), 'XMLs')
xmlElem = render_xml(path, 'jinja_template.xml', True, tag1='oi',
tag2='ola', tag3='comovai')
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, encoding=str)
result = open(os.path.join(path, 'jinja_remove_empty.xml'), 'r').read()
self.assertEqual(xml + '\n', result)
result = open(os.path.join(path, "jinja_remove_empty.xml"), "r").read()
self.assertEqual(xml + "\n", result)
def test_sanitize_response(self):
path = os.path.join(os.path.dirname(__file__), 'XMLs')
xml_to_clear = open(os.path.join(path, 'jinja_result.xml'), 'r').read()
path = os.path.join(os.path.dirname(__file__), "XMLs")
xml_to_clear = open(os.path.join(path, "jinja_result.xml"), "r").read()
xml, obj = sanitize_response(xml_to_clear)
self.assertEqual(xml, xml_to_clear)
self.assertEqual(obj.tpAmb, 'oi')
self.assertEqual(obj.CNPJ, 'ola')
self.assertEqual(obj.indNFe, '')
self.assertEqual(obj.indEmi, 'comovai')
self.assertEqual(obj.tpAmb, "oi")
self.assertEqual(obj.CNPJ, "ola")
self.assertEqual(obj.indNFe, "")
self.assertEqual(obj.indEmi, "comovai")
Loading…
Cancel
Save