From 916ab9e17dc584410990bd97110d4e56078ac3d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marinho=20Brand=C3=A3o?= Date: Wed, 13 Jan 2010 14:37:04 -0200 Subject: [PATCH] Implementando a fonte de dados para manter o repositorio dos objetos em memoria e para permitir a implementacao de persistencia para o desenvolvedor que for utilizar a biblioteca. --- pynfe/entidades/__init__.py | 1 + pynfe/entidades/base.py | 8 +- pynfe/entidades/emitente.py | 3 + pynfe/entidades/fontes_dados.py | 126 ++++++++++++++++++++++++ pynfe/excecoes.py | 6 ++ pynfe/processamento/interfaces.py | 54 ---------- pynfe/processamento/serializacao.py | 100 +++++++++++++++++++ tests/01-basico.txt | 40 ++++---- tests/02-modelo-00-definicoes-gerais.txt | 102 +++++++++++++++++++ tests/03-processamento-00-definicoes-gerais.txt | 75 ++++++++++++++ tests/03-processamento-01-serializacao-xml.txt | 16 +++ tests/03-processamento.txt | 75 -------------- 12 files changed, 458 insertions(+), 148 deletions(-) create mode 100644 pynfe/entidades/fontes_dados.py create mode 100644 pynfe/excecoes.py delete mode 100644 pynfe/processamento/interfaces.py create mode 100644 pynfe/processamento/serializacao.py create mode 100644 tests/03-processamento-00-definicoes-gerais.txt create mode 100644 tests/03-processamento-01-serializacao-xml.txt delete mode 100644 tests/03-processamento.txt diff --git a/pynfe/entidades/__init__.py b/pynfe/entidades/__init__.py index 7ebdc55..43fa430 100644 --- a/pynfe/entidades/__init__.py +++ b/pynfe/entidades/__init__.py @@ -4,4 +4,5 @@ from cliente import Cliente from transportadora import Transportadora from notafiscal import NotaFiscal from lotes import LoteNotaFiscal +from fontes_dados import FonteDados diff --git a/pynfe/entidades/base.py b/pynfe/entidades/base.py index 1988de4..bec7cc1 100644 --- a/pynfe/entidades/base.py +++ b/pynfe/entidades/base.py @@ -1,12 +1,18 @@ # -*- coding: utf-8 -*- class Entidade(object): + _fonte_dados = None + def __init__(self, **kwargs): # Codigo para dinamizar a criacao de instancias de entidade, # aplicando os valores dos atributos na instanciacao - for k, v in kwargs: + for k, v in kwargs.items(): setattr(self, k, v) + # Adiciona o objeto à fonte de dados informada + if self._fonte_dados: + self._fonte_dados.adicionar_objeto(self) + class Lote(object): pass diff --git a/pynfe/entidades/emitente.py b/pynfe/entidades/emitente.py index 49b0678..b4ba179 100644 --- a/pynfe/entidades/emitente.py +++ b/pynfe/entidades/emitente.py @@ -54,3 +54,6 @@ class Emitente(Entidade): # Logotipo logotipo = None + def __repr__(self): + return ''%(self.cnpj, self.razao_social) + diff --git a/pynfe/entidades/fontes_dados.py b/pynfe/entidades/fontes_dados.py new file mode 100644 index 0000000..bb6b159 --- /dev/null +++ b/pynfe/entidades/fontes_dados.py @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- +from pynfe.excecoes import NenhumObjetoEncontrado, MuitosObjetosEncontrados + +class FonteDados(object): + u"""Classe responsável por ser o repositório dos objetos em memória e que + pode ser extendida para persistir esses objetos. Também tem a função de + memorizar os objetos redundantes como um só e assim otimizar o desempenho.""" + + _objetos = None + + def __init__(self, objetos=None): + # Inicializa variável que armazena os objetos contidos na Fonte de Dados + if objetos: + self._objetos = objetos + else: + self._objetos = [] + + def carregar_objetos(self, **kwargs): + u"""Método responsavel por retornar os objetos que casem com os atributos + informados no argumento **kwargs (argumentos nomeados). + + Um argumento especial é o '_classe', que representa a classe da entidade + desejada. + + FIXME: Este algoritimo pode ser melhorado pra fazer pesquisas melhores, + mas por enquanto vamos nos focar no processo em geral para só depois nos + preocupar com otimizações e desempenho.""" + + # Função de filtro + def filtrar(obj): + ret = True + + for k,v in kwargs.items(): + # Filtra pela classe e pelos atributos + ret = (k == '_classe' and isinstance(obj, v)) or\ + (k != '_classe' and getattr(obj, k, None) == v) + + if not ret: + break + + return ret + + # Filtra a lista de objetos + lista = filter(filtrar, self._objetos) + + return lista + + def adicionar_objeto(self, _objeto): + u"""Método responsável por adicionar o(s) objeto(s) informado(s) ao + repositorio de objetos da fonte de dados.""" + + from base import Entidade + + # Adiciona _objeto como objeto + if isinstance(_objeto, Entidade): + self._objetos.append(_objeto) + + # Adiciona _objeto como lista + elif isinstance(_objeto, (list, tuple)): + self._objetos += _objeto + + else: + raise Exception('Objeto informado e invalido!') + + def remover_objeto(self, _objeto=None, **kwargs): + u"""Método responsavel por remover os objetos que casem com os atributos + informados no argumento **kwargs (argumentos nomeados). + + Um argumento especial é o '_classe', que representa a classe da entidade + desejada. + + Outro argumetno especial é o '_objeto', que representa o objeto a ser + removido. Caso o argumento _objeto seja uma lista de objetos, eles serão + removidos também.""" + + from base import Entidade + + lista = None + + # Remove objetos + if not _objeto: + lista = self.carregar_objetos(**kwargs) + + # Remove _objeto como objeto + elif isinstance(_objeto, Entidade): + lista = [_objeto] + + # Remove _objeto como objeto + elif isinstance(_objeto, (list, tuple)): + lista = _objeto + + else: + raise Exception('Objeto informado e invalido!') + + # Efetiva a remoção + for obj in lista: + self._objetos.remove(obj) + + def obter_objeto(self, **kwargs): + u"""Faz a ponte para o método 'carregar_objetos' mas obriga o retorno de + apenas um objeto, levantando exceção se nenhum for encontrado ou se forem + encontrados mais de um.""" + + lista = self.carregar_objetos(**kwargs) + + if len(lista) == 0: + raise NenhumObjetoEncontrado('Nenhum objeto foi encontrado!') + elif len(lista) > 1: + raise MuitosObjetosEncontrados('Muitos objetos foram encontrados!') + + return lista[0] + + def obter_lista(self, **kwargs): + u"""Método de proxy, que somente repassa a chamada ao metodo 'carregar_objetos'""" + return self.carregar_objetos(**kwargs) + + def contar_objetos(self, **kwargs): + u"""Método que repassa a chamada ao metodo 'carregar_objetos' mas retorna + somente a quantidade de objetos encontrados.""" + + if kwargs: + return len(self.carregar_objetos(**kwargs)) + else: + return len(self._objetos) + + diff --git a/pynfe/excecoes.py b/pynfe/excecoes.py new file mode 100644 index 0000000..cd9250e --- /dev/null +++ b/pynfe/excecoes.py @@ -0,0 +1,6 @@ +class NenhumObjetoEncontrado(Exception): + pass + +class MuitosObjetosEncontrados(Exception): + pass + diff --git a/pynfe/processamento/interfaces.py b/pynfe/processamento/interfaces.py deleted file mode 100644 index eeff24a..0000000 --- a/pynfe/processamento/interfaces.py +++ /dev/null @@ -1,54 +0,0 @@ -try: - from lxml import etree -except ImportError: - try: - # Python 2.5 - cElementTree - import xml.etree.cElementTree as etree - except ImportError: - try: - # Python 2.5 - ElementTree - import xml.etree.ElementTree as etree - except ImportError: - try: - # Instalacao normal do cElementTree - import cElementTree as etree - except ImportError: - try: - # Instalacao normal do ElementTree - import elementtree.ElementTree as etree - except ImportError: - raise Exception('Falhou ao importar lxml/ElementTree') - -class Interface(object): - """Classe abstrata responsavel por fornecer as funcionalidades basicas para - exportacao e importacao de Notas Fiscais eletronicas para formatos serializados - de arquivos. Como XML, JSON, binario, etc. - - Nao deve ser instanciada diretamente!""" - - lista_de_nfs = None - - def __new__(cls, *args, **kwargs): - if cls == Interface: - raise Exception('Esta classe nao pode ser instanciada diretamente!') - else: - return cls(*args, **kwargs) - - def __init__(self, nf_ou_lista): - self.lista_de_nfs = isinstance(nf_ou_lista, list) and nf_ou_lista or [nf_ou_lista] - - def exportar(self, destino): - """Gera o(s) arquivo(s) de exportacao a partir da Nofa Fiscal eletronica - ou lista delas.""" - - raise Exception('Metodo nao implementado') - - def importar(self, origem): - """Fabrica que recebe o caminho ou objeto de origem e instancia os objetos - da PyNFe""" - - raise Exception('Metodo nao implementado') - -class InterfaceXML(Interface): - pass - diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py new file mode 100644 index 0000000..c5ddab0 --- /dev/null +++ b/pynfe/processamento/serializacao.py @@ -0,0 +1,100 @@ +try: + from lxml import etree +except ImportError: + try: + # Python 2.5 - cElementTree + import xml.etree.cElementTree as etree + except ImportError: + try: + # Python 2.5 - ElementTree + import xml.etree.ElementTree as etree + except ImportError: + try: + # Instalacao normal do cElementTree + import cElementTree as etree + except ImportError: + try: + # Instalacao normal do ElementTree + import elementtree.ElementTree as etree + except ImportError: + raise Exception('Falhou ao importar lxml/ElementTree') + +class Serializacao(object): + """Classe abstrata responsavel por fornecer as funcionalidades basicas para + exportacao e importacao de Notas Fiscais eletronicas para formatos serializados + de arquivos. Como XML, JSON, binario, etc. + + Nao deve ser instanciada diretamente!""" + + lista_de_nfs = None + + def __new__(cls, *args, **kwargs): + if cls == Serializacao: + raise Exception('Esta classe nao pode ser instanciada diretamente!') + else: + return cls(*args, **kwargs) + + def __init__(self, nf_ou_lista): + self.lista_de_nfs = isinstance(nf_ou_lista, list) and nf_ou_lista or [nf_ou_lista] + + def exportar(self, destino): + """Gera o(s) arquivo(s) de exportacao a partir da Nofa Fiscal eletronica + ou lista delas.""" + + raise Exception('Metodo nao implementado') + + def importar(self, origem): + """Fabrica que recebe o caminho ou objeto de origem e instancia os objetos + da PyNFe""" + + raise Exception('Metodo nao implementado') + +class SerializacaoXML(Serializacao): + def exportar(self, objetos, destino): + """Gera o(s) arquivo(s) de Nofa Fiscal eletronica no padrao oficial da SEFAZ + e Receita Federal, para ser(em) enviado(s) para o webservice ou para ser(em) + armazenado(s) em cache local.""" + + saida = [] + + # Dados do emitente + saida.append(self._serializar_emitente(objetos)) + + # Certificado Digital? XXX + + # Clientes + saida.append(self._serializar_clientes(objetos)) + + # Transportadoras + saida.append(self._serializar_transportadoras(objetos)) + + # Produtos + saida.append(self._serializar_produtos(objetos)) + + # Lote de Notas Fiscais + saida.append(self._serializar_notas_fiscais(objetos)) + + # FIXME + return '\n'.join(saida) + + def importar(self, objetos, origem): + """Cria as instancias do PyNFe a partir de arquivos XML no formato padrao da + SEFAZ e Receita Federal.""" + + raise Exception('Metodo nao implementado') + + def _serializar_emitente(self, objetos): + return '' + + def _serializar_clientes(self, objetos): + return '' + + def _serializar_transportadoras(self, objetos): + return '' + + def _serializar_produtos(self, objetos): + return '' + + def _serializar_notas_fiscais(self, objetos): + return '' + diff --git a/tests/01-basico.txt b/tests/01-basico.txt index 3c7cddc..e7e3b70 100644 --- a/tests/01-basico.txt +++ b/tests/01-basico.txt @@ -1,7 +1,11 @@ TESTES BASICOS ============== - >>> import sets + >>> try: + ... set + ... except: + ... from sets import Set as set + A biblioteca deve fornecer uma colecao de utilitarios para consumir o webservice da NF-e. @@ -16,9 +20,9 @@ modelo: | MODELO DE ENTIDADES | --------------------------------------------------------------------- | | - | ------------ | - | | Entidade | | - | ------------ | + | ------------ -------------- | + | | Entidade |-------<>| FonteDados | | + | ------------ -------------- | | A | | | | | ----especializacao-------------------------- | @@ -41,16 +45,16 @@ modelo: | PROCESSAMENTO | -------------------------------------------------------------------------- | | - | ---------------- -------------- -------------------------------- | - | | InterfaceXML | | Assinatura | | Comunicacao | | - | ---------------- -------------- -------------------------------- | - | | exportar() | | assinar() | | transmitir() | | - | | importar() | -------------- | cancelar() | | - | ---------------- | situacao_nfe() | | - | ------------- | status_servico() | | - | -------------- | Validacao | | consultar_cadastro() | | - | | DANFE | ------------- | inutilizar_faixa_numeracao() | | - | -------------- | validar() | -------------------------------- | + | ------------------- -------------- -------------------------------- | + | | SerializacaoXML | | Assinatura | | Comunicacao | | + | ------------------- -------------- -------------------------------- | + | | exportar() | | assinar() | | transmitir() | | + | | importar() | -------------- | cancelar() | | + | ------------------- | situacao_nfe() | | + | ------------- | status_servico() | | + | -------------- | Validacao | | consultar_cadastro() | | + | | DANFE | ------------- | inutilizar_faixa_numeracao() | | + | -------------- | validar() | -------------------------------- | | | imprimir() | ------------- | | -------------- | | | @@ -62,23 +66,23 @@ Os pacotes da biblioteca sao: biblioteca, incluindo flags e funcoes genericas) >>> from pynfe import utils - >>> sets.Set([attr for attr in dir(utils) if not attr.startswith('__')]) == sets.Set(['flags']) + >>> set([attr for attr in dir(utils) if not attr.startswith('__')]) == set(['flags']) True - entidades (contem todas as entidades da biblioteca) >>> from pynfe import entidades - >>> sets.Set([attr for attr in dir(entidades) if not attr.startswith('__')]) == sets.Set([ + >>> set([attr for attr in dir(entidades) if not attr.startswith('__')]) == set([ ... 'Cliente', 'Emitente', 'LoteNotaFiscal', 'NotaFiscal', 'Produto', ... 'Transportadora', 'base', 'cliente', 'emitente', 'lotes', 'notafiscal', - ... 'produto', 'transportadora']) + ... 'produto', 'transportadora', 'fontes_dados', 'FonteDados']) True - processamento (contem todas as funcionalidades de processamento da biblioteca >>> from pynfe import processamento - >>> sets.Set([attr for attr in dir(processamento) if not attr.startswith('__')]) == sets.Set([ + >>> set([attr for attr in dir(processamento) if not attr.startswith('__')]) == set([ ... 'Assinatura', 'Comunicacao', 'DANFE', 'InterfaceXML', 'Validacao', ... 'assinatura', 'comunicacao', 'danfe', 'interfaces', 'validacao']) True diff --git a/tests/02-modelo-00-definicoes-gerais.txt b/tests/02-modelo-00-definicoes-gerais.txt index fe9fc10..b2d02b8 100644 --- a/tests/02-modelo-00-definicoes-gerais.txt +++ b/tests/02-modelo-00-definicoes-gerais.txt @@ -5,3 +5,105 @@ Modelo das entidades e como elas se relacionam. Nenhum dos campos deve permitir acentos e/ou cedilhas. +Todas as entidades devem referenciar uma Fonte de Dados, de forma a evitar +redundancia de dados (com o objetivo de melhorar o desempenho e possibilitar +o uso de cache de persistencia de dados serializados). + + >>> from pynfe.entidades import FonteDados + >>> fonte_dados = FonteDados() + +Nao eh da funcao do PyNFe efetuar a persistencia dos objetos, mas a classe +FonteDados deve facilitar esse processo ao software que for implementa-la. + + >>> hasattr(FonteDados, 'carregar_objetos') + True + + >>> from pynfe.entidades import Emitente + +Populando fonte de dados com objetos + + >>> bool(Emitente(cnpj='12.345.678/0001-90', _fonte_dados=fonte_dados)) + True + + >>> bool(Emitente(razao_social='JKL Calcados Ltda.', _fonte_dados=fonte_dados)) + True + + >>> bool(Emitente(razao_social='JKL Calcados Ltda.', _fonte_dados=fonte_dados)) + True + +O metodo carregar_objetos pode ser sobrecarregado para alterar o carregamento de +objetos da memoria para forcar mocking ou para carregar de camada persistente. +Ele sempre retorna uma lista de objetos, independente se vazia ou com qualquer +quantidade de objetos. + + >>> def carregar_objetos(self, **kwargs): + ... if kwargs.get('cnpj', None) == 'xxx': + ... return ['encontrado!'] + ... + ... return self.antigo_carregar_objetos(**kwargs) + +Substituindo metodo 'carregar_objetos' + + >>> fonte_dados.antigo_carregar_objetos = fonte_dados.carregar_objetos + + >>> import new + >>> fonte_dados.carregar_objetos = new.instancemethod(carregar_objetos, fonte_dados, FonteDados) + + >>> fonte_dados.obter_objeto(cnpj='xxx') + 'encontrado!' + +O metodo 'obter_objeto' retorna um unico objeto que atende aos atributos informados. +O argumento especial '_classe' eh utilizado para indicar que a classe da entidade eh +a atribuida a esse argumento. + + >>> emitente = fonte_dados.obter_objeto(cnpj='12.345.678/0001-90', _classe=Emitente) + + >>> isinstance(emitente, Emitente) + True + +Caso nenhum objeto seja encontrado, uma excecao deve ser levantada. + + >>> from pynfe.excecoes import NenhumObjetoEncontrado + + >>> try: + ... fonte_dados.obter_objeto(cnpj='98.765.432/0001-10', _classe=Emitente) + ... except NenhumObjetoEncontrado, e: + ... print e.message + Nenhum objeto foi encontrado! + +Caso mais de um objeto sejam encontrados, uma excecao deve ser levantada tambem. + + >>> from pynfe.excecoes import MuitosObjetosEncontrados + + >>> try: + ... fonte_dados.obter_objeto(razao_social='JKL Calcados Ltda.', _classe=Emitente) + ... except MuitosObjetosEncontrados, e: + ... print e.message + Muitos objetos foram encontrados! + +O metodo 'obter_lista' retorna uma lista de objetos, mesmo que vazia. + + >>> len(fonte_dados.obter_lista(razao_social='JKL Calcados Ltda.')) + 2 + + >>> len(fonte_dados.obter_lista(razao_social='Inexistente S/A')) + 0 + +Qualquer entidade que for instanciada deve ser acrescentada automaticamente a lista de +objetos da Fonte de Dados, atraves de um metodo especifico pra isso + + >>> conta_antes = fonte_dados.contar_objetos() + >>> emitente = Emitente(razao_social='Emitente Novo', _fonte_dados=fonte_dados) + >>> fonte_dados.contar_objetos() - conta_antes + 1 + +O contador de objetos retorna a quantidade de instancias que casem com os argumentos passados + + >>> fonte_dados.contar_objetos(_classe=Emitente, razao_social='Emitente Novo') + 1 + +Permitir tambem remover objetos (que por padrao remove apenas da lista da memoria e nao +eh persistente. + + >>> fonte_dados.remover_objeto(emitente) + diff --git a/tests/03-processamento-00-definicoes-gerais.txt b/tests/03-processamento-00-definicoes-gerais.txt new file mode 100644 index 0000000..a71eb2d --- /dev/null +++ b/tests/03-processamento-00-definicoes-gerais.txt @@ -0,0 +1,75 @@ +PROCESSAMENTO +============= + +Gerar arquivos XML +------------------ + +- Gera os arquivos XML a partir dos dados das instancias da NF-e +- Quando gerados me lote, apenas o primeiro arquivo deve ter o cabecalho + padrao do XML 1.0 + - +- Namespace + - + - A declaração do namespace da assinatura digital deverá ser realizada na + própria tag , conforme exemplo abaixo. + - Cada documento XML deverá ter o seu namespace individual em seu elemento raiz. + +Validar NF-e +------------ + +- Efetuar validacoes dos XSD no(s) XML(s) gerado(s) + +Assinar NF-e +------------ + +- Na hora de assinar, selecionar um Certificado Digital + + +Transmitir NF-e (ou lote de NF-e`s) +----------------------------------- + +- Enviar arquivos +- Validar mensagens de retorno +- Confirmar transmissao efetuada com sucesso +- Notas Fiscais em processamento +- Faixas de numeracao a serem inutilizadas +- Utilizar SSL 3.0 +- O certificado eh exigido na transmissao + +A troca de mensagens entre os Web Services do ambiente do Sistema de Recepção da NFe +e o aplicativo da empresa será realizada no padrão SOAP versão 1.2, com troca de +mensagens XML no padrão Style/Enconding: Document/Literal. + +Os serviços da NF-e serão implementados da seguinte forma: + +Recepção de NF-e Assincrona +Cancelamento de NF-e Sincrona +Inutilização de Numeração de NF-e Sincrona +Consulta da situação atual da NF-e Sincrona +Consulta do status do serviço Sincrona +Consulta cadastro Sincrona + +Imprimir DANF-e +--------------- + +- Geracao baseada no Geraldo Reports +- Gerar codigo de barras (padrao CODE-128C) +- Opcao de formulario pre-impresso (Sim/Nao) +- Chave de acesso + +Cancelar NF-e +------------- + +Cancela uma NF-e mesmo depois de autorizada + +Outras funcoes +-------------- + +- Consultar Situacao na SEFAZ + - Consulta a situacao de uma NF-e que esteja com status 'Em Processamento' + - Justificativa (texto de 15 a 255 caracteres) +- Carregar de XML + - Fabrica para instanciar classes Python a partir de arquivo(s) XML +- Consulta de status do servico +- Consulta de cadastro + diff --git a/tests/03-processamento-01-serializacao-xml.txt b/tests/03-processamento-01-serializacao-xml.txt new file mode 100644 index 0000000..3fda08e --- /dev/null +++ b/tests/03-processamento-01-serializacao-xml.txt @@ -0,0 +1,16 @@ +PROCESSAMENTO - SERIALIZACAO PARA XML +===================================== + +Gerar arquivos XML +------------------ + +- Gera os arquivos XML a partir dos dados das instancias da NF-e +- Quando gerados me lote, apenas o primeiro arquivo deve ter o cabecalho + padrao do XML 1.0 + - +- Namespace + - + - A declaração do namespace da assinatura digital deverá ser realizada na + própria tag , conforme exemplo abaixo. + - Cada documento XML deverá ter o seu namespace individual em seu elemento raiz. + diff --git a/tests/03-processamento.txt b/tests/03-processamento.txt deleted file mode 100644 index bfa5492..0000000 --- a/tests/03-processamento.txt +++ /dev/null @@ -1,75 +0,0 @@ -PROCESSAMENTO -============= - -Validar NF-e ------------- - -- Efetuar validacoes dos XSD no(s) XML(s) gerado(s) - -Assinar NF-e ------------- - -- Na hora de assinar, selecionar um Certificado Digital - -Gerar arquivos XML ------------------- - -- Gera os arquivos XML a partir dos dados das instancias da NF-e -- Quando gerados me lote, apenas o primeiro arquivo deve ter o cabecalho - padrao do XML 1.0 - - -- Namespace - - - - A declaração do namespace da assinatura digital deverá ser realizada na - própria tag , conforme exemplo abaixo. - - Cada documento XML deverá ter o seu namespace individual em seu elemento raiz. - - -Transmitir NF-e (ou lote de NF-e`s) ------------------------------------ - -- Enviar arquivos -- Validar mensagens de retorno -- Confirmar transmissao efetuada com sucesso -- Notas Fiscais em processamento -- Faixas de numeracao a serem inutilizadas -- Utilizar SSL 3.0 -- O certificado eh exigido na transmissao - -A troca de mensagens entre os Web Services do ambiente do Sistema de Recepção da NFe -e o aplicativo da empresa será realizada no padrão SOAP versão 1.2, com troca de -mensagens XML no padrão Style/Enconding: Document/Literal. - -Os serviços da NF-e serão implementados da seguinte forma: - -Recepção de NF-e Assincrona -Cancelamento de NF-e Sincrona -Inutilização de Numeração de NF-e Sincrona -Consulta da situação atual da NF-e Sincrona -Consulta do status do serviço Sincrona -Consulta cadastro Sincrona - -Imprimir NF-e -------------- - -- Geracao baseada no Geraldo Reports -- Gerar codigo de barras (padrao CODE-128C) -- Opcao de formulario pre-impresso (Sim/Nao) -- Chave de acesso - -Cancelar NF-e -------------- - -Cancela uma NF-e mesmo depois de autorizada - -Outras funcoes --------------- - -- Consultar Situacao na SEFAZ - - Consulta a situacao de uma NF-e que esteja com status 'Em Processamento' - - Justificativa (texto de 15 a 255 caracteres) -- Carregar de XML - - Fabrica para instanciar classes Python a partir de arquivo(s) XML -- Consulta de status do servico -- Consulta de cadastro -