Browse Source

Merge pull request #265 from danimaribeiro/gabicavalcante-master3

Adicionando NFSe de Natal
pull/266/head
Danimar Ribeiro 6 years ago
committed by GitHub
parent
commit
09428c65a3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      .flake8
  2. 4
      .gitignore
  3. 165
      docs/conf.py
  4. 1024
      pytrustnfe/Servidores.py
  5. 8
      pytrustnfe/__init__.py
  6. 12
      pytrustnfe/certificado.py
  7. 12
      pytrustnfe/client.py
  8. 4
      pytrustnfe/exceptions.py
  9. 165
      pytrustnfe/nfe/__init__.py
  10. 27
      pytrustnfe/nfe/assinatura.py
  11. 358
      pytrustnfe/nfe/danfce.py
  12. 1066
      pytrustnfe/nfe/danfe.py
  13. 36
      pytrustnfe/nfe/patch.py
  14. 43
      pytrustnfe/nfse/aparecida/__init__.py
  15. 23
      pytrustnfe/nfse/assinatura.py
  16. 76
      pytrustnfe/nfse/betha/__init__.py
  17. 51
      pytrustnfe/nfse/bh/__init__.py
  18. 21
      pytrustnfe/nfse/bh/assinatura.py
  19. 53
      pytrustnfe/nfse/carioca/__init__.py
  20. 69
      pytrustnfe/nfse/dsf/__init__.py
  21. 96
      pytrustnfe/nfse/floripa/__init__.py
  22. 59
      pytrustnfe/nfse/ginfes/__init__.py
  23. 40
      pytrustnfe/nfse/imperial/__init__.py
  24. 47
      pytrustnfe/nfse/mga/__init__.py
  25. 17
      pytrustnfe/nfse/mga/assinatura.py
  26. 116
      pytrustnfe/nfse/natal/__init__.py
  27. 8
      pytrustnfe/nfse/natal/templates/EnvelopeSoap.xml
  28. 111
      pytrustnfe/nfse/natal/templates/Exemplo_LoteRPS.xml
  29. 112
      pytrustnfe/nfse/natal/templates/Rps.xml
  30. 3
      pytrustnfe/nfse/natal/templates/cabecalho.xml
  31. 15
      pytrustnfe/nfse/natal/templates/cancelarNfse.xml
  32. 7
      pytrustnfe/nfse/natal/templates/consultarLoteRps.xml
  33. 11
      pytrustnfe/nfse/natal/templates/recepcionarLoteRps.xml
  34. 68
      pytrustnfe/nfse/paulistana/__init__.py
  35. 64
      pytrustnfe/nfse/simpliss/__init__.py
  36. 34
      pytrustnfe/nfse/susesu/__init__.py
  37. 9
      pytrustnfe/nfse/susesu/templates/EnviarNota.xml
  38. 262
      pytrustnfe/urls.py
  39. 60
      pytrustnfe/utils.py
  40. 19
      pytrustnfe/xml/__init__.py
  41. 27
      pytrustnfe/xml/filters.py
  42. 4
      pytrustnfe/xml/validate.py
  43. 4
      requirements-dev.txt
  44. 76
      setup.py
  45. 154
      tests/XMLs/natal_sent_xml.xml
  46. 72
      tests/test_assinatura.py
  47. 110
      tests/test_certificado.py
  48. 6
      tests/test_danfe.py
  49. 13
      tests/test_ginfes.py
  50. 145
      tests/test_nfse_paulistana.py
  51. 37
      tests/test_servidores.py
  52. 134
      tests/test_utils.py
  53. 17
      tests/test_xml.py
  54. 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 .cache
.pytest_cache .pytest_cache
.vscode/ .vscode/
*.pfx
sent_xml.xml
received_xml.xml
nfse-exemplo.py

165
docs/conf.py

@ -19,89 +19,89 @@ import shlex
# If extensions (or modules to document with autodoc) are in another directory, # If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the # add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here. # documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------ # -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here. # If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be # Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones. # ones.
extensions = [ extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.coverage',
"sphinx.ext.autodoc",
"sphinx.ext.coverage",
] ]
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix(es) of source filenames. # The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string: # You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md'] # source_suffix = ['.rst', '.md']
source_suffix = '.rst'
source_suffix = ".rst"
# The encoding of source files. # The encoding of source files.
#source_encoding = 'utf-8-sig'
# source_encoding = 'utf-8-sig'
# The master toctree document. # The master toctree document.
master_doc = 'index'
master_doc = "index"
# General information about the project. # 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 # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = '1.0'
version = "1.0"
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '1.0'
release = "1.0"
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.
# #
# This is also used if you do content translation via gettext catalogs. # This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases. # 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 # There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used: # non-false value, then it is used:
#today = ''
# today = ''
# Else, today_fmt is used as the format for a strftime call. # Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and # List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files. # 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 # The reST default role (used for this markup: `text`) to use for all
# documents. # documents.
#default_role = None
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text. # If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# add_function_parentheses = True
# If true, the current module name will be prepended to all description # If true, the current module name will be prepended to all description
# unit titles (such as .. function::). # unit titles (such as .. function::).
#add_module_names = True
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the # If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default. # output. They are ignored by default.
#show_authors = False
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use. # 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. # A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents. # If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# keep_warnings = False
# If true, `todo` and `todoList` produce output, else they produce nothing. # If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False todo_include_todos = False
@ -111,156 +111,155 @@ todo_include_todos = False
# The theme to use for HTML and HTML Help pages. See the documentation for # The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes. # 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 # 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 # further. For a list of options available for each theme, see the
# documentation. # documentation.
#html_theme_options = {}
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory. # Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to # The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation". # "<project> v<release> documentation".
#html_title = None
# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title. # A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top # The name of an image file (relative to this directory) to place at the top
# of the sidebar. # of the sidebar.
#html_logo = None
# html_logo = None
# The name of an image file (within the static path) to use as favicon of the # The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large. # pixels large.
#html_favicon = None
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here, # 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, # relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css". # 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 # Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied # .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation. # directly to the root of the documentation.
#html_extra_path = []
# html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format. # using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to # If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities. # typographically correct entities.
#html_use_smartypants = True
# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names. # Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to # Additional templates that should be rendered to pages, maps page names to
# template names. # template names.
#html_additional_pages = {}
# html_additional_pages = {}
# If false, no module index is generated. # If false, no module index is generated.
#html_domain_indices = True
# html_domain_indices = True
# If false, no index is generated. # If false, no index is generated.
#html_use_index = True
# html_use_index = True
# If true, the index is split into individual pages for each letter. # If true, the index is split into individual pages for each letter.
#html_split_index = False
# html_split_index = False
# If true, links to the reST sources are added to the pages. # If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will # If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the # contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served. # base URL from which the finished HTML is served.
#html_use_opensearch = ''
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml"). # This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# html_file_suffix = None
# Language to be used for generating the HTML full-text search index. # Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages: # Sphinx supports the following languages:
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
#html_search_language = 'en'
# html_search_language = 'en'
# A dictionary with options for the search language support, empty by default. # A dictionary with options for the search language support, empty by default.
# Now only 'ja' uses this config value # Now only 'ja' uses this config value
#html_search_options = {'type': 'default'}
# html_search_options = {'type': 'default'}
# The name of a javascript file (relative to the configuration directory) that # The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used. # implements a search results scorer. If empty, the default will be used.
#html_search_scorer = 'scorer.js'
# html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder. # Output file base name for HTML help builder.
htmlhelp_basename = 'PyTrustNFedoc'
htmlhelp_basename = "PyTrustNFedoc"
# -- Options for LaTeX output --------------------------------------------- # -- Options for LaTeX output ---------------------------------------------
latex_elements = { 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',
# 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',
} }
# Grouping the document tree into LaTeX files. List of tuples # Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, # (source start file, target name, title,
# author, documentclass [howto, manual, or own class]). # author, documentclass [howto, manual, or own class]).
latex_documents = [ 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 # The name of an image file (relative to this directory) to place at the top of
# the title page. # the title page.
#latex_logo = None
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts, # For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters. # not chapters.
#latex_use_parts = False
# latex_use_parts = False
# If true, show page references after internal links. # If true, show page references after internal links.
#latex_show_pagerefs = False
# latex_show_pagerefs = False
# If true, show URL addresses after external links. # If true, show URL addresses after external links.
#latex_show_urls = False
# latex_show_urls = False
# Documents to append as an appendix to all manuals. # Documents to append as an appendix to all manuals.
#latex_appendices = []
# latex_appendices = []
# If false, no module index is generated. # If false, no module index is generated.
#latex_domain_indices = True
# latex_domain_indices = True
# -- Options for manual page output --------------------------------------- # -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples # One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section). # (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. # If true, show URL addresses after external links.
#man_show_urls = False
# man_show_urls = False
# -- Options for Texinfo output ------------------------------------------- # -- Options for Texinfo output -------------------------------------------
@ -269,19 +268,25 @@ man_pages = [
# (source start file, target name, title, author, # (source start file, target name, title, author,
# dir menu entry, description, category) # dir menu entry, description, category)
texinfo_documents = [ 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. # Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# texinfo_appendices = []
# If false, no module index is generated. # If false, no module index is generated.
#texinfo_domain_indices = True
# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'. # How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu. # If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
# texinfo_no_detailmenu = False

1024
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): class HttpClient(object):
def __init__(self, url): def __init__(self, url):
self.url = url self.url = url
def _headers(self, action): def _headers(self, action):
return { 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): def post_soap(self, xml_soap, action):

12
pytrustnfe/certificado.py

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

12
pytrustnfe/client.py

@ -8,26 +8,22 @@ import suds_requests
def get_authenticated_client(base_url, cert, key): def get_authenticated_client(base_url, cert, key):
cache_location = '/tmp/suds'
cache_location = "/tmp/suds"
cache = suds.cache.DocumentCache(location=cache_location) cache = suds.cache.DocumentCache(location=cache_location)
session = requests.Session() session = requests.Session()
session.cert = (cert, key) session.cert = (cert, key)
return suds.client.Client( 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): def get_client(base_url):
cache_location = '/tmp/suds'
cache_location = "/tmp/suds"
cache = suds.cache.DocumentCache(location=cache_location) cache = suds.cache.DocumentCache(location=cache_location)
session = requests.Session() session = requests.Session()
return suds.client.Client( 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""" """Exceção para erro na validação do esquema da NFe"""
def __init__(self, message, *args, **kwargs): 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) super(NFeValidationException, self).__init__(message, *args, **kwargs)

165
pytrustnfe/nfe/__init__.py

@ -22,44 +22,48 @@ from zeep.transports import Transport
def _generate_nfe_id(**kwargs): def _generate_nfe_id(**kwargs):
for item in kwargs['NFes']:
for item in kwargs["NFes"]:
vals = { vals = {
'cnpj': item['infNFe']['emit']['cnpj_cpf'],
'estado': item['infNFe']['ide']['cUF'],
'emissao': '%s%s' % (item['infNFe']['ide']['dhEmi'][2:4],
item['infNFe']['ide']['dhEmi'][5:7]),
'modelo': item['infNFe']['ide']['mod'],
'serie': item['infNFe']['ide']['serie'],
'numero': item['infNFe']['ide']['nNF'],
'tipo': item['infNFe']['ide']['tpEmis'],
'codigo': item['infNFe']['ide']['cNF'],
"cnpj": item["infNFe"]["emit"]["cnpj_cpf"],
"estado": item["infNFe"]["ide"]["cUF"],
"emissao": "%s%s"
% (
item["infNFe"]["ide"]["dhEmi"][2:4],
item["infNFe"]["ide"]["dhEmi"][5:7],
),
"modelo": item["infNFe"]["ide"]["mod"],
"serie": item["infNFe"]["ide"]["serie"],
"numero": item["infNFe"]["ide"]["nNF"],
"tipo": item["infNFe"]["ide"]["tpEmis"],
"codigo": item["infNFe"]["ide"]["cNF"],
} }
chave_nfe = ChaveNFe(**vals) chave_nfe = ChaveNFe(**vals)
chave_nfe = gerar_chave(chave_nfe, 'NFe')
item['infNFe']['Id'] = chave_nfe
item['infNFe']['ide']['cDV'] = chave_nfe[len(chave_nfe) - 1:]
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): 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 = 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: if sign:
signer = Assinatura(certificado.pfx, certificado.password) signer = Assinatura(certificado.pfx, certificado.password)
if method == 'NfeInutilizacao':
xml_send = signer.assina_xml(xmlElem_send, kwargs['obj']['id'])
if method == 'NfeAutorizacao':
if method == "NfeInutilizacao":
xml_send = signer.assina_xml(xmlElem_send, kwargs["obj"]["id"])
if method == "NfeAutorizacao":
xml_send = signer.assina_xml( xml_send = signer.assina_xml(
xmlElem_send, kwargs['NFes'][0]['infNFe']['Id'])
elif method == 'RecepcaoEvento':
xmlElem_send, kwargs["NFes"][0]["infNFe"]["Id"]
)
elif method == "RecepcaoEvento":
xml_send = signer.assina_xml(xmlElem_send, kwargs["eventos"][0]["Id"])
elif method == "RecepcaoEventoManifesto":
xml_send = signer.assina_xml( xml_send = signer.assina_xml(
xmlElem_send, kwargs['eventos'][0]['Id'])
elif method == 'RecepcaoEventoManifesto':
xml_send = signer.assina_xml(
xmlElem_send, kwargs['manifesto']['identificador'])
xmlElem_send, kwargs["manifesto"]["identificador"]
)
else: else:
xml_send = etree.tostring(xmlElem_send, encoding=str) xml_send = etree.tostring(xmlElem_send, encoding=str)
@ -67,8 +71,7 @@ def _render(certificado, method, sign, **kwargs):
def _get_session(certificado): 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) cert, key = save_cert_key(cert, key)
session = Session() session = Session()
@ -80,19 +83,23 @@ def _get_session(certificado):
def _get_client(base_url, transport): def _get_client(base_url, transport):
client = Client(base_url, transport=transport) client = Client(base_url, transport=transport)
port = next(iter(client.wsdl.port_types)) 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 return first_operation, client
def _send(certificado, method, **kwargs): def _send(certificado, method, **kwargs):
xml_send = kwargs["xml"] xml_send = kwargs["xml"]
base_url = localizar_url( base_url = localizar_url(
method, kwargs['estado'], kwargs['modelo'], kwargs['ambiente'])
method, kwargs["estado"], kwargs["modelo"], kwargs["ambiente"]
)
session = _get_session(certificado) session = _get_session(certificado)
patch = has_patch(kwargs['estado'], method)
patch = has_patch(kwargs["estado"], method)
if patch: if patch:
return patch(session, xml_send, kwargs['ambiente'])
return patch(session, xml_send, kwargs["ambiente"])
transport = Transport(session=session) transport = Transport(session=session)
first_op, client = _get_client(base_url, transport) first_op, client = _get_client(base_url, transport)
return _send_zeep(first_op, client, xml_send) return _send_zeep(first_op, client, xml_send)
@ -104,140 +111,138 @@ def _send_zeep(first_operation, client, xml_send):
namespaceNFe = xml.find(".//{http://www.portalfiscal.inf.br/nfe}NFe") namespaceNFe = xml.find(".//{http://www.portalfiscal.inf.br/nfe}NFe")
if namespaceNFe is not None: 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) requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
with client.settings(raw_response=True): with client.settings(raw_response=True):
response = client.service[first_operation](xml) response = client.service[first_operation](xml)
response, obj = sanitize_response(response.text) response, obj = sanitize_response(response.text)
return { 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): def xml_autorizar_nfe(certificado, **kwargs):
_generate_nfe_id(**kwargs) _generate_nfe_id(**kwargs)
return _render(certificado, 'NfeAutorizacao', True, **kwargs)
return _render(certificado, "NfeAutorizacao", True, **kwargs)
def autorizar_nfe(certificado, **kwargs): # Assinar def autorizar_nfe(certificado, **kwargs): # Assinar
if "xml" not in kwargs: 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): 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): def retorno_autorizar_nfe(certificado, **kwargs):
if "xml" not in 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 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 def recepcao_evento_cancelamento(certificado, **kwargs): # Assinar
if "xml" not in kwargs: 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): def xml_inutilizar_nfe(certificado, **kwargs):
return _render(certificado, 'NfeInutilizacao', True, **kwargs)
return _render(certificado, "NfeInutilizacao", True, **kwargs)
def inutilizar_nfe(certificado, **kwargs): def inutilizar_nfe(certificado, **kwargs):
if "xml" not in 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): 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): def consultar_protocolo_nfe(certificado, **kwargs):
if "xml" not in 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): 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): def nfe_status_servico(certificado, **kwargs):
if "xml" not in 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): def xml_consulta_cadastro(certificado, **kwargs):
return _render(certificado, 'NfeConsultaCadastro', False, **kwargs)
return _render(certificado, "NfeConsultaCadastro", False, **kwargs)
def consulta_cadastro(certificado, **kwargs): def consulta_cadastro(certificado, **kwargs):
if "xml" not in 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 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 def recepcao_evento_carta_correcao(certificado, **kwargs): # Assinar
if "xml" not in kwargs: 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 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 def recepcao_evento_manifesto(certificado, **kwargs): # Assinar
if "xml" not in kwargs: 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 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): def consulta_distribuicao_nfe(certificado, **kwargs):
if "xml" not in 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) return _send_v310(certificado, **kwargs)
def xml_download_nfe(certificado, **kwargs): # Assinar def xml_download_nfe(certificado, **kwargs): # Assinar
return _render(certificado, 'NFeDistribuicaoDFe', False, **kwargs)
return _render(certificado, "NFeDistribuicaoDFe", False, **kwargs)
def download_nfe(certificado, **kwargs): def download_nfe(certificado, **kwargs):
if "xml" not in 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) return _send_v310(certificado, **kwargs)
def _send_v310(certificado, **kwargs): def _send_v310(certificado, **kwargs):
xml_send = kwargs["xml"] xml_send = kwargs["xml"]
base_url = localizar_url( 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) cert, key = save_cert_key(cert, key)
session = Session() session = Session()
@ -246,16 +251,20 @@ def _send_v310(certificado, **kwargs):
transport = Transport(session=session) transport = Transport(session=session)
xml = etree.fromstring(xml_send) 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) client = Client(base_url, transport=transport)
port = next(iter(client.wsdl.port_types)) port = next(iter(client.wsdl.port_types))
first_operation = next(iter(client.wsdl.port_types[port].operations)) first_operation = next(iter(client.wsdl.port_types[port].operations))
with client.settings(raw_response=True): 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) response, obj = sanitize_response(response.text)
return { 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): class Assinatura(object):
def __init__(self, arquivo, senha): def __init__(self, arquivo, senha):
self.arquivo = arquivo self.arquivo = arquivo
self.senha = senha 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) cert, key = extract_cert_and_key_from_pfx(self.arquivo, self.senha)
for element in xml_element.iter("*"): for element in xml_element.iter("*"):
@ -22,24 +21,30 @@ class Assinatura(object):
element.text = None element.text = None
signer = XMLSigner( 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 = {}
ns[None] = signer.namespaces['ds']
ns[None] = signer.namespaces["ds"]
signer.namespaces = ns signer.namespaces = ns
ref_uri = ('#%s' % reference) if reference else None
ref_uri = ("#%s" % reference) if reference else None
signed_root = signer.sign( signed_root = signer.sign(
xml_element, key=key.encode(), cert=cert.encode(),
reference_uri=ref_uri)
xml_element, key=key.encode(), cert=cert.encode(), reference_uri=ref_uri
)
if reference: if reference:
element_signed = signed_root.find(".//*[@Id='%s']" % reference) element_signed = signed_root.find(".//*[@Id='%s']" % reference)
signature = signed_root.find( 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 = element_signed.getparent()
parent.append(signature) parent.append(signature)
return etree.tostring(signed_root, encoding=str) return etree.tostring(signed_root, encoding=str)

358
pytrustnfe/nfe/danfce.py

@ -19,25 +19,34 @@ from reportlab.lib.styles import ParagraphStyle
def format_cnpj_cpf(value): def format_cnpj_cpf(value):
if len(value) < 12: # CPF 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: 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 return cValue
def getdateUTC(cDateUTC): def getdateUTC(cDateUTC):
cDt = cDateUTC[0:10].split('-')
cDt = cDateUTC[0:10].split("-")
cDt.reverse() 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: if cNumber:
number = float(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 "" return ""
@ -46,7 +55,7 @@ def tagtext(oNode=None, cTag=None):
xpath = ".//{http://www.portalfiscal.inf.br/nfe}%s" % (cTag) xpath = ".//{http://www.portalfiscal.inf.br/nfe}%s" % (cTag)
cText = oNode.find(xpath).text cText = oNode.find(xpath).text
except: except:
cText = ''
cText = ""
return cText return cText
@ -58,24 +67,19 @@ def get_image(path, width=1 * cm):
def format_telefone(telefone): def format_telefone(telefone):
telefone = re.sub('[^0-9]', '', telefone)
telefone = re.sub("[^0-9]", "", telefone)
if len(telefone) == 10: 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: 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 return telefone
class danfce(object): class danfce(object):
def __init__(self, list_xml, logo=None, timezone=None): def __init__(self, list_xml, logo=None, timezone=None):
self.current_font_size = 7 self.current_font_size = 7
self.current_font_name = 'NimbusSanL-Regu'
self.current_font_name = "NimbusSanL-Regu"
self.max_height = 840 self.max_height = 840
self.min_height = 1 self.min_height = 1
@ -85,8 +89,8 @@ class danfce(object):
self.oPDF_IO = BytesIO() self.oPDF_IO = BytesIO()
self.canvas = canvas.Canvas(self.oPDF_IO, pagesize=(7.2 * cm, 30 * cm)) 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.canvas.setFont(self.current_font_name, self.current_font_size)
self.list_xml = list_xml self.list_xml = list_xml
@ -99,52 +103,69 @@ class danfce(object):
elem_emit = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}emit") elem_emit = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}emit")
# Razão Social emitente # Razão Social emitente
nomeEmpresa = tagtext(oNode=elem_emit, cTag='xFant')
nomeEmpresa = tagtext(oNode=elem_emit, cTag="xFant")
self.drawTitle(nomeEmpresa, 10) self.drawTitle(nomeEmpresa, 10)
if self.logo: if self.logo:
img = get_image(self.logo, width=10 * mm) img = get_image(self.logo, width=10 * mm)
img.drawOn(self.canvas, 5, 830) 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._drawCenteredParagraph(cEnd)
self.drawLine() self.drawLine()
def danfce_information(self, oXML=None): def danfce_information(self, oXML=None):
el_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide") el_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide")
tipo_emissao = tagtext(oNode=el_ide, cTag='tpEmis')
if tipo_emissao in ('5', '9'):
tipo_emissao = tagtext(oNode=el_ide, cTag="tpEmis")
if tipo_emissao in ("5", "9"):
self.current_height -= 5 self.current_height -= 5
self.drawTitle("EMITIDA EM CONTINGÊNCIA",9, 'NimbusSanL-Bold')
self.drawTitle("Pendente de autorização", 7, 'NimbusSanL-Bold')
self.drawTitle("EMITIDA EM CONTINGÊNCIA", 9, "NimbusSanL-Bold")
self.drawTitle("Pendente de autorização", 7, "NimbusSanL-Bold")
self.drawLine() self.drawLine()
else: else:
self.drawTitle( self.drawTitle(
"DANFE NFC-e - Documento Auxiliar da Nota Fiscal de", "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)
self.drawString("NFC-e não permite aproveitamento de crédito de ICMS", True)
self.drawLine() 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) colWidths = (25, 90, 15, 15, 25, 25)
rowHeights = [7] rowHeights = [7]
@ -153,17 +174,14 @@ class danfce(object):
item = el_det[id] item = el_det[id]
el_prod = item.find(".//{http://www.portalfiscal.inf.br/nfe}prod") el_prod = item.find(".//{http://www.portalfiscal.inf.br/nfe}prod")
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 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] new_row = [cod, descricao, qtde, Un, vl_unit, vl_total]
@ -174,54 +192,57 @@ class danfce(object):
def _draw_product_table(self, rows, colWidths, rowHeights): def _draw_product_table(self, rows, colWidths, rowHeights):
table = Table(rows, colWidths, tuple(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) w, h = table.wrapOn(self.canvas, 200, 450)
table.drawOn(self.canvas, 0, self.current_height - (h * 1.2)) 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): def totais(self, oXML=None):
# Impostos # Impostos
el_total = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}total") el_total = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}total")
total_tributo = format_number( 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") el_pag = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}pag")
troco = tagtext(oNode=el_pag, cTag="vTroco") troco = tagtext(oNode=el_pag, cTag="vTroco")
payment_method_list = {'01': 'Dinheiro',
'02': 'Cheque',
'03': 'Cartão de Crédito',
'04': 'Cartão de Débito',
"05": "Crédito Loja",
'10': 'Vale Alimentação',
'11': 'Vale Refeição',
'12': 'Vale Presente',
'13': 'Vale Combustível',
'14': 'Duplicata Mercantil',
'15': 'Boleto Bancario',
'90': 'Sem Pagamento',
'99': 'Outros'}
quant_produtos = len(oXML.findall(
".//{http://www.portalfiscal.inf.br/nfe}det"))
payment_method_list = {
"01": "Dinheiro",
"02": "Cheque",
"03": "Cartão de Crédito",
"04": "Cartão de Débito",
"05": "Crédito Loja",
"10": "Vale Alimentação",
"11": "Vale Refeição",
"12": "Vale Presente",
"13": "Vale Combustível",
"14": "Duplicata Mercantil",
"15": "Boleto Bancario",
"90": "Sem Pagamento",
"99": "Outros",
}
quant_produtos = len(oXML.findall(".//{http://www.portalfiscal.inf.br/nfe}det"))
payment_methods = [] payment_methods = []
for pagId, item in enumerate(el_pag): for pagId, item in enumerate(el_pag):
@ -236,13 +257,13 @@ class danfce(object):
payment_methods.append(payment) payment_methods.append(payment)
values = { 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) self.draw_totals_table(values)
@ -252,26 +273,30 @@ class danfce(object):
def draw_totals_table(self, values): def draw_totals_table(self, values):
rowHeights = [7, 7, 7, 7, 7] rowHeights = [7, 7, 7, 7, 7]
data = [ data = [
['QTD.TOTAL DE ITENS', values['quantidade_itens']],
['VALOR TOTAL R$', values['valor_total']],
['DESCONTO R$', values['desconto']],
['VALOR A PAGAR R$', values['valor_a_pagar']],
['FORMA DE PAGAMENTO', 'VALOR PAGO R$'],
]
for item in values['formas_de_pagamento']:
["QTD.TOTAL DE ITENS", values["quantidade_itens"]],
["VALOR TOTAL R$", values["valor_total"]],
["DESCONTO R$", values["desconto"]],
["VALOR A PAGAR R$", values["valor_a_pagar"]],
["FORMA DE PAGAMENTO", "VALOR PAGO R$"],
]
for item in values["formas_de_pagamento"]:
data.append([item[0], item[1]]) data.append([item[0], item[1]])
rowHeights.append(7) rowHeights.append(7)
data.append(['TROCO', format_number(values['troco'], precision=2)])
data.append(["TROCO", format_number(values["troco"], precision=2)])
rowHeights.append(7) rowHeights.append(7)
table2 = Table(data, colWidths=(150, 50), rowHeights=tuple(rowHeights)) 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) w, h = table2.wrapOn(self.canvas, 200, 450)
table2.drawOn(self.canvas, 0, self.current_height - (h * 1.1)) table2.drawOn(self.canvas, 0, self.current_height - (h * 1.1))
self.current_height -= h self.current_height -= h
@ -281,27 +306,27 @@ class danfce(object):
# n nfce, serie e data de solicitacao # n nfce, serie e data de solicitacao
el_ide = oXML.find(".//{http://www.portalfiscal.inf.br/nfe}ide") 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") el_dest = el_infNFe.find(".//{http://www.portalfiscal.inf.br/nfe}dest")
# chave, n protocolo, data autorizacao # 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") 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: if cnpj:
cnpj_cpf = format_cnpj_cpf(cnpj) cnpj_cpf = format_cnpj_cpf(cnpj)
cnpj_cpf = "CONSUMIDOR CNPJ: %s" % (cnpj) cnpj_cpf = "CONSUMIDOR CNPJ: %s" % (cnpj)
@ -312,45 +337,53 @@ class danfce(object):
cnpj_cpf = u"CONSUMIDOR NÃO IDENTIFICADO" cnpj_cpf = u"CONSUMIDOR NÃO IDENTIFICADO"
nNFC = tagtext(oNode=el_ide, cTag="nNF") 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 = getdateUTC(tagtext(oNode=el_ide, cTag="dhEmi"))
dataSolicitacao = dataSolicitacao[0] + " " + dataSolicitacao[1] dataSolicitacao = dataSolicitacao[0] + " " + dataSolicitacao[1]
text = u"%s <br />%s <br />NFC-e nº%s Série %s %s<br />" % ( 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) 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.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: else:
numProtocolo = tagtext(oNode=el_prot_nfe, cTag="nProt") 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] dataAutorizacao = dataAutorizacao[0] + " " + dataAutorizacao[1]
text = "Protocolo de autorização: %s<br />Data de autorização %s<br />" % ( text = "Protocolo de autorização: %s<br />Data de autorização %s<br />" % (
numProtocolo, dataAutorizacao)
numProtocolo,
dataAutorizacao,
)
self._drawCenteredParagraph(text) self._drawCenteredParagraph(text)
self.draw_qr_code(qrcode) self.draw_qr_code(qrcode)
infAdFisco = tagtext(oNode=el_infAdic, cTag='infAdFisco')
infAdFisco = tagtext(oNode=el_infAdic, cTag="infAdFisco")
self._drawCenteredParagraph(infAdFisco) self._drawCenteredParagraph(infAdFisco)
infCpl = tagtext(oNode=el_infAdic, cTag='infCpl')
infCpl = tagtext(oNode=el_infAdic, cTag="infCpl")
self._drawCenteredParagraph(infCpl) self._drawCenteredParagraph(infCpl)
def _drawCenteredParagraph(self, text): def _drawCenteredParagraph(self, text):
style = ParagraphStyle( style = ParagraphStyle(
name='Normal',
fontName='NimbusSanL-Regu',
name="Normal",
fontName="NimbusSanL-Regu",
fontSize=7, fontSize=7,
alignment=TA_CENTER, alignment=TA_CENTER,
leading=7, leading=7,
@ -359,27 +392,28 @@ class danfce(object):
paragraph = Paragraph(text, style=style) paragraph = Paragraph(text, style=style)
w, h = paragraph.wrapOn(self.canvas, 180, 300) w, h = paragraph.wrapOn(self.canvas, 180, 300)
paragraph.drawOn(self.canvas, 10, self.current_height - h) 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): def drawString(self, string, centered=False):
if centered: if centered:
self.canvas.drawCentredString( 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 self.current_height -= self.current_font_size
else: else:
self.canvas.drawString(self.min_width, self.current_height, string) self.canvas.drawString(self.min_width, self.current_height, string)
self.current_height -= self.current_font_size 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.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.current_height -= self.current_font_size
self.canvas.setFont(self.current_font_name, self.current_font_size) self.canvas.setFont(self.current_font_name, self.current_font_size)
def drawLine(self): 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 self.current_height -= self.current_font_size
def draw_qr_code(self, string): def draw_qr_code(self, string):
@ -396,8 +430,7 @@ class danfce(object):
def nfce_generate(self): def nfce_generate(self):
for oXML in self.list_xml: 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.NrPages = 1
self.Page = 1 self.Page = 1
@ -413,17 +446,17 @@ class danfce(object):
list_cod_prod = [] list_cod_prod = []
nPg = 0 nPg = 0
for nId, item in enumerate(el_det): 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( 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: if infAdProd is not None:
list_.extend(wrap(infAdProd.text, 56)) list_.extend(wrap(infAdProd.text, 56))
list_desc.append(list_) 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) list_cod_prod.append(list_cProd)
# Nr linhas necessárias p/ descrição item # Nr linhas necessárias p/ descrição item
@ -440,14 +473,19 @@ class danfce(object):
oPaginator[nPg][1] = nId + 1 oPaginator[nPg][1] = nId + 1
oPaginator[nPg][2] += nLin_Itens oPaginator[nPg][2] += nLin_Itens
self.NrPages = len(oPaginator) # Calculando nr. páginas
self.NrPages = len(oPaginator) # Calculando nr. páginas
self.ide_emit(oXML=oXML) self.ide_emit(oXML=oXML)
# self.destinatario(oXML=oXML) # self.destinatario(oXML=oXML)
self.danfce_information(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() self.drawLine()
@ -461,9 +499,13 @@ class danfce(object):
self.newpage() self.newpage()
self.ide_emit(oXML=oXML) self.ide_emit(oXML=oXML)
# self.destinatario(oXML=oXML) # self.destinatario(oXML=oXML)
self.produtos(oXML=oXML, el_det=el_det, oPaginator=oPag,
list_desc=list_desc,
list_cod_prod=list_cod_prod)
self.produtos(
oXML=oXML,
el_det=el_det,
oPaginator=oPag,
list_desc=list_desc,
list_cod_prod=list_cod_prod,
)
self.totais(oXML=oXML) self.totais(oXML=oXML)
self.inf_authentication(oXML=oXML) self.inf_authentication(oXML=oXML)

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

36
pytrustnfe/nfe/patch.py

@ -3,37 +3,43 @@ from pytrustnfe.xml import sanitize_response
def nfeInutilizacaoCE(session, xml_send, ambiente): 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"\ <nfeDadosMsg xmlns="http://www.portalfiscal.inf.br/nfe/wsdl/NFeInutilizacao4"\
>' + xml_send + '</nfeDadosMsg></Body></Envelope>'
>'
+ xml_send
+ "</nfeDadosMsg></Body></Envelope>"
)
headers = { headers = {
'SOAPAction': "",
'Content-Type': 'application/soap+xml; charset="utf-8"'
"SOAPAction": "",
"Content-Type": 'application/soap+xml; charset="utf-8"',
} }
if ambiente == 1: if ambiente == 1:
response = session.post( 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: else:
response = session.post( 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) response, obj = sanitize_response(response.text)
return { 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): def has_patch(cod_estado, metodo):
uf = SIGLA_ESTADO[cod_estado] uf = SIGLA_ESTADO[cod_estado]
method = metodo+uf
method = metodo + uf
if method in methods: if method in methods:
return methods[method] return methods[method]
return None return None

43
pytrustnfe/nfse/aparecida/__init__.py

@ -13,24 +13,23 @@ from pytrustnfe.nfe.assinatura import Assinatura
def _render(certificado, method, **kwargs): 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) signer = Assinatura(certificado.pfx, certificado.password)
xml_send = signer.assina_xml(xml_send, reference) xml_send = signer.assina_xml(xml_send, reference)
return xml_send return xml_send
def _send(certificado, method, **kwargs): 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: 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) cert, key = save_cert_key(cert, key)
disable_warnings() disable_warnings()
@ -41,41 +40,37 @@ def _send(certificado, method, **kwargs):
client = Client(base_url, transport=transport) client = Client(base_url, transport=transport)
xml_send = kwargs['xml']
xml_send = kwargs["xml"]
response = client.service[method](xml_send) response = client.service[method](xml_send)
response, obj = sanitize_response(response) 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): def xml_recepcionar_lote_rps(certificado, **kwargs):
return _render(certificado, 'recepcionarLoteRps', **kwargs)
return _render(certificado, "recepcionarLoteRps", **kwargs)
def recepcionar_lote_rps(certificado, **kwargs): def recepcionar_lote_rps(certificado, **kwargs):
if "xml" not in 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): def xml_consultar_lote_rps(certificado, **kwargs):
return _render(certificado, 'consultarLoteRps', **kwargs)
return _render(certificado, "consultarLoteRps", **kwargs)
def consultar_lote_rps(certificado, **kwargs): def consultar_lote_rps(certificado, **kwargs):
if "xml" not in 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): def xml_cancelar_nfse(certificado, **kwargs):
return _render(certificado, 'cancelarNfse', **kwargs)
return _render(certificado, "cancelarNfse", **kwargs)
def cancelar_nfse(certificado, **kwargs): def cancelar_nfse(certificado, **kwargs):
if "xml" not in 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 consts = xmlsec.constants
NAMESPACE_SIG = 'http://www.w3.org/2000/09/xmldsig#'
NAMESPACE_SIG = "http://www.w3.org/2000/09/xmldsig#"
class Assinatura(object): class Assinatura(object):
def __init__(self, cert_pem, private_key, password): def __init__(self, cert_pem, private_key, password):
self.cert_pem = cert_pem self.cert_pem = cert_pem
self.private_key = private_key self.private_key = private_key
@ -20,22 +19,27 @@ class Assinatura(object):
def _checar_certificado(self): def _checar_certificado(self):
if not os.path.isfile(self.private_key): 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): def assina_xml(self, xml, reference):
self._checar_certificado() self._checar_certificado()
template = etree.fromstring(xml) template = etree.fromstring(xml)
key = xmlsec.Key.from_file( 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( 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) template.append(signature_node)
ref = xmlsec.template.add_reference( 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.TransformEnveloped)
xmlsec.template.add_transform(ref, consts.TransformInclC14N) xmlsec.template.add_transform(ref, consts.TransformInclC14N)
@ -46,8 +50,7 @@ class Assinatura(object):
ctx = xmlsec.SignatureContext() ctx = xmlsec.SignatureContext()
ctx.key = key 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) ctx.sign(signature_node)
return etree.tostring(template, encoding=str) 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): def sign_tag(certificado, **kwargs):
pkcs12 = crypto.load_pkcs12(certificado.pfx, certificado.password) pkcs12 = crypto.load_pkcs12(certificado.pfx, certificado.password)
key = pkcs12.get_privatekey() 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): 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) 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: 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) cert, key = save_cert_key(cert, key)
client = get_authenticated_client(url, cert, key) client = get_authenticated_client(url, cert, key)
pfx_path = certificado.save_pfx() pfx_path = certificado.save_pfx()
signer = Assinatura(pfx_path, certificado.password) signer = Assinatura(pfx_path, certificado.password)
xml_send = signer.assina_xml(xml_send, '')
xml_send = signer.assina_xml(xml_send, "")
try: try:
response = getattr(client.service, method)(1, xml_send) response = getattr(client.service, method)(1, xml_send)
except suds.WebFault as e: except suds.WebFault as e:
return { 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) 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): def gerar_nfse(certificado, **kwargs):
return _send(certificado, 'GerarNfse', **kwargs)
return _send(certificado, "GerarNfse", **kwargs)
def envio_lote_rps_assincrono(certificado, **kwargs): def envio_lote_rps_assincrono(certificado, **kwargs):
return _send(certificado, 'RecepcionarLoteRps', **kwargs)
return _send(certificado, "RecepcionarLoteRps", **kwargs)
def envio_lote_rps(certificado, **kwargs): def envio_lote_rps(certificado, **kwargs):
return _send(certificado, 'RecepcionarLoteRpsSincrono', **kwargs)
return _send(certificado, "RecepcionarLoteRpsSincrono", **kwargs)
def cancelar_nfse(certificado, **kwargs): def cancelar_nfse(certificado, **kwargs):
return _send(certificado, 'CancelarNfse', **kwargs)
return _send(certificado, "CancelarNfse", **kwargs)
def substituir_nfse(certificado, **kwargs): def substituir_nfse(certificado, **kwargs):
return _send(certificado, 'SubstituirNfse', **kwargs)
return _send(certificado, "SubstituirNfse", **kwargs)
def consulta_situacao_lote_rps(certificado, **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): def consulta_nfse_por_rps(certificado, **kwargs):
return _send(certificado, 'ConsultaNfsePorRps', **kwargs)
return _send(certificado, "ConsultaNfsePorRps", **kwargs)
def consultar_lote_rps(certificado, **kwargs): def consultar_lote_rps(certificado, **kwargs):
return _send(certificado, 'ConsultarLoteRps', **kwargs)
return _send(certificado, "ConsultarLoteRps", **kwargs)
def consulta_nfse_servico_prestado(certificado, **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): def consultar_nfse_servico_tomado(certificado, **kwargs):
return _send(certificado, 'ConsultarNfseServicoTomado', **kwargs)
return _send(certificado, "ConsultarNfseServicoTomado", **kwargs)
def consulta_nfse_faixe(certificado, **kwargs): def consulta_nfse_faixe(certificado, **kwargs):
return _send(certificado, 'ConsultarNfseFaixa', **kwargs)
return _send(certificado, "ConsultarNfseFaixa", **kwargs)
def consulta_cnpj(certificado, **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): 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) signer = Assinatura(certificado.pfx, certificado.password)
xml_send = signer.assina_xml(xml_send, reference) xml_send = signer.assina_xml(xml_send, reference)
if ref_lote: if ref_lote:
xml_send = signer.assina_xml(etree.fromstring(xml_send), 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): 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: 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"?>\ xml_cabecalho = '<?xml version="1.0" encoding="UTF-8"?>\
<cabecalho xmlns="http://www.abrasf.org.br/nfse.xsd" versao="1.00">\ <cabecalho xmlns="http://www.abrasf.org.br/nfse.xsd" versao="1.00">\
<versaoDados>1.00</versaoDados></cabecalho>' <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) cert, key = save_cert_key(cert, key)
session = Session() session = Session()
@ -57,28 +56,24 @@ def _send(certificado, method, **kwargs):
response = client.service[method](xml_cabecalho, xml_send) response = client.service[method](xml_cabecalho, xml_send)
response, obj = sanitize_response(response) 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): def xml_gerar_nfse(certificado, **kwargs):
return _render(certificado, 'GerarNfse', **kwargs)
return _render(certificado, "GerarNfse", **kwargs)
def gerar_nfse(certificado, **kwargs): def gerar_nfse(certificado, **kwargs):
if "xml" not in 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): def xml_cancelar_nfse(certificado, **kwargs):
return _render(certificado, 'CancelarNfse', **kwargs)
return _render(certificado, "CancelarNfse", **kwargs)
def cancelar_nfse(certificado, **kwargs): def cancelar_nfse(certificado, **kwargs):
if "xml" not in 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): class Assinatura(object):
def __init__(self, arquivo, senha): def __init__(self, arquivo, senha):
self.arquivo = arquivo self.arquivo = arquivo
self.senha = senha self.senha = senha
@ -22,21 +21,25 @@ class Assinatura(object):
element.text = None element.text = None
signer = XMLSigner( 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 = {}
ns[None] = signer.namespaces['ds']
ns[None] = signer.namespaces["ds"]
signer.namespaces = ns signer.namespaces = ns
ref_uri = ('#%s' % reference) if reference else None
ref_uri = ("#%s" % reference) if reference else None
signed_root = signer.sign( signed_root = signer.sign(
xml_element, key=key.encode(), cert=cert.encode(),
reference_uri=ref_uri)
xml_element, key=key.encode(), cert=cert.encode(), reference_uri=ref_uri
)
if reference: if reference:
element_signed = signed_root.find(".//*[@Id='%s']" % 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: if element_signed is not None and signature is not None:
parent = element_signed.getparent() 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): 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) signer = Assinatura(certificado.pfx, certificado.password)
xml_send = signer.assina_xml(xml_send, reference) 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): 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: 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) cert, key = save_cert_key(cert, key)
client = get_authenticated_client(base_url, 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) response = getattr(client.service, method)(xml_send)
except suds.WebFault as e: except suds.WebFault as e:
return { 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) 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): def xml_gerar_nfse(certificado, **kwargs):
return _render(certificado, 'GerarNfse', **kwargs)
return _render(certificado, "GerarNfse", **kwargs)
def gerar_nfse(certificado, **kwargs): def gerar_nfse(certificado, **kwargs):
if "xml" not in 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): def xml_cancelar_nfse(certificado, **kwargs):
return _render(certificado, 'CancelarNfse', **kwargs)
return _render(certificado, "CancelarNfse", **kwargs)
def cancelar_nfse(certificado, **kwargs): def cancelar_nfse(certificado, **kwargs):
if "xml" not in 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)

69
pytrustnfe/nfse/dsf/__init__.py

@ -12,11 +12,11 @@ from pytrustnfe.client import get_client
def _render(certificado, method, **kwargs): 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": if method == "testeEnviar":
xml_send = render_xml(path, 'enviar.xml', True, **kwargs)
xml_send = render_xml(path, "enviar.xml", True, **kwargs)
else: 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: if type(xml_send) != str:
xml_send = etree.tostring(xml_send) xml_send = etree.tostring(xml_send)
@ -26,59 +26,56 @@ def _render(certificado, method, **kwargs):
def _get_url(**kwargs): def _get_url(**kwargs):
try: try:
cod_cidade = kwargs['nfse']['cidade']
cod_cidade = kwargs["nfse"]["cidade"]
except (KeyError, TypeError): except (KeyError, TypeError):
raise KeyError("Código de cidade inválido!") raise KeyError("Código de cidade inválido!")
urls = { urls = {
# Belém - PA # 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 # 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 # Teresina - PI
'1219': 'http://www.issdigitalthe.com.br/WsNFe2/LoteRps.jws?wsdl',
"1219": "http://www.issdigitalthe.com.br/WsNFe2/LoteRps.jws?wsdl",
# Campinas - SP # 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 # 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 # 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 # 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: try:
return urls[str(cod_cidade)] return urls[str(cod_cidade)]
except KeyError: 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): def _send(certificado, method, **kwargs):
url = _get_url(**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) xml_send = _render(path, method, **kwargs)
client = get_client(url) client = get_client(url)
response = False response = False
if certificado: 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) cert, key = save_cert_key(cert, key)
signer = Assinatura(cert, key, certificado.password) signer = Assinatura(cert, key, certificado.password)
xml_send = signer.assina_xml(xml_send, '')
xml_send = signer.assina_xml(xml_send, "")
try: try:
response = getattr(client.service, method)(xml_send) response = getattr(client.service, method)(xml_send)
response, obj = sanitize_response(response.encode()) response, obj = sanitize_response(response.encode())
except suds.WebFault as e: except suds.WebFault as e:
return { 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: except Exception as e:
if response: if response:
@ -86,46 +83,42 @@ def _send(certificado, method, **kwargs):
else: else:
raise e 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): def xml_enviar(certificado, **kwargs):
return _render(certificado, 'enviar', **kwargs)
return _render(certificado, "enviar", **kwargs)
def enviar(certificado, **kwargs): def enviar(certificado, **kwargs):
if "xml" not in 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): def xml_teste_enviar(certificado, **kwargs):
return _render(certificado, 'testeEnviar', **kwargs)
return _render(certificado, "testeEnviar", **kwargs)
def teste_enviar(certificado, **kwargs): def teste_enviar(certificado, **kwargs):
if "xml" not in 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)
def cancelar(certificado, **kwargs):
return _send(certificado, "cancelar", **kwargs)
def consulta_lote(**kwargs): def consulta_lote(**kwargs):
return _send(False, 'consultarLote', **kwargs)
return _send(False, "consultarLote", **kwargs)
def xml_consultar_nfse_rps(certificado, **kwargs): def xml_consultar_nfse_rps(certificado, **kwargs):
return _render(certificado, 'consultarNFSeRps', **kwargs)
return _render(certificado, "consultarNFSeRps", **kwargs)
def consultar_nfse_rps(certificado, **kwargs): def consultar_nfse_rps(certificado, **kwargs):
if "xml" not in 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)

96
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 from pytrustnfe.nfse.assinatura import Assinatura
URLS = { 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): 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) cert, key = save_cert_key(cert, key)
signer = Assinatura(cert, key, certificado.password) signer = Assinatura(cert, key, certificado.password)
xml_send = signer.assina_xml(xml_send, '')
xml_send = signer.assina_xml(xml_send, "")
return xml_send return xml_send
def _get_oauth_token(**kwargs): 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: 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() m = hashlib.md5()
secret = "%s:%s" % (kwargs["client_id"], kwargs["secret_id"]) secret = "%s:%s" % (kwargs["client_id"], kwargs["secret_id"])
auth = base64.b64encode(secret.encode('utf-8'))
auth = base64.b64encode(secret.encode("utf-8"))
headers = { headers = {
"Content-Type": "application/x-www-form-urlencoded", "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() 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) r = requests.post(url, data=dados, headers=headers)
if r.status_code == 200: if r.status_code == 200:
return r.json() return r.json()
@ -60,57 +61,66 @@ def _get_oauth_token(**kwargs):
def _send(certificado, method, **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) token = _get_oauth_token(**kwargs)
if "access_token" not in token: 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",
"Content-Type": "application/xml",
"Authorization": "Bearer %s" % kwargs['access_token']}
headers = {
"Accept": "application/xml;charset=UTF-8",
"Content-Type": "application/xml",
"Authorization": "Bearer %s" % kwargs["access_token"],
}
r = requests.post(url, headers=headers, data=xml_send) r = requests.post(url, headers=headers, data=xml_send)
response, obj = sanitize_response(r.text.strip()) response, obj = sanitize_response(r.text.strip())
return { 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): def xml_processar_nota(certificado, **kwargs):
return _render(certificado, 'processar_nota', **kwargs)
return _render(certificado, "processar_nota", **kwargs)
def processar_nota(certificado, **kwargs): def processar_nota(certificado, **kwargs):
if "xml" not in 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): def xml_cancelar_nota(certificado, **kwargs):
return _render(certificado, 'cancelar_nota', **kwargs)
return _render(certificado, "cancelar_nota", **kwargs)
def cancelar_nota(certificado, **kwargs): def cancelar_nota(certificado, **kwargs):
if "xml" not in 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): 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: 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) r = requests.get(url, headers=headers)
if r.status_code == 200: if r.status_code == 200:
return r.text return r.text

59
pytrustnfe/nfse/ginfes/__init__.py

@ -14,12 +14,12 @@ from pytrustnfe.nfe.assinatura import Assinatura
def _render(certificado, method, **kwargs): 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) signer = Assinatura(certificado.pfx, certificado.password)
xml_send = signer.assina_xml(xml_send, reference) xml_send = signer.assina_xml(xml_send, reference)
@ -27,17 +27,16 @@ def _render(certificado, method, **kwargs):
def _send(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: 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) 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
header = '<ns2:cabecalho xmlns:ns2="http://www.ginfes.com.br/cabecalho_v03.xsd" versao="3"><versaoDados>3</versaoDados></ns2:cabecalho>' # noqa
disable_warnings() disable_warnings()
session = Session() session = Session()
@ -47,60 +46,56 @@ def _send(certificado, method, **kwargs):
client = Client(base_url, transport=transport) client = Client(base_url, transport=transport)
xml_send = kwargs['xml']
xml_send = kwargs["xml"]
response = client.service[method](header, xml_send) response = client.service[method](header, xml_send)
response, obj = sanitize_response(response) 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): def xml_recepcionar_lote_rps(certificado, **kwargs):
return _render(certificado, 'RecepcionarLoteRpsV3', **kwargs)
return _render(certificado, "RecepcionarLoteRpsV3", **kwargs)
def recepcionar_lote_rps(certificado, **kwargs): def recepcionar_lote_rps(certificado, **kwargs):
if "xml" not in 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): def xml_consultar_situacao_lote(certificado, **kwargs):
return _render(certificado, 'ConsultarSituacaoLoteRpsV3', **kwargs)
return _render(certificado, "ConsultarSituacaoLoteRpsV3", **kwargs)
def consultar_situacao_lote(certificado, **kwargs): def consultar_situacao_lote(certificado, **kwargs):
if "xml" not in 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): def consultar_nfse_por_rps(certificado, **kwargs):
return _send(certificado, 'ConsultarNfsePorRpsV3', **kwargs)
return _send(certificado, "ConsultarNfsePorRpsV3", **kwargs)
def xml_consultar_lote_rps(certificado, **kwargs): def xml_consultar_lote_rps(certificado, **kwargs):
return _render(certificado, 'ConsultarLoteRpsV3', **kwargs)
return _render(certificado, "ConsultarLoteRpsV3", **kwargs)
def consultar_lote_rps(certificado, **kwargs): def consultar_lote_rps(certificado, **kwargs):
if "xml" not in 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): def consultar_nfse(certificado, **kwargs):
return _send(certificado, 'ConsultarNfseV3', **kwargs)
return _send(certificado, "ConsultarNfseV3", **kwargs)
def xml_cancelar_nfse(certificado, **kwargs): def xml_cancelar_nfse(certificado, **kwargs):
return _render(certificado, 'CancelarNfseV3', **kwargs)
return _render(certificado, "CancelarNfseV3", **kwargs)
def cancelar_nfse(certificado, **kwargs): def cancelar_nfse(certificado, **kwargs):
if "xml" not in 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 os
import requests import requests
from lxml import etree from lxml import etree
from requests import Session
from zeep import Client from zeep import Client
from zeep.transports import Transport
from pytrustnfe.xml import render_xml, sanitize_response from pytrustnfe.xml import render_xml, sanitize_response
def _render(certificado, method, **kwargs): 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) return etree.tostring(xml_send)
def _send(certificado, method, **kwargs): 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: 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 = { 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) r = requests.post(base_url, data=xml_send, headers=headers)
response, obj = sanitize_response(r.text.strip()) 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): def xml_gerar_nota(certificado, **kwargs):
return _render(certificado, 'GerarNota', **kwargs)
return _render(certificado, "GerarNota", **kwargs)
def gerar_nota(certificado, **kwargs): def gerar_nota(certificado, **kwargs):
if "xml" not in 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): def xml_cancelar_nota(certificado, **kwargs):
return _render(certificado, 'CancelarNota', **kwargs)
return _render(certificado, "CancelarNota", **kwargs)
def cancelar_nota(certificado, **kwargs): def cancelar_nota(certificado, **kwargs):
if "xml" not in 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): 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) signer = Assinatura(certificado.pfx, certificado.password)
xml_send = signer.assina_xml(xml_send, reference) 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): 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: 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) cert, key = save_cert_key(cert, key)
session = Session() session = Session()
@ -48,28 +47,24 @@ def _send(certificado, method, **kwargs):
response = client.service[method](xml_send) response = client.service[method](xml_send)
response, obj = sanitize_response(response) 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): def xml_gerar_nfse(certificado, **kwargs):
return _render(certificado, 'GerarNfse', **kwargs)
return _render(certificado, "GerarNfse", **kwargs)
def gerar_nfse(certificado, **kwargs): def gerar_nfse(certificado, **kwargs):
if "xml" not in 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): def xml_cancelar_nfse(certificado, **kwargs):
return _render(certificado, 'CancelarNfse', **kwargs)
return _render(certificado, "CancelarNfse", **kwargs)
def cancelar_nfse(certificado, **kwargs): def cancelar_nfse(certificado, **kwargs):
if "xml" not in 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): class Assinatura(object):
def __init__(self, arquivo, senha): def __init__(self, arquivo, senha):
self.arquivo = arquivo self.arquivo = arquivo
self.senha = senha self.senha = senha
@ -22,22 +21,26 @@ class Assinatura(object):
element.text = None element.text = None
signer = XMLSigner( 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 = {}
ns[None] = signer.namespaces['ds']
ns[None] = signer.namespaces["ds"]
signer.namespaces = ns signer.namespaces = ns
element_to_be_signed = xml_element.getchildren()[0].getchildren()[0] element_to_be_signed = xml_element.getchildren()[0].getchildren()[0]
signed_root = signer.sign( 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: if reference:
element_signed = xml_element.find(".//*[@Id='%s']" % reference) element_signed = xml_element.find(".//*[@Id='%s']" % reference)
signature = signed_root.find( 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 element_signed is not None and signature is not None:
parent = xml_element.getchildren()[0] 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): def sign_tag(certificado, **kwargs):
pkcs12 = crypto.load_pkcs12(certificado.pfx, certificado.password) pkcs12 = crypto.load_pkcs12(certificado.pfx, certificado.password)
key = pkcs12.get_privatekey() 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): def _send(certificado, method, **kwargs):
# A little hack to test # 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) 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: 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) cert, key = save_cert_key(cert, key)
client = get_authenticated_client(base_url, cert, key) client = get_authenticated_client(base_url, cert, key)
signer = Assinatura(cert, key, certificado.password) signer = Assinatura(cert, key, certificado.password)
xml_send = signer.assina_xml(xml_send, '')
xml_send = signer.assina_xml(xml_send, "")
try: try:
response = getattr(client.service, method)(1, xml_send) response = getattr(client.service, method)(1, xml_send)
except suds.WebFault as e: except suds.WebFault as e:
return { 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) 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): 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 # Testado pois usa o mesmo xml que o teste_envio_lote_rps
def envio_lote_rps(certificado, **kwargs): def envio_lote_rps(certificado, **kwargs):
return _send(certificado, 'EnvioLoteRPS', **kwargs)
return _send(certificado, "EnvioLoteRPS", **kwargs)
# Testado # Testado
def teste_envio_lote_rps(certificado, **kwargs): def teste_envio_lote_rps(certificado, **kwargs):
return _send(certificado, 'TesteEnvioLoteRPS', **kwargs)
return _send(certificado, "TesteEnvioLoteRPS", **kwargs)
def cancelamento_nfe(certificado, **kwargs): def cancelamento_nfe(certificado, **kwargs):
return _send(certificado, 'CancelamentoNFe', **kwargs)
return _send(certificado, "CancelamentoNFe", **kwargs)
# Testado # Testado
def consulta_nfe(certificado, **kwargs): def consulta_nfe(certificado, **kwargs):
return _send(certificado, 'ConsultaNFe', **kwargs)
return _send(certificado, "ConsultaNFe", **kwargs)
# Testado # Testado
def consulta_nfe_recebidas(certificado, **kwargs): def consulta_nfe_recebidas(certificado, **kwargs):
return _send(certificado, 'ConsultaNFeRecebidas', **kwargs)
return _send(certificado, "ConsultaNFeRecebidas", **kwargs)
# Testado # Testado
def consulta_nfe_emitidas(certificado, **kwargs): def consulta_nfe_emitidas(certificado, **kwargs):
return _send(certificado, 'ConsultaNFeEmitidas', **kwargs)
return _send(certificado, "ConsultaNFeEmitidas", **kwargs)
# Testado # Testado
def consulta_lote(certificado, **kwargs): def consulta_lote(certificado, **kwargs):
return _send(certificado, 'ConsultaLote', **kwargs)
return _send(certificado, "ConsultaLote", **kwargs)
# Testado # Testado
def consulta_informacoes_lote(certificado, **kwargs): def consulta_informacoes_lote(certificado, **kwargs):
return _send(certificado, 'ConsultaInformacoesLote', **kwargs)
return _send(certificado, "ConsultaInformacoesLote", **kwargs)
# Testado # Testado
def consulta_cnpj(certificado, **kwargs): def consulta_cnpj(certificado, **kwargs):
return _send(certificado, 'ConsultaCNPJ', **kwargs)
return _send(certificado, "ConsultaCNPJ", **kwargs)

64
pytrustnfe/nfse/simpliss/__init__.py

@ -15,16 +15,16 @@ from pytrustnfe.xml import render_xml, sanitize_response
def _render_xml(certificado, method, **kwargs): 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) xml_send = etree.tostring(xml_send)
return xml_send return xml_send
def _validate(method, xml): 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) nfe = etree.fromstring(xml)
esquema = etree.XMLSchema(etree.parse(schema)) esquema = etree.XMLSchema(etree.parse(schema))
@ -34,86 +34,82 @@ def _validate(method, xml):
def _send(method, **kwargs): 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: 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"] 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) client = HttpClient(base_url)
response = client.post_soap(soap, act) response = client.post_soap(soap, act)
response, obj = sanitize_response(response) 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): 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): def recepcionar_lote_rps(certificado, **kwargs):
if "xml" not in 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): 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): def consultar_situacao_lote(certificado, **kwargs):
if "xml" not in 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): def consultar_nfse_por_rps(certificado, **kwargs):
return _send('ConsultarNfsePorRps', **kwargs)
return _send("ConsultarNfsePorRps", **kwargs)
def xml_consultar_lote_rps(certificado, **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): def consultar_lote_rps(certificado, **kwargs):
if "xml" not in 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): def xml_consultar_nfse(certificado, **kwargs):
return _render_xml(certificado, 'ConsultarNfse', **kwargs)
return _render_xml(certificado, "ConsultarNfse", **kwargs)
def consultar_nfse(certificado, **kwargs): def consultar_nfse(certificado, **kwargs):
return _send('ConsultarNfse', **kwargs)
return _send("ConsultarNfse", **kwargs)
def xml_cancelar_nfse(certificado, **kwargs): def xml_cancelar_nfse(certificado, **kwargs):
return _render_xml(certificado, 'CancelarNfse', **kwargs)
return _render_xml(certificado, "CancelarNfse", **kwargs)
def cancelar_nfse(certificado, **kwargs): def cancelar_nfse(certificado, **kwargs):
if "xml" not in 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): def xml_gerar_nfse(certificado, **kwargs):
return _render_xml(certificado, 'GerarNfse', **kwargs)
return _render_xml(certificado, "GerarNfse", **kwargs)
def gerar_nfse(certificado, **kwargs): def gerar_nfse(certificado, **kwargs):
if "xml" not in 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): 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 return xml_send
def _send(method, **kwargs): 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: 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) client = get_client(base_url)
try: try:
xml_send = kwargs["xml"] 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: except Exception as e:
return { return {
'sent_xml': xml_send,
'received_xml': e.fault.faultstring,
"sent_xml": xml_send,
"received_xml": e.fault.faultstring,
} }
result = str(result) result = str(result)
result = unicodedata.normalize('NFKD', result).encode('ascii', 'ignore')
result = unicodedata.normalize("NFKD", result).encode("ascii", "ignore")
return { return {
'sent_xml': xml_send,
'received_xml': result,
"sent_xml": xml_send,
"received_xml": result,
} }
def xml_enviar_nota(**kwargs): def xml_enviar_nota(**kwargs):
return _render_xml('EnviarNota', **kwargs)
return _render_xml("EnviarNota", **kwargs)
def enviar_nota(**kwargs): def enviar_nota(**kwargs):
if "xml" not in 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): def xml_enviar_nota_retorna_url(**kwargs):
return _render_xml('EnviarNotaRetornaurlNota', **kwargs)
return _render_xml("EnviarNotaRetornaurlNota", **kwargs)
def enviar_nota_retorna_url(**kwargs): def enviar_nota_retorna_url(**kwargs):
if "xml" not in 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/"> <EnviarNota xmlns="http://susesu.com.br/">
<codPrefeitura>{{ codigo_prefeitura }}</codPrefeitura> <codPrefeitura>{{ codigo_prefeitura }}</codPrefeitura>
<notaFiscal> <notaFiscal>
@ -10,5 +11,5 @@
<cnpjPrestador>{{ cnpj_prestador }}</cnpjPrestador> <cnpjPrestador>{{ cnpj_prestador }}</cnpjPrestador>
<senhaNFD>{{ senha_nfd }}</senhaNFD> <senhaNFD>{{ senha_nfd }}</senhaNFD>
</EnviarNota> </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 = { URLS = {
PRODUCAO: { 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: { 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 = { URLS_EXIBICAO = {
PRODUCAO: { 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: { 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): class ChaveNFe(object):
def __init__(self, **kwargs): 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): 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): 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" assert isinstance(obj_chave, ChaveNFe), "Objeto deve ser do tipo ChaveNFe"
obj_chave.validar() obj_chave.validar()
chave_parcial = "%s%s%s%s%s%s%d%s" % ( 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 soma = 0
contador = 2 contador = 2
for c in reversed(chave_parcial): for c in reversed(chave_parcial):
@ -75,16 +79,16 @@ def _find_node(xml, node):
def gerar_nfeproc(envio, recibo): 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) 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") nfe = _find_node(docEnvio, "NFe")
protocolo = _find_node(docRecibo, "protNFe") protocolo = _find_node(docRecibo, "protNFe")
if nfe is None or protocolo is None: if nfe is None or protocolo is None:
return b''
return b""
root.append(nfe) root.append(nfe)
root.append(protocolo) root.append(protocolo)
return ET.tostring(root) return ET.tostring(root)
@ -96,6 +100,6 @@ def gerar_nfeproc_cancel(nfe_proc, cancelamento):
ev_cancelamento = _find_node(docCancel, "retEvento") ev_cancelamento = _find_node(docCancel, "retEvento")
if ev_cancelamento is None: if ev_cancelamento is None:
return b''
return b""
docEnvio.append(ev_cancelamento) docEnvio.append(ev_cancelamento)
return ET.tostring(docEnvio) 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): def render_xml(path, template_name, remove_empty, **nfe):
nfe = recursively_normalize(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"] = filters.strip_line_feed
env.filters["normalize_str"] = filters.normalize_str env.filters["normalize_str"] = filters.normalize_str
env.filters["format_percent"] = filters.format_percent 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 env.filters["comma"] = filters.format_with_comma
template = env.get_template(template_name) 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) root = etree.fromstring(xml, parser=parser)
for element in root.iter("*"): # remove espaços em branco for element in root.iter("*"): # remove espaços em branco
if element.text is not None and not element.text.strip(): 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): 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 # Remove namespaces inuteis na resposta
for elem in tree.getiterator(): for elem in tree.getiterator():
if not hasattr(elem.tag, 'find'):
if not hasattr(elem.tag, "find"):
continue continue
i = elem.tag.find('}')
i = elem.tag.find("}")
if i >= 0: if i >= 0:
elem.tag = elem.tag[i + 1:] elem.tag = elem.tag[i + 1:]
objectify.deannotate(tree, cleanup_namespaces=True) objectify.deannotate(tree, cleanup_namespaces=True)

27
pytrustnfe/xml/filters.py

@ -14,23 +14,24 @@ def normalize_str(string):
""" """
if string: if string:
if not isinstance(string, str): 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): def strip_line_feed(string):
if string: if string:
if not isinstance(string, str): if not isinstance(string, str):
string = str(string, 'utf-8', 'replace')
string = str(string, "utf-8", "replace")
remap = { 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.translate(remap).strip()
return string return string
@ -45,7 +46,7 @@ def format_datetime(value):
""" """
Format datetime Format datetime
""" """
dt_format = '%Y-%m-%dT%H:%M:%I'
dt_format = "%Y-%m-%dT%H:%M:%I"
if isinstance(value, datetime): if isinstance(value, datetime):
return value.strftime(dt_format) return value.strftime(dt_format)
return value return value
@ -55,7 +56,7 @@ def format_date(value):
""" """
Format date Format date
""" """
dt_format = '%Y-%m-%d'
dt_format = "%Y-%m-%d"
if isinstance(value, date): if isinstance(value, date):
return value.strftime(dt_format) return value.strftime(dt_format)
return value return value
@ -63,5 +64,5 @@ def format_date(value):
def format_with_comma(value): def format_with_comma(value):
if isinstance(value, float): if isinstance(value, float):
return ('%.2f' % value).replace('.', ',')
return ("%.2f" % value).replace(".", ",")
return value return value

4
pytrustnfe/xml/validate.py

@ -7,8 +7,8 @@ import os
from lxml import etree from lxml import etree
PATH = os.path.dirname(os.path.abspath(__file__)) PATH = os.path.dirname(os.path.abspath(__file__))
SCHEMA = os.path.join(PATH, 'schemas/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): 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

76
setup.py

@ -2,51 +2,53 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages
VERSION = "1.0.46"
VERSION = "1.0.47"
setup( setup(
name="pytrustnfe3", name="pytrustnfe3",
version=VERSION, version=VERSION,
author="Danimar Ribeiro", author="Danimar Ribeiro",
author_email='danimaribeiro@gmail.com',
keywords=['nfe', 'mdf-e'],
author_email="danimaribeiro@gmail.com",
keywords=["nfe", "mdf-e"],
classifiers=[ 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=[ install_requires=[
'urllib3', 'urllib3',
'xmlsec==1.3.3', # apt update;apt install libxmlsec1-dev pkg-config -y 'xmlsec==1.3.3', # apt update;apt install libxmlsec1-dev pkg-config -y

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>

72
tests/test_assinatura.py

@ -1,9 +1,9 @@
# coding=utf-8 # coding=utf-8
'''
"""
Created on Jun 14, 2015 Created on Jun 14, 2015
@author: danimar @author: danimar
'''
"""
import os import os
import os.path import os.path
import unittest import unittest
@ -11,18 +11,22 @@ from lxml import etree
from pytrustnfe.nfe.assinatura import Assinatura 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): class test_assinatura(unittest.TestCase):
@ -30,26 +34,36 @@ class test_assinatura(unittest.TestCase):
caminho = os.path.dirname(__file__) caminho = os.path.dirname(__file__)
def test_assinar_xml_senha_invalida(self): def test_assinar_xml_senha_invalida(self):
pfx = open(os.path.join(self.caminho, 'teste.pfx'), 'rb').read()
signer = Assinatura(pfx, '123')
self.assertRaises(Exception, signer.assina_xml, signer,
etree.fromstring(XML_ASSINAR),
'NFe43150602261542000143550010000000761792265342')
pfx = open(os.path.join(self.caminho, "teste.pfx"), "rb").read()
signer = Assinatura(pfx, "123")
self.assertRaises(
Exception,
signer.assina_xml,
signer,
etree.fromstring(XML_ASSINAR),
"NFe43150602261542000143550010000000761792265342",
)
def test_assinar_xml_invalido(self): 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,
etree.fromstring(XML_ERRADO),
'NFe43150602261542000143550010000000761792265342')
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",
)
@unittest.skip
def test_assinar_xml_valido(self): 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( xml = signer.assina_xml(
etree.fromstring(XML_ASSINAR), 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 # coding=utf-8
'''
"""
Created on Jun 14, 2015 Created on Jun 14, 2015
@author: danimar @author: danimar
'''
"""
import unittest import unittest
import os import os
import os.path import os.path
@ -11,37 +11,41 @@ from pytrustnfe.certificado import Certificado
from pytrustnfe.certificado import save_cert_key from pytrustnfe.certificado import save_cert_key
from pytrustnfe.certificado import extract_cert_and_key_from_pfx 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): class test_assinatura(unittest.TestCase):
@ -49,34 +53,34 @@ class test_assinatura(unittest.TestCase):
caminho = os.path.dirname(__file__) caminho = os.path.dirname(__file__)
def test_preparar_pfx(self): def test_preparar_pfx(self):
dir_pfx = open(os.path.join(self.caminho, 'teste.pfx'), '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): 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() 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): 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_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): 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): 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__) caminho = os.path.dirname(__file__)
def test_can_generate_danfe(self): 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_string = open('/home/danimar/Downloads/NFe (5).xml', "r").read()
xml_element = etree.fromstring(xml_string) xml_element = etree.fromstring(xml_string)
@ -22,5 +22,5 @@ class test_danfe(unittest.TestCase):
# Para testar localmente o Danfe # Para testar localmente o Danfe
# with open('/home/danimar/danfe.pdf', 'w') as oFile: # with open('/home/danimar/danfe.pdf', 'w') as oFile:
with tempfile.TemporaryFile(mode='wb') as oFile:
with tempfile.TemporaryFile(mode="wb") as oFile:
oDanfe.writeto_pdf(oFile) oDanfe.writeto_pdf(oFile)

13
tests/test_ginfes.py

@ -13,12 +13,11 @@ class test_nfse_ginfes(unittest.TestCase):
@unittest.skip @unittest.skip
def test_consulta_situacao_lote(self): def test_consulta_situacao_lote(self):
pfx_source = open('/home/danimar/Downloads/machado.pfx', '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.certificado import Certificado
from pytrustnfe.nfse.paulistana import envio_lote_rps from pytrustnfe.nfse.paulistana import envio_lote_rps
from pytrustnfe.nfse.paulistana import cancelamento_nfe 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): class test_nfse_paulistana(unittest.TestCase):
@ -17,127 +15,126 @@ class test_nfse_paulistana(unittest.TestCase):
def _get_nfse(self): def _get_nfse(self):
rps = [ 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 = { 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 return nfse
def test_envio_nfse(self): 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() 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() retorno = mock.MagicMock()
client.return_value = retorno client.return_value = retorno
retorno.service.EnvioLoteRPS.return_value = xml_return retorno.service.EnvioLoteRPS.return_value = xml_return
retorno = envio_lote_rps(pfx, nfse=nfse) 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): 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() 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() retorno = mock.MagicMock()
client.return_value = retorno 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) 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): def _get_cancelamento(self):
return { 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): 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() 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() retorno = mock.MagicMock()
client.return_value = retorno client.return_value = retorno
retorno.service.CancelamentoNFe.return_value = xml_return retorno.service.CancelamentoNFe.return_value = xml_return
retorno = cancelamento_nfe(pfx, cancelamento=cancelamento) 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): 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() 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() retorno = mock.MagicMock()
client.return_value = retorno client.return_value = retorno
retorno.service.CancelamentoNFe.return_value = xml_return retorno.service.CancelamentoNFe.return_value = xml_return
retorno = cancelamento_nfe(pfx, cancelamento=cancelamento) 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)

37
tests/test_servidores.py

@ -1,47 +1,46 @@
# coding=utf-8 # coding=utf-8
'''
"""
Created on Jun 14, 2015 Created on Jun 14, 2015
@author: danimar @author: danimar
'''
"""
import unittest import unittest
from pytrustnfe.Servidores import localizar_url, localizar_qrcode 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): class test_servidores(unittest.TestCase):
def test_localizar_url(self): def test_localizar_url(self):
url = localizar_url('NfeAutorizacao', '29', ambiente=1)
url = localizar_url("NfeAutorizacao", "29", ambiente=1)
self.assertEqual(url, url_ba) self.assertEqual(url, url_ba)
url = localizar_url('NfeAutorizacao', '35', ambiente=1)
url = localizar_url("NfeAutorizacao", "35", ambiente=1)
self.assertEqual(url, url_sp) self.assertEqual(url, url_sp)
url = localizar_url('NfeAutorizacao', '42', ambiente=1)
url = localizar_url("NfeAutorizacao", "42", ambiente=1)
self.assertEqual(url, url_sc) self.assertEqual(url, url_sc)
url = localizar_url('NfeAutorizacao', '43', ambiente=1)
url = localizar_url("NfeAutorizacao", "43", ambiente=1)
self.assertEqual(url, url_rs) 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) 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) self.assertEqual(url, url_cad_sc)
def test_localizar_qrcode(self): def test_localizar_qrcode(self):
url = localizar_qrcode('35')
url = localizar_qrcode("35")
self.assertEqual(url, url_qrcode_homologacao_sp) self.assertEqual(url, url_qrcode_homologacao_sp)

134
tests/test_utils.py

@ -1,48 +1,52 @@
# coding=utf-8 # coding=utf-8
'''
"""
Created on Jun 16, 2015 Created on Jun 16, 2015
@author: danimar @author: danimar
'''
"""
import unittest import unittest
import datetime 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 from pytrustnfe.utils import ChaveNFe
class test_utils(unittest.TestCase): class test_utils(unittest.TestCase):
kwargs = { 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): def test_date_tostring(self):
hoje = datetime.date.today() hoje = datetime.date.today()
data = date_tostring(hoje) 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") self.assertRaises(Exception, date_tostring, "Not a date")
def test_datetime_tostring(self): def test_datetime_tostring(self):
hoje = datetime.datetime.now() hoje = datetime.datetime.now()
data = datetime_tostring(hoje) 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") self.assertRaises(Exception, datetime_tostring, "Not a date")
def test_geracao_chave(self): def test_geracao_chave(self):
chave = ChaveNFe(**self.kwargs) chave = ChaveNFe(**self.kwargs)
str_chave = gerar_chave(chave) 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")
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): def test_chave_nfe(self):
chave = ChaveNFe(**self.kwargs) chave = ChaveNFe(**self.kwargs)
with self.assertRaises(AssertionError) as cm: with self.assertRaises(AssertionError) as cm:
chave.cnpj = ''
chave.cnpj = ""
chave.validar() chave.validar()
chave.cnpj = '1234567891011'
self.assertEqual('CNPJ necessário para criar chave NF-e',
str(cm.exception),
'Validação da chave nf-e incorreta')
chave.cnpj = "1234567891011"
self.assertEqual(
"CNPJ necessário para criar chave NF-e",
str(cm.exception),
"Validação da chave nf-e incorreta",
)
with self.assertRaises(AssertionError) as cm: with self.assertRaises(AssertionError) as cm:
chave.estado = ''
chave.estado = ""
chave.validar() chave.validar()
chave.estado = '42'
self.assertEqual('Estado necessário para criar chave NF-e',
str(cm.exception),
'Validação da chave nf-e incorreta')
chave.estado = "42"
self.assertEqual(
"Estado necessário para criar chave NF-e",
str(cm.exception),
"Validação da chave nf-e incorreta",
)
with self.assertRaises(AssertionError) as cm: with self.assertRaises(AssertionError) as cm:
chave.emissao = ''
chave.emissao = ""
chave.validar() chave.validar()
chave.emissao = '0'
self.assertEqual('Emissão necessário para criar chave NF-e',
str(cm.exception),
'Validação da chave nf-e incorreta')
chave.emissao = "0"
self.assertEqual(
"Emissão necessário para criar chave NF-e",
str(cm.exception),
"Validação da chave nf-e incorreta",
)
with self.assertRaises(AssertionError) as cm: with self.assertRaises(AssertionError) as cm:
chave.modelo = ''
chave.modelo = ""
chave.validar() chave.validar()
chave.modelo = '55'
self.assertEqual('Modelo necessário para criar chave NF-e',
str(cm.exception),
'Validação da chave nf-e incorreta')
chave.modelo = "55"
self.assertEqual(
"Modelo necessário para criar chave NF-e",
str(cm.exception),
"Validação da chave nf-e incorreta",
)
with self.assertRaises(AssertionError) as cm: with self.assertRaises(AssertionError) as cm:
chave.serie = ''
chave.serie = ""
chave.validar() chave.validar()
chave.serie = '012'
self.assertEqual('Série necessária para criar chave NF-e',
str(cm.exception),
'Validação da chave nf-e incorreta')
chave.serie = "012"
self.assertEqual(
"Série necessária para criar chave NF-e",
str(cm.exception),
"Validação da chave nf-e incorreta",
)
with self.assertRaises(AssertionError) as cm: with self.assertRaises(AssertionError) as cm:
chave.numero = ''
chave.numero = ""
chave.validar() chave.validar()
chave.numero = '000000780'
self.assertEqual('Número necessário para criar chave NF-e',
str(cm.exception),
'Validação da chave nf-e incorreta')
chave.numero = "000000780"
self.assertEqual(
"Número necessário para criar chave NF-e",
str(cm.exception),
"Validação da chave nf-e incorreta",
)
with self.assertRaises(AssertionError) as cm: with self.assertRaises(AssertionError) as cm:
chave.tipo = ''
chave.tipo = ""
chave.validar() chave.validar()
chave.tipo = '42'
self.assertEqual('Tipo necessário para criar chave NF-e',
str(cm.exception),
'Validação da chave nf-e incorreta')
chave.tipo = "42"
self.assertEqual(
"Tipo necessário para criar chave NF-e",
str(cm.exception),
"Validação da chave nf-e incorreta",
)
with self.assertRaises(AssertionError) as cm: with self.assertRaises(AssertionError) as cm:
chave.codigo = ''
chave.codigo = ""
chave.validar() chave.validar()
self.assertEqual('Código necessário para criar chave NF-e',
str(cm.exception),
'Validação da chave nf-e incorreta')
self.assertEqual(
"Código necessário para criar chave NF-e",
str(cm.exception),
"Validação da chave nf-e incorreta",
)

17
tests/test_xml.py

@ -1,9 +1,9 @@
# coding=utf-8 # coding=utf-8
'''
"""
Created on Jun 14, 2015 Created on Jun 14, 2015
@author: danimar @author: danimar
'''
"""
import unittest import unittest
from datetime import datetime from datetime import datetime
from pytrustnfe.xml.filters import normalize_str from pytrustnfe.xml.filters import normalize_str
@ -14,17 +14,16 @@ from pytrustnfe.xml.filters import format_datetime
class test_xmlfilters(unittest.TestCase): class test_xmlfilters(unittest.TestCase):
def test_xmlfilters(self): 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(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) 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 ") word = strip_line_feed("olá\ncomo vai\r senhor ")
self.assertEqual(word, "olá como vai 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): class test_xml_serializacao(unittest.TestCase):
def test_serializacao_default(self): 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) self.assertEqual(xml + "\n", result)
def test_serializacao_remove_empty(self): def test_serializacao_remove_empty(self):
path = os.path.join(os.path.dirname(__file__), 'XMLs')
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) 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): 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) xml, obj = sanitize_response(xml_to_clear)
self.assertEqual(xml, xml_to_clear) self.assertEqual(xml, xml_to_clear)
self.assertEqual(obj.tpAmb, 'oi')
self.assertEqual(obj.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