feat: add student conflict detection in appointments
- Add hasStudentConflict() to Appointment model - Check student conflicts in store() and update() - Returns 409 Conflict when student has overlapping appointment - Fixed PostgreSQL sequence for appointments id
This commit is contained in:
@@ -42,6 +42,19 @@ final class AppointmentsController
|
||||
}
|
||||
}
|
||||
|
||||
// Conflict check for student
|
||||
if (isset($data['student_id'], $data['start_at'], $data['end_at'])) {
|
||||
if (Appointment::hasStudentConflict(
|
||||
$data['student_id'] ? (int) $data['student_id'] : null,
|
||||
$data['start_at'],
|
||||
$data['end_at']
|
||||
)) {
|
||||
return $this->json($response, [
|
||||
'message' => 'Student has a conflicting appointment in this time range'
|
||||
], 409);
|
||||
}
|
||||
}
|
||||
|
||||
$appointment = Appointment::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'student_id' => $data['student_id'] ?? null,
|
||||
@@ -84,6 +97,20 @@ final class AppointmentsController
|
||||
}
|
||||
}
|
||||
|
||||
// Conflict check for student (exclude current appointment)
|
||||
if (isset($data['student_id'], $data['start_at'], $data['end_at'])) {
|
||||
if (Appointment::hasStudentConflict(
|
||||
$data['student_id'] ? (int) $data['student_id'] : null,
|
||||
$data['start_at'],
|
||||
$data['end_at'],
|
||||
(int) $args['id']
|
||||
)) {
|
||||
return $this->json($response, [
|
||||
'message' => 'Student has a conflicting appointment in this time range'
|
||||
], 409);
|
||||
}
|
||||
}
|
||||
|
||||
$data['updated_by'] = $user['id'];
|
||||
$appointment->update(array_intersect_key($data, array_flip([
|
||||
'student_id', 'instructor_id', 'lesson_type_id', 'title', 'start_at', 'end_at', 'units', 'status', 'notes', 'warning_acknowledged', 'updated_by'
|
||||
|
||||
@@ -28,7 +28,6 @@ final class Appointment extends Model
|
||||
|
||||
/**
|
||||
* Check if instructor has a conflicting appointment in the given time range.
|
||||
* Excludes the appointment with $excludeId (for updates).
|
||||
*/
|
||||
public static function hasInstructorConflict(
|
||||
int $instructorId,
|
||||
@@ -52,4 +51,34 @@ final class Appointment extends Model
|
||||
|
||||
return $query->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if student has a conflicting appointment in the given time range.
|
||||
*/
|
||||
public static function hasStudentConflict(
|
||||
?int $studentId,
|
||||
string $start,
|
||||
string $end,
|
||||
?int $excludeId = null
|
||||
): bool {
|
||||
if ($studentId === null) {
|
||||
return false; // No student assigned, no conflict possible
|
||||
}
|
||||
|
||||
$query = self::where('student_id', $studentId)
|
||||
->where('status', '!=', 'cancelled')
|
||||
->where(function ($q) use ($start, $end) {
|
||||
$q->whereBetween('start_at', [$start, $end])
|
||||
->orWhereBetween('end_at', [$start, $end])
|
||||
->orWhere(function ($q2) use ($start, $end) {
|
||||
$q2->where('start_at', '<=', $start)->where('end_at', '>=', $end);
|
||||
});
|
||||
});
|
||||
|
||||
if ($excludeId !== null) {
|
||||
$query->where('id', '!=', $excludeId);
|
||||
}
|
||||
|
||||
return $query->exists();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user