diff --git a/pynfe/entidades/__init__.py b/pynfe/entidades/__init__.py index 47dfe50..f4fa7d4 100644 --- a/pynfe/entidades/__init__.py +++ b/pynfe/entidades/__init__.py @@ -6,4 +6,5 @@ from .notafiscal import NotaFiscal from .lotes import LoteNotaFiscal from .fonte_dados import _fonte_dados from .certificado import CertificadoA1 +from .evento import EventoCancelarNota diff --git a/pynfe/entidades/evento.py b/pynfe/entidades/evento.py new file mode 100644 index 0000000..045f8cd --- /dev/null +++ b/pynfe/entidades/evento.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +""" + @author: Junior Tada, Leonardo Tada +""" + +from .base import Entidade + +class Evento(Entidade): + pass + +class EventoCancelarNota(Evento): + # - Identificador da TAG a ser assinada, a regra de formação do Id é: “ID” + tpEvento + chave da NF-e + nSeqEvento + id = str() + # - Código do órgão de recepção do Evento. Utilizar a Tabela do IBGE, utilizar 91 para identificar o Ambiente Nacional. + orgao = str() + # - CNPJ (obrigatorio) + cnpj = str() + # - Chave de Acesso da NF-e vinculada ao Evento + chave = str() + # - Data e hora do evento no formato AAAA-MM-DDThh:mm:ssTZD + data_emissao = None + # - uf de onde a nota foi enviada + uf = str() + # - Código do evento = 110111 + tp_evento = '110111' + # - Sequencial do evento para o mesmo tipo de evento. Para maioria dos eventos nSeqEvento=1, nos casos em quepossa existir mais de um evento, como é o caso da Carta de Correção, o autor do evento deve numerar de forma sequencial. + n_seq_evento = '1' + # - Versão do detalhe do evento (grupo detEvento – HP17), informação utilizada para a SEFAZ validar o grupo detEvento. + ver_evento = str() + # - Informações do Pedido de Cancelamento + det_evento = str() + # - Versão do Pedido de Cancelamento, deve ser informado com a mesma informação da tag verEvento (HP16) + versao = str() + # - descEvento + descricao = 'Cancelamento' + # - Informar o número do Protocolo de Autorização da NF-e a ser Cancelada. (vide item 5.8). + protocolo = str() + # - Informar a justificativa do cancelamento (min 15 max 255 caracteres) + justificativa = str() + + @property + def identificador(self): + """ + Gera o valor para o campo id + A regra de formação do Id é: “ID” + tpEvento + chave da NF-e + nSeqEvento + """ + self.id = "ID%(tp_evento)s%(chave)s%(n_seq_evento)s"%{ + 'tp_evento': self.tp_evento, + 'chave': self.chave, + 'n_seq_evento': self.n_seq_evento, + } + return self.id \ No newline at end of file diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 0f15ede..7b7cd3c 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import datetime +import time import requests from pynfe.utils import etree, so_numeros from pynfe.utils.flags import NAMESPACE_NFE, NAMESPACE_SOAP, VERSAO_PADRAO, CODIGOS_ESTADOS @@ -49,8 +50,26 @@ class ComunicacaoSefaz(Comunicacao): return xml #return self._post(url, xml, self._post_header()) - def cancelar(self, nota_fiscal): - pass + def cancelar(self, modelo, xml): + """ Envia um evento de cancelamento de nota fiscal """ + # timezone Brasília -03:00 + tz = time.strftime("%z") + tz = "{}:{}".format(tz[:-2], tz[-2:]) + + # url do serviço + url = self._get_url(modelo=modelo, consulta='EVENTOS') + # Monta XML do corpo da requisição + raiz = etree.Element('envEvento') + #etree.SubElement(raiz, 'versao').text = self._versao # Na documentaçao 6.0 está desta forma + etree.SubElement(raiz, 'versaoDados').text = self._versao # Na documentaçao 6.0 está desta forma + etree.SubElement(raiz, 'idLote').text = str(1) # numero autoincremental gerado pelo sistema + evento = etree.SubElement(raiz, 'evento') + etree.SubElement(evento, 'versao').text = '1' # versao do leiaute do evento (cancelamento = 1) + etree.SubElement(raiz, 'infEvento').text = xml # Evento, um lote pode conter até 20 eventos + dados = etree.tostring(raiz, encoding="unicode") + xml = self._construir_xml_status_pr(cabecalho=self._cabecalho_soap(), dados=dados, url=url) + return xml + #return self._post(url, xml, self._post_header()) def situacao_nfe(self, nota_fiscal): pass diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 7444453..85bdc74 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -372,6 +372,34 @@ class SerializacaoXML(Serializacao): else: return raiz + def _serializar_evento(self, evento, tag_raiz='infEvento', retorna_string=False): + + # timezone Brasília -03:00 + tz = time.strftime("%z") + tz = "{}:{}".format(tz[:-2], tz[-2:]) + + raiz = etree.Element(tag_raiz) + etree.SubElement(raiz, 'Id').text = evento.id + etree.SubElement(raiz, 'cOrgao').text = CODIGOS_ESTADOS[evento.uf.upper()] + etree.SubElement(raiz, 'tpAmb').text = str(self._ambiente) + etree.SubElement(raiz, 'CNPJ').text = evento.cnpj + #etree.SubElement(raiz, 'CPF').text = '' + etree.SubElement(raiz, 'chNFe').text = evento.chave + etree.SubElement(raiz, 'dhEvento').text = evento.data_emissao.strftime('%Y-%m-%dT%H:%M:%S') + tz + etree.SubElement(raiz, 'tpEvento').text = evento.tp_evento + etree.SubElement(raiz, 'nSeqEvento').text = evento.n_seq_evento + etree.SubElement(raiz, 'verEvento').text = evento.ver_evento + etree.SubElement(raiz, 'detEvento').text = evento.det_evento + etree.SubElement(raiz, 'versao').text = evento.versao + etree.SubElement(raiz, 'descEvento').text = evento.descricao + etree.SubElement(raiz, 'nPro').text = evento.protocolo + etree.SubElement(raiz, 'xJust').text = evento.justificativa + + if retorna_string: + return etree.tostring(raiz, encoding="unicode", pretty_print=True) + else: + return raiz + class SerializacaoPipes(Serializacao): """Serialização utilizada pela SEFAZ-SP para a importação de notas."""