permisos de agenda: selector de acciones por usuario (ver, agendar, editar, eliminar, visitas)
- roles.py: AGENDA_PERMS, AGENDA_PERM_KEYS, WRITE_RANK_MIN, agenda_defaults_for, resolve_agenda_perms, AGENDA_STATUS_PERM, AGENDA_STATE_PERM, _agenda_status_perm - frontend_user.py: campo agenda_perms, _agenda_perms_list(), to_public_dict() expone agenda_perms y agenda_perms_custom - auth.py: require_agenda(perm) decorador + agenda_perm_error() - frontend.py: @require_agenda aplicado a 22 endpoints + branch en update_appointment_status por accion - auth_users_update: whitelist acepta agenda_perms - Odoo upgrade: columna skeen_frontend_user.agenda_perms - Frontend: types AgendaPerm, AGENDA_PERM_ITEMS, canAgenda() en auth.tsx - UsuariosPanel: checkboxes de permisos de agenda en modal de permisos - Agenda.tsx: gates por permiso en crear/editar/eliminar/visitas/dragdrop/bloqueos - EstadoChecklist/BloqueosModal/ListaEspera: hide actions without permission Verificacion API: 18/18 tests OK (admin hereda, recorte=403, lect=403, restore=null)
This commit is contained in:
@@ -5,7 +5,7 @@ import json
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
from ..roles import ROLE_SELECTION, ROLE_RANKS
|
||||
from ..roles import ROLE_SELECTION, ROLE_RANKS, resolve_agenda_perms
|
||||
|
||||
try:
|
||||
from werkzeug.security import check_password_hash, generate_password_hash
|
||||
@@ -34,6 +34,10 @@ class SkeenFrontendUser(models.Model):
|
||||
notes = fields.Text(string='Notas')
|
||||
allowed_menus = fields.Text(string='Menús Permitidos',
|
||||
help='JSON array de hrefs visibles; vacío = defaults por rol')
|
||||
agenda_perms = fields.Text(
|
||||
string='Permisos de Agenda',
|
||||
help="JSON array con las acciones permitidas dentro de la Agenda "
|
||||
"(ver, agendar, editar, eliminar, visitas). Vacío = heredar del rol.")
|
||||
|
||||
_sql_constraints = [
|
||||
('unique_login', 'unique(login)', 'El usuario ya existe.'),
|
||||
@@ -72,6 +76,22 @@ class SkeenFrontendUser(models.Model):
|
||||
return None
|
||||
return val if isinstance(val, list) and val else None
|
||||
|
||||
def _agenda_perms_list(self):
|
||||
"""Lista explícita de acciones de agenda, o None si el usuario hereda del rol.
|
||||
|
||||
A diferencia de allowed_menus, una lista vacía SÍ es un valor válido
|
||||
("desmarcar todo" = solo consultar); por eso solo se devuelve None
|
||||
cuando el campo está vacío o el JSON es inválido.
|
||||
"""
|
||||
self.ensure_one()
|
||||
if not self.agenda_perms:
|
||||
return None
|
||||
try:
|
||||
val = json.loads(self.agenda_perms)
|
||||
except Exception:
|
||||
return None
|
||||
return val if isinstance(val, list) else None
|
||||
|
||||
def to_public_dict(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
@@ -82,4 +102,8 @@ class SkeenFrontendUser(models.Model):
|
||||
'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(),
|
||||
# Lista resuelta (lo que realmente puede hacer) + la lista propia
|
||||
# cruda, para que el editor de usuarios sepa si está heredando.
|
||||
'agenda_perms': resolve_agenda_perms(self),
|
||||
'agenda_perms_custom': self._agenda_perms_list(),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user