# -*- coding: utf-8 -*- """Emisión de eventos de webhook desde los flujos existentes. Cada override comprueba primero si hay algún webhook activo (una consulta barata sobre una tabla normalmente vacía); si no lo hay, se comporta EXACTAMENTE como antes y no crea ninguna fila. La entrega real es asíncrona (cron), así que aquí solo se encola. """ import logging from odoo import api, models _logger = logging.getLogger(__name__) def _emitir(env, evento, payload, clave_unica=None): """Encapsula la emisión para que NUNCA tumbe la operación de negocio.""" try: service = env['skeen.webhook.service'].sudo() if not service.hay_activos(): return service.emitir(evento, payload, clave_unica=clave_unica) except Exception: _logger.exception('No se pudo emitir el evento de webhook %s', evento) def _val(record, campo): valor = getattr(record, campo, None) if hasattr(valor, 'id'): # many2one return valor.id or False if hasattr(valor, 'name'): # recordset return valor.name if hasattr(valor, 'isoformat'): # fecha/datetime return valor.isoformat() return valor def _cita_payload(cita): return { 'id': cita.id, 'reference': cita.name, 'state': cita.state, 'date': cita.date.isoformat() if cita.date else '', 'patient_id': cita.partner_id.id, 'patient': cita.partner_id.name or '', 'service_id': cita.servicio_id.id if cita.servicio_id else None, 'servicio': cita.servicio_id.name if cita.servicio_id else '', 'doctor': cita.doctor_id.name if getattr(cita, 'doctor_id', None) else '', } class SkeenCita(models.Model): _inherit = 'skeen.cita' @api.model_create_multi def create(self, vals_list): citas = super().create(vals_list) for cita in citas: _emitir(self.env, 'cita.creada', _cita_payload(cita), clave_unica='cita.creada:%s' % cita.id) return citas def write(self, vals): if 'state' not in vals: return super().write(vals) previo = {c.id: c.state for c in self} res = super().write(vals) destinos = { 'confirmed': 'cita.confirmada', 'cancelled': 'cita.cancelada', 'no_show': 'cita.no_show', 'done': 'cita.completada', } for cita in self: evento = destinos.get(cita.state) if evento and previo.get(cita.id) != cita.state: _emitir(self.env, evento, _cita_payload(cita), clave_unica='%s:%s' % (evento, cita.id)) return res class SkeenVenta(models.Model): _inherit = 'skeen.venta' def write(self, vals): if 'state' not in vals and 'refunded' not in vals: return super().write(vals) previo = {v.id: (v.state, v.refunded) for v in self} res = super().write(vals) for venta in self: antes = previo.get(venta.id, (None, None)) if venta.state == 'paid' and antes[0] != 'paid': _emitir(self.env, 'venta.pagada', { 'id': venta.id, 'reference': venta.name, 'state': venta.state, 'total': _val(venta, 'total'), 'patient_id': venta.partner_id.id, }, clave_unica='venta.pagada:%s' % venta.id) if (venta.state == 'cancelled' and antes[0] != 'cancelled') or \ (venta.refunded and not antes[1]): _emitir(self.env, 'venta.cancelada', { 'id': venta.id, 'reference': venta.name, 'state': venta.state, 'refunded': bool(venta.refunded), 'refund_amount': _val(venta, 'refund_amount'), }, clave_unica='venta.cancelada:%s' % venta.id) return res class SkeenPago(models.Model): _inherit = 'skeen.pago' @api.model_create_multi def create(self, vals_list): pagos = super().create(vals_list) for pago in pagos: _emitir(self.env, 'pago.registrado', { 'id': pago.id, 'reference': pago.name, 'amount': pago.amount, 'payment_method': pago.payment_method, 'state': pago.state, 'patient_id': pago.partner_id.id if pago.partner_id else None, }, clave_unica='pago.registrado:%s' % pago.id) return pagos class ResPartner(models.Model): _inherit = 'res.partner' @api.model_create_multi def create(self, vals_list): partners = super().create(vals_list) for partner in partners: if not partner.is_patient: continue _emitir(self.env, 'paciente.creado', { 'id': partner.id, 'name': partner.name, 'phone': partner.phone or '', 'email': partner.email or '', 'is_patient': True, }, clave_unica='paciente.creado:%s' % partner.id) return partners class SkeenMembresia(models.Model): _inherit = 'skeen.membresia' def action_renovar(self, *args, **kwargs): res = super().action_renovar(*args, **kwargs) for membresia in self: _emitir(self.env, 'membresia.renovada', { 'id': membresia.id, 'plan': membresia.plan_id.name if membresia.plan_id else '', 'estado': membresia.estado, 'periodo_actual_inicio': _val(membresia, 'periodo_actual_inicio'), 'periodo_actual_fin': _val(membresia, 'periodo_actual_fin'), 'patient_id': membresia.partner_id.id if membresia.partner_id else None, }, clave_unica='membresia.renovada:%s:%s' % (membresia.id, _val(membresia, 'periodo_actual_inicio'))) return res class SkeenFormularioRespuesta(models.Model): _inherit = 'skeen.formulario.respuesta' def write(self, vals): if vals.get('estado') != 'llenado': return super().write(vals) previo = {r.id: r.estado for r in self} res = super().write(vals) for respuesta in self: if previo.get(respuesta.id) != 'llenado': _emitir(self.env, 'formulario.llenado', { 'id': respuesta.id, 'plantilla': _val(getattr(respuesta, 'plantilla_id', None), 'name') if getattr(respuesta, 'plantilla_id', None) else '', 'estado': respuesta.estado, 'patient_id': respuesta.partner_id.id if getattr(respuesta, 'partner_id', None) else None, }, clave_unica='formulario.llenado:%s' % respuesta.id) return res class SkeenCompra(models.Model): _inherit = 'skeen.compra' def write(self, vals): if 'estado' not in vals: return super().write(vals) previo = {c.id: c.estado for c in self} res = super().write(vals) for compra in self: if compra.estado in ('recibida', 'recibida_parcial') \ and previo.get(compra.id) != compra.estado: _emitir(self.env, 'compra.recibida', { 'id': compra.id, 'referencia': _val(compra, 'name') or _val(compra, 'referencia'), 'estado': compra.estado, }, clave_unica='compra.recibida:%s:%s' % (compra.id, compra.estado)) return res