# -*- coding: utf-8 -*- from odoo import models, fields, api, _ from odoo.exceptions import ValidationError class SkeenMonedero(models.Model): _name = 'skeen.monedero' _description = 'Monedero Digital de Fidelidad' _order = 'create_date desc' # Paciente partner_id = fields.Many2one('res.partner', string='Paciente', required=True, domain=[('is_patient', '=', True)]) partner_phone = fields.Char(related='partner_id.phone', string='Teléfono', readonly=True) # Puntos points = fields.Integer(string='Puntos', default=0) equivalent_mxn = fields.Float(string='Equivalente MXN', compute='_compute_equivalent') # Historial de transacciones transaction_ids = fields.One2many('skeen.monedero.transaction', 'monedero_id', string='Transacciones') # Estado active = fields.Boolean(string='Activo', default=True) @api.depends('points') def _compute_equivalent(self): for monedero in self: monedero.equivalent_mxn = monedero.points * 1.0 # 1 punto = $1 MXN def get_balance(self, phone): """Obtener saldo por teléfono (para API)""" partner = self.env['res.partner'].search([('phone', '=', phone)], limit=1) if not partner: return {'points': 0, 'equivalent_mxn': 0, 'history': []} monedero = self.search([('partner_id', '=', partner.id)], limit=1) if not monedero: return {'points': 0, 'equivalent_mxn': 0, 'history': []} history = [] for tx in monedero.transaction_ids: history.append({ 'date': tx.create_date.strftime('%d/%m/%Y'), 'type': tx.type, 'points': tx.points, 'description': tx.description, }) return { 'points': monedero.points, 'equivalent_mxn': monedero.equivalent_mxn, 'history': history, } def add_points(self, points, description='', reference=''): """Agregar puntos al monedero""" self.ensure_one() self.write({'points': self.points + points}) self.env['skeen.monedero.transaction'].create({ 'monedero_id': self.id, 'type': 'accrual', 'points': points, 'description': description or 'Acumulación de puntos', 'reference': reference, }) return True def redeem_points(self, points, description='', reference=''): """Redimir puntos del monedero""" self.ensure_one() if points > self.points: raise ValidationError(_('No tiene suficientes puntos!')) self.write({'points': self.points - points}) self.env['skeen.monedero.transaction'].create({ 'monedero_id': self.id, 'type': 'redemption', 'points': -points, 'description': description or 'Redención de puntos', 'reference': reference, }) return True def adjust_points(self, delta, description='', reference=''): """Ajuste de puntos (puede ser negativo). Hace piso en 0 para no dejar saldo negativo. Usado por devoluciones para revertir puntos acumulados por una venta. Devuelve el delta efectivamente aplicado (puede ser menor si se pisó en 0). """ self.ensure_one() delta = int(delta or 0) new_points = self.points + delta effective_delta = delta if new_points < 0: effective_delta = -self.points # solo revertir hasta 0 new_points = 0 self.write({'points': new_points}) self.env['skeen.monedero.transaction'].create({ 'monedero_id': self.id, 'type': 'adjustment', 'points': effective_delta, 'description': description or 'Ajuste de puntos', 'reference': reference, }) return effective_delta class SkeenMonederoTransaction(models.Model): _name = 'skeen.monedero.transaction' _description = 'Transacción de Monedero' _order = 'create_date desc' monedero_id = fields.Many2one('skeen.monedero', string='Monedero', required=True, ondelete='cascade') partner_id = fields.Many2one(related='monedero_id.partner_id', string='Paciente', readonly=True) # Transacción type = fields.Selection([ ('accrual', 'Acumulación'), ('redemption', 'Redención'), ('adjustment', 'Ajuste'), ('expiration', 'Vencimiento'), ], string='Tipo', required=True) points = fields.Integer(string='Puntos', required=True) description = fields.Text(string='Descripción') reference = fields.Char(string='Referencia') # Fecha create_date = fields.Datetime(string='Fecha', readonly=True, default=fields.Datetime.now)