Oleada UX 5: Servicios (duplicar, favoritos y orden para POS, mas vendidos) y Medicos (semana tipo con validacion en agenda, meta mensual con progreso)

This commit is contained in:
2026-09-17 21:28:43 +00:00
parent 9ee3343e22
commit cd945df6a5
10 changed files with 885 additions and 41 deletions

View File

@@ -265,10 +265,28 @@ class SkeenCita(models.Model):
if b.all_day or (ini < b.time_to and fin > b.time_from):
raise ValidationError(_('El médico tiene un bloqueo (%s)') % (b.motivo or 'agenda bloqueada'))
@api.constrains('date', 'time', 'doctor_id')
def _check_horario_medico(self):
"""Rechaza citas fuera del horario semanal del médico.
Sólo aplica a médicos CON horario configurado y sólo al crear/editar la
fecha, la hora o el médico: las citas históricas fuera de horario se
conservan intactas (cambios de estado u otros campos no las revalidan).
"""
for cita in self:
if cita.state in ('cancelled', 'no_show') or not cita.doctor_id or not cita.date:
continue
dur = (cita.servicio_id.duration_min if cita.servicio_id else 30) or 30
ok, mensaje = cita.doctor_id._valida_cita_en_horario(cita.date, cita.time, dur)
if not ok:
raise ValidationError(_(mensaje))
def get_available_slots(self, date, servicio_id, doctor_id=None):
"""Retorna slots disponibles para una fecha y servicio.
Con doctor_id, además excluye los horarios bloqueados de ese médico
(el usuario no debe poder elegir una hora bloqueada)."""
(el usuario no debe poder elegir una hora bloqueada) y respeta su
horario semanal cuando lo tiene configurado (sin horario configurado
el comportamiento es el de siempre: 8:00 a 19:00)."""
servicio = self.env['skeen.servicio'].browse(servicio_id)
duration = servicio.duration_min if servicio else 30
@@ -281,6 +299,16 @@ class SkeenCita(models.Model):
start_time = 8.0 # 8:00 AM
end_time = 19.0 # 7:00 PM
# Horario del médico (si lo tiene configurado manda sobre el de la clínica)
if doctor_id:
doctor = self.env['hr.employee'].sudo().browse(int(doctor_id))
if doctor.exists() and doctor.horario_ids:
linea = doctor._horario_del_dia(date)
if not linea:
return [] # día no laborable para el médico
start_time = max(start_time, linea.hour_from)
end_time = min(end_time, linea.hour_to)
# Generar slots
slots = []
current = start_time