diff --git a/README.md b/README.md index 3cb837b..af27e0f 100644 --- a/README.md +++ b/README.md @@ -92,3 +92,8 @@ Documentação ----------- - https://github.com/leotada/PyNFe/wiki - http://pynfe.readthedocs.org/pt/latest/ + +backlog: +- renomeado metodo serializar_evento (_serializar_evento) +- removido metoco con.cancelar (utilizar con.evento) +- add evento carta de correção (con.evento) diff --git a/pynfe/entidades/evento.py b/pynfe/entidades/evento.py index bb2bd1b..f52c75d 100644 --- a/pynfe/entidades/evento.py +++ b/pynfe/entidades/evento.py @@ -7,9 +7,6 @@ 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. @@ -22,16 +19,14 @@ class EventoCancelarNota(Evento): 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 = int() + # - Código do evento = Cancelamento(110111), Carta de Correcao(110110) + tp_evento = str() + # - Sequencial do evento para o mesmo tipo de evento. + """ Para maioria dos eventos nSeqEvento=1, nos casos em que possa 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 # - 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() + descricao = str() @property def identificador(self): @@ -44,4 +39,40 @@ class EventoCancelarNota(Evento): 'chave': self.chave, 'n_seq_evento': str(self.n_seq_evento).zfill(2), } - return self.id \ No newline at end of file + return self.id + + +class EventoCancelarNota(Evento): + + def __init__(self, *args, **kwargs): + super(EventoCancelarNota, self).__init__(*args, **kwargs) + # - Código do evento = 110111 + self.tp_evento = '110111' + # - "Cancelamento" + self.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() + + +class EventoCartaCorrecao(Evento): + + def __init__(self, *args, **kwargs): + super(EventoCartaCorrecao, self).__init__(*args, **kwargs) + # - Código do evento = 110110 + self.tp_evento = '110110' + # - “Carta de Correção” ou “Carta de Correcao” + self.descricao = 'Carta de Correcao' + + """ - xCondUso - Condições de uso da Carta de Correção, informar a literal : + A Carta de Correcao e disciplinada pelo paragrafo 1o-A do art. 7o do Convenio S/N, de 15 de dezembro de 1970 + e pode ser utilizada para regularizacao de erro ocorrido na emissao de documento fiscal, + desde que o erro nao esteja relacionado com: + I - as variaveis que determinam o valor do imposto tais como: base de calculo, aliquota, diferenca de preco, quantidade, valor da operacao ou da prestacao; + II - a correcao de dados cadastrais que implique mudanca do remetente ou do destinatario; + III - a data de emissao ou de saida.""" + cond_uso = 'A Carta de Correcao e disciplinada pelo paragrafo 1o-A do art. 7o do Convenio S/N, de 15 de dezembro de 1970 e pode ser utilizada para regularizacao de erro ocorrido na emissao de documento fiscal, desde que o erro nao esteja relacionado com: I - as variaveis que determinam o valor do imposto tais como: base de calculo, aliquota, diferenca de preco, quantidade, valor da operacao ou da prestacao; II - a correcao de dados cadastrais que implique mudanca do remetente ou do destinatario; III - a data de emissao ou de saida.' + # - xCorrecao - Correção a ser considerada, texto livre. A correção mais recente substitui as anteriores. min 15 max 1000 + correcao = str() diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index fd70125..ad682aa 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -147,8 +147,30 @@ class ComunicacaoSefaz(Comunicacao): def consulta_distribuicao(self, cnpj, nsu=0): pass - def cancelar(self, modelo, evento, idlote=1): - """ Envia um evento de cancelamento de nota fiscal """ + def consulta_cadastro(self, modelo, cnpj): + # RS implementa um método diferente na consulta de cadastro + if self.uf.upper() == 'RS': + url = NFE['RS']['CADASTRO'] + elif self.uf.upper() == 'SVRS': + url = NFE['SVRS']['CADASTRO'] + elif self.uf.upper() == 'SVC-RS': + url = NFE['SVC-RS']['CADASTRO'] + else: + url = self._get_url(modelo=modelo, consulta='CADASTRO') + + raiz = etree.Element('ConsCad', versao='2.00', xmlns=NAMESPACE_NFE) + info = etree.SubElement(raiz, 'infCons') + etree.SubElement(info, 'xServ').text = 'CONS-CAD' + etree.SubElement(info, 'UF').text = self.uf.upper() + etree.SubElement(info, 'CNPJ').text = cnpj + #etree.SubElement(info, 'CPF').text = cpf + # Monta XML para envio da requisição + xml = self._construir_xml_status_pr(cabecalho=self._cabecalho_soap(metodo='CadConsultaCadastro2'), metodo='CadConsultaCadastro2', dados=raiz) + # Chama método que efetua a requisição POST no servidor SOAP + return self._post(url, xml) + + def evento(self, modelo, evento, idlote=1): + """ Envia um evento de nota fiscal (cancelamento e carta de correção)""" # url do serviço url = self._get_url(modelo=modelo, consulta='EVENTOS') # Monta XML do corpo da requisição @@ -175,28 +197,6 @@ class ComunicacaoSefaz(Comunicacao): # Chama método que efetua a requisição POST no servidor SOAP return self._post(url, xml) - def consultar_cadastro(self, modelo, cnpj): - # RS implementa um método diferente na consulta de cadastro - if self.uf.upper() == 'RS': - url = NFE['RS']['CADASTRO'] - elif self.uf.upper() == 'SVRS': - url = NFE['SVRS']['CADASTRO'] - elif self.uf.upper() == 'SVC-RS': - url = NFE['SVC-RS']['CADASTRO'] - else: - url = self._get_url(modelo=modelo, consulta='CADASTRO') - - raiz = etree.Element('ConsCad', versao='2.00', xmlns=NAMESPACE_NFE) - info = etree.SubElement(raiz, 'infCons') - etree.SubElement(info, 'xServ').text = 'CONS-CAD' - etree.SubElement(info, 'UF').text = self.uf.upper() - etree.SubElement(info, 'CNPJ').text = cnpj - #etree.SubElement(info, 'CPF').text = cpf - # Monta XML para envio da requisição - xml = self._construir_xml_status_pr(cabecalho=self._cabecalho_soap(metodo='CadConsultaCadastro2'), metodo='CadConsultaCadastro2', dados=raiz) - # Chama método que efetua a requisição POST no servidor SOAP - return self._post(url, xml) - def download(self, cnpj, chave): """ Metodo para download de NFe por parte de destinatário. @@ -303,8 +303,7 @@ class ComunicacaoSefaz(Comunicacao): # nfce Ex: https://homologacao.nfce.fazenda.pr.gov.br/nfce/NFeStatusServico3 self.url = NFCE[self.uf.upper()][ambiente] + NFCE[self.uf.upper()][consulta] else: - # TODO implementar outros tipos de notas como NFS-e - pass + raise Exception('Modelo não encontrado! Defina modelo="nfe" ou "nfce"') # Estados que utilizam outros ambientes else: self._get_url_uf(modelo, consulta) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 500306e..40c0347 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -582,7 +582,7 @@ class SerializacaoXML(Serializacao): else: return raiz - def _serializar_evento(self, evento, tag_raiz='evento', retorna_string=False): + def serializar_evento(self, evento, tag_raiz='evento', retorna_string=False): # timezone Brasília -03:00 tz = time.strftime("%z") @@ -602,8 +602,12 @@ class SerializacaoXML(Serializacao): etree.SubElement(e, 'verEvento').text = '1.00' det = etree.SubElement(e, 'detEvento', versao='1.00') etree.SubElement(det, 'descEvento').text = evento.descricao - etree.SubElement(det, 'nProt').text = evento.protocolo - etree.SubElement(det, 'xJust').text = evento.justificativa + if evento.descricao == 'Cancelamento': + etree.SubElement(det, 'nProt').text = evento.protocolo + etree.SubElement(det, 'xJust').text = evento.justificativa + elif evento.descricao == 'Carta de Correcao': + etree.SubElement(det, 'xCorrecao').text = evento.correcao + etree.SubElement(det, 'xCondUso').text = evento.cond_uso if retorna_string: return etree.tostring(raiz, encoding="unicode", pretty_print=True)