Odoo电子发票认证失败:留存错误信息同时阻止单据过账
Odoo电子发票认证异常处理解决方案
核心问题
开发第三方发票认证集成时遇到两难场景:
- 使用
raise UserError弹出错误提示,会触发事务回滚,导致已关联到发票的错误信息丢失 - 不抛出异常,错误信息能正常保存,但Odoo会继续执行发票过账流程,违背业务规则
需要同时实现:留存错误信息 + 阻断过账流程 + 弹出错误提示
解决方案
关键在于先手动提交错误记录的数据库变更,再抛出异常终止流程。这样错误信息不会因事务回滚丢失,同时异常会阻断后续过账并向用户提示错误。
修改后的代码如下:
def certificar_documento(self, url, headers, tree): certificar_xml = ET.tostring(tree.getroot(), encoding="UTF-8", method="xml") respuesta = requests.post(url, headers=headers, data=certificar_xml) respuesta_json = respuesta.json() return certificar_xml, respuesta_json def establecer_error(self, json): errores = self.env["errores.fel"] for error in json["descripcion_errores"]: errores.create( { "mensaje_error": error.get("mensaje_error"), "fecha_hora_error": datetime.now(), "account_move_factuas_ids": self.id, } ) # 手动提交错误记录,避免后续异常回滚导致信息丢失 self.env.cr.commit() def facturacion_electronica(self): url = "https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml" headers = { "UsuarioFirma": f"{self.company_id.usuario}", "LlaveFirma": f"{self.company_id.llave_firma}", "UsuarioApi": f"{self.company_id.usuario}", "LlaveApi": f"{self.company_id.llave_api}", "Content-Type": "application/xml", } for factura in self: if ( factura.tipo_factura == "none" and factura.move_type != "out_refund" and factura.move_type != "in_invoice" and not factura.debit_origin_id ): raise UserError("Debe ingresar el tipo de documento que va a emitir") elif factura.debit_origin_id: tree = NDEB.generar_nota_debito(self, factura) elif factura.move_type == "out_refund": tree = NCRE.generar_nota_credito(self, factura) elif factura.tipo_factura == "fact" or factura.tipo_factura == "fact_cambiaria": tree = FACT.generar_factura(self, factura) elif factura.factura_especial: tree = FESP.generar_especial(self, factura) else: continue certificar_xml, respuesta_json = self.certificar_documento(url, headers, tree) if respuesta_json["resultado"] == False: mensajes_error = "\n".join(error["mensaje_error"] for error in respuesta_json["descripcion_errores"]) # 先创建并保存错误信息 self.establecer_error(respuesta_json) # 抛出异常终止过账流程并提示用户 raise UserError(f"La factura no se certificó:\n{mensajes_error}") else: factura.numero_autorizacion = respuesta_json["uuid"] factura.serie = respuesta_json["serie"] factura.numero_dte = respuesta_json["numero"] factura.xml_generado = certificar_xml factura.fecha_emision = datetime.now() fecha_hora_certificado = respuesta_json["fecha"].split("T") hora_certificado = fecha_hora_certificado[1].split("-") fecha_hora_str = f"{fecha_hora_certificado[0]} {hora_certificado[0]}" fecha_hora_time = datetime.strptime(fecha_hora_str, "%Y-%m-%d %H:%M:%S") fecha_hora_modificada = fecha_hora_time + timedelta(hours=6) factura.fecha_certificacion = fecha_hora_modificada factura.xml_certificado = respuesta_json["xml_certificado"] factura.certificada = True var_numero_autorizacion = respuesta_json["uuid"] var_serie = respuesta_json["serie"] var_numero_dte = respuesta_json["numero"] var_fecha = date.today() link_factura = ( f"https://report.feel.com.gt/ingfacereport/ingfacereport_documento?uuid={var_numero_autorizacion}" ) mensaje = f""" <strong style='display:block;'>Datos FEL de la factura</strong> <ul> <li> Número de autorización: {var_numero_autorizacion} </li> <li> Serie: {var_serie} </li> <li> Número DTE: {var_numero_dte} </li> <li> Fecha: {var_fecha} </li> </ul> <a href='{link_factura}' target='_blank'>Ver factura<a/> """ factura.message_post( body=mensaje, body_is_html=True, author_id=self.env.ref("base.partner_root").id, subject="Factura certificada correctamente", message_type="comment", subtype_xmlid="mail.mt_comment", )
修改要点
- 在
establecer_error方法末尾添加self.env.cr.commit():手动提交错误记录的数据库变更,确保后续抛出异常时,错误信息不会被回滚删除。 - 恢复异常抛出逻辑:调用
establecer_error保存错误后,重新抛出UserError,终止过账流程并向用户展示错误详情。
注意事项
- 手动提交事务需谨慎,确保仅提交错误记录相关变更,避免影响其他未完成操作。
- 若需更精细的事务控制,也可使用Odoo的
savepoint机制,但直接提交错误记录是最直接的解决方案。
内容的提问来源于stack exchange,提问作者damar
相关产品推荐
相关产品推荐

