def extrair_certificado_a1(self, arquivo, senha): ''' Extrai o conteúdo do certificado A1 @param arquivo:arquivo binário do certificado @param senha: senha do certificado. @return: dicionário com a string do certificado, chave privada, emissor, proprietario, data_inicio_validade e data_final_validade. ''' conteudo_pkcs12 = crypto.load_pkcs12(arquivo, senha) key_str = crypto.dump_privatekey(crypto.FILETYPE_PEM, conteudo_pkcs12.get_privatekey()) cert_str = crypto.dump_certificate(crypto.FILETYPE_PEM, conteudo_pkcs12.get_certificate()) certificado = Certificado() certificado.prepara_certificado_txt(cert_str) vals = { 'cert': cert_str, 'key': key_str, 'emissor': certificado.emissor.get('OU'), 'proprietario': certificado.proprietario.get('CN'), 'data_inicio_validade': certificado.data_inicio_validade, 'data_final_validade': certificado.data_fim_validade, } return vals
def __init__(self): self.ambiente = 2 self.estado = u'MG' self.versao = u'2.00' self.certificado = Certificado() self.caminho = u'' self.salvar_arquivos = True self.contingencia_SCAN = False self.caminho_temporario = u'' self._servidor = u'' self._url = u'' self._soap_envio = None self._soap_retorno = None
def __init__(self): self.ambiente = 2 self.estado = u'MG' self.versao = u'2.00' self.certificado = Certificado() self.caminho = u'' self.salvar_arquivos = True self.tipo_contingencia = False self.danfe = DANFE() self.caminho_temporario = u'' self.processos = [] self._servidor = u'' self._url = u'' self._soap_envio = None self._soap_retorno = None
def extrair_certificado_a1(self, arquivo, senha): ''' Extrai o conteúdo do certificado A1 @param arquivo:arquivo binário do certificado @param senha: senha do certificado. @return: dicionário com a string do certificado, chave privada, emissor, proprietario, data_inicio_validade e data_final_validade. ''' conteudo_pkcs12 = crypto.load_pkcs12(arquivo, senha) key_str = crypto.dump_privatekey(crypto.FILETYPE_PEM, conteudo_pkcs12.get_privatekey()) cert_str = crypto.dump_certificate(crypto.FILETYPE_PEM, conteudo_pkcs12.get_certificate()) certificado = Certificado() certificado.prepara_certificado_txt(cert_str) vals = {'cert': cert_str, 'key': key_str, 'emissor': certificado.emissor.get('OU'), 'proprietario': certificado.proprietario.get('CN'), 'data_inicio_validade': certificado.data_inicio_validade, 'data_final_validade': certificado.data_fim_validade, } return vals
class ProcessadorNFe(object): def __init__(self): self.ambiente = 2 self.estado = u'MG' self.versao = u'2.00' self.certificado = Certificado() self.caminho = u'' self.salvar_arquivos = True self.tipo_contingencia = False self.danfe = DANFE() self.caminho_temporario = u'' self.processos = [] self._servidor = u'' self._url = u'' self._soap_envio = None self._soap_retorno = None def _conectar_servico(self, servico, envio, resposta, ambiente=None): print 'nfe versao...', self.versao print 'conectando ao servico SEFAZ.........' if ambiente is None: ambiente = self.ambiente if self.versao == u'2.00': self._soap_envio = SOAPEnvio_200() self._soap_envio.webservice = webservices_2.METODO_WS[servico]['webservice'] self._soap_envio.metodo = webservices_2.METODO_WS[servico]['metodo'] self._soap_envio.cUF = UF_CODIGO[self.estado] self._soap_envio.envio = envio self._soap_retorno = SOAPRetorno_200() self._soap_retorno.webservice = webservices_2.METODO_WS[servico]['webservice'] self._soap_retorno.metodo = webservices_2.METODO_WS[servico]['metodo'] self._soap_retorno.resposta = resposta if not self.tipo_contingencia: self._servidor = webservices_2.ESTADO_WS[self.estado][ambiente][u'servidor'] self._url = webservices_2.ESTADO_WS[self.estado][ambiente][servico] elif self.tipo_contingencia == 'SCAN': self._servidor = webservices_2.SCAN[ambiente][u'servidor'] self._url = webservices_2.SCAN[ambiente][servico] elif self.tipo_contingencia == 'SVC-AN': self._servidor = webservices_2.SVC_AN[ambiente][u'servidor'] self._url = webservices_2.SVC_AN[ambiente][servico] elif self.tipo_contingencia == 'SVC-RS': self._servidor = webservices_2.SVC_RS[ambiente][u'servidor'] self._url = webservices_2.SVC_RS[ambiente][servico] if self.versao == u'3.10': self._soap_envio = SOAPEnvio_310() self._soap_envio.webservice = webservices_3.METODO_WS[servico]['webservice'] self._soap_envio.metodo = webservices_3.METODO_WS[servico]['metodo'] self._soap_envio.cUF = UF_CODIGO[self.estado] self._soap_envio.envio = envio self._soap_retorno = SOAPRetorno_310() self._soap_retorno.webservice = webservices_3.METODO_WS[servico]['webservice'] self._soap_retorno.metodo = webservices_3.METODO_WS[servico]['metodo'] self._soap_retorno.resposta = resposta if not self.tipo_contingencia: self._servidor = webservices_3.ESTADO_WS[self.estado][ambiente][u'servidor'] self._url = webservices_3.ESTADO_WS[self.estado][ambiente][servico] elif self.tipo_contingencia == 'SCAN': self._servidor = webservices_3.SCAN[ambiente][u'servidor'] self._url = webservices_3.SCAN[ambiente][servico] elif self.tipo_contingencia == 'SVC-AN': self._servidor = webservices_3.SVC_AN[ambiente][u'servidor'] self._url = webservices_3.SVC_AN[ambiente][servico] elif self.tipo_contingencia == 'SVC-RS': self._servidor = webservices_3.SVC_RS[ambiente][u'servidor'] self._url = webservices_3.SVC_RS[ambiente][servico] #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 u'/tmp/' nome_arq_chave = tempfile.NamedTemporaryFile('w') nome_arq_chave.write(self.certificado.chave) nome_arq_chave.flush() nome_arq_certificado = tempfile.NamedTemporaryFile('w') nome_arq_certificado.write(self.certificado.certificado) nome_arq_certificado.flush() con = HTTPSConnection(self._servidor, key_file=nome_arq_chave.name, cert_file=nome_arq_certificado.name) #con = ConexaoHTTPS(self._servidor, key_file=nome_arq_chave, cert_file=nome_arq_certificado) con.request(u'POST', u'/' + self._url, self._soap_envio.xml.encode(u'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 # nome_arq_chave.close() nome_arq_certificado.close() # 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')) print 'servidor', self._servidor print 'STATUS________________________________________-', self._soap_retorno.resposta.original # Tudo certo! if self._soap_retorno.resposta.status == 200: self._soap_retorno.xml = self._soap_retorno.resposta.original print 15*'==' print self._soap_retorno.xml print 15*'==' #except Exception, e: #raise e #else: con.close() def enviar_lote(self, numero_lote=None, lista_nfes=[]): novos_arquivos = [] if self.versao == u'2.00': envio = EnviNFe_200() resposta = RetEnviNFe_200() webservice = WS_NFE_ENVIO_LOTE elif self.versao == u'3.10': envio = EnviNFe_310() resposta = RetEnviNFe_310() webservice = WS_NFE_AUTORIZACAO processo = ProcessoNFe(webservice=webservice, 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 novos_arquivos.append(('numero_lote', numero_lote)) envio.validar() for n in lista_nfes: n.monta_chave() novo_arquivo_nome = n.chave + u'-nfe.xml' novo_arquivo = n.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) novo_arquivo_nome = unicode(envio.idLote.valor).strip().rjust(15, u'0') + u'-env-lot.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self._conectar_servico(webservice, envio, resposta) novo_arquivo_nome = unicode(envio.idLote.valor).strip().rjust(15, u'0') + u'-rec' if resposta.cStat.valor == u'103': novo_arquivo_nome += u'.xml' else: novo_arquivo_nome += u'-rej.xml' novo_arquivo = resposta.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) return processo, novos_arquivos def consultar_recibo(self, ambiente=None, numero_recibo=None): novos_arquivos = [] if self.versao == u'2.00': envio = ConsReciNFe_200() resposta = RetConsReciNFe_200() webservice = WS_NFE_CONSULTA_RECIBO elif self.versao == u'3.10': envio = ConsReciNFe_310() resposta = RetConsReciNFe_310() webservice = WS_NFE_RET_AUTORIZACAO processo = ProcessoNFe(webservice=webservice, envio=envio, resposta=resposta) if ambiente is None: ambiente = self.ambiente envio.tpAmb.valor = ambiente envio.nRec.valor = numero_recibo envio.validar() novo_arquivo_nome = unicode(envio.nRec.valor).strip().rjust(15, u'0') + u'-ped-rec.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self._conectar_servico(webservice, envio, resposta, ambiente) novo_arquivo_nome = unicode(envio.nRec.valor).strip().rjust(15, u'0') + u'-pro-rec' if resposta.cStat.valor == u'104': novo_arquivo_nome += u'.xml' else: novo_arquivo_nome += u'-rej.xml' novo_arquivo = resposta.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) # # Salvar os resultados dos processamentos # for pn in resposta.protNFe: novo_arquivo_nome = unicode(pn.infProt.chNFe.valor).strip().rjust(44, u'0') + u'-pro-nfe-' # NF-e autorizada if pn.infProt.cStat.valor == u'100': novo_arquivo_nome += u'aut.xml' # NF-e denegada elif pn.infProt.cStat.valor in (u'110', u'301', u'302'): novo_arquivo_nome += u'den.xml' # NF-e rejeitada else: novo_arquivo_nome += u'rej.xml' novo_arquivo = pn.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) #-- novos_arquivos.append(('status_resp', (pn.infProt.cStat.valor,pn.infProt.xMotivo.valor))) return processo, novos_arquivos def consultar_manifesto_destinatario(self, cnpj=None, ambiente=None, tipo_evento=None, chave_nfe=None): novos_arquivos = [] envio = EnvConfRecebto_200() resposta = RetEnvConfRecebto_200() processo = ProcessoNFe(webservice=WS_NFE_EVENTO, envio=envio, resposta=resposta) if ambiente is None: ambiente = self.ambiente self.caminho = self.monta_caminho_nfe(ambiente=ambiente, chave_nfe=chave_nfe) #evento envio.infEvento.tpAmb.valor = ambiente envio.infEvento.chNFe.valor = chave_nfe envio.infEvento.descEvento.valor = 'Confirmacao da Operacao' envio.infEvento.cOrgao.valor = 91# UF_CODIGO[self.estado] #O correto deve ser o codigo do ESTADO, mas como nem todos os webservices ainda estão disponíveis será usado 91 envio.infEvento.tpEvento.valor = tipo_evento envio.infEvento.CNPJ.valor = cnpj dt = datetime.now() dt = dt.replace(tzinfo=pytz.utc) data_hora_tz = datetime.astimezone(dt, pytz.timezone("Brazil/East")) data_evento = datetime.strftime(data_hora_tz, u'%Y-%m-%dT%H:%M:%S')+str(data_hora_tz)[-6:] envio.infEvento.dhEvento.valor = data_evento self.certificado.prepara_certificado_arquivo_pfx() self.certificado.assina_xmlnfe(envio) envio.validar() novo_arquivo_nome = unicode(envio.infEvento.chNFe.valor).strip().rjust(44, u'0') + u'-consulta-evento.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) # print 'nfeevento', WS_NFE_EVENTO self._conectar_servico(WS_NFE_EVENTO, envio, resposta, ambiente) # Se for autorizado, monta o processo if resposta.cStat.valor == u'128': processo_evento_nfe = ProcEventoNFeRecebto_200() processo_evento_nfe.envEvento = envio processo_evento_nfe.retEnvEvento = resposta processo_evento_nfe.validar() processo.processo_evento_nfe = processo_evento_nfe novo_arquivo_nome = unicode(envio.infEvento.chNFe.valor).strip().rjust(44, u'0') + u'-proc-evento-' # Evento autorizado if resposta.infEvento.cStat.valor in (u'135'): novo_arquivo_nome += u'aut.xml' else: novo_arquivo_nome += u'rej.xml' novo_arquivo = resposta.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) # Se for autorizado, monta o processo do evento da manifestação do destinatário if resposta.infEvento.cStat.valor == u'135': novo_arquivo_nome = unicode(envio.infEvento.chNFe.valor).strip().rjust(44, u'0') + u'-evento.xml' novo_arquivo = processo_evento_nfe.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) return processo def consultar_notas_destinatario(self, cnpj=None, ambiente=None, indnfe=None, indemi=None, nsu='0'): novos_arquivos = [] envio = ConsNFeDest_200() resposta = RetConsNFeDest_200() processo = ProcessoNFe(webservice=WS_NFE_CONSULTA_DESTINATARIO, envio=envio, resposta=resposta) if ambiente is None: ambiente = self.ambiente self.caminho = self.monta_caminho_nfe(ambiente=ambiente, chave_nfe=cnpj) #evento envio.tpAmb.valor = ambiente envio.CNPJ.valor = cnpj envio.indNFe.valor = indnfe envio.indEmi.valor = indemi envio.ultNSU.valor = nsu self.certificado.prepara_certificado_arquivo_pfx() envio.validar() novo_arquivo_nome = unicode(envio.CNPJ.valor).strip().rjust(14, u'0') + u'-consulta-cnpj-evento.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self._conectar_servico(WS_NFE_CONSULTA_DESTINATARIO, envio, resposta, ambiente) return processo def download_nfe_xml(self, cnpj=None,ambiente=None, lista_chaves=[]): novos_arquivos = [] #if self.versao == u'2.00': envio = DownloadNFe_200() resposta = RetDownloadNFe_200() processo = ProcessoNFe(webservice=WS_NFE_DOWNLOAD_XML_DESTINATARIO, envio=envio, resposta=resposta) if ambiente is None: ambiente = self.ambiente self.caminho = self.monta_caminho_nfe(ambiente=ambiente, chave_nfe=cnpj) #evento envio.tpAmb.valor = ambiente envio.CNPJ.valor = cnpj #envio.chNFe.valor = chave_nfe envio.chNFe = [TagChNFe(valor=ch) for ch in lista_chaves] envio.xServ.valor = u'DOWNLOAD NFE' self.certificado.prepara_certificado_arquivo_pfx() envio.validar() novo_arquivo_nome = unicode(envio.CNPJ.valor).strip().rjust(14, u'0') + u'-consulta-cnpj-evento.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self._conectar_servico(WS_NFE_DOWNLOAD_XML_DESTINATARIO, envio, resposta, ambiente) return processo def consultar_cadastro_contribuinte(self, cpf_cnpj=None, inscricao_estadual=None, uf=None, ambiente=None): novos_arquivos = [] if self.versao == u'2.00': envio = ConsCad_200() resposta = RetConsCad_200() elif self.versao == u'3.10': envio = ConsCad_310() resposta = RetConsCad_310() processo = ProcessoNFe(webservice=WS_NFE_CONSULTA_CADASTRO, envio=envio, resposta=resposta) if ambiente is None: ambiente = self.ambiente if not uf: uf = self.estado envio.infCons.UF.valor = uf if inscricao_estadual: envio.infCons.IE.valor = inscricao_estadual nome = 'IE_' + inscricao_estadual elif len(cpf_cnpj) == 11: envio.infCons.CPF.valor = cpf_cnpj nome = 'CPF_' + cpf_cnpj elif len(cpf_cnpj) == 14: envio.infCons.CNPJ.valor = cpf_cnpj nome = 'CNPJ_' + cpf_cnpj envio.validar() self._conectar_servico(WS_NFE_CONSULTA_CADASTRO, envio, resposta, 1) novo_arquivo_nome = nome + u'-cons-cad.xml' novo_arquivo = resposta.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) return processo def corrigir_nota(self, chave_nfe=None, cnpj=None, texto_correcao=None, ambiente=None, sequencia=None): novos_arquivos = [] if self.versao == u'3.10': envio = EnvEventoCCe_310() resposta = RetEnvEventoCCe_310() elif self.versao == u'2.00': envio = EnvEventoCCe_200() resposta = RetEnvEventoCCe_200() processo = ProcessoNFe(webservice=WS_NFE_EVENTO, envio=envio, resposta=resposta) if ambiente is None: ambiente = self.ambiente self.caminho = self.monta_caminho_nfe(ambiente=ambiente, chave_nfe=cnpj) envio.infEvento.tpAmb.valor = ambiente envio.infEvento.cOrgao.valor = UF_CODIGO[self.estado] envio.infEvento.CNPJ.valor = cnpj envio.infEvento.chNFe.valor = chave_nfe dt = datetime.now() dt = dt.replace(tzinfo=pytz.utc) data_hora_tz = datetime.astimezone(dt, pytz.timezone("Brazil/East")) data_evento = datetime.strftime(data_hora_tz, u'%Y-%m-%dT%H:%M:%S')+str(data_hora_tz)[-6:] envio.infEvento.dhEvento.valor = data_evento envio.infEvento.detEvento.xCorrecao.valor = texto_correcao envio.infEvento.nSeqEvento.valor = sequencia or 1 self.certificado.prepara_certificado_arquivo_pfx() self.certificado.assina_xmlnfe(envio) envio.validar() novo_arquivo_nome = unicode(envio.infEvento.chNFe.valor).strip().rjust(44, u'0') + u'-cce.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self._conectar_servico(WS_NFE_EVENTO, envio, resposta, ambiente) # Se for autorizado if resposta.cStat.valor == u'128': if self.versao == u'2.00': processo_correcao_nfe = ProcEventoNFeCCe_200() elif self.versao == u'3.10': processo_correcao_nfe = ProcEventoNFeCCe_310() processo_correcao_nfe.envEvento = envio processo_correcao_nfe.retEnvEvento = resposta # processo_correcao_nfe.validar() processo.processo_correcao_nfe = processo_correcao_nfe if self.salvar_arquivos: novo_arquivo_nome = unicode(envio.infEvento.chNFe.valor).strip().rjust(44, u'0') + u'-cce' # Correção autorizada if resposta.infEvento.cStat.valor in (u'135'): novo_arquivo_nome += u'aut.xml' else: novo_arquivo_nome += u'rej.xml' novo_arquivo = resposta.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) # Se for autorizado, monta o processo de cancelamento if resposta.infEvento.cStat.valor == u'135': # Estranhamente, o nome desse arquivo, pelo manual, deve ser chave-can.xml novo_arquivo_nome = unicode(envio.infEvento.chNFe.valor).strip().rjust(44, u'0') + u'-cce.xml' novo_arquivo = processo_correcao_nfe.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) return processo def cancelar_nota(self, cnpj=None, ambiente=None, chave_nfe=None, numero_protocolo=None, justificativa=None): novos_arquivos = [] if self.versao == u'2.00': envio = EnvEvento_200() resposta = RetEnvEvento_200() elif self.versao == u'3.10': envio = EnvEvento_310() resposta = RetEnvEvento_310() processo = ProcessoNFe(webservice=WS_NFE_EVENTO, envio=envio, resposta=resposta) if ambiente is None: ambiente = self.ambiente self.caminho = self.monta_caminho_nfe(ambiente=ambiente, chave_nfe=chave_nfe) #evento envio.infEvento.tpAmb.valor = ambiente envio.infEvento.chNFe.valor = chave_nfe envio.infEvento.nProt.valor = numero_protocolo envio.infEvento.xJust.valor = justificativa envio.infEvento.cOrgao.valor = UF_CODIGO[self.estado] dt = datetime.now() dt = dt.replace(tzinfo=pytz.utc) data_hora_tz = datetime.astimezone(dt, pytz.timezone("Brazil/East")) data_evento = datetime.strftime(data_hora_tz, u'%Y-%m-%dT%H:%M:%S')+str(data_hora_tz)[-6:] envio.infEvento.dhEvento.valor = data_evento envio.infEvento.CNPJ.valor = cnpj self.certificado.prepara_certificado_arquivo_pfx() self.certificado.assina_xmlnfe(envio) envio.validar() novo_arquivo_nome = unicode(envio.infEvento.chNFe.valor).strip().rjust(44, u'0') + u'-ped-can.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self._conectar_servico(WS_NFE_EVENTO, envio, resposta, ambiente) # Se for autorizado, monta o processo de cancelamento if resposta.cStat.valor == u'128': if self.versao == u'2.00': processo_cancelamento_nfe = ProcEventoNFe_200() elif self.versao == u'3.10': processo_cancelamento_nfe = ProcEventoNFe_310() processo_cancelamento_nfe.envEvento = envio processo_cancelamento_nfe.retEnvEvento = resposta processo_cancelamento_nfe.validar() processo.processo_cancelamento_nfe = processo_cancelamento_nfe novo_arquivo_nome = unicode(envio.infEvento.chNFe.valor).strip().rjust(44, u'0') + u'-pro-can-' # Cancelamento autorizado if resposta.infEvento.cStat.valor in (u'135'): novo_arquivo_nome += u'aut.xml' else: novo_arquivo_nome += u'rej.xml' novo_arquivo = resposta.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) # Se for autorizado, monta o processo de cancelamento if resposta.infEvento.cStat.valor == u'135': # Estranhamente, o nome desse arquivo, pelo manual, deve ser chave-can.xml novo_arquivo_nome = unicode(envio.infEvento.chNFe.valor).strip().rjust(44, u'0') + u'-can.xml' novo_arquivo = processo_cancelamento_nfe.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) 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): novos_arquivos = [] if self.versao == u'2.00': envio = InutNFe_200() resposta = RetInutNFe_200() elif self.versao == u'3.10': envio = InutNFe_310() resposta = RetInutNFe_310() 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(u'%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.prepara_certificado_arquivo_pfx() self.certificado.assina_xmlnfe(envio) envio.validar() novo_arquivo_nome = unicode(envio.chave).strip().rjust(41, u'0') + u'-ped-inu.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self._conectar_servico(WS_NFE_INUTILIZACAO, envio, resposta, ambiente) # Se for autorizada, monta o processo de inutilização if resposta.infInut.cStat.valor == u'102': if self.versao == u'2.00': processo_inutilizacao_nfe = ProcInutNFe_200() elif self.versao == u'3.10': processo_inutilizacao_nfe = ProcInutNFe_310() 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: novo_arquivo_nome = unicode(envio.chave).strip().rjust(41, u'0') + u'-pro-inu-' # Inutilização autorizada if resposta.infInut.cStat.valor == u'102': novo_arquivo_nome += u'aut.xml' else: novo_arquivo_nome += u'rej.xml' novo_arquivo = resposta.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) # Se for autorizada, monta o processo de inutilização if resposta.infInut.cStat.valor == u'102': novo_arquivo_nome = unicode(envio.chave).strip().rjust(41, u'0') + u'-proc-inut-nfe.xml' novo_arquivo = processo_inutilizacao_nfe.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) # Estranhamente, o nome desse arquivo, pelo manual, deve ser chave-inu.xml novo_arquivo_nome = unicode(envio.chave).strip().rjust(41, u'0') + u'-inu.xml' novo_arquivo = processo_inutilizacao_nfe.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) return processo def consultar_nota(self, ambiente=None, chave_nfe=None, nfe=None): novos_arquivos = [] if self.versao == u'2.00': envio = ConsSitNFe_200() resposta = RetConsSitNFe_200() elif self.versao == u'3.10': envio = ConsSitNFe_310() resposta = RetConsSitNFe_310() 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() novo_arquivo_nome = unicode(chave_nfe).strip().rjust(44, u'0') + u'-ped-sit.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self._conectar_servico(WS_NFE_CONSULTA, envio, resposta, ambiente) novo_arquivo_nome = unicode(chave_nfe).strip().rjust(44, u'0') + u'-sit.xml' novo_arquivo = resposta.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) return processo, novos_arquivos def consultar_servico(self, ambiente=None, codigo_estado=None): novos_arquivos = [] if self.versao == u'2.00': envio = ConsStatServ_200() resposta = RetConsStatServ_200() elif self.versao == u'3.10': envio = ConsStatServ_310() resposta = RetConsStatServ_310() 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() novo_arquivo_nome = envio.data.strftime(u'%Y%m%dT%H%M%S') + u'-ped-sta.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self._conectar_servico(WS_NFE_SITUACAO, envio, resposta, ambiente) novo_arquivo_nome = envio.data.strftime(u'%Y%m%dT%H%M%S') + u'-sta.xml' novo_arquivo = resposta.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) return processo, novos_arquivos def processar_notas(self, lista_nfes): # # Definir o caminho geral baseado na 1ª NF-e # self.processos = processos = OrderedDict() novos_arquivos = [] 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, novos_arquivos = self.consultar_servico(ambiente=ambiente) processos['servico'] = proc_servico, novos_arquivos yield proc_servico # # Serviço em operação? # print 'resposta', proc_servico.resposta.cStat.valor if proc_servico.resposta.cStat.valor == u'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, novos_arquivos = self.consultar_nota(ambiente=nfe.infNFe.ide.tpAmb.valor, chave_nfe=nfe.chave) processos['consulta'] = proc_consulta, novos_arquivos yield proc_consulta # # Se a nota já constar na SEFAZ # if not ( ((self.versao == u'1.10') and (proc_consulta.resposta.infProt.cStat.valor in (u'217', u'999',))) or ((self.versao in (u'2.00',u'3.10')) and (proc_consulta.resposta.cStat.valor in (u'217', u'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, novos_arquivos = self.enviar_lote(lista_nfes=lista_nfes) processos['envio'] = proc_envio, novos_arquivos yield proc_envio ret_envi_nfe = proc_envio.resposta # # Deu certo? # print 'Deu certo.... :)',ret_envi_nfe.cStat.valor if ret_envi_nfe.cStat.valor == u'103': time.sleep(ret_envi_nfe.infRec.tMed.valor * 3) # Espere o processamento antes de consultar o recibo proc_recibo, novos_arquivos = self.consultar_recibo(ambiente=ret_envi_nfe.tpAmb.valor, numero_recibo=ret_envi_nfe.infRec.nRec.valor) processos['recibo'] = proc_recibo, novos_arquivos yield proc_recibo # Montar os processos das NF-es dic_protNFe = proc_recibo.resposta.dic_protNFe dic_procNFe = proc_recibo.resposta.dic_procNFe self.caminho = caminho_original novos_processos = self.montar_processo_lista_notas(lista_nfes, dic_protNFe, dic_procNFe) for i,novo_processo in enumerate(novos_processos): processos['nota_%i' % i] = novo_processo return def montar_processo_lista_notas(self, lista_nfes, dic_protNFe, dic_procNFe): processos = [] for nfe in lista_nfes: if dic_protNFe.has_key(nfe.chave): protocolo = dic_protNFe[nfe.chave] processo, novos_arquivos = self.montar_processo_uma_nota(nfe, protnfe_recibo=protocolo) processos.append((processo, novos_arquivos)) if processo is not None: dic_procNFe[nfe.chave] = processo return processos def montar_processo_uma_nota(self, nfe, protnfe_recibo=None, protnfe_consulta_110=None, retcancnfe=None): novos_arquivos = [] # # 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... # 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 if protnfe_recibo.infProt.cStat.valor in (u'100', u'110', u'301', u'302'): if self.versao == u'2.00': processo = ProcNFe_200() elif self.versao == u'3.10': processo = ProcNFe_310() processo.NFe = nfe processo.protNFe = protnfe_recibo novos_arquivos.append(('numero_protocolo', protnfe_recibo.infProt.nProt.valor)) novo_arquivo_nome = unicode(nfe.chave).strip().rjust(44, u'0') + u'-proc-nfe.xml' novo_arquivo = processo.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) # 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 == u'100': novo_arquivo_nome = unicode(nfe.chave).strip().rjust(44, u'0') + u'-nfe.xml' else: novo_arquivo_nome = unicode(nfe.chave).strip().rjust(44, u'0') + u'-den.xml' novo_arquivo_nome = novo_arquivo_nome novo_arquivo = processo.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self.caminho = caminho_original return processo, novos_arquivos 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 = u'20' + chave_nfe[2:4] + u'-' + chave_nfe[4:6] serie = chave_nfe[22:25] numero = chave_nfe[25:34] caminho = os.path.join(caminho, data + u'/') caminho = os.path.join(caminho, serie + u'-' + numero + u'/') 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(u'%Y-%m') + u'/') serie = unicode(serie).strip().rjust(3, u'0') numero_inicial = unicode(numero_inicial).strip().rjust(9, u'0') numero_final = unicode(numero_final).strip().rjust(9, u'0') caminho = os.path.join(caminho, serie + u'-' + numero_inicial + u'-' + numero_final + u'/') try: os.makedirs(caminho) except: pass return caminho def salvar_arquivos_agora(self): caminho = self.caminho if self.ambiente == 1: caminho = os.path.join(caminho, 'producao/') else: caminho = os.path.join(caminho, 'homologacao/') try: os.makedirs(caminho) except: pass for nome, processo in self.processos.iteritems(): for arquivo in processo[1]: nome_arquivo, conteudo = arquivo arquivo_em_disco = open(os.path.join(caminho, nome_arquivo), 'w') if hasattr(conteudo, 'getvalue'): arquivo_em_disco.write(conteudo.getvalue()) else: arquivo_em_disco.write(conteudo) arquivo_em_disco.close()
class ProcessadorNFe(object): def __init__(self): self.ambiente = 2 self.estado = u'MG' self.versao = u'2.00' self.certificado = Certificado() self.caminho = u'' self.salvar_arquivos = True self.tipo_contingencia = False self.danfe = DANFE() self.caminho_temporario = u'' self.processos = [] self._servidor = u'' self._url = u'' self._soap_envio = None self._soap_retorno = None def _conectar_servico(self, servico, envio, resposta, ambiente=None): print 'nfe versao...', self.versao print 'conectando ao servico SEFAZ.........' if ambiente is None: ambiente = self.ambiente if self.versao == u'2.00': self._soap_envio = SOAPEnvio_200() self._soap_envio.webservice = webservices_2.METODO_WS[servico][ 'webservice'] self._soap_envio.metodo = webservices_2.METODO_WS[servico][ 'metodo'] self._soap_envio.cUF = UF_CODIGO[self.estado] self._soap_envio.envio = envio self._soap_retorno = SOAPRetorno_200() self._soap_retorno.webservice = webservices_2.METODO_WS[servico][ 'webservice'] self._soap_retorno.metodo = webservices_2.METODO_WS[servico][ 'metodo'] self._soap_retorno.resposta = resposta if not self.tipo_contingencia: self._servidor = webservices_2.ESTADO_WS[ self.estado][ambiente][u'servidor'] self._url = webservices_2.ESTADO_WS[ self.estado][ambiente][servico] elif self.tipo_contingencia == 'SCAN': self._servidor = webservices_2.SCAN[ambiente][u'servidor'] self._url = webservices_2.SCAN[ambiente][servico] elif self.tipo_contingencia == 'SVC-AN': self._servidor = webservices_2.SVC_AN[ambiente][u'servidor'] self._url = webservices_2.SVC_AN[ambiente][servico] elif self.tipo_contingencia == 'SVC-RS': self._servidor = webservices_2.SVC_RS[ambiente][u'servidor'] self._url = webservices_2.SVC_RS[ambiente][servico] if self.versao == u'3.10': self._soap_envio = SOAPEnvio_310() self._soap_envio.webservice = webservices_3.METODO_WS[servico][ 'webservice'] self._soap_envio.metodo = webservices_3.METODO_WS[servico][ 'metodo'] self._soap_envio.cUF = UF_CODIGO[self.estado] self._soap_envio.envio = envio self._soap_retorno = SOAPRetorno_310() self._soap_retorno.webservice = webservices_3.METODO_WS[servico][ 'webservice'] self._soap_retorno.metodo = webservices_3.METODO_WS[servico][ 'metodo'] self._soap_retorno.resposta = resposta if not self.tipo_contingencia: self._servidor = webservices_3.ESTADO_WS[ self.estado][ambiente][u'servidor'] self._url = webservices_3.ESTADO_WS[ self.estado][ambiente][servico] elif self.tipo_contingencia == 'SCAN': self._servidor = webservices_3.SCAN[ambiente][u'servidor'] self._url = webservices_3.SCAN[ambiente][servico] elif self.tipo_contingencia == 'SVC-AN': self._servidor = webservices_3.SVC_AN[ambiente][u'servidor'] self._url = webservices_3.SVC_AN[ambiente][servico] elif self.tipo_contingencia == 'SVC-RS': self._servidor = webservices_3.SVC_RS[ambiente][u'servidor'] self._url = webservices_3.SVC_RS[ambiente][servico] #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 u'/tmp/' nome_arq_chave = tempfile.NamedTemporaryFile('w') nome_arq_chave.write(self.certificado.chave) nome_arq_chave.flush() nome_arq_certificado = tempfile.NamedTemporaryFile('w') nome_arq_certificado.write(self.certificado.certificado) nome_arq_certificado.flush() con = HTTPSConnection(self._servidor, key_file=nome_arq_chave.name, cert_file=nome_arq_certificado.name) #con = ConexaoHTTPS(self._servidor, key_file=nome_arq_chave, cert_file=nome_arq_certificado) con.request(u'POST', u'/' + self._url, self._soap_envio.xml.encode(u'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 # nome_arq_chave.close() nome_arq_certificado.close() # 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')) print 'servidor', self._servidor print 'STATUS________________________________________-', self._soap_retorno.resposta.original # Tudo certo! if self._soap_retorno.resposta.status == 200: self._soap_retorno.xml = self._soap_retorno.resposta.original print 15 * '==' print self._soap_retorno.xml print 15 * '==' #except Exception, e: #raise e #else: con.close() def enviar_lote(self, numero_lote=None, lista_nfes=[]): novos_arquivos = [] if self.versao == u'2.00': envio = EnviNFe_200() resposta = RetEnviNFe_200() webservice = WS_NFE_ENVIO_LOTE elif self.versao == u'3.10': envio = EnviNFe_310() resposta = RetEnviNFe_310() webservice = WS_NFE_AUTORIZACAO processo = ProcessoNFe(webservice=webservice, 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 novos_arquivos.append(('numero_lote', numero_lote)) envio.validar() for n in lista_nfes: n.monta_chave() novo_arquivo_nome = n.chave + u'-nfe.xml' novo_arquivo = n.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) novo_arquivo_nome = unicode(envio.idLote.valor).strip().rjust( 15, u'0') + u'-env-lot.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self._conectar_servico(webservice, envio, resposta) novo_arquivo_nome = unicode(envio.idLote.valor).strip().rjust( 15, u'0') + u'-rec' if resposta.cStat.valor == u'103': novo_arquivo_nome += u'.xml' else: novo_arquivo_nome += u'-rej.xml' novo_arquivo = resposta.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) return processo, novos_arquivos def consultar_recibo(self, ambiente=None, numero_recibo=None): novos_arquivos = [] if self.versao == u'2.00': envio = ConsReciNFe_200() resposta = RetConsReciNFe_200() webservice = WS_NFE_CONSULTA_RECIBO elif self.versao == u'3.10': envio = ConsReciNFe_310() resposta = RetConsReciNFe_310() webservice = WS_NFE_RET_AUTORIZACAO processo = ProcessoNFe(webservice=webservice, envio=envio, resposta=resposta) if ambiente is None: ambiente = self.ambiente envio.tpAmb.valor = ambiente envio.nRec.valor = numero_recibo envio.validar() novo_arquivo_nome = unicode(envio.nRec.valor).strip().rjust( 15, u'0') + u'-ped-rec.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self._conectar_servico(webservice, envio, resposta, ambiente) novo_arquivo_nome = unicode(envio.nRec.valor).strip().rjust( 15, u'0') + u'-pro-rec' if resposta.cStat.valor == u'104': novo_arquivo_nome += u'.xml' else: novo_arquivo_nome += u'-rej.xml' novo_arquivo = resposta.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) # # Salvar os resultados dos processamentos # for pn in resposta.protNFe: novo_arquivo_nome = unicode(pn.infProt.chNFe.valor).strip().rjust( 44, u'0') + u'-pro-nfe-' # NF-e autorizada if pn.infProt.cStat.valor == u'100': novo_arquivo_nome += u'aut.xml' # NF-e denegada elif pn.infProt.cStat.valor in (u'110', u'301', u'302'): novo_arquivo_nome += u'den.xml' # NF-e rejeitada else: novo_arquivo_nome += u'rej.xml' novo_arquivo = pn.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) #-- novos_arquivos.append(('status_resp', (pn.infProt.cStat.valor, pn.infProt.xMotivo.valor))) return processo, novos_arquivos def consultar_manifesto_destinatario(self, cnpj=None, ambiente=None, tipo_evento=None, chave_nfe=None): novos_arquivos = [] envio = EnvConfRecebto_200() resposta = RetEnvConfRecebto_200() processo = ProcessoNFe(webservice=WS_NFE_EVENTO, envio=envio, resposta=resposta) if ambiente is None: ambiente = self.ambiente self.caminho = self.monta_caminho_nfe(ambiente=ambiente, chave_nfe=chave_nfe) #evento envio.infEvento.tpAmb.valor = ambiente envio.infEvento.chNFe.valor = chave_nfe envio.infEvento.descEvento.valor = 'Confirmacao da Operacao' envio.infEvento.cOrgao.valor = 91 # UF_CODIGO[self.estado] #O correto deve ser o codigo do ESTADO, mas como nem todos os webservices ainda estão disponíveis será usado 91 envio.infEvento.tpEvento.valor = tipo_evento envio.infEvento.CNPJ.valor = cnpj dt = datetime.now() dt = dt.replace(tzinfo=pytz.utc) data_hora_tz = datetime.astimezone(dt, pytz.timezone("Brazil/East")) data_evento = datetime.strftime( data_hora_tz, u'%Y-%m-%dT%H:%M:%S') + str(data_hora_tz)[-6:] envio.infEvento.dhEvento.valor = data_evento self.certificado.prepara_certificado_arquivo_pfx() self.certificado.assina_xmlnfe(envio) envio.validar() novo_arquivo_nome = unicode(envio.infEvento.chNFe.valor).strip().rjust( 44, u'0') + u'-consulta-evento.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) # print 'nfeevento', WS_NFE_EVENTO self._conectar_servico(WS_NFE_EVENTO, envio, resposta, ambiente) # Se for autorizado, monta o processo if resposta.cStat.valor == u'128': processo_evento_nfe = ProcEventoNFeRecebto_200() processo_evento_nfe.envEvento = envio processo_evento_nfe.retEnvEvento = resposta processo_evento_nfe.validar() processo.processo_evento_nfe = processo_evento_nfe novo_arquivo_nome = unicode( envio.infEvento.chNFe.valor).strip().rjust( 44, u'0') + u'-proc-evento-' # Evento autorizado if resposta.infEvento.cStat.valor in (u'135'): novo_arquivo_nome += u'aut.xml' else: novo_arquivo_nome += u'rej.xml' novo_arquivo = resposta.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) # Se for autorizado, monta o processo do evento da manifestação do destinatário if resposta.infEvento.cStat.valor == u'135': novo_arquivo_nome = unicode( envio.infEvento.chNFe.valor).strip().rjust( 44, u'0') + u'-evento.xml' novo_arquivo = processo_evento_nfe.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) return processo def consultar_notas_destinatario(self, cnpj=None, ambiente=None, indnfe=None, indemi=None, nsu='0'): novos_arquivos = [] envio = ConsNFeDest_200() resposta = RetConsNFeDest_200() processo = ProcessoNFe(webservice=WS_NFE_CONSULTA_DESTINATARIO, envio=envio, resposta=resposta) if ambiente is None: ambiente = self.ambiente self.caminho = self.monta_caminho_nfe(ambiente=ambiente, chave_nfe=cnpj) #evento envio.tpAmb.valor = ambiente envio.CNPJ.valor = cnpj envio.indNFe.valor = indnfe envio.indEmi.valor = indemi envio.ultNSU.valor = nsu self.certificado.prepara_certificado_arquivo_pfx() envio.validar() novo_arquivo_nome = unicode(envio.CNPJ.valor).strip().rjust( 14, u'0') + u'-consulta-cnpj-evento.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self._conectar_servico(WS_NFE_CONSULTA_DESTINATARIO, envio, resposta, ambiente) return processo def download_nfe_xml(self, cnpj=None, ambiente=None, lista_chaves=[]): novos_arquivos = [] #if self.versao == u'2.00': envio = DownloadNFe_200() resposta = RetDownloadNFe_200() processo = ProcessoNFe(webservice=WS_NFE_DOWNLOAD_XML_DESTINATARIO, envio=envio, resposta=resposta) if ambiente is None: ambiente = self.ambiente self.caminho = self.monta_caminho_nfe(ambiente=ambiente, chave_nfe=cnpj) #evento envio.tpAmb.valor = ambiente envio.CNPJ.valor = cnpj #envio.chNFe.valor = chave_nfe envio.chNFe = [TagChNFe(valor=ch) for ch in lista_chaves] envio.xServ.valor = u'DOWNLOAD NFE' self.certificado.prepara_certificado_arquivo_pfx() envio.validar() novo_arquivo_nome = unicode(envio.CNPJ.valor).strip().rjust( 14, u'0') + u'-consulta-cnpj-evento.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self._conectar_servico(WS_NFE_DOWNLOAD_XML_DESTINATARIO, envio, resposta, ambiente) return processo def consultar_cadastro_contribuinte(self, cpf_cnpj=None, inscricao_estadual=None, uf=None, ambiente=None): novos_arquivos = [] if self.versao == u'2.00': envio = ConsCad_200() resposta = RetConsCad_200() elif self.versao == u'3.10': envio = ConsCad_310() resposta = RetConsCad_310() processo = ProcessoNFe(webservice=WS_NFE_CONSULTA_CADASTRO, envio=envio, resposta=resposta) if ambiente is None: ambiente = self.ambiente if not uf: uf = self.estado envio.infCons.UF.valor = uf if inscricao_estadual: envio.infCons.IE.valor = inscricao_estadual nome = 'IE_' + inscricao_estadual elif len(cpf_cnpj) == 11: envio.infCons.CPF.valor = cpf_cnpj nome = 'CPF_' + cpf_cnpj elif len(cpf_cnpj) == 14: envio.infCons.CNPJ.valor = cpf_cnpj nome = 'CNPJ_' + cpf_cnpj envio.validar() self._conectar_servico(WS_NFE_CONSULTA_CADASTRO, envio, resposta, 1) novo_arquivo_nome = nome + u'-cons-cad.xml' novo_arquivo = resposta.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) return processo def corrigir_nota(self, chave_nfe=None, cnpj=None, texto_correcao=None, ambiente=None, sequencia=None): novos_arquivos = [] if self.versao == u'3.10': envio = EnvEventoCCe_310() resposta = RetEnvEventoCCe_310() elif self.versao == u'2.00': envio = EnvEventoCCe_200() resposta = RetEnvEventoCCe_200() processo = ProcessoNFe(webservice=WS_NFE_EVENTO, envio=envio, resposta=resposta) if ambiente is None: ambiente = self.ambiente self.caminho = self.monta_caminho_nfe(ambiente=ambiente, chave_nfe=cnpj) envio.infEvento.tpAmb.valor = ambiente envio.infEvento.cOrgao.valor = UF_CODIGO[self.estado] envio.infEvento.CNPJ.valor = cnpj envio.infEvento.chNFe.valor = chave_nfe dt = datetime.now() dt = dt.replace(tzinfo=pytz.utc) data_hora_tz = datetime.astimezone(dt, pytz.timezone("Brazil/East")) data_evento = datetime.strftime( data_hora_tz, u'%Y-%m-%dT%H:%M:%S') + str(data_hora_tz)[-6:] envio.infEvento.dhEvento.valor = data_evento envio.infEvento.detEvento.xCorrecao.valor = texto_correcao envio.infEvento.nSeqEvento.valor = sequencia or 1 self.certificado.prepara_certificado_arquivo_pfx() self.certificado.assina_xmlnfe(envio) envio.validar() novo_arquivo_nome = unicode(envio.infEvento.chNFe.valor).strip().rjust( 44, u'0') + u'-cce.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self._conectar_servico(WS_NFE_EVENTO, envio, resposta, ambiente) # Se for autorizado if resposta.cStat.valor == u'128': if self.versao == u'2.00': processo_correcao_nfe = ProcEventoNFeCCe_200() elif self.versao == u'3.10': processo_correcao_nfe = ProcEventoNFeCCe_310() processo_correcao_nfe.envEvento = envio processo_correcao_nfe.retEnvEvento = resposta # processo_correcao_nfe.validar() processo.processo_correcao_nfe = processo_correcao_nfe if self.salvar_arquivos: novo_arquivo_nome = unicode( envio.infEvento.chNFe.valor).strip().rjust(44, u'0') + u'-cce' # Correção autorizada if resposta.infEvento.cStat.valor in (u'135'): novo_arquivo_nome += u'aut.xml' else: novo_arquivo_nome += u'rej.xml' novo_arquivo = resposta.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) # Se for autorizado, monta o processo de cancelamento if resposta.infEvento.cStat.valor == u'135': # Estranhamente, o nome desse arquivo, pelo manual, deve ser chave-can.xml novo_arquivo_nome = unicode( envio.infEvento.chNFe.valor).strip().rjust( 44, u'0') + u'-cce.xml' novo_arquivo = processo_correcao_nfe.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) return processo def cancelar_nota(self, cnpj=None, ambiente=None, chave_nfe=None, numero_protocolo=None, justificativa=None): novos_arquivos = [] if self.versao == u'2.00': envio = EnvEvento_200() resposta = RetEnvEvento_200() elif self.versao == u'3.10': envio = EnvEvento_310() resposta = RetEnvEvento_310() processo = ProcessoNFe(webservice=WS_NFE_EVENTO, envio=envio, resposta=resposta) if ambiente is None: ambiente = self.ambiente self.caminho = self.monta_caminho_nfe(ambiente=ambiente, chave_nfe=chave_nfe) #evento envio.infEvento.tpAmb.valor = ambiente envio.infEvento.chNFe.valor = chave_nfe envio.infEvento.nProt.valor = numero_protocolo envio.infEvento.xJust.valor = justificativa envio.infEvento.cOrgao.valor = UF_CODIGO[self.estado] dt = datetime.now() dt = dt.replace(tzinfo=pytz.utc) data_hora_tz = datetime.astimezone(dt, pytz.timezone("Brazil/East")) data_evento = datetime.strftime( data_hora_tz, u'%Y-%m-%dT%H:%M:%S') + str(data_hora_tz)[-6:] envio.infEvento.dhEvento.valor = data_evento envio.infEvento.CNPJ.valor = cnpj self.certificado.prepara_certificado_arquivo_pfx() self.certificado.assina_xmlnfe(envio) envio.validar() novo_arquivo_nome = unicode(envio.infEvento.chNFe.valor).strip().rjust( 44, u'0') + u'-ped-can.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self._conectar_servico(WS_NFE_EVENTO, envio, resposta, ambiente) # Se for autorizado, monta o processo de cancelamento if resposta.cStat.valor == u'128': if self.versao == u'2.00': processo_cancelamento_nfe = ProcEventoNFe_200() elif self.versao == u'3.10': processo_cancelamento_nfe = ProcEventoNFe_310() processo_cancelamento_nfe.envEvento = envio processo_cancelamento_nfe.retEnvEvento = resposta processo_cancelamento_nfe.validar() processo.processo_cancelamento_nfe = processo_cancelamento_nfe novo_arquivo_nome = unicode( envio.infEvento.chNFe.valor).strip().rjust(44, u'0') + u'-pro-can-' # Cancelamento autorizado if resposta.infEvento.cStat.valor in (u'135'): novo_arquivo_nome += u'aut.xml' else: novo_arquivo_nome += u'rej.xml' novo_arquivo = resposta.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) # Se for autorizado, monta o processo de cancelamento if resposta.infEvento.cStat.valor == u'135': # Estranhamente, o nome desse arquivo, pelo manual, deve ser chave-can.xml novo_arquivo_nome = unicode( envio.infEvento.chNFe.valor).strip().rjust( 44, u'0') + u'-can.xml' novo_arquivo = processo_cancelamento_nfe.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) 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): novos_arquivos = [] if self.versao == u'2.00': envio = InutNFe_200() resposta = RetInutNFe_200() elif self.versao == u'3.10': envio = InutNFe_310() resposta = RetInutNFe_310() 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(u'%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.prepara_certificado_arquivo_pfx() self.certificado.assina_xmlnfe(envio) envio.validar() novo_arquivo_nome = unicode(envio.chave).strip().rjust( 41, u'0') + u'-ped-inu.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self._conectar_servico(WS_NFE_INUTILIZACAO, envio, resposta, ambiente) # Se for autorizada, monta o processo de inutilização if resposta.infInut.cStat.valor == u'102': if self.versao == u'2.00': processo_inutilizacao_nfe = ProcInutNFe_200() elif self.versao == u'3.10': processo_inutilizacao_nfe = ProcInutNFe_310() 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: novo_arquivo_nome = unicode(envio.chave).strip().rjust( 41, u'0') + u'-pro-inu-' # Inutilização autorizada if resposta.infInut.cStat.valor == u'102': novo_arquivo_nome += u'aut.xml' else: novo_arquivo_nome += u'rej.xml' novo_arquivo = resposta.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) # Se for autorizada, monta o processo de inutilização if resposta.infInut.cStat.valor == u'102': novo_arquivo_nome = unicode(envio.chave).strip().rjust( 41, u'0') + u'-proc-inut-nfe.xml' novo_arquivo = processo_inutilizacao_nfe.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) # Estranhamente, o nome desse arquivo, pelo manual, deve ser chave-inu.xml novo_arquivo_nome = unicode(envio.chave).strip().rjust( 41, u'0') + u'-inu.xml' novo_arquivo = processo_inutilizacao_nfe.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) return processo def consultar_nota(self, ambiente=None, chave_nfe=None, nfe=None): novos_arquivos = [] if self.versao == u'2.00': envio = ConsSitNFe_200() resposta = RetConsSitNFe_200() elif self.versao == u'3.10': envio = ConsSitNFe_310() resposta = RetConsSitNFe_310() 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() novo_arquivo_nome = unicode(chave_nfe).strip().rjust( 44, u'0') + u'-ped-sit.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self._conectar_servico(WS_NFE_CONSULTA, envio, resposta, ambiente) novo_arquivo_nome = unicode(chave_nfe).strip().rjust( 44, u'0') + u'-sit.xml' novo_arquivo = resposta.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) return processo, novos_arquivos def consultar_servico(self, ambiente=None, codigo_estado=None): novos_arquivos = [] if self.versao == u'2.00': envio = ConsStatServ_200() resposta = RetConsStatServ_200() elif self.versao == u'3.10': envio = ConsStatServ_310() resposta = RetConsStatServ_310() 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() novo_arquivo_nome = envio.data.strftime( u'%Y%m%dT%H%M%S') + u'-ped-sta.xml' novo_arquivo = envio.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self._conectar_servico(WS_NFE_SITUACAO, envio, resposta, ambiente) novo_arquivo_nome = envio.data.strftime(u'%Y%m%dT%H%M%S') + u'-sta.xml' novo_arquivo = resposta.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) return processo, novos_arquivos def processar_notas(self, lista_nfes): # # Definir o caminho geral baseado na 1ª NF-e # self.processos = processos = OrderedDict() novos_arquivos = [] 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, novos_arquivos = self.consultar_servico( ambiente=ambiente) processos['servico'] = proc_servico, novos_arquivos yield proc_servico # # Serviço em operação? # print 'resposta', proc_servico.resposta.cStat.valor if proc_servico.resposta.cStat.valor == u'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, novos_arquivos = self.consultar_nota( ambiente=nfe.infNFe.ide.tpAmb.valor, chave_nfe=nfe.chave) processos['consulta'] = proc_consulta, novos_arquivos yield proc_consulta # # Se a nota já constar na SEFAZ # if not (((self.versao == u'1.10') and (proc_consulta.resposta.infProt.cStat.valor in ( u'217', u'999', ))) or ((self.versao in (u'2.00', u'3.10')) and (proc_consulta.resposta.cStat.valor in ( u'217', u'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, novos_arquivos = self.enviar_lote( lista_nfes=lista_nfes) processos['envio'] = proc_envio, novos_arquivos yield proc_envio ret_envi_nfe = proc_envio.resposta # # Deu certo? # print 'Deu certo.... :)', ret_envi_nfe.cStat.valor if ret_envi_nfe.cStat.valor == u'103': time.sleep( ret_envi_nfe.infRec.tMed.valor * 3) # Espere o processamento antes de consultar o recibo proc_recibo, novos_arquivos = self.consultar_recibo( ambiente=ret_envi_nfe.tpAmb.valor, numero_recibo=ret_envi_nfe.infRec.nRec.valor) processos['recibo'] = proc_recibo, novos_arquivos yield proc_recibo # Montar os processos das NF-es dic_protNFe = proc_recibo.resposta.dic_protNFe dic_procNFe = proc_recibo.resposta.dic_procNFe self.caminho = caminho_original novos_processos = self.montar_processo_lista_notas( lista_nfes, dic_protNFe, dic_procNFe) for i, novo_processo in enumerate(novos_processos): processos['nota_%i' % i] = novo_processo return def montar_processo_lista_notas(self, lista_nfes, dic_protNFe, dic_procNFe): processos = [] for nfe in lista_nfes: if dic_protNFe.has_key(nfe.chave): protocolo = dic_protNFe[nfe.chave] processo, novos_arquivos = self.montar_processo_uma_nota( nfe, protnfe_recibo=protocolo) processos.append((processo, novos_arquivos)) if processo is not None: dic_procNFe[nfe.chave] = processo return processos def montar_processo_uma_nota(self, nfe, protnfe_recibo=None, protnfe_consulta_110=None, retcancnfe=None): novos_arquivos = [] # # 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... # 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 if protnfe_recibo.infProt.cStat.valor in (u'100', u'110', u'301', u'302'): if self.versao == u'2.00': processo = ProcNFe_200() elif self.versao == u'3.10': processo = ProcNFe_310() processo.NFe = nfe processo.protNFe = protnfe_recibo novos_arquivos.append( ('numero_protocolo', protnfe_recibo.infProt.nProt.valor)) novo_arquivo_nome = unicode(nfe.chave).strip().rjust( 44, u'0') + u'-proc-nfe.xml' novo_arquivo = processo.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) # 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 == u'100': novo_arquivo_nome = unicode(nfe.chave).strip().rjust( 44, u'0') + u'-nfe.xml' else: novo_arquivo_nome = unicode(nfe.chave).strip().rjust( 44, u'0') + u'-den.xml' novo_arquivo_nome = novo_arquivo_nome novo_arquivo = processo.xml.encode('utf-8') novos_arquivos.append((novo_arquivo_nome, novo_arquivo)) self.caminho = caminho_original return processo, novos_arquivos 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 = u'20' + chave_nfe[2:4] + u'-' + chave_nfe[4:6] serie = chave_nfe[22:25] numero = chave_nfe[25:34] caminho = os.path.join(caminho, data + u'/') caminho = os.path.join(caminho, serie + u'-' + numero + u'/') 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(u'%Y-%m') + u'/') serie = unicode(serie).strip().rjust(3, u'0') numero_inicial = unicode(numero_inicial).strip().rjust(9, u'0') numero_final = unicode(numero_final).strip().rjust(9, u'0') caminho = os.path.join( caminho, serie + u'-' + numero_inicial + u'-' + numero_final + u'/') try: os.makedirs(caminho) except: pass return caminho def salvar_arquivos_agora(self): caminho = self.caminho if self.ambiente == 1: caminho = os.path.join(caminho, 'producao/') else: caminho = os.path.join(caminho, 'homologacao/') try: os.makedirs(caminho) except: pass for nome, processo in self.processos.iteritems(): for arquivo in processo[1]: nome_arquivo, conteudo = arquivo arquivo_em_disco = open(os.path.join(caminho, nome_arquivo), 'w') if hasattr(conteudo, 'getvalue'): arquivo_em_disco.write(conteudo.getvalue()) else: arquivo_em_disco.write(conteudo) arquivo_em_disco.close()
class ProcessadorNFe(object): def __init__(self): self.ambiente = 2 self.estado = u'MG' self.versao = u'2.00' self.certificado = Certificado() self.caminho = u'' self.salvar_arquivos = True self.contingencia_SCAN = False self.caminho_temporario = u'' self._servidor = u'' self._url = u'' self._soap_envio = None self._soap_retorno = None def _conectar_servico(self, servico, envio, resposta, ambiente=None): if ambiente is None: ambiente = self.ambiente if self.versao == u'1.10': self._soap_envio = SOAPEnvio_110() self._soap_envio.webservice = webservices_1.METODO_WS[servico][ 'webservice'] self._soap_envio.metodo = webservices_1.METODO_WS[servico][ 'metodo'] self._soap_envio.envio = envio #self._soap_envio.nfeDadosMsg.dados = envio #self._soap_envio.nfeCabecMsg.cabec.versaoDados.valor = envio.versao.valor self._soap_retorno = SOAPRetorno_110() self._soap_retorno.webservice = webservices_1.METODO_WS[servico][ 'webservice'] self._soap_retorno.metodo = webservices_1.METODO_WS[servico][ 'metodo'] self._soap_retorno.resposta = resposta if self.contingencia_SCAN: self._servidor = webservices_1.SCAN[ambiente][u'servidor'] self._url = webservices_1.SCAN[ambiente][servico] else: self._servidor = webservices_1.ESTADO_WS[ self.estado][ambiente][u'servidor'] self._url = webservices_1.ESTADO_WS[ self.estado][ambiente][servico] elif self.versao == u'2.00': self._soap_envio = SOAPEnvio_200() self._soap_envio.webservice = webservices_2.METODO_WS[servico][ 'webservice'] self._soap_envio.metodo = webservices_2.METODO_WS[servico][ 'metodo'] self._soap_envio.cUF = UF_CODIGO[self.estado] self._soap_envio.envio = envio self._soap_retorno = SOAPRetorno_200() self._soap_retorno.webservice = webservices_2.METODO_WS[servico][ 'webservice'] self._soap_retorno.metodo = webservices_2.METODO_WS[servico][ 'metodo'] self._soap_retorno.resposta = resposta if self.contingencia_SCAN: self._servidor = webservices_2.SCAN[ambiente][u'servidor'] self._url = webservices_2.SCAN[ambiente][servico] else: self._servidor = webservices_2.ESTADO_WS[ self.estado][ambiente][u'servidor'] self._url = webservices_2.ESTADO_WS[ self.estado][ambiente][servico] #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 u'/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(u'POST', u'/' + self._url, self._soap_envio.xml.encode(u'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 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 == u'1.10': envio = EnviNFe_110() resposta = RetEnviNFe_110() elif self.versao == u'2.00': envio = EnviNFe_200() resposta = RetEnviNFe_200() 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 + u'-nfe.xml', 'w') arq.write(n.xml.encode(u'utf-8')) arq.close arq = open( self.caminho + unicode(envio.idLote.valor).strip().rjust(15, u'0') + u'-env-lot.xml', 'w') arq.write(envio.xml.encode(u'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, u'0') + u'-rec' if resposta.cStat.valor != u'103': nome_arq += u'-rej.xml' else: nome_arq += u'.xml' arq = open(nome_arq, 'w') arq.write(resposta.xml.encode(u'utf-8')) arq.close() return processo def consultar_recibo(self, ambiente=None, numero_recibo=None): if self.versao == u'1.10': envio = ConsReciNFe_110() resposta = RetConsReciNFe_110() elif self.versao == u'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, u'0') + u'-ped-rec.xml', 'w') arq.write(envio.xml.encode(u'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, u'0') + u'-pro-rec' if resposta.cStat.valor != u'104': nome_arq += u'-rej.xml' else: nome_arq += u'.xml' arq = open(nome_arq, 'w') arq.write(resposta.xml.encode(u'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, u'0') + u'-pro-nfe-' # NF-e autorizada if pn.infProt.cStat.valor == u'100': nome_arq += u'aut.xml' # NF-e denegada elif pn.infProt.cStat.valor in (u'110', u'301', u'302'): nome_arq += u'den.xml' # NF-e rejeitada else: nome_arq += u'rej.xml' arq = open(nome_arq, 'w') arq.write(pn.xml.encode(u'utf-8')) arq.close() return processo def cancelar_nota(self, ambiente=None, chave_nfe=None, numero_protocolo=None, justificativa=None): if self.versao == u'1.10': envio = CancNFe_107() resposta = RetCancNFe_107() elif self.versao == u'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.prepara_certificado_arquivo_pfx() self.certificado.assina_xmlnfe(envio) envio.validar() if self.salvar_arquivos: arq = open( self.caminho + unicode(envio.infCanc.chNFe.valor).strip().rjust(44, u'0') + u'-ped-can.xml', 'w') arq.write(envio.xml.encode(u'utf-8')) arq.close() self._conectar_servico(WS_NFE_CANCELAMENTO, envio, resposta, ambiente) #resposta.validar() # Se for autorizado, monta o processo de cancelamento if resposta.infCanc.cStat.valor == u'101': if self.versao == u'1.10': processo_cancelamento_nfe = ProcCancNFe_107() elif self.versao == u'2.00': processo_cancelamento_nfe = ProcCancNFe_200() nome_arq = self.caminho + unicode( envio.infCanc.chNFe.valor).strip().rjust( 44, u'0') + u'-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, u'0') + u'-pro-can-' # Cancelamento autorizado if resposta.infCanc.cStat.valor == u'101': nome_arq += u'aut.xml' else: nome_arq += u'rej.xml' arq = open(nome_arq, 'w') arq.write(resposta.xml.encode(u'utf-8')) arq.close() # Se for autorizado, monta o processo de cancelamento if resposta.infCanc.cStat.valor == u'101': nome_arq = self.caminho + unicode( envio.infCanc.chNFe.valor).strip().rjust( 44, u'0') + u'-proc-canc-nfe.xml' arq = open(nome_arq, 'w') arq.write(processo_cancelamento_nfe.xml.encode(u'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, u'0') + u'-can.xml' arq = open(nome_arq, 'w') arq.write(processo_cancelamento_nfe.xml.encode(u'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 == u'1.10': envio = InutNFe_107() resposta = RetInutNFe_107() elif self.versao == u'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(u'%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.prepara_certificado_arquivo_pfx() self.certificado.assina_xmlnfe(envio) envio.validar() if self.salvar_arquivos: arq = open( self.caminho + unicode(envio.chave).strip().rjust(41, u'0') + u'-ped-inu.xml', 'w') arq.write(envio.xml.encode(u'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 == u'102': if self.versao == u'1.10': processo_inutilizacao_nfe = ProcInutNFe_107() elif self.versao == u'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, u'0') + u'-pro-inu-' # Inutilização autorizada if resposta.infInut.cStat.valor == u'102': nome_arq += u'aut.xml' else: nome_arq += u'rej.xml' arq = open(nome_arq, 'w') arq.write(resposta.xml.encode(u'utf-8')) arq.close() # Se for autorizada, monta o processo de inutilização if resposta.infInut.cStat.valor == u'102': nome_arq = self.caminho + unicode(envio.chave).strip().rjust( 41, u'0') + u'-proc-inut-nfe.xml' arq = open(nome_arq, 'w') arq.write(processo_inutilizacao_nfe.xml.encode(u'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, u'0') + u'-inu.xml' arq = open(nome_arq, 'w') arq.write(processo_inutilizacao_nfe.xml.encode(u'utf-8')) arq.close() return processo def consultar_nota(self, ambiente=None, chave_nfe=None, nfe=None): if self.versao == u'1.10': envio = ConsSitNFe_107() resposta = RetConsSitNFe_107() elif self.versao == u'2.00': envio = ConsSitNFe_200() resposta = RetConsSitNFe_200() 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, u'0') + u'-ped-sit.xml', 'w') arq.write(envio.xml.encode(u'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, u'0') + u'-sit.xml' arq = open(nome_arq, 'w') arq.write(resposta.xml.encode(u'utf-8')) arq.close() return processo def consultar_servico(self, ambiente=None, codigo_estado=None): if self.versao == u'1.10': envio = ConsStatServ_107() resposta = RetConsStatServ_107() elif self.versao == u'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(u'%Y%m%dT%H%M%S') + u'-ped-sta.xml', 'w') arq.write(envio.xml.encode(u'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(u'%Y%m%dT%H%M%S') + u'-sta.xml', 'w') arq.write(envio.xml.encode(u'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? # print 'resposta', proc_servico.resposta.cStat.valor if proc_servico.resposta.cStat.valor == u'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 == u'1.10') and (proc_consulta.resposta.infProt.cStat.valor in ( u'217', u'999', ))) or ((self.versao == u'2.00') and (proc_consulta.resposta.cStat.valor in ( u'217', u'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? # print 'se o retorna,103 deu certo', ret_envi_nfe.cStat.valor if ret_envi_nfe.cStat.valor == u'103': 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 dic_protNFe.has_key(nfe.chave): 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 if protnfe_recibo.infProt.cStat.valor in (u'100', u'110', u'301', u'302'): if self.versao == u'1.10': processo = ProcNFe_110() elif self.versao == u'2.00': processo = ProcNFe_200() processo.NFe = nfe processo.protNFe = protnfe_recibo if self.salvar_arquivos: nome_arq = self.caminho + unicode(nfe.chave).strip().rjust( 44, u'0') + u'-proc-nfe.xml' arq = open(nome_arq, 'w') arq.write(processo.xml.encode(u'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 == u'100': nome_arq = self.caminho + unicode(nfe.chave).strip().rjust( 44, u'0') + u'-nfe.xml' else: nome_arq = self.caminho + unicode(nfe.chave).strip().rjust( 44, u'0') + u'-den.xml' arq = open(nome_arq, 'w') arq.write(processo.xml.encode(u'utf-8')) 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 = u'20' + chave_nfe[2:4] + u'-' + chave_nfe[4:6] serie = chave_nfe[22:25] numero = chave_nfe[25:34] caminho = os.path.join(caminho, data + u'/') caminho = os.path.join(caminho, serie + u'-' + numero + u'/') 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(u'%Y-%m') + u'/') serie = unicode(serie).strip().rjust(3, u'0') numero_inicial = unicode(numero_inicial).strip().rjust(9, u'0') numero_final = unicode(numero_final).strip().rjust(9, u'0') caminho = os.path.join( caminho, serie + u'-' + numero_inicial + u'-' + numero_final + u'/') try: os.makedirs(caminho) except: pass return caminho