# -*- coding: utf-8 -*- """Glosario configurable de la clínica (terminología white-label). Solo lee/escribe el parámetro `skeen.glosario` (JSON). No hay modelo propio: el pack base y los overrides se resuelven en memoria. Estructura guardada: {"pack": "clinica", "overrides": {"patient_singular": "Huésped"}} Resolución: terms = PACKS[pack] (o 'clinica' si el pack no existe) + overrides El pack 'custom' es válido: parte del pack 'clinica' y aplica los overrides. Así el endpoint siempre devuelve las 24 claves canónicas resueltas. """ import json GLOSARIO_KEY = 'skeen.glosario' DEFAULT_PACK = 'clinica' CUSTOM_PACK = 'custom' # Las 24 claves canónicas, en el orden del brief (contrato del frontend). GLOSARIO_KEYS = ( 'patient_singular', 'patient_plural', 'appointment_singular', 'appointment_plural', 'visit_singular', 'visit_plural', 'record_singular', 'record_plural', 'doctor_singular', 'doctor_plural', 'staff_singular', 'staff_plural', 'branch_singular', 'branch_plural', 'room_singular', 'room_plural', 'service_singular', 'service_plural', 'package_singular', 'package_plural', 'prescription_singular', 'prescription_plural', 'treatment_singular', 'treatment_plural', ) # Nombre legible de cada pack para el selector de configuración. PACK_LABELS = { 'clinica': 'Clínica', 'spa': 'Spa / Wellness', 'estetica': 'Estética', 'dental': 'Dental', } PACKS = { 'clinica': { 'patient_singular': 'Paciente', 'patient_plural': 'Pacientes', 'appointment_singular': 'Cita', 'appointment_plural': 'Citas', 'visit_singular': 'Visita', 'visit_plural': 'Visitas', 'record_singular': 'Expediente', 'record_plural': 'Expedientes', 'doctor_singular': 'Médico', 'doctor_plural': 'Médicos', 'staff_singular': 'Personal', 'staff_plural': 'Personal', 'branch_singular': 'Sucursal', 'branch_plural': 'Sucursales', 'room_singular': 'Cabina', 'room_plural': 'Cabinas', 'service_singular': 'Servicio', 'service_plural': 'Servicios', 'package_singular': 'Paquete', 'package_plural': 'Paquetes', 'prescription_singular': 'Receta', 'prescription_plural': 'Recetas', 'treatment_singular': 'Tratamiento', 'treatment_plural': 'Tratamientos', }, 'spa': { 'patient_singular': 'Cliente', 'patient_plural': 'Clientes', 'appointment_singular': 'Reserva', 'appointment_plural': 'Reservas', 'visit_singular': 'Sesión', 'visit_plural': 'Sesiones', 'record_singular': 'Ficha', 'record_plural': 'Fichas', 'doctor_singular': 'Terapeuta', 'doctor_plural': 'Terapeutas', 'staff_singular': 'Personal', 'staff_plural': 'Personal', 'branch_singular': 'Sucursal', 'branch_plural': 'Sucursales', 'room_singular': 'Sala', 'room_plural': 'Salas', 'service_singular': 'Servicio', 'service_plural': 'Servicios', 'package_singular': 'Paquete', 'package_plural': 'Paquetes', 'prescription_singular': 'Recomendación', 'prescription_plural': 'Recomendaciones', 'treatment_singular': 'Tratamiento', 'treatment_plural': 'Tratamientos', }, 'estetica': { 'patient_singular': 'Cliente', 'patient_plural': 'Clientes', 'appointment_singular': 'Cita', 'appointment_plural': 'Citas', 'visit_singular': 'Sesión', 'visit_plural': 'Sesiones', 'record_singular': 'Ficha', 'record_plural': 'Fichas', 'doctor_singular': 'Especialista', 'doctor_plural': 'Especialistas', 'staff_singular': 'Personal', 'staff_plural': 'Personal', 'branch_singular': 'Sucursal', 'branch_plural': 'Sucursales', 'room_singular': 'Cabina', 'room_plural': 'Cabinas', 'service_singular': 'Servicio', 'service_plural': 'Servicios', 'package_singular': 'Paquete', 'package_plural': 'Paquetes', 'prescription_singular': 'Receta', 'prescription_plural': 'Recetas', 'treatment_singular': 'Tratamiento', 'treatment_plural': 'Tratamientos', }, 'dental': { 'patient_singular': 'Paciente', 'patient_plural': 'Pacientes', 'appointment_singular': 'Cita', 'appointment_plural': 'Citas', 'visit_singular': 'Consulta', 'visit_plural': 'Consultas', 'record_singular': 'Expediente', 'record_plural': 'Expedientes', 'doctor_singular': 'Odontólogo', 'doctor_plural': 'Odontólogos', 'staff_singular': 'Personal', 'staff_plural': 'Personal', 'branch_singular': 'Sucursal', 'branch_plural': 'Sucursales', 'room_singular': 'Consultorio', 'room_plural': 'Consultorios', 'service_singular': 'Tratamiento', 'service_plural': 'Tratamientos', 'package_singular': 'Paquete', 'package_plural': 'Paquetes', 'prescription_singular': 'Receta', 'prescription_plural': 'Recetas', 'treatment_singular': 'Procedimiento', 'treatment_plural': 'Procedimientos', }, } VALID_PACKS = tuple(PACKS) + (CUSTOM_PACK,) def pack_terms(pack): """Términos base de un pack (copia). 'custom' parte del pack por defecto.""" base = PACKS.get(pack) or PACKS[DEFAULT_PACK] return dict(base) def get_config(env): """Config vigente: {'pack': str, 'overrides': {clave: valor}}.""" raw = env['ir.config_parameter'].sudo().get_param(GLOSARIO_KEY) pack = DEFAULT_PACK overrides = {} if raw: try: guardado = json.loads(raw) except (ValueError, TypeError): guardado = None if isinstance(guardado, dict): p = guardado.get('pack') if isinstance(p, str) and p: pack = p o = guardado.get('overrides') if isinstance(o, dict): overrides = {k: str(v) for k, v in o.items() if k in GLOSARIO_KEYS and v is not None and str(v).strip()} return {'pack': pack, 'overrides': overrides} def resolve(env): """(pack, terms): las 24 claves resueltas (pack + overrides).""" cfg = get_config(env) terms = pack_terms(cfg['pack']) terms.update(cfg['overrides']) return cfg['pack'], terms def set_config(env, pack=None, overrides=None): """Guarda pack/overrides. None = conserva lo actual.""" actual = get_config(env) nuevo_pack = actual['pack'] if pack is None else (str(pack).strip() or DEFAULT_PACK) nuevos_overrides = actual['overrides'] if overrides is not None: if not isinstance(overrides, dict): raise ValueError('overrides debe ser un objeto') nuevos_overrides = {k: str(v).strip() for k, v in overrides.items() if k in GLOSARIO_KEYS and v is not None and str(v).strip()} env['ir.config_parameter'].sudo().set_param( GLOSARIO_KEY, json.dumps({'pack': nuevo_pack, 'overrides': nuevos_overrides}, ensure_ascii=False)) return get_config(env) def list_packs(): """Packs disponibles con nombre legible.""" return [{'code': code, 'name': PACK_LABELS.get(code, code.capitalize())} for code in VALID_PACKS]