Esempio n. 1
0
    def __init__(self):
        self.ambiente = 2
        self.estado = 'SP'
        self.versao = '2.00'
        self.certificado = Certificado()
        self.caminho = ''
        self.salvar_arquivos = True
        self.contingencia_SCAN = False
        self.danfe = DANFE()
        self.caminho_temporario = ''
        self.maximo_tentativas_consulta_recibo = 5

        self._servidor = ''
        self._url = ''
        self._soap_envio = None
        self._soap_retorno = None
Esempio n. 2
0
    def __init__(self):
        self.ambiente = 2
        self.estado = 'SP'
        self.versao = '2.00'
        self.certificado = Certificado()
        self.caminho = ''
        self.salvar_arquivos = True
        self.contingencia_SCAN = False
        self.danfe = DANFE()
        self.caminho_temporario = ''
        self.maximo_tentativas_consulta_recibo = 5

        self._servidor     = ''
        self._url          = ''
        self._soap_envio   = None
        self._soap_retorno = None
Esempio n. 3
0
class ProcessadorNFe(object):
    def __init__(self):
        self.ambiente = 2
        self.estado = 'SP'
        self.versao = '2.00'
        self.certificado = Certificado()
        self.caminho = ''
        self.salvar_arquivos = True
        self.contingencia_SCAN = False
        self.danfe = DANFE()
        self.caminho_temporario = ''
        self.maximo_tentativas_consulta_recibo = 5

        self._servidor     = ''
        self._url          = ''
        self._soap_envio   = None
        self._soap_retorno = None

    def _conectar_servico(self, servico, envio, resposta, ambiente=None, somente_ambiente_nacional=False):
        if ambiente is None:
            ambiente = self.ambiente

        if self.versao == '1.10':
            metodo_ws = webservices_1.METODO_WS
            self._soap_envio   = SOAPEnvio_110()
            self._soap_retorno = SOAPRetorno_110()

            if self.contingencia_SCAN:
                self._servidor = webservices_1.SCAN[ambiente]['servidor']
                self._url      = webservices_1.SCAN[ambiente][servico]
            else:
                self._servidor = webservices_1.ESTADO_WS[self.estado][ambiente]['servidor']
                self._url      = webservices_1.ESTADO_WS[self.estado][ambiente][servico]

        elif self.versao == '2.00':
            metodo_ws = webservices_2.METODO_WS
            self._soap_envio   = SOAPEnvio_200()
            self._soap_retorno = SOAPRetorno_200()
            self._soap_envio.cUF = UF_CODIGO[self.estado]

            if somente_ambiente_nacional:
                self._servidor = webservices_2.AN[ambiente]['servidor']
                self._url      = webservices_2.AN[ambiente][servico]

            elif servico == WS_NFE_DOWNLOAD:
                self._servidor = webservices_2.SVAN[ambiente]['servidor']
                self._url      = webservices_2.SVAN[ambiente][servico]

            elif self.contingencia_SCAN:
                self._servidor = webservices_2.SCAN[ambiente]['servidor']
                self._url      = webservices_2.SCAN[ambiente][servico]

            else:
                #
                # Testa a opção de um estado, para determinado serviço, usar o WS
                # de outro estado
                #
                if type(webservices_2.ESTADO_WS[self.estado][ambiente][servico]) == dict:
                    ws_a_usar = webservices_2.ESTADO_WS[self.estado][ambiente][servico]
                else:
                    ws_a_usar = webservices_2.ESTADO_WS[self.estado]

                self._servidor = ws_a_usar[ambiente]['servidor']
                self._url      = ws_a_usar[ambiente][servico]

        self._soap_envio.webservice = metodo_ws[servico]['webservice']
        self._soap_envio.metodo     = metodo_ws[servico]['metodo']
        self._soap_envio.envio      = envio

        self._soap_retorno.webservice = self._soap_envio.webservice
        self._soap_retorno.metodo     = self._soap_envio.metodo
        self._soap_retorno.resposta   = resposta

        #try:
        self.certificado.prepara_certificado_arquivo_pfx()

        #
        # Salva o certificado e a chave privada para uso na conexão HTTPS
        # Salvamos como um arquivo de nome aleatório para evitar o conflito
        # de uso de vários certificados e chaves diferentes na mesma máquina
        # ao mesmo tempo
        #
        self.caminho_temporario = self.caminho_temporario or '/tmp/'

        nome_arq_chave = self.caminho_temporario + uuid4().hex
        arq_tmp = open(nome_arq_chave, 'w')
        arq_tmp.write(self.certificado.chave)
        arq_tmp.close()

        nome_arq_certificado = self.caminho_temporario + uuid4().hex
        arq_tmp = open(nome_arq_certificado, 'w')
        arq_tmp.write(self.certificado.certificado)
        arq_tmp.close()

        #con = HTTPSConnection(self._servidor, key_file=nome_arq_chave, cert_file=nome_arq_certificado)
        con = ConexaoHTTPS(self._servidor, key_file=nome_arq_chave, cert_file=nome_arq_certificado)
        #con.request('POST', '/' + self._url, self._soap_envio.xml.decode('utf-8'), self._soap_envio.header)
        #
        # É preciso definir o POST abaixo como bytestring, já que importamos
        # os unicode_literals... Dá um pau com xml com acentos sem isso...
        #
        con.request(b'POST', b'/' + self._url.encode('utf-8'), self._soap_envio.xml.encode('utf-8'), self._soap_envio.header)
        resp = con.getresponse()

        #
        # Apagamos os arquivos do certificado e o da chave privada, para evitar
        # um potencial risco de segurança; muito embora o uso da chave privada
        # para assinatura exija o uso da senha, pode haver serviços que exijam
        # apenas o uso do certificado para validar a identidade, independente
        # da existência de assinatura digital
        #
        os.remove(nome_arq_chave)
        os.remove(nome_arq_certificado)

        # Dados do envelope de envio salvos para possível debug
        envio.original = self._soap_envio.xml

        # Dados da resposta salvos para possível debug
        self._soap_retorno.resposta.version  = resp.version
        self._soap_retorno.resposta.status   = resp.status
        self._soap_retorno.resposta.reason   = unicode(resp.reason.decode('utf-8'))
        self._soap_retorno.resposta.msg      = resp.msg
        self._soap_retorno.resposta.original = unicode(resp.read().decode('utf-8'))

        # Tudo certo!
        if self._soap_retorno.resposta.status == 200:
            self._soap_retorno.xml = self._soap_retorno.resposta.original
        #except Exception, e:
            #raise e
        #else:
        con.close()

    def enviar_lote(self, numero_lote=None, lista_nfes=[]):
        if self.versao == '1.10':
            envio = EnviNFe_110()
            resposta = RetEnviNFe_110()

        elif self.versao == '2.00':
            envio = EnviNFe_200()
            resposta = RetEnviNFe_200()

            if self.ambiente == 2: # Homologação tem detalhes especificos desde a NT2011_002
                for nfe in lista_nfes:
                    nfe.infNFe.dest.CNPJ.valor = '99999999000191'
                    nfe.infNFe.dest.xNome.valor = 'NF-E EMITIDA EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL'
                    nfe.infNFe.dest.IE.valor = ''

        processo = ProcessoNFe(webservice=WS_NFE_ENVIO_LOTE, envio=envio, resposta=resposta)

        #
        # Vamos assinar e validar todas as NF-e antes da transmissão, evitando
        # rejeição na SEFAZ por incorreção no schema dos arquivos
        #
        for nfe in lista_nfes:
            self.certificado.assina_xmlnfe(nfe)
            nfe.validar()

        envio.NFe = lista_nfes

        if numero_lote is None:
            numero_lote = datetime.now().strftime('%Y%m%d%H%M%S')

        envio.idLote.valor = numero_lote

        envio.validar()
        if self.salvar_arquivos:
            for n in lista_nfes:
                n.monta_chave()
                arq = open(self.caminho + n.chave + '-nfe.xml', 'w')
                arq.write(n.xml.encode('utf-8'))
                arq.close

            arq = open(self.caminho + unicode(envio.idLote.valor).strip().rjust(15, '0') + '-env-lot.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        self._conectar_servico(WS_NFE_ENVIO_LOTE, envio, resposta)

        #resposta.validar()
        if self.salvar_arquivos:
            nome_arq = self.caminho + unicode(envio.idLote.valor).strip().rjust(15, '0') + '-rec'

            if resposta.cStat.valor != '103':
                nome_arq += '-rej.xml'
            else:
                nome_arq += '.xml'

            arq = open(nome_arq, 'w')
            arq.write(resposta.xml.encode('utf-8'))
            arq.close()

        return processo

    def consultar_recibo(self, ambiente=None, numero_recibo=None):
        if self.versao == '1.10':
            envio = ConsReciNFe_110()
            resposta = RetConsReciNFe_110()

        elif self.versao == '2.00':
            envio = ConsReciNFe_200()
            resposta = RetConsReciNFe_200()

        processo = ProcessoNFe(webservice=WS_NFE_CONSULTA_RECIBO, envio=envio, resposta=resposta)

        if ambiente is None:
            ambiente = self.ambiente

        envio.tpAmb.valor = ambiente
        envio.nRec.valor  = numero_recibo

        envio.validar()
        if self.salvar_arquivos:
            arq = open(self.caminho + unicode(envio.nRec.valor).strip().rjust(15, '0') + '-ped-rec.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        self._conectar_servico(WS_NFE_CONSULTA_RECIBO, envio, resposta, ambiente)

        #resposta.validar()
        if self.salvar_arquivos:
            nome_arq = self.caminho + unicode(envio.nRec.valor).strip().rjust(15, '0') + '-pro-rec'

            if resposta.cStat.valor != '104':
                nome_arq += '-rej.xml'
            else:
                nome_arq += '.xml'

            arq = open(nome_arq, 'w')
            arq.write(resposta.xml.encode('utf-8'))
            arq.close()

            #
            # Salvar os resultados dos processamentos
            #
            for pn in resposta.protNFe:
                nome_arq = self.caminho + unicode(pn.infProt.chNFe.valor).strip().rjust(44, '0') + '-pro-nfe-'

                # NF-e autorizada
                if pn.infProt.cStat.valor == '100':
                    nome_arq += 'aut.xml'

                # NF-e denegada
                elif pn.infProt.cStat.valor in ('110', '301', '302'):
                    nome_arq += 'den.xml'

                # NF-e rejeitada
                else:
                    nome_arq += 'rej.xml'

                arq = open(nome_arq, 'w')
                arq.write(pn.xml.encode('utf-8'))
                arq.close()

        return processo

    def cancelar_nota(self, ambiente=None, chave_nfe=None, numero_protocolo=None, justificativa=None):
        if self.versao == '1.10':
            envio = CancNFe_107()
            resposta = RetCancNFe_107()

        elif self.versao == '2.00':
            envio = CancNFe_200()
            resposta = RetCancNFe_200()

        processo = ProcessoNFe(webservice=WS_NFE_CANCELAMENTO, envio=envio, resposta=resposta)

        if ambiente is None:
            ambiente = self.ambiente

        self.caminho = self.monta_caminho_nfe(ambiente=ambiente, chave_nfe=chave_nfe)

        envio.infCanc.tpAmb.valor = ambiente
        envio.infCanc.chNFe.valor = chave_nfe
        envio.infCanc.nProt.valor = numero_protocolo
        envio.infCanc.xJust.valor = justificativa

        self.certificado.assina_xmlnfe(envio)

        envio.validar()
        if self.salvar_arquivos:
            arq = open(self.caminho + unicode(envio.infCanc.chNFe.valor).strip().rjust(44, '0') + '-ped-can.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        self._conectar_servico(WS_NFE_CANCELAMENTO, envio, resposta, ambiente)

        #resposta.validar()

        #
        # Se for autorizado, monta o processo de cancelamento
        # 101 - cancelado dentro do prazo
        # 151 - cancelado fora do prazo
        #
        if resposta.infCanc.cStat.valor in ('101', '151'):
            if self.versao == '1.10':
                processo_cancelamento_nfe = ProcCancNFe_107()

            elif self.versao == '2.00':
                processo_cancelamento_nfe = ProcCancNFe_200()

            nome_arq = self.caminho + unicode(envio.infCanc.chNFe.valor).strip().rjust(44, '0') + '-proc-canc-nfe.xml'
            processo_cancelamento_nfe.cancNFe = envio
            processo_cancelamento_nfe.retCancNFe = resposta

            processo_cancelamento_nfe.validar()

            processo.processo_cancelamento_nfe = processo_cancelamento_nfe

        if self.salvar_arquivos:
            nome_arq = self.caminho + unicode(envio.infCanc.chNFe.valor).strip().rjust(44, '0') + '-pro-can-'

            # Cancelamento autorizado
            if resposta.infCanc.cStat.valor == '101':
                nome_arq += 'aut.xml'
            else:
                nome_arq += 'rej.xml'

            arq = open(nome_arq, 'w')
            arq.write(resposta.xml.encode('utf-8'))
            arq.close()

            # Se for autorizado, monta o processo de cancelamento
            if resposta.infCanc.cStat.valor == '101':
                nome_arq = self.caminho + unicode(envio.infCanc.chNFe.valor).strip().rjust(44, '0') + '-proc-canc-nfe.xml'
                arq = open(nome_arq, 'w')
                arq.write(processo_cancelamento_nfe.xml.encode('utf-8'))
                arq.close()

                # Estranhamente, o nome desse arquivo, pelo manual, deve ser chave-can.xml
                nome_arq = self.caminho + unicode(envio.infCanc.chNFe.valor).strip().rjust(44, '0') + '-can.xml'
                arq = open(nome_arq, 'w')
                arq.write(processo_cancelamento_nfe.xml.encode('utf-8'))
                arq.close()

        return processo

    def inutilizar_nota(self, ambiente=None, codigo_estado=None, ano=None, cnpj=None, serie=None, numero_inicial=None, numero_final=None, justificativa=None):
        if self.versao == '1.10':
            envio = InutNFe_107()
            resposta = RetInutNFe_107()

        elif self.versao == '2.00':
            envio = InutNFe_200()
            resposta = RetInutNFe_200()

        processo = ProcessoNFe(webservice=WS_NFE_INUTILIZACAO, envio=envio, resposta=resposta)

        if ambiente is None:
            ambiente = self.ambiente

        if codigo_estado is None:
            codigo_estado = UF_CODIGO[self.estado]

        if ano is None:
            ano = datetime.now().strftime('%y')

        if numero_final is None:
            numero_final = numero_inicial

        self.caminho = self.monta_caminho_inutilizacao(ambiente=ambiente, serie=serie, numero_inicial=numero_inicial, numero_final=numero_final)

        envio.infInut.tpAmb.valor  = ambiente
        envio.infInut.cUF.valor    = codigo_estado
        envio.infInut.ano.valor    = ano
        envio.infInut.CNPJ.valor   = cnpj
        #envio.infInut.mod.valor    = 55
        envio.infInut.serie.valor  = serie
        envio.infInut.nNFIni.valor = numero_inicial
        envio.infInut.nNFFin.valor = numero_final
        envio.infInut.xJust.valor  = justificativa

        envio.gera_nova_chave()
        self.certificado.assina_xmlnfe(envio)

        envio.validar()
        if self.salvar_arquivos:
            arq = open(self.caminho + unicode(envio.chave).strip().rjust(41, '0') + '-ped-inu.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        self._conectar_servico(WS_NFE_INUTILIZACAO, envio, resposta, ambiente)

        #resposta.validar()

        # Se for autorizada, monta o processo de inutilização
        if resposta.infInut.cStat.valor == '102':
            if self.versao == '1.10':
                processo_inutilizacao_nfe = ProcInutNFe_107()

            elif self.versao == '2.00':
                processo_inutilizacao_nfe = ProcInutNFe_200()

            processo_inutilizacao_nfe.inutNFe = envio
            processo_inutilizacao_nfe.retInutNFe = resposta

            processo_inutilizacao_nfe.validar()

            processo.processo_inutilizacao_nfe = processo_inutilizacao_nfe

        if self.salvar_arquivos:
            nome_arq = self.caminho + unicode(envio.chave).strip().rjust(41, '0') + '-pro-inu-'

            # Inutilização autorizada
            if resposta.infInut.cStat.valor == '102':
                nome_arq += 'aut.xml'
            else:
                nome_arq += 'rej.xml'

            arq = open(nome_arq, 'w')
            arq.write(resposta.xml.encode('utf-8'))
            arq.close()

            # Se for autorizada, monta o processo de inutilização
            if resposta.infInut.cStat.valor == '102':
                nome_arq = self.caminho + unicode(envio.chave).strip().rjust(41, '0') + '-proc-inut-nfe.xml'
                arq = open(nome_arq, 'w')
                arq.write(processo_inutilizacao_nfe.xml.encode('utf-8'))
                arq.close()

                # Estranhamente, o nome desse arquivo, pelo manual, deve ser chave-inu.xml
                nome_arq = self.caminho + unicode(envio.chave).strip().rjust(41, '0') + '-inu.xml'
                arq = open(nome_arq, 'w')
                arq.write(processo_inutilizacao_nfe.xml.encode('utf-8'))
                arq.close()

        return processo

    def consultar_nota(self, ambiente=None, chave_nfe=None, nfe=None):
        if self.versao == '1.10':
            envio = ConsSitNFe_107()
            resposta = RetConsSitNFe_107()

        elif self.versao == '2.00':
            envio = ConsSitNFe_201()
            resposta = RetConsSitNFe_201()

        processo = ProcessoNFe(webservice=WS_NFE_CONSULTA, envio=envio, resposta=resposta)

        if ambiente is None:
            ambiente = self.ambiente

        caminho_original = self.caminho
        self.caminho = self.monta_caminho_nfe(ambiente, chave_nfe)

        envio.tpAmb.valor = ambiente
        envio.chNFe.valor = chave_nfe

        envio.validar()
        if self.salvar_arquivos:
            arq = open(self.caminho + unicode(chave_nfe).strip().rjust(44, '0') + '-ped-sit.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        self._conectar_servico(WS_NFE_CONSULTA, envio, resposta, ambiente)

        #resposta.validar()
        if self.salvar_arquivos:
            nome_arq = self.caminho + unicode(chave_nfe).strip().rjust(44, '0') + '-sit.xml'
            arq = open(nome_arq, 'w')
            arq.write(resposta.xml.encode('utf-8'))
            arq.close()

        self.caminho = caminho_original
        #
        # Se a NF-e tiver sido informada, montar o processo da NF-e
        #
        if nfe:
           nfe.procNFe = self.montar_processo_uma_nota(nfe, protnfe_recibo=resposta.protNFe)

        return processo

    def consultar_servico(self, ambiente=None, codigo_estado=None):
        if self.versao == '1.10':
            envio = ConsStatServ_107()
            resposta = RetConsStatServ_107()

        elif self.versao == '2.00':
            envio = ConsStatServ_200()
            resposta = RetConsStatServ_200()

        processo = ProcessoNFe(webservice=WS_NFE_SITUACAO, envio=envio, resposta=resposta)

        if ambiente is None:
            ambiente = self.ambiente

        if codigo_estado is None:
            codigo_estado = UF_CODIGO[self.estado]

        envio.tpAmb.valor = ambiente
        envio.cUF.valor   = codigo_estado
        envio.data        = datetime.now()

        envio.validar()
        if self.salvar_arquivos:
            arq = open(self.caminho + envio.data.strftime('%Y%m%dT%H%M%S') + '-ped-sta.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        self._conectar_servico(WS_NFE_SITUACAO, envio, resposta, ambiente)

        #resposta.validar()
        if self.salvar_arquivos:
            arq = open(self.caminho + envio.data.strftime('%Y%m%dT%H%M%S') + '-sta.xml', 'w')
            arq.write(resposta.xml.encode('utf-8'))
            arq.close()

        return processo

    def processar_notas(self, lista_nfes):
        #
        # Definir o caminho geral baseado na 1ª NF-e
        #
        caminho_original = self.caminho
        nfe = lista_nfes[0]
        nfe.monta_chave()
        self.caminho = caminho_original
        ambiente = nfe.infNFe.ide.tpAmb.valor
        self.caminho = self.monta_caminho_nfe(ambiente=nfe.infNFe.ide.tpAmb.valor, chave_nfe=nfe.chave)

        proc_servico = self.consultar_servico(ambiente=ambiente)
        yield proc_servico

        #
        # Serviço em operação?
        #
        if proc_servico.resposta.cStat.valor == '107':
            #
            # Verificar se as notas já não foram emitadas antes
            #
            for nfe in lista_nfes:
                nfe.monta_chave()
                self.caminho = caminho_original
                proc_consulta = self.consultar_nota(ambiente=nfe.infNFe.ide.tpAmb.valor, chave_nfe=nfe.chave)
                yield proc_consulta

                #
                # Se a nota já constar na SEFAZ
                #
                if not (
                    ((self.versao == '1.10') and (proc_consulta.resposta.infProt.cStat.valor in ('217', '999',)))
                    or
                    ((self.versao == '2.00') and (proc_consulta.resposta.cStat.valor in ('217', '999',)))
                ):
                    #
                    # Interrompe todo o processo
                    #
                    return

            #
            # Nenhuma das notas estava já enviada, enviá-las então
            #
            nfe = lista_nfes[0]
            nfe.monta_chave()
            self.caminho = caminho_original
            self.caminho = self.monta_caminho_nfe(ambiente=nfe.infNFe.ide.tpAmb.valor, chave_nfe=nfe.chave)
            proc_envio = self.enviar_lote(lista_nfes=lista_nfes)
            yield proc_envio

            ret_envi_nfe = proc_envio.resposta

            #
            # Deu certo?
            #
            if ret_envi_nfe.cStat.valor == '103':
                #
                # Aguarda o tempo do processamento antes da consulta
                #
                time.sleep(ret_envi_nfe.infRec.tMed.valor * 1.3)

                proc_recibo = self.consultar_recibo(ambiente=ret_envi_nfe.tpAmb.valor, numero_recibo=ret_envi_nfe.infRec.nRec.valor)

                #
                # Tenta receber o resultado do processamento do lote
                #
                tentativa = 0
                while proc_recibo.resposta.cStat.valor == '105' and tentativa < self.maximo_tentativas_consulta_recibo:
                    time.sleep(ret_envi_nfe.infRec.tMed.valor * 1.5)
                    tentativa += 1
                    proc_recibo = self.consultar_recibo(ambiente=ret_envi_nfe.tpAmb.valor, numero_recibo=ret_envi_nfe.infRec.nRec.valor)

                # Montar os processos das NF-es
                dic_protNFe = proc_recibo.resposta.dic_protNFe
                dic_procNFe = proc_recibo.resposta.dic_procNFe

                self.caminho = caminho_original
                self.montar_processo_lista_notas(lista_nfes, dic_protNFe, dic_procNFe)

                yield proc_recibo

    def montar_processo_lista_notas(self, lista_nfes, dic_protNFe, dic_procNFe):
        for nfe in lista_nfes:
            if nfe.chave in dic_protNFe:
                protocolo = dic_protNFe[nfe.chave]
                processo = self.montar_processo_uma_nota(nfe, protnfe_recibo=protocolo)

                if processo is not None:
                    dic_procNFe[nfe.chave] = processo

    def montar_processo_uma_nota(self, nfe, protnfe_recibo=None, protnfe_consulta_110=None, retcancnfe=None):
        #
        # Somente para a versão 1.10
        # Caso processarmos o protocolo vindo de uma consulta,
        # temos que converter esse protocolo no formato
        # do protocolo que retorna quando o recibo do lote é consultado.
        #
        # Sim, as informações são as mesmas, mas o leiaute não...
        # Vai entender...
        #
        if protnfe_consulta_110 is not None:
            protnfe_recibo = ProtNFe_110()
            protnfe_recibo.infProt.tpAmb.valor = protnfe_consulta_110.infProt.tpAmb.valor
            protnfe_recibo.infProt.verAplic.valor = protnfe_consulta_110.infProt.verAplic.valor
            protnfe_recibo.infProt.chNFe.valor = protnfe_consulta_110.infProt.chNFe.valor
            protnfe_recibo.infProt.dhRecbto.valor = protnfe_consulta_110.infProt.dhRecbto.valor
            protnfe_recibo.infProt.cStat.valor = protnfe_consulta_110.infProt.cStat.valor
            protnfe_recibo.infProt.xMotivo.valor = protnfe_consulta_110.infProt.xMotivo.valor
            protnfe_recibo.infProt.nProt.valor = protnfe_consulta_110.infProt.nProt.valor
            protnfe_recibo.infProt.digVal.valor = protnfe_consulta_110.infProt.digVal.valor

        caminho_original = self.caminho
        self.caminho = self.monta_caminho_nfe(ambiente=nfe.infNFe.ide.tpAmb.valor, chave_nfe=nfe.chave)

        processo = None
        #
        # Se nota foi autorizada ou denegada
        # 100 - autorizada
        # 150 - autorizada fora do prazo
        # 110 - denegada
        # 301 - denegada por irregularidade do emitente
        # 302 - denegada por irregularidade do destinatário
        #
        if protnfe_recibo.infProt.cStat.valor in ('100', '150', '110', '301', '302'):
            if self.versao == '1.10':
                processo = ProcNFe_110()

            elif self.versao == '2.00':
                processo = ProcNFe_200()

            processo.NFe     = nfe
            processo.protNFe = protnfe_recibo

            self.danfe.NFe     = nfe
            self.danfe.protNFe = protnfe_recibo
            self.danfe.salvar_arquivo = False
            self.danfe.gerar_danfe()
            processo.danfe_pdf = self.danfe.conteudo_pdf

            if self.salvar_arquivos:
                nome_arq = self.caminho + unicode(nfe.chave).strip().rjust(44, '0') + '-proc-nfe.xml'
                arq = open(nome_arq, 'w')
                arq.write(processo.xml.encode('utf-8'))
                arq.close()

                # Estranhamente, o nome desse arquivo, pelo manual, deve ser chave-nfe.xml ou chave-den.xml
                # para notas denegadas
                if protnfe_recibo.infProt.cStat.valor in ('100', '150'):
                    nome_arq = self.caminho + unicode(nfe.chave).strip().rjust(44, '0') + '-nfe.xml'
                else:
                    nome_arq = self.caminho + unicode(nfe.chave).strip().rjust(44, '0') + '-den.xml'

                arq = open(nome_arq, 'w')
                arq.write(processo.xml.encode('utf-8'))
                arq.close()

                nome_arq = self.caminho + unicode(nfe.chave).strip().rjust(44, '0') + '.pdf'
                arq = open(nome_arq, 'w')
                arq.write(processo.danfe_pdf)
                arq.close()

        self.caminho = caminho_original
        return processo

    def monta_caminho_nfe(self, ambiente, chave_nfe):
        caminho = self.caminho

        if ambiente == 1:
            caminho = os.path.join(caminho, 'producao/')
        else:
            caminho = os.path.join(caminho, 'homologacao/')

        data = '20' + chave_nfe[2:4] + '-' + chave_nfe[4:6]
        serie = chave_nfe[22:25]
        numero = chave_nfe[25:34]

        caminho = os.path.join(caminho, data + '/')
        caminho = os.path.join(caminho, serie + '-' + numero + '/')

        try:
            os.makedirs(caminho)
        except:
            pass

        return caminho

    def monta_caminho_inutilizacao(self, ambiente=None, data=None, serie=None, numero_inicial=None, numero_final=None):
        caminho = self.caminho

        if ambiente == 1:
            caminho = os.path.join(caminho, 'producao/')
        else:
            caminho = os.path.join(caminho, 'homologacao/')

        if data is None:
            data = datetime.now()

        caminho = os.path.join(caminho, data.strftime('%Y-%m') + '/')

        serie          = unicode(serie).strip().rjust(3, '0')
        numero_inicial = unicode(numero_inicial).strip().rjust(9, '0')
        numero_final   = unicode(numero_final).strip().rjust(9, '0')

        caminho = os.path.join(caminho, serie + '-' + numero_inicial + '-' + numero_final + '/')

        try:
            os.makedirs(caminho)
        except:
            pass

        return caminho

    def montar_processo_lista_eventos(self, lista_eventos, dic_retEvento, dic_procEvento, classe_procEvento):
        for evento in lista_eventos:
            chave = evento.infEvento.chNFe.valor
            if chave in dic_retEvento:
                retorno = dic_retEvento[chave]
                processo = classe_procEvento()
                processo.evento = evento
                processo.retEvento = retorno
                dic_procEvento[chave] = processo

    def _enviar_lote_evento(self, tipo_evento, numero_lote=None, lista_eventos=[]):
        #
        # Determina o tipo do evento
        #
        if tipo_evento == 'cce':
            classe_evento = ProcEventoCCe_100
            envio = EnvEventoCCe_100()
            resposta = RetEnvEventoCCe_100()

        elif tipo_evento == 'can':
            classe_evento = ProcEventoCancNFe_100
            envio = EnvEventoCancNFe_100()
            resposta = RetEnvEventoCancNFe_100()

        elif tipo_evento == 'confrec':
            classe_evento = ProcEventoConfRecebimento_100
            envio = EnvEventoConfRecebimento_100()
            resposta = RetEnvEventoConfRecebimento_100()

        processo = ProcessoNFe(webservice=WS_NFE_RECEPCAO_EVENTO, envio=envio, resposta=resposta)

        #
        # Vamos assinar e validar todas os Eventos antes da transmissão, evitando
        # rejeição na SEFAZ por incorreção no schema dos arquivos
        #
        for evento in lista_eventos:
            #
            # No caso de eventos de confirmação de recebimento, só é possível o
            # envio para o ambiente nacional, então é preciso forçar o cOrgao
            # nos eventos
            #
            if tipo_evento == 'confrec':
                evento.infEvento.cOrgao.valor = UF_CODIGO['RFB']

            self.certificado.assina_xmlnfe(evento)
            #evento.validar()

        envio.evento = lista_eventos

        if numero_lote is None:
            numero_lote = datetime.now().strftime('%Y%m%d%H%M%S')

        envio.idLote.valor = numero_lote

        envio.validar()
        if self.salvar_arquivos:
            for evento in lista_eventos:
                chave = evento.infEvento.chNFe.valor
                ambiente = evento.infEvento.tpAmb.valor
                caminho = self.monta_caminho_nfe(ambiente=ambiente, chave_nfe=chave)
                numero_sequencia = evento.infEvento.nSeqEvento.valor
                nome_arq = caminho + chave + '-' + unicode(numero_sequencia).zfill(2)
                arq = open(nome_arq + '-' + tipo_evento + '.xml', 'w')
                arq.write(evento.xml.encode('utf-8'))
                arq.close

            arq = open(caminho + unicode(envio.idLote.valor).strip().rjust(15, '0') + '-env-' + tipo_evento + '.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        self._conectar_servico(WS_NFE_RECEPCAO_EVENTO, envio, resposta, somente_ambiente_nacional=tipo_evento=='confrec')

        #resposta.validar()
        if self.salvar_arquivos:
            nome_arq = caminho + unicode(envio.idLote.valor).strip().rjust(15, '0') + '-rec-' + tipo_evento

            if resposta.cStat.valor != '129':
                nome_arq += '-rej.xml'
            else:
                nome_arq += '.xml'

            arq = open(nome_arq, 'w')
            arq.write(resposta.xml.encode('utf-8'))
            arq.close()

            self.montar_processo_lista_eventos(lista_eventos, processo.resposta.dic_retEvento, processo.resposta.dic_procEvento, classe_evento)

            #
            # Salva o processamento de cada arquivo
            #
            for ret in resposta.retEvento:
                chave = ret.infEvento.chNFe.valor
                ambiente = ret.infEvento.tpAmb.valor
                caminho = self.monta_caminho_nfe(ambiente=ambiente, chave_nfe=chave)
                nome_arq = caminho + ret.infEvento.chNFe.valor + '-' + unicode(ret.infEvento.nSeqEvento.valor).zfill(2)

                #
                # O evento foi aceito e vinculado à NF-e
                #
                if ret.infEvento.cStat.valor == '135':
                    arq = open(nome_arq + '-ret-' + tipo_evento + '.xml', 'w')
                    arq.write(ret.xml.encode('utf-8'))
                    arq.close

                    #
                    # Salva o processo do evento
                    #
                    arq = open(nome_arq + '-proc-' + tipo_evento + '.xml', 'w')
                    arq.write(processo.resposta.dic_procEvento[chave].xml.encode('utf-8'))
                    arq.close

                #
                # O evento foi aceito, mas não foi vinculado à NF-e
                #
                elif ret.infEvento.cStat.valor == '136':
                    arq = open(nome_arq + '-ret-' + tipo_evento + '-sv.xml', 'w') # -sv = sem vínculo
                    arq.write(ret.xml.encode('utf-8'))
                    arq.close

                    #
                    # Salva o processo do evento
                    #
                    arq = open(nome_arq + '-proc-' + tipo_evento + '.xml', 'w')
                    arq.write(processo.resposta.dic_procEvento[chave].xml.encode('utf-8'))
                    arq.close

                #
                # O evento foi aceito e vinculado à NF-e, é um cancelamento for do prazo
                #
                elif ret.infEvento.cStat.valor == '155':
                    arq = open(nome_arq + '-ret-' + tipo_evento + '.xml', 'w')
                    arq.write(ret.xml.encode('utf-8'))
                    arq.close

                    #
                    # Salva o processo do evento
                    #
                    arq = open(nome_arq + '-proc-' + tipo_evento + '.xml', 'w')
                    arq.write(processo.resposta.dic_procEvento[chave].xml.encode('utf-8'))
                    arq.close

                #
                # O evento foi rejeitado
                #
                else:
                    arq = open(nome_arq + '-ret-' + tipo_evento + '-rej.xml', 'w')
                    arq.write(ret.xml.encode('utf-8'))
                    arq.close

        return processo

    def enviar_lote_cce(self, numero_lote=None, lista_eventos=[]):
        return self._enviar_lote_evento('cce', numero_lote, lista_eventos)

    def enviar_lote_cancelamento(self, numero_lote=None, lista_eventos=[]):
        return self._enviar_lote_evento('can', numero_lote, lista_eventos)

    def enviar_lote_confirmacao_recebimento(self, numero_lote=None, lista_eventos=[]):
        return self._enviar_lote_evento('confrec', numero_lote, lista_eventos)

    def consultar_notas_destinadas(self, ambiente=None, cnpj=None, ultimo_nsu='0', tipo_emissao='0', tipo_nfe='0'):
        envio = ConsNFeDest_101()
        resposta = RetConsNFeDest_101()

        envio.tpAmb.valor = ambiente or self.ambiente
        envio.CNPJ.valor = cnpj
        envio.ultNSU.valor = ultimo_nsu
        envio.indNFe.valor = tipo_nfe
        envio.indEmi.valor = tipo_emissao

        processo = ProcessoNFe(webservice=WS_NFE_CONSULTA_DESTINADAS, envio=envio, resposta=resposta)

        numero_lote = datetime.now().strftime('%Y%m%d%H%M%S')

        envio.validar()
        if self.salvar_arquivos:
            arq = open(self.caminho + unicode(numero_lote).strip().rjust(15, '0') + '-consnfedest.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        self._conectar_servico(WS_NFE_CONSULTA_DESTINADAS, envio, resposta, somente_ambiente_nacional=True)

        #resposta.validar()
        if self.salvar_arquivos:
            arq = open(self.caminho + unicode(numero_lote).strip().rjust(15, '0') + '-consnfedest-resp.xml', 'w')
            arq.write(resposta.original.encode('utf-8'))
            arq.close()

        return processo

    def baixar_notas_destinadas(self, ambiente=None, cnpj=None, lista_chaves=[]):
        envio = DownloadNFe_100()
        resposta = RetDownloadNFe_100()

        envio.tpAmb.valor = ambiente or self.ambiente
        envio.CNPJ.valor = cnpj
        envio.chNFe = [TagChNFe_100(valor=ch) for ch in lista_chaves]

        processo = ProcessoNFe(webservice=WS_NFE_DOWNLOAD, envio=envio, resposta=resposta)

        numero_lote = datetime.now().strftime('%Y%m%d%H%M%S')

        envio.validar()
        if self.salvar_arquivos:
            arq = open(self.caminho + unicode(numero_lote).strip().rjust(15, '0') + '-downloadnfe.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        self._conectar_servico(WS_NFE_DOWNLOAD, envio, resposta)

        #resposta.validar()
        if self.salvar_arquivos:
            arq = open(self.caminho + unicode(numero_lote).strip().rjust(15, '0') + '-downloadnfe-resp.xml', 'w')
            arq.write(resposta.original.encode('utf-8'))
            arq.close()

        return processo

    def cancelar_nota_evento(self, ambiente=None, chave_nfe=None, numero_protocolo=None, justificativa=None):
        evento = EventoCancNFe_100()
        evento.infEvento.tpAmb.valor = ambiente or self.ambiente
        evento.infEvento.cOrgao.valor = UF_CODIGO[self.estado]
        evento.infEvento.CNPJ.valor = chave_nfe[6:20] # Extrai o CNPJ da própria chave da NF-e
        evento.infEvento.chNFe.valor = chave_nfe
        evento.infEvento.dhEvento.valor = datetime.now()
        evento.infEvento.detEvento.nProt.valor = numero_protocolo
        evento.infEvento.detEvento.xJust.valor = justificativa

        processo = self.enviar_lote_cancelamento(lista_eventos=[evento])
        return processo

    def corrigir_nota_evento(self, ambiente=None, chave_nfe=None, numero_sequencia=None, correcao=None):
        evento = EventoCCe_100()
        evento.infEvento.tpAmb.valor = ambiente or self.ambiente
        evento.infEvento.cOrgao.valor = UF_CODIGO[self.estado]
        evento.infEvento.CNPJ.valor = chave_nfe[6:20] # Extrai o CNPJ da própria chave da NF-e
        evento.infEvento.chNFe.valor = chave_nfe
        evento.infEvento.dhEvento.valor = datetime.now()
        evento.infEvento.detEvento.xCorrecao.valor = correcao
        evento.infEvento.nSeqEvento.valor = numero_sequencia or 1

        processo = self.enviar_lote_cce(lista_eventos=[evento])
        return processo

    def confirmar_operacao_evento(self, ambiente=None, chave_nfe=None, cnpj=None):
        evento = EventoConfRecebimento_100()
        evento.infEvento.tpAmb.valor = ambiente or self.ambiente
        evento.infEvento.cOrgao.valor = UF_CODIGO[self.estado]
        evento.infEvento.CNPJ.valor = cnpj
        evento.infEvento.chNFe.valor = chave_nfe
        evento.infEvento.dhEvento.valor = datetime.now()
        evento.infEvento.tpEvento.valor = CONF_RECEBIMENTO_CONFIRMAR_OPERACAO
        evento.infEvento.detEvento.descEvento.valor = DESCEVENTO_CONF_RECEBIMENTO[evento.infEvento.tpEvento.valor]

        processo = self.enviar_lote_confirmacao_recebimento(lista_eventos=[evento])
        return processo

    def conhecer_operacao_evento(self, ambiente=None, chave_nfe=None, cnpj=None):
        evento = EventoConfRecebimento_100()
        evento.infEvento.tpAmb.valor = ambiente or self.ambiente
        evento.infEvento.cOrgao.valor = UF_CODIGO[self.estado]
        evento.infEvento.CNPJ.valor = cnpj
        evento.infEvento.chNFe.valor = chave_nfe
        evento.infEvento.dhEvento.valor = datetime.now()
        evento.infEvento.tpEvento.valor = CONF_RECEBIMENTO_CIENCIA_OPERACAO
        evento.infEvento.detEvento.descEvento.valor = DESCEVENTO_CONF_RECEBIMENTO[evento.infEvento.tpEvento.valor]

        processo = self.enviar_lote_confirmacao_recebimento(lista_eventos=[evento])
        return processo

    def desconhecer_operacao_evento(self, ambiente=None, chave_nfe=None, cnpj=None):
        evento = EventoConfRecebimento_100()
        evento.infEvento.tpAmb.valor = ambiente or self.ambiente
        evento.infEvento.cOrgao.valor = UF_CODIGO[self.estado]
        evento.infEvento.CNPJ.valor = cnpj
        evento.infEvento.chNFe.valor = chave_nfe
        evento.infEvento.dhEvento.valor = datetime.now()
        evento.infEvento.tpEvento.valor = CONF_RECEBIMENTO_DESCONHECIMENTO_OPERACAO
        evento.infEvento.detEvento.descEvento.valor = DESCEVENTO_CONF_RECEBIMENTO[evento.infEvento.tpEvento.valor]

        processo = self.enviar_lote_confirmacao_recebimento(lista_eventos=[evento])
        return processo

    def nao_realizar_operacao_evento(self, ambiente=None, chave_nfe=None, cnpj=None, justificativa=None):
        evento = EventoConfRecebimento_100()
        evento.infEvento.tpAmb.valor = ambiente or self.ambiente
        evento.infEvento.cOrgao.valor = UF_CODIGO[self.estado]
        evento.infEvento.CNPJ.valor = cnpj
        evento.infEvento.chNFe.valor = chave_nfe
        evento.infEvento.dhEvento.valor = datetime.now()
        evento.infEvento.tpEvento.valor = CONF_RECEBIMENTO_OPERACAO_NAO_REALIZADA
        evento.infEvento.detEvento.descEvento.valor = DESCEVENTO_CONF_RECEBIMENTO[evento.infEvento.tpEvento.valor]
        evento.infEvento.detEvento.xJust.valor = justificativa

        processo = self.enviar_lote_confirmacao_recebimento(lista_eventos=[evento])
        return processo

    def consultar_cadastro(self, estado=None, ie=None, cnpj_cpf=None):
        if self.versao == '1.10':
            envio = ConsCad_101()
            resposta = RetConsCad_101()

        elif self.versao == '2.00':
            envio = ConsCad_200()
            resposta = RetConsCad_200()

        processo = ProcessoNFe(webservice=WS_NFE_CONSULTA_CADASTRO, envio=envio, resposta=resposta)

        if estado is None:
            estado = self.estado

        envio.infCons.UF.valor = estado
        
        if ie is not None:
            envio.infCons.IE.valor = ie
            nome = 'IE_' + ie
        elif cnpj_cpf is not None:
            if len(cnpj_cpf) == 11:
                envio.infCons.CPF.valor = cnpj_cpf
                nome = 'CPF_' + cnpj_cpf
            else:
                envio.infCons.CNPJ.valor = cnpj_cpf
                nome = 'CNPJ_' + cnpj_cpf

        envio.validar()
        if self.salvar_arquivos:
            arq = open(self.caminho + nome + '-cons-cad.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        # Consulta de cadastro é sempre feita em ambiente de produção
        self._conectar_servico(WS_NFE_CONSULTA_CADASTRO, envio, resposta, 1)

        #resposta.validar()
        if self.salvar_arquivos:
            arq = open(self.caminho + nome + '-cad.xml', 'w')
            arq.write(resposta.xml.encode('utf-8'))
            arq.close()

        return processo
Esempio n. 4
0
class ProcessadorNFe(object):
    def __init__(self):
        self.ambiente = 2
        self.estado = 'SP'
        self.versao = '2.00'
        self.certificado = Certificado()
        self.caminho = ''
        self.salvar_arquivos = True
        self.contingencia_SCAN = False
        self.danfe = DANFE()
        self.caminho_temporario = ''
        self.maximo_tentativas_consulta_recibo = 5

        self._servidor = ''
        self._url = ''
        self._soap_envio = None
        self._soap_retorno = None

    def _conectar_servico(self,
                          servico,
                          envio,
                          resposta,
                          ambiente=None,
                          somente_ambiente_nacional=False):
        if ambiente is None:
            ambiente = self.ambiente

        if self.versao == '1.10':
            metodo_ws = webservices_1.METODO_WS
            self._soap_envio = SOAPEnvio_110()
            self._soap_retorno = SOAPRetorno_110()

            if self.contingencia_SCAN:
                self._servidor = webservices_1.SCAN[ambiente]['servidor']
                self._url = webservices_1.SCAN[ambiente][servico]
            else:
                self._servidor = webservices_1.ESTADO_WS[
                    self.estado][ambiente]['servidor']
                self._url = webservices_1.ESTADO_WS[
                    self.estado][ambiente][servico]

        elif self.versao == '2.00':
            metodo_ws = webservices_2.METODO_WS
            self._soap_envio = SOAPEnvio_200()
            self._soap_retorno = SOAPRetorno_200()
            self._soap_envio.cUF = UF_CODIGO[self.estado]

            if somente_ambiente_nacional:
                self._servidor = webservices_2.AN[ambiente]['servidor']
                self._url = webservices_2.AN[ambiente][servico]

            elif servico == WS_NFE_DOWNLOAD:
                self._servidor = webservices_2.SVAN[ambiente]['servidor']
                self._url = webservices_2.SVAN[ambiente][servico]

            elif self.contingencia_SCAN:
                self._servidor = webservices_2.SCAN[ambiente]['servidor']
                self._url = webservices_2.SCAN[ambiente][servico]

            else:
                #
                # Testa a opção de um estado, para determinado serviço, usar o WS
                # de outro estado
                #
                if type(webservices_2.ESTADO_WS[self.estado][ambiente]
                        [servico]) == dict:
                    ws_a_usar = webservices_2.ESTADO_WS[
                        self.estado][ambiente][servico]
                else:
                    ws_a_usar = webservices_2.ESTADO_WS[self.estado]

                self._servidor = ws_a_usar[ambiente]['servidor']
                self._url = ws_a_usar[ambiente][servico]

        self._soap_envio.webservice = metodo_ws[servico]['webservice']
        self._soap_envio.metodo = metodo_ws[servico]['metodo']
        self._soap_envio.envio = envio

        self._soap_retorno.webservice = self._soap_envio.webservice
        self._soap_retorno.metodo = self._soap_envio.metodo
        self._soap_retorno.resposta = resposta

        #try:
        self.certificado.prepara_certificado_arquivo_pfx()

        #
        # Salva o certificado e a chave privada para uso na conexão HTTPS
        # Salvamos como um arquivo de nome aleatório para evitar o conflito
        # de uso de vários certificados e chaves diferentes na mesma máquina
        # ao mesmo tempo
        #
        self.caminho_temporario = self.caminho_temporario or '/tmp/'

        nome_arq_chave = self.caminho_temporario + uuid4().hex
        arq_tmp = open(nome_arq_chave, 'w')
        arq_tmp.write(self.certificado.chave)
        arq_tmp.close()

        nome_arq_certificado = self.caminho_temporario + uuid4().hex
        arq_tmp = open(nome_arq_certificado, 'w')
        arq_tmp.write(self.certificado.certificado)
        arq_tmp.close()

        #con = HTTPSConnection(self._servidor, key_file=nome_arq_chave, cert_file=nome_arq_certificado)
        con = ConexaoHTTPS(self._servidor,
                           key_file=nome_arq_chave,
                           cert_file=nome_arq_certificado)
        #con.request('POST', '/' + self._url, self._soap_envio.xml.decode('utf-8'), self._soap_envio.header)
        #
        # É preciso definir o POST abaixo como bytestring, já que importamos
        # os unicode_literals... Dá um pau com xml com acentos sem isso...
        #
        con.request(b'POST', b'/' + self._url.encode('utf-8'),
                    self._soap_envio.xml.encode('utf-8'),
                    self._soap_envio.header)
        resp = con.getresponse()

        #
        # Apagamos os arquivos do certificado e o da chave privada, para evitar
        # um potencial risco de segurança; muito embora o uso da chave privada
        # para assinatura exija o uso da senha, pode haver serviços que exijam
        # apenas o uso do certificado para validar a identidade, independente
        # da existência de assinatura digital
        #
        os.remove(nome_arq_chave)
        os.remove(nome_arq_certificado)

        # Dados do envelope de envio salvos para possível debug
        envio.original = self._soap_envio.xml

        # Dados da resposta salvos para possível debug
        self._soap_retorno.resposta.version = resp.version
        self._soap_retorno.resposta.status = resp.status
        self._soap_retorno.resposta.reason = unicode(
            resp.reason.decode('utf-8'))
        self._soap_retorno.resposta.msg = resp.msg
        self._soap_retorno.resposta.original = unicode(
            resp.read().decode('utf-8'))

        # Tudo certo!
        if self._soap_retorno.resposta.status == 200:
            self._soap_retorno.xml = self._soap_retorno.resposta.original
        #except Exception, e:
        #raise e
        #else:
        con.close()

    def enviar_lote(self, numero_lote=None, lista_nfes=[]):
        if self.versao == '1.10':
            envio = EnviNFe_110()
            resposta = RetEnviNFe_110()

        elif self.versao == '2.00':
            envio = EnviNFe_200()
            resposta = RetEnviNFe_200()

            if self.ambiente == 2:  # Homologação tem detalhes especificos desde a NT2011_002
                for nfe in lista_nfes:
                    nfe.infNFe.dest.CNPJ.valor = '99999999000191'
                    nfe.infNFe.dest.xNome.valor = 'NF-E EMITIDA EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL'
                    nfe.infNFe.dest.IE.valor = ''

        processo = ProcessoNFe(webservice=WS_NFE_ENVIO_LOTE,
                               envio=envio,
                               resposta=resposta)

        #
        # Vamos assinar e validar todas as NF-e antes da transmissão, evitando
        # rejeição na SEFAZ por incorreção no schema dos arquivos
        #
        for nfe in lista_nfes:
            self.certificado.assina_xmlnfe(nfe)
            nfe.validar()

        envio.NFe = lista_nfes

        if numero_lote is None:
            numero_lote = datetime.now().strftime('%Y%m%d%H%M%S')

        envio.idLote.valor = numero_lote

        envio.validar()
        if self.salvar_arquivos:
            for n in lista_nfes:
                n.monta_chave()
                arq = open(self.caminho + n.chave + '-nfe.xml', 'w')
                arq.write(n.xml.encode('utf-8'))
                arq.close

            arq = open(
                self.caminho +
                unicode(envio.idLote.valor).strip().rjust(15, '0') +
                '-env-lot.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        self._conectar_servico(WS_NFE_ENVIO_LOTE, envio, resposta)

        #resposta.validar()
        if self.salvar_arquivos:
            nome_arq = self.caminho + unicode(
                envio.idLote.valor).strip().rjust(15, '0') + '-rec'

            if resposta.cStat.valor != '103':
                nome_arq += '-rej.xml'
            else:
                nome_arq += '.xml'

            arq = open(nome_arq, 'w')
            arq.write(resposta.xml.encode('utf-8'))
            arq.close()

        return processo

    def consultar_recibo(self, ambiente=None, numero_recibo=None):
        if self.versao == '1.10':
            envio = ConsReciNFe_110()
            resposta = RetConsReciNFe_110()

        elif self.versao == '2.00':
            envio = ConsReciNFe_200()
            resposta = RetConsReciNFe_200()

        processo = ProcessoNFe(webservice=WS_NFE_CONSULTA_RECIBO,
                               envio=envio,
                               resposta=resposta)

        if ambiente is None:
            ambiente = self.ambiente

        envio.tpAmb.valor = ambiente
        envio.nRec.valor = numero_recibo

        envio.validar()
        if self.salvar_arquivos:
            arq = open(
                self.caminho +
                unicode(envio.nRec.valor).strip().rjust(15, '0') +
                '-ped-rec.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        self._conectar_servico(WS_NFE_CONSULTA_RECIBO, envio, resposta,
                               ambiente)

        #resposta.validar()
        if self.salvar_arquivos:
            nome_arq = self.caminho + unicode(envio.nRec.valor).strip().rjust(
                15, '0') + '-pro-rec'

            if resposta.cStat.valor != '104':
                nome_arq += '-rej.xml'
            else:
                nome_arq += '.xml'

            arq = open(nome_arq, 'w')
            arq.write(resposta.xml.encode('utf-8'))
            arq.close()

            #
            # Salvar os resultados dos processamentos
            #
            for pn in resposta.protNFe:
                nome_arq = self.caminho + unicode(
                    pn.infProt.chNFe.valor).strip().rjust(44,
                                                          '0') + '-pro-nfe-'

                # NF-e autorizada
                if pn.infProt.cStat.valor == '100':
                    nome_arq += 'aut.xml'

                # NF-e denegada
                elif pn.infProt.cStat.valor in ('110', '301', '302'):
                    nome_arq += 'den.xml'

                # NF-e rejeitada
                else:
                    nome_arq += 'rej.xml'

                arq = open(nome_arq, 'w')
                arq.write(pn.xml.encode('utf-8'))
                arq.close()

        return processo

    def cancelar_nota(self,
                      ambiente=None,
                      chave_nfe=None,
                      numero_protocolo=None,
                      justificativa=None):
        if self.versao == '1.10':
            envio = CancNFe_107()
            resposta = RetCancNFe_107()

        elif self.versao == '2.00':
            envio = CancNFe_200()
            resposta = RetCancNFe_200()

        processo = ProcessoNFe(webservice=WS_NFE_CANCELAMENTO,
                               envio=envio,
                               resposta=resposta)

        if ambiente is None:
            ambiente = self.ambiente

        self.caminho = self.monta_caminho_nfe(ambiente=ambiente,
                                              chave_nfe=chave_nfe)

        envio.infCanc.tpAmb.valor = ambiente
        envio.infCanc.chNFe.valor = chave_nfe
        envio.infCanc.nProt.valor = numero_protocolo
        envio.infCanc.xJust.valor = justificativa

        self.certificado.assina_xmlnfe(envio)

        envio.validar()
        if self.salvar_arquivos:
            arq = open(
                self.caminho +
                unicode(envio.infCanc.chNFe.valor).strip().rjust(44, '0') +
                '-ped-can.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        self._conectar_servico(WS_NFE_CANCELAMENTO, envio, resposta, ambiente)

        #resposta.validar()

        #
        # Se for autorizado, monta o processo de cancelamento
        # 101 - cancelado dentro do prazo
        # 151 - cancelado fora do prazo
        #
        if resposta.infCanc.cStat.valor in ('101', '151'):
            if self.versao == '1.10':
                processo_cancelamento_nfe = ProcCancNFe_107()

            elif self.versao == '2.00':
                processo_cancelamento_nfe = ProcCancNFe_200()

            nome_arq = self.caminho + unicode(
                envio.infCanc.chNFe.valor).strip().rjust(
                    44, '0') + '-proc-canc-nfe.xml'
            processo_cancelamento_nfe.cancNFe = envio
            processo_cancelamento_nfe.retCancNFe = resposta

            processo_cancelamento_nfe.validar()

            processo.processo_cancelamento_nfe = processo_cancelamento_nfe

        if self.salvar_arquivos:
            nome_arq = self.caminho + unicode(
                envio.infCanc.chNFe.valor).strip().rjust(44, '0') + '-pro-can-'

            # Cancelamento autorizado
            if resposta.infCanc.cStat.valor == '101':
                nome_arq += 'aut.xml'
            else:
                nome_arq += 'rej.xml'

            arq = open(nome_arq, 'w')
            arq.write(resposta.xml.encode('utf-8'))
            arq.close()

            # Se for autorizado, monta o processo de cancelamento
            if resposta.infCanc.cStat.valor == '101':
                nome_arq = self.caminho + unicode(
                    envio.infCanc.chNFe.valor).strip().rjust(
                        44, '0') + '-proc-canc-nfe.xml'
                arq = open(nome_arq, 'w')
                arq.write(processo_cancelamento_nfe.xml.encode('utf-8'))
                arq.close()

                # Estranhamente, o nome desse arquivo, pelo manual, deve ser chave-can.xml
                nome_arq = self.caminho + unicode(
                    envio.infCanc.chNFe.valor).strip().rjust(44,
                                                             '0') + '-can.xml'
                arq = open(nome_arq, 'w')
                arq.write(processo_cancelamento_nfe.xml.encode('utf-8'))
                arq.close()

        return processo

    def inutilizar_nota(self,
                        ambiente=None,
                        codigo_estado=None,
                        ano=None,
                        cnpj=None,
                        serie=None,
                        numero_inicial=None,
                        numero_final=None,
                        justificativa=None):
        if self.versao == '1.10':
            envio = InutNFe_107()
            resposta = RetInutNFe_107()

        elif self.versao == '2.00':
            envio = InutNFe_200()
            resposta = RetInutNFe_200()

        processo = ProcessoNFe(webservice=WS_NFE_INUTILIZACAO,
                               envio=envio,
                               resposta=resposta)

        if ambiente is None:
            ambiente = self.ambiente

        if codigo_estado is None:
            codigo_estado = UF_CODIGO[self.estado]

        if ano is None:
            ano = datetime.now().strftime('%y')

        if numero_final is None:
            numero_final = numero_inicial

        self.caminho = self.monta_caminho_inutilizacao(
            ambiente=ambiente,
            serie=serie,
            numero_inicial=numero_inicial,
            numero_final=numero_final)

        envio.infInut.tpAmb.valor = ambiente
        envio.infInut.cUF.valor = codigo_estado
        envio.infInut.ano.valor = ano
        envio.infInut.CNPJ.valor = cnpj
        #envio.infInut.mod.valor    = 55
        envio.infInut.serie.valor = serie
        envio.infInut.nNFIni.valor = numero_inicial
        envio.infInut.nNFFin.valor = numero_final
        envio.infInut.xJust.valor = justificativa

        envio.gera_nova_chave()
        self.certificado.assina_xmlnfe(envio)

        envio.validar()
        if self.salvar_arquivos:
            arq = open(
                self.caminho + unicode(envio.chave).strip().rjust(41, '0') +
                '-ped-inu.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        self._conectar_servico(WS_NFE_INUTILIZACAO, envio, resposta, ambiente)

        #resposta.validar()

        # Se for autorizada, monta o processo de inutilização
        if resposta.infInut.cStat.valor == '102':
            if self.versao == '1.10':
                processo_inutilizacao_nfe = ProcInutNFe_107()

            elif self.versao == '2.00':
                processo_inutilizacao_nfe = ProcInutNFe_200()

            processo_inutilizacao_nfe.inutNFe = envio
            processo_inutilizacao_nfe.retInutNFe = resposta

            processo_inutilizacao_nfe.validar()

            processo.processo_inutilizacao_nfe = processo_inutilizacao_nfe

        if self.salvar_arquivos:
            nome_arq = self.caminho + unicode(envio.chave).strip().rjust(
                41, '0') + '-pro-inu-'

            # Inutilização autorizada
            if resposta.infInut.cStat.valor == '102':
                nome_arq += 'aut.xml'
            else:
                nome_arq += 'rej.xml'

            arq = open(nome_arq, 'w')
            arq.write(resposta.xml.encode('utf-8'))
            arq.close()

            # Se for autorizada, monta o processo de inutilização
            if resposta.infInut.cStat.valor == '102':
                nome_arq = self.caminho + unicode(envio.chave).strip().rjust(
                    41, '0') + '-proc-inut-nfe.xml'
                arq = open(nome_arq, 'w')
                arq.write(processo_inutilizacao_nfe.xml.encode('utf-8'))
                arq.close()

                # Estranhamente, o nome desse arquivo, pelo manual, deve ser chave-inu.xml
                nome_arq = self.caminho + unicode(envio.chave).strip().rjust(
                    41, '0') + '-inu.xml'
                arq = open(nome_arq, 'w')
                arq.write(processo_inutilizacao_nfe.xml.encode('utf-8'))
                arq.close()

        return processo

    def consultar_nota(self, ambiente=None, chave_nfe=None, nfe=None):
        if self.versao == '1.10':
            envio = ConsSitNFe_107()
            resposta = RetConsSitNFe_107()

        elif self.versao == '2.00':
            envio = ConsSitNFe_201()
            resposta = RetConsSitNFe_201()

        processo = ProcessoNFe(webservice=WS_NFE_CONSULTA,
                               envio=envio,
                               resposta=resposta)

        if ambiente is None:
            ambiente = self.ambiente

        self.caminho = self.monta_caminho_nfe(ambiente, chave_nfe)

        envio.tpAmb.valor = ambiente
        envio.chNFe.valor = chave_nfe

        envio.validar()
        if self.salvar_arquivos:
            arq = open(
                self.caminho + unicode(chave_nfe).strip().rjust(44, '0') +
                '-ped-sit.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        self._conectar_servico(WS_NFE_CONSULTA, envio, resposta, ambiente)

        #resposta.validar()
        if self.salvar_arquivos:
            nome_arq = self.caminho + unicode(chave_nfe).strip().rjust(
                44, '0') + '-sit.xml'
            arq = open(nome_arq, 'w')
            arq.write(resposta.xml.encode('utf-8'))
            arq.close()

        #
        # Se a NF-e tiver sido informada, montar o processo da NF-e
        #
        if nfe:
            nfe.procNFe = self.montar_processo_uma_nota(
                nfe, protnfe_recibo=resposta.protNFe)

        return processo

    def consultar_servico(self, ambiente=None, codigo_estado=None):
        if self.versao == '1.10':
            envio = ConsStatServ_107()
            resposta = RetConsStatServ_107()

        elif self.versao == '2.00':
            envio = ConsStatServ_200()
            resposta = RetConsStatServ_200()

        processo = ProcessoNFe(webservice=WS_NFE_SITUACAO,
                               envio=envio,
                               resposta=resposta)

        if ambiente is None:
            ambiente = self.ambiente

        if codigo_estado is None:
            codigo_estado = UF_CODIGO[self.estado]

        envio.tpAmb.valor = ambiente
        envio.cUF.valor = codigo_estado
        envio.data = datetime.now()

        envio.validar()
        if self.salvar_arquivos:
            arq = open(
                self.caminho + envio.data.strftime('%Y%m%dT%H%M%S') +
                '-ped-sta.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        self._conectar_servico(WS_NFE_SITUACAO, envio, resposta, ambiente)

        #resposta.validar()
        if self.salvar_arquivos:
            arq = open(
                self.caminho + envio.data.strftime('%Y%m%dT%H%M%S') +
                '-sta.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        return processo

    def processar_notas(self, lista_nfes):
        #
        # Definir o caminho geral baseado na 1ª NF-e
        #
        caminho_original = self.caminho
        nfe = lista_nfes[0]
        nfe.monta_chave()
        self.caminho = caminho_original
        ambiente = nfe.infNFe.ide.tpAmb.valor
        self.caminho = self.monta_caminho_nfe(
            ambiente=nfe.infNFe.ide.tpAmb.valor, chave_nfe=nfe.chave)

        proc_servico = self.consultar_servico(ambiente=ambiente)
        yield proc_servico

        #
        # Serviço em operação?
        #
        if proc_servico.resposta.cStat.valor == '107':
            #
            # Verificar se as notas já não foram emitadas antes
            #
            for nfe in lista_nfes:
                nfe.monta_chave()
                self.caminho = caminho_original
                proc_consulta = self.consultar_nota(
                    ambiente=nfe.infNFe.ide.tpAmb.valor, chave_nfe=nfe.chave)
                yield proc_consulta

                #
                # Se a nota já constar na SEFAZ
                #
                if not (((self.versao == '1.10') and
                         (proc_consulta.resposta.infProt.cStat.valor in (
                             '217',
                             '999',
                         ))) or ((self.versao == '2.00') and
                                 (proc_consulta.resposta.cStat.valor in (
                                     '217',
                                     '999',
                                 )))):
                    #
                    # Interrompe todo o processo
                    #
                    return

            #
            # Nenhuma das notas estava já enviada, enviá-las então
            #
            nfe = lista_nfes[0]
            nfe.monta_chave()
            self.caminho = caminho_original
            self.caminho = self.monta_caminho_nfe(
                ambiente=nfe.infNFe.ide.tpAmb.valor, chave_nfe=nfe.chave)
            proc_envio = self.enviar_lote(lista_nfes=lista_nfes)
            yield proc_envio

            ret_envi_nfe = proc_envio.resposta

            #
            # Deu certo?
            #
            if ret_envi_nfe.cStat.valor == '103':
                #
                # Aguarda o tempo do processamento antes da consulta
                #
                time.sleep(ret_envi_nfe.infRec.tMed.valor * 1.3)

                proc_recibo = self.consultar_recibo(
                    ambiente=ret_envi_nfe.tpAmb.valor,
                    numero_recibo=ret_envi_nfe.infRec.nRec.valor)

                #
                # Tenta receber o resultado do processamento do lote
                #
                tentativa = 0
                while proc_recibo.resposta.cStat.valor == '105' and tentativa < self.maximo_tentativas_consulta_recibo:
                    time.sleep(ret_envi_nfe.infRec.tMed.valor * 1.5)
                    tentativa += 1
                    proc_recibo = self.consultar_recibo(
                        ambiente=ret_envi_nfe.tpAmb.valor,
                        numero_recibo=ret_envi_nfe.infRec.nRec.valor)

                # Montar os processos das NF-es
                dic_protNFe = proc_recibo.resposta.dic_protNFe
                dic_procNFe = proc_recibo.resposta.dic_procNFe

                self.caminho = caminho_original
                self.montar_processo_lista_notas(lista_nfes, dic_protNFe,
                                                 dic_procNFe)

                yield proc_recibo

    def montar_processo_lista_notas(self, lista_nfes, dic_protNFe,
                                    dic_procNFe):
        for nfe in lista_nfes:
            if nfe.chave in dic_protNFe:
                protocolo = dic_protNFe[nfe.chave]
                processo = self.montar_processo_uma_nota(
                    nfe, protnfe_recibo=protocolo)

                if processo is not None:
                    dic_procNFe[nfe.chave] = processo

    def montar_processo_uma_nota(self,
                                 nfe,
                                 protnfe_recibo=None,
                                 protnfe_consulta_110=None,
                                 retcancnfe=None):
        #
        # Somente para a versão 1.10
        # Caso processarmos o protocolo vindo de uma consulta,
        # temos que converter esse protocolo no formato
        # do protocolo que retorna quando o recibo do lote é consultado.
        #
        # Sim, as informações são as mesmas, mas o leiaute não...
        # Vai entender...
        #
        if protnfe_consulta_110 is not None:
            protnfe_recibo = ProtNFe_110()
            protnfe_recibo.infProt.tpAmb.valor = protnfe_consulta.infProt.tpAmb.valor
            protnfe_recibo.infProt.verAplic.valor = protnfe_consulta.infProt.verAplic.valor
            protnfe_recibo.infProt.chNFe.valor = protnfe_consulta.infProt.chNFe.valor
            protnfe_recibo.infProt.dhRecbto.valor = protnfe_consulta.infProt.dhRecbto.valor
            protnfe_recibo.infProt.cStat.valor = protnfe_consulta.infProt.cStat.valor
            protnfe_recibo.infProt.xMotivo.valor = protnfe_consulta.infProt.xMotivo.valor
            protnfe_recibo.infProt.nProt.valor = protnfe_consulta.infProt.nProt.valor
            protnfe_recibo.infProt.digVal.valor = protnfe_consulta.infProt.digVal.valor

        caminho_original = self.caminho
        self.caminho = self.monta_caminho_nfe(
            ambiente=nfe.infNFe.ide.tpAmb.valor, chave_nfe=nfe.chave)

        processo = None
        #
        # Se nota foi autorizada ou denegada
        # 100 - autorizada
        # 150 - autorizada fora do prazo
        # 110 - denegada
        # 301 - denegada por irregularidade do emitente
        # 302 - denegada por irregularidade do destinatário
        #
        if protnfe_recibo.infProt.cStat.valor in ('100', '150', '110', '301',
                                                  '302'):
            if self.versao == '1.10':
                processo = ProcNFe_110()

            elif self.versao == '2.00':
                processo = ProcNFe_200()

            processo.NFe = nfe
            processo.protNFe = protnfe_recibo

            self.danfe.NFe = nfe
            self.danfe.protNFe = protnfe_recibo
            self.danfe.salvar_arquivo = False
            self.danfe.gerar_danfe()
            processo.danfe_pdf = self.danfe.conteudo_pdf

            if self.salvar_arquivos:
                nome_arq = self.caminho + unicode(nfe.chave).strip().rjust(
                    44, '0') + '-proc-nfe.xml'
                arq = open(nome_arq, 'w')
                arq.write(processo.xml.encode('utf-8'))
                arq.close()

                # Estranhamente, o nome desse arquivo, pelo manual, deve ser chave-nfe.xml ou chave-den.xml
                # para notas denegadas
                if protnfe_recibo.infProt.cStat.valor in ('100', '150'):
                    nome_arq = self.caminho + unicode(nfe.chave).strip().rjust(
                        44, '0') + '-nfe.xml'
                else:
                    nome_arq = self.caminho + unicode(nfe.chave).strip().rjust(
                        44, '0') + '-den.xml'

                arq = open(nome_arq, 'w')
                arq.write(processo.xml.encode('utf-8'))
                arq.close()

                nome_arq = self.caminho + unicode(nfe.chave).strip().rjust(
                    44, '0') + '.pdf'
                arq = open(nome_arq, 'w')
                arq.write(processo.danfe_pdf)
                arq.close()

        self.caminho = caminho_original
        return processo

    def monta_caminho_nfe(self, ambiente, chave_nfe):
        caminho = self.caminho

        if ambiente == 1:
            caminho = os.path.join(caminho, 'producao/')
        else:
            caminho = os.path.join(caminho, 'homologacao/')

        data = '20' + chave_nfe[2:4] + '-' + chave_nfe[4:6]
        serie = chave_nfe[22:25]
        numero = chave_nfe[25:34]

        caminho = os.path.join(caminho, data + '/')
        caminho = os.path.join(caminho, serie + '-' + numero + '/')

        try:
            os.makedirs(caminho)
        except:
            pass

        return caminho

    def monta_caminho_inutilizacao(self,
                                   ambiente=None,
                                   data=None,
                                   serie=None,
                                   numero_inicial=None,
                                   numero_final=None):
        caminho = self.caminho

        if ambiente == 1:
            caminho = os.path.join(caminho, 'producao/')
        else:
            caminho = os.path.join(caminho, 'homologacao/')

        if data is None:
            data = datetime.now()

        caminho = os.path.join(caminho, data.strftime('%Y-%m') + '/')

        serie = unicode(serie).strip().rjust(3, '0')
        numero_inicial = unicode(numero_inicial).strip().rjust(9, '0')
        numero_final = unicode(numero_final).strip().rjust(9, '0')

        caminho = os.path.join(
            caminho, serie + '-' + numero_inicial + '-' + numero_final + '/')

        try:
            os.makedirs(caminho)
        except:
            pass

        return caminho

    def montar_processo_lista_eventos(self, lista_eventos, dic_retEvento,
                                      dic_procEvento, classe_procEvento):
        for evento in lista_eventos:
            chave = evento.infEvento.chNFe.valor
            if chave in dic_retEvento:
                retorno = dic_retEvento[chave]
                processo = classe_procEvento()
                processo.evento = evento
                processo.retEvento = retorno
                dic_procEvento[chave] = processo

    def _enviar_lote_evento(self,
                            tipo_evento,
                            numero_lote=None,
                            lista_eventos=[]):
        #
        # Determina o tipo do evento
        #
        if tipo_evento == 'cce':
            classe_evento = ProcEventoCCe_100
            envio = EnvEventoCCe_100()
            resposta = RetEnvEventoCCe_100()

        elif tipo_evento == 'can':
            classe_evento = ProcEventoCancNFe_100
            envio = EnvEventoCancNFe_100()
            resposta = RetEnvEventoCancNFe_100()

        elif tipo_evento == 'confrec':
            classe_evento = ProcEventoConfRecebimento_100
            envio = EnvEventoConfRecebimento_100()
            resposta = RetEnvEventoConfRecebimento_100()

        processo = ProcessoNFe(webservice=WS_NFE_RECEPCAO_EVENTO,
                               envio=envio,
                               resposta=resposta)

        #
        # Vamos assinar e validar todas os Eventos antes da transmissão, evitando
        # rejeição na SEFAZ por incorreção no schema dos arquivos
        #
        for evento in lista_eventos:
            #
            # No caso de eventos de confirmação de recebimento, só é possível o
            # envio para o ambiente nacional, então é preciso forçar o cOrgao
            # nos eventos
            #
            if tipo_evento == 'confrec':
                evento.infEvento.cOrgao.valor = UF_CODIGO['RFB']

            self.certificado.assina_xmlnfe(evento)
            #evento.validar()

        envio.evento = lista_eventos

        if numero_lote is None:
            numero_lote = datetime.now().strftime('%Y%m%d%H%M%S')

        envio.idLote.valor = numero_lote

        envio.validar()
        if self.salvar_arquivos:
            for evento in lista_eventos:
                chave = evento.infEvento.chNFe.valor
                ambiente = evento.infEvento.tpAmb.valor
                caminho = self.monta_caminho_nfe(ambiente=ambiente,
                                                 chave_nfe=chave)
                numero_sequencia = evento.infEvento.nSeqEvento.valor
                nome_arq = caminho + chave + '-' + unicode(
                    numero_sequencia).zfill(2)
                arq = open(nome_arq + '-' + tipo_evento + '.xml', 'w')
                arq.write(evento.xml.encode('utf-8'))
                arq.close

            arq = open(
                caminho + unicode(envio.idLote.valor).strip().rjust(15, '0') +
                '-env-' + tipo_evento + '.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        self._conectar_servico(
            WS_NFE_RECEPCAO_EVENTO,
            envio,
            resposta,
            somente_ambiente_nacional=tipo_evento == 'confrec')

        #resposta.validar()
        if self.salvar_arquivos:
            nome_arq = caminho + unicode(envio.idLote.valor).strip().rjust(
                15, '0') + '-rec-' + tipo_evento

            if resposta.cStat.valor != '129':
                nome_arq += '-rej.xml'
            else:
                nome_arq += '.xml'

            arq = open(nome_arq, 'w')
            arq.write(resposta.xml.encode('utf-8'))
            arq.close()

            self.montar_processo_lista_eventos(
                lista_eventos, processo.resposta.dic_retEvento,
                processo.resposta.dic_procEvento, classe_evento)

            #
            # Salva o processamento de cada arquivo
            #
            for ret in resposta.retEvento:
                chave = ret.infEvento.chNFe.valor
                ambiente = ret.infEvento.tpAmb.valor
                caminho = self.monta_caminho_nfe(ambiente=ambiente,
                                                 chave_nfe=chave)
                nome_arq = caminho + ret.infEvento.chNFe.valor + '-' + unicode(
                    ret.infEvento.nSeqEvento.valor).zfill(2)

                #
                # O evento foi aceito e vinculado à NF-e
                #
                if ret.infEvento.cStat.valor == '135':
                    arq = open(nome_arq + '-ret-' + tipo_evento + '.xml', 'w')
                    arq.write(ret.xml.encode('utf-8'))
                    arq.close

                    #
                    # Salva o processo do evento
                    #
                    arq = open(nome_arq + '-proc-' + tipo_evento + '.xml', 'w')
                    arq.write(
                        processo.resposta.dic_procEvento[chave].xml.encode(
                            'utf-8'))
                    arq.close

                #
                # O evento foi aceito, mas não foi vinculado à NF-e
                #
                elif ret.infEvento.cStat.valor == '136':
                    arq = open(nome_arq + '-ret-' + tipo_evento + '-sv.xml',
                               'w')
                    arq.write(ret.xml.encode('utf-8'))
                    arq.close

                    #
                    # Salva o processo do evento
                    #
                    arq = open(nome_arq + '-proc-' + tipo_evento + '.xml', 'w')
                    arq.write(
                        processo.resposta.dic_procEvento[chave].xml.encode(
                            'utf-8'))
                    arq.close

                #
                # O evento foi rejeitado
                #
                else:
                    arq = open(nome_arq + '-ret-' + tipo_evento + '-rej.xml',
                               'w')
                    arq.write(ret.xml.encode('utf-8'))
                    arq.close

        return processo

    def enviar_lote_cce(self, numero_lote=None, lista_eventos=[]):
        return self._enviar_lote_evento('cce', numero_lote, lista_eventos)

    def enviar_lote_cancelamento(self, numero_lote=None, lista_eventos=[]):
        return self._enviar_lote_evento('can', numero_lote, lista_eventos)

    def enviar_lote_confirmacao_recebimento(self,
                                            numero_lote=None,
                                            lista_eventos=[]):
        return self._enviar_lote_evento('confrec', numero_lote, lista_eventos)

    def consultar_notas_destinadas(self,
                                   ambiente=None,
                                   cnpj=None,
                                   ultimo_nsu='0',
                                   tipo_emissao='0',
                                   tipo_nfe='0'):
        envio = ConsNFeDest_101()
        resposta = RetConsNFeDest_101()

        envio.tpAmb.valor = ambiente or self.ambiente
        envio.CNPJ.valor = cnpj
        envio.ultNSU.valor = ultimo_nsu
        envio.indNFe.valor = tipo_nfe
        envio.indEmi.valor = tipo_emissao

        processo = ProcessoNFe(webservice=WS_NFE_CONSULTA_DESTINADAS,
                               envio=envio,
                               resposta=resposta)

        numero_lote = datetime.now().strftime('%Y%m%d%H%M%S')

        envio.validar()
        if self.salvar_arquivos:
            arq = open(
                self.caminho + unicode(numero_lote).strip().rjust(15, '0') +
                '-consnfedest.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        self._conectar_servico(WS_NFE_CONSULTA_DESTINADAS,
                               envio,
                               resposta,
                               somente_ambiente_nacional=True)

        #resposta.validar()
        if self.salvar_arquivos:
            arq = open(
                self.caminho + unicode(numero_lote).strip().rjust(15, '0') +
                '-consnfedest-resp.xml', 'w')
            arq.write(resposta.original.encode('utf-8'))
            arq.close()

        return processo

    def baixar_notas_destinadas(self,
                                ambiente=None,
                                cnpj=None,
                                lista_chaves=[]):
        envio = DownloadNFe_100()
        resposta = RetDownloadNFe_100()

        envio.tpAmb.valor = ambiente or self.ambiente
        envio.CNPJ.valor = cnpj
        envio.chNFe = [TagChNFe_100(valor=ch) for ch in lista_chaves]

        processo = ProcessoNFe(webservice=WS_NFE_DOWNLOAD,
                               envio=envio,
                               resposta=resposta)

        numero_lote = datetime.now().strftime('%Y%m%d%H%M%S')

        envio.validar()
        if self.salvar_arquivos:
            arq = open(
                self.caminho + unicode(numero_lote).strip().rjust(15, '0') +
                '-downloadnfe.xml', 'w')
            arq.write(envio.xml.encode('utf-8'))
            arq.close()

        self._conectar_servico(WS_NFE_DOWNLOAD, envio, resposta)

        #resposta.validar()
        if self.salvar_arquivos:
            arq = open(
                self.caminho + unicode(numero_lote).strip().rjust(15, '0') +
                '-downloadnfe-resp.xml', 'w')
            arq.write(resposta.original.encode('utf-8'))
            arq.close()

        return processo

    def cancelar_nota_evento(self,
                             ambiente=None,
                             chave_nfe=None,
                             numero_protocolo=None,
                             justificativa=None):
        evento = EventoCancNFe_100()
        evento.infEvento.tpAmb.valor = ambiente or self.ambiente
        evento.infEvento.cOrgao.valor = UF_CODIGO[self.estado]
        evento.infEvento.CNPJ.valor = chave_nfe[
            6:20]  # Extrai o CNPJ da própria chave da NF-e
        evento.infEvento.chNFe.valor = chave_nfe
        evento.infEvento.dhEvento.valor = datetime.now()
        evento.infEvento.detEvento.nProt.valor = numero_protocolo
        evento.infEvento.detEvento.xJust.valor = justificativa

        processo = self.enviar_lote_cancelamento(lista_eventos=[evento])
        return processo

    def corrigir_nota_evento(self,
                             ambiente=None,
                             chave_nfe=None,
                             numero_sequencia=None,
                             correcao=None):
        evento = EventoCCe_100()
        evento.infEvento.tpAmb.valor = ambiente or self.ambiente
        evento.infEvento.cOrgao.valor = UF_CODIGO[self.estado]
        evento.infEvento.CNPJ.valor = chave_nfe[
            6:20]  # Extrai o CNPJ da própria chave da NF-e
        evento.infEvento.chNFe.valor = chave_nfe
        evento.infEvento.dhEvento.valor = datetime.now()
        evento.infEvento.detEvento.xCorrecao.valor = correcao
        evento.infEvento.nSeqEvento.valor = numero_sequencia or 1

        processo = self.enviar_lote_cce(lista_eventos=[evento])
        return processo

    def confirmar_operacao_evento(self,
                                  ambiente=None,
                                  chave_nfe=None,
                                  cnpj=None):
        evento = EventoConfRecebimento_100()
        evento.infEvento.tpAmb.valor = ambiente or self.ambiente
        evento.infEvento.cOrgao.valor = UF_CODIGO[self.estado]
        evento.infEvento.CNPJ.valor = cnpj
        evento.infEvento.chNFe.valor = chave_nfe
        evento.infEvento.dhEvento.valor = datetime.now()
        evento.infEvento.tpEvento.valor = CONF_RECEBIMENTO_CONFIRMAR_OPERACAO
        evento.infEvento.detEvento.descEvento.valor = DESCEVENTO_CONF_RECEBIMENTO[
            evento.infEvento.tpEvento.valor]

        processo = self.enviar_lote_confirmacao_recebimento(
            lista_eventos=[evento])
        return processo

    def conhecer_operacao_evento(self,
                                 ambiente=None,
                                 chave_nfe=None,
                                 cnpj=None):
        evento = EventoConfRecebimento_100()
        evento.infEvento.tpAmb.valor = ambiente or self.ambiente
        evento.infEvento.cOrgao.valor = UF_CODIGO[self.estado]
        evento.infEvento.CNPJ.valor = cnpj
        evento.infEvento.chNFe.valor = chave_nfe
        evento.infEvento.dhEvento.valor = datetime.now()
        evento.infEvento.tpEvento.valor = CONF_RECEBIMENTO_CIENCIA_OPERACAO
        evento.infEvento.detEvento.descEvento.valor = DESCEVENTO_CONF_RECEBIMENTO[
            evento.infEvento.tpEvento.valor]

        processo = self.enviar_lote_confirmacao_recebimento(
            lista_eventos=[evento])
        return processo

    def desconhecer_operacao_evento(self,
                                    ambiente=None,
                                    chave_nfe=None,
                                    cnpj=None):
        evento = EventoConfRecebimento_100()
        evento.infEvento.tpAmb.valor = ambiente or self.ambiente
        evento.infEvento.cOrgao.valor = UF_CODIGO[self.estado]
        evento.infEvento.CNPJ.valor = cnpj
        evento.infEvento.chNFe.valor = chave_nfe
        evento.infEvento.dhEvento.valor = datetime.now()
        evento.infEvento.tpEvento.valor = CONF_RECEBIMENTO_DESCONHECIMENTO_OPERACAO
        evento.infEvento.detEvento.descEvento.valor = DESCEVENTO_CONF_RECEBIMENTO[
            evento.infEvento.tpEvento.valor]

        processo = self.enviar_lote_confirmacao_recebimento(
            lista_eventos=[evento])
        return processo

    def nao_realizar_operacao_evento(self,
                                     ambiente=None,
                                     chave_nfe=None,
                                     cnpj=None,
                                     justificativa=None):
        evento = EventoConfRecebimento_100()
        evento.infEvento.tpAmb.valor = ambiente or self.ambiente
        evento.infEvento.cOrgao.valor = UF_CODIGO[self.estado]
        evento.infEvento.CNPJ.valor = cnpj
        evento.infEvento.chNFe.valor = chave_nfe
        evento.infEvento.dhEvento.valor = datetime.now()
        evento.infEvento.tpEvento.valor = CONF_RECEBIMENTO_OPERACAO_NAO_REALIZADA
        evento.infEvento.detEvento.descEvento.valor = DESCEVENTO_CONF_RECEBIMENTO[
            evento.infEvento.tpEvento.valor]
        evento.infEvento.detEvento.xJust.valor = justificativa

        processo = self.enviar_lote_confirmacao_recebimento(
            lista_eventos=[evento])
        return processo