fix(services): add double-booking check and schedule validation on create
Before creating a service, now: 1. Checks if the requested slot (day + range1_hour1) is already occupied by a non-cancelled/denied service for that professional — rejects with 400 2. Validates the requested day has an enabled schedule for the professional; rejects if the professional does not work that weekday 3. Validates the requested hour falls within the configured hour ranges (continuous day: range1_hour1→range2_hour2; split day: morning + afternoon blocks) — rejects if outside configured hours day_of_week conversion: JS Date.getDay() (0=Sun) → DB convention (0=Mon) via (getDay()+6)%7 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
72777df52c
commit
c8b2c0c901
@@ -36,6 +36,50 @@ export class ServicesService {
|
||||
if (!prof) prof = await this.prisma.professionals.findUnique({ where: { user_id: data.professional_id } });
|
||||
if (!prof || !prof.is_active) throw new BadRequestException('Profesional no disponible');
|
||||
|
||||
if (data.range1_hour1) {
|
||||
const slotTime = new Date(`1970-01-01T${data.range1_hour1}:00`);
|
||||
|
||||
// Double-booking check: reject if this slot already has a non-cancelled/denied service
|
||||
const conflict = await this.prisma.services.findFirst({
|
||||
where: {
|
||||
professional_id: prof.id,
|
||||
day: new Date(data.day),
|
||||
range1_hour1: slotTime,
|
||||
status: { notIn: ['denied', 'cancelled'] },
|
||||
},
|
||||
});
|
||||
if (conflict) throw new BadRequestException('Este horario ya está ocupado');
|
||||
|
||||
// Schedule validation: confirm the slot is within the professional's configured hours
|
||||
// JS getDay() → 0=Sun…6=Sat; DB convention → 0=Mon…6=Sun, so: (getDay()+6)%7
|
||||
const jsDay = new Date(data.day).getDay();
|
||||
const dayOfWeek = (jsDay + 6) % 7;
|
||||
const schedule = await this.prisma.schedules.findFirst({
|
||||
where: { professional_id: prof.id, day_of_week: dayOfWeek, enabled: true },
|
||||
});
|
||||
if (!schedule) throw new BadRequestException('El profesional no atiende ese día');
|
||||
|
||||
const toMins = (d: Date | null) => d ? d.getUTCHours() * 60 + d.getUTCMinutes() : null;
|
||||
const [sh, sm] = data.range1_hour1.split(':').map(Number);
|
||||
const reqMins = sh * 60 + sm;
|
||||
|
||||
let inRange = false;
|
||||
if (schedule.continuous_day) {
|
||||
// Full day: range1_hour1 → range2_hour2
|
||||
const start = toMins(schedule.range1_hour1);
|
||||
const end = toMins(schedule.range2_hour2);
|
||||
if (start !== null && end !== null) inRange = reqMins >= start && reqMins < end;
|
||||
} else {
|
||||
// Morning block: range1_hour1 → range1_hour2
|
||||
const s1 = toMins(schedule.range1_hour1), e1 = toMins(schedule.range1_hour2);
|
||||
if (s1 !== null && e1 !== null) inRange = inRange || (reqMins >= s1 && reqMins < e1);
|
||||
// Afternoon block: range2_hour1 → range2_hour2
|
||||
const s2 = toMins(schedule.range2_hour1), e2 = toMins(schedule.range2_hour2);
|
||||
if (s2 !== null && e2 !== null) inRange = inRange || (reqMins >= s2 && reqMins < e2);
|
||||
}
|
||||
if (!inRange) throw new BadRequestException('La hora seleccionada está fuera del horario del profesional');
|
||||
}
|
||||
|
||||
return this.prisma.services.create({
|
||||
data: {
|
||||
professional_id: prof.id,
|
||||
|
||||
Reference in New Issue
Block a user