# -*- coding: utf-8 -*- import json from odoo import models, fields, api, _ from odoo.exceptions import ValidationError from ..roles import ROLE_SELECTION, ROLE_RANKS try: from werkzeug.security import check_password_hash, generate_password_hash except Exception: # pragma: no cover check_password_hash = None generate_password_hash = None class SkeenFrontendUser(models.Model): _name = 'skeen.frontend.user' _description = 'Usuario del Frontend React' _order = 'name, login' _rec_name = 'name' login = fields.Char(string='Usuario', required=True, index=True) name = fields.Char(string='Nombre', required=True) password_hash = fields.Char(string='Hash de contraseña', required=True) role = fields.Selection(ROLE_SELECTION, string='Rol', default='recepcion', required=True) active = fields.Boolean(string='Activo', default=True) must_change_password = fields.Boolean(string='Debe cambiar contraseña', default=False) last_login = fields.Datetime(string='Último acceso', readonly=True) notes = fields.Text(string='Notas') allowed_menus = fields.Text(string='Menús Permitidos', help='JSON array de hrefs visibles; vacío = defaults por rol') _sql_constraints = [ ('unique_login', 'unique(login)', 'El usuario ya existe.'), ] def set_password(self, plain): self.ensure_one() if not generate_password_hash: raise ValidationError(_('Hash de contraseña no disponible')) if not plain or len(plain) < 8: raise ValidationError(_('La contraseña debe tener al menos 8 caracteres')) self.password_hash = generate_password_hash(plain) def check_password(self, plain): self.ensure_one() if not self.password_hash or not check_password_hash: return False try: return check_password_hash(self.password_hash, plain or '') except Exception: return False def role_rank(self): """Jerarquía simple para permisos: mayor número = más acceso.""" self.ensure_one() return ROLE_RANKS.get(self.role, 0) def _allowed_menus_list(self): """Devuelve la lista de hrefs permitidos o None (usar defaults por rol)""" self.ensure_one() if not self.allowed_menus: return None try: val = json.loads(self.allowed_menus) except Exception: return None return val if isinstance(val, list) and val else None def to_public_dict(self): self.ensure_one() return { 'id': self.id, 'login': self.login, 'name': self.name, 'role': self.role, 'must_change_password': self.must_change_password, 'last_login': self.last_login.strftime('%Y-%m-%d %H:%M') if self.last_login else None, 'allowed_menus': self._allowed_menus_list(), }