94 lines
3.3 KiB
PHP
94 lines
3.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Repositories\AppointmentRepository;
|
|
use App\Repositories\ReferenceDataRepository;
|
|
use DateInterval;
|
|
use DateTimeImmutable;
|
|
|
|
final class CalendarService
|
|
{
|
|
public function validate(int $tenantId, array $payload, ?int $appointmentId = null): array
|
|
{
|
|
$appointmentRepository = new AppointmentRepository();
|
|
$lessonType = (new ReferenceDataRepository())->findLessonType($tenantId, (int) $payload['lesson_type_id']);
|
|
$warnings = [
|
|
'conflicts' => [],
|
|
'rest_period_warnings' => [],
|
|
];
|
|
|
|
$conflicts = $appointmentRepository->overlappingForInstructor(
|
|
$tenantId,
|
|
(int) $payload['instructor_id'],
|
|
$payload['start_at'],
|
|
$payload['end_at'],
|
|
$appointmentId
|
|
);
|
|
|
|
foreach ($conflicts as $conflict) {
|
|
$warnings['conflicts'][] = [
|
|
'type' => 'instructor_overlap',
|
|
'message' => 'Fahrlehrer ist in diesem Zeitraum bereits verplant.',
|
|
'appointment_id' => (int) $conflict['id'],
|
|
];
|
|
}
|
|
|
|
if (!empty($payload['student_id'])) {
|
|
$studentConflicts = $appointmentRepository->overlappingForStudent(
|
|
$tenantId,
|
|
(int) $payload['student_id'],
|
|
$payload['start_at'],
|
|
$payload['end_at'],
|
|
$appointmentId
|
|
);
|
|
foreach ($studentConflicts as $conflict) {
|
|
$warnings['conflicts'][] = [
|
|
'type' => 'student_overlap',
|
|
'message' => 'Fahrschueler ist in diesem Zeitraum bereits verplant.',
|
|
'appointment_id' => (int) $conflict['id'],
|
|
];
|
|
}
|
|
}
|
|
|
|
if ($lessonType !== null && (int) $lessonType['is_rest_relevant'] === 1) {
|
|
$newStart = new DateTimeImmutable($payload['start_at']);
|
|
$dayStart = $newStart->setTime(0, 0, 0);
|
|
$dayEnd = $dayStart->modify('+1 day');
|
|
$hasLaterRelevantOnDay = $appointmentRepository->hasRestRelevantAfterStartOnDay(
|
|
$tenantId,
|
|
(int) $payload['instructor_id'],
|
|
$dayStart->format(DATE_ATOM),
|
|
$dayEnd->format(DATE_ATOM),
|
|
$payload['start_at']
|
|
);
|
|
|
|
if ($hasLaterRelevantOnDay) {
|
|
return $warnings;
|
|
}
|
|
|
|
$lastRelevant = $appointmentRepository->lastRestRelevantForInstructor(
|
|
$tenantId,
|
|
(int) $payload['instructor_id'],
|
|
$payload['start_at'],
|
|
$appointmentId
|
|
);
|
|
if ($lastRelevant !== null) {
|
|
$restEnds = (new DateTimeImmutable($lastRelevant['end_at']))->add(new DateInterval('PT11H'));
|
|
if ($newStart < $restEnds) {
|
|
$warnings['rest_period_warnings'][] = [
|
|
'type' => 'rest_period',
|
|
'message' => 'Achtung: Dieser Termin liegt innerhalb der 11-Stunden-Ruhezeit.',
|
|
'rest_until' => $restEnds->format(DATE_ATOM),
|
|
'blocking_appointment_id' => (int) $lastRelevant['id'],
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $warnings;
|
|
}
|
|
}
|