Backup: old custom PHP API code removed from fahrschultermin.de
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Repositories\AppointmentRepository;
|
||||
use App\Repositories\InstructorRepository;
|
||||
use App\Repositories\ReferenceDataRepository;
|
||||
use App\Repositories\StudentRepository;
|
||||
use App\Services\CalendarService;
|
||||
use App\Services\RequirementService;
|
||||
use App\Support\Auth;
|
||||
use App\Support\Request;
|
||||
use App\Support\Response;
|
||||
|
||||
final class AppointmentsController
|
||||
{
|
||||
public function index(Request $request): void
|
||||
{
|
||||
$user = Auth::requireUser();
|
||||
$from = (string) $request->query('from');
|
||||
$to = (string) $request->query('to');
|
||||
$instructorId = null;
|
||||
if ($user['role'] === 'instructor') {
|
||||
$instructor = (new InstructorRepository())->findByUserId((int) $user['tenant_id'], (int) $user['id']);
|
||||
$instructorId = $instructor ? (int) $instructor['id'] : null;
|
||||
}
|
||||
$data = (new AppointmentRepository())->listForTenant(
|
||||
(int) $user['tenant_id'],
|
||||
$from,
|
||||
$to,
|
||||
$instructorId
|
||||
);
|
||||
Response::json(['data' => $data]);
|
||||
}
|
||||
|
||||
public function validate(Request $request): void
|
||||
{
|
||||
$user = Auth::requireUser();
|
||||
$payload = $this->normalizePayload((int) $user['tenant_id'], $request->input());
|
||||
$warnings = (new CalendarService())->validate((int) $user['tenant_id'], $payload);
|
||||
Response::json(['warnings' => $warnings, 'payload' => $payload]);
|
||||
}
|
||||
|
||||
public function store(Request $request): void
|
||||
{
|
||||
$user = Auth::requireRole(['tenant_admin', 'dispatcher', 'instructor']);
|
||||
$payload = $this->normalizePayload((int) $user['tenant_id'], $request->input());
|
||||
$warnings = (new CalendarService())->validate((int) $user['tenant_id'], $payload);
|
||||
|
||||
if ($this->hasWarnings($warnings) && empty($payload['warning_acknowledged'])) {
|
||||
Response::json(['message' => 'Warnings require acknowledgement', 'warnings' => $warnings], 409);
|
||||
}
|
||||
|
||||
$record = (new AppointmentRepository())->create((int) $user['tenant_id'], $payload, (int) $user['id']);
|
||||
Response::json([
|
||||
'data' => $record,
|
||||
'warnings' => $warnings,
|
||||
'progress' => $payload['student_id'] ? (new RequirementService())->progressForStudent((int) $payload['student_id']) : [],
|
||||
], 201);
|
||||
}
|
||||
|
||||
public function update(Request $request, array $params): void
|
||||
{
|
||||
$user = Auth::requireRole(['tenant_admin', 'dispatcher', 'instructor']);
|
||||
$payload = $this->normalizePayload((int) $user['tenant_id'], $request->input());
|
||||
$warnings = (new CalendarService())->validate((int) $user['tenant_id'], $payload, (int) $params['id']);
|
||||
|
||||
if ($this->hasWarnings($warnings) && empty($payload['warning_acknowledged'])) {
|
||||
Response::json(['message' => 'Warnings require acknowledgement', 'warnings' => $warnings], 409);
|
||||
}
|
||||
|
||||
$record = (new AppointmentRepository())->update((int) $user['tenant_id'], (int) $params['id'], $payload, (int) $user['id']);
|
||||
Response::json([
|
||||
'data' => $record,
|
||||
'warnings' => $warnings,
|
||||
'progress' => $payload['student_id'] ? (new RequirementService())->progressForStudent((int) $payload['student_id']) : [],
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy(Request $request, array $params): void
|
||||
{
|
||||
$user = Auth::requireRole(['tenant_admin', 'dispatcher']);
|
||||
(new AppointmentRepository())->delete((int) $user['tenant_id'], (int) $params['id']);
|
||||
Response::noContent();
|
||||
}
|
||||
|
||||
private function normalizePayload(int $tenantId, array $payload): array
|
||||
{
|
||||
$referenceRepository = new ReferenceDataRepository();
|
||||
$lessonType = $referenceRepository->findLessonType($tenantId, (int) $payload['lesson_type_id']);
|
||||
if ($lessonType === null) {
|
||||
Response::json(['message' => 'Unbekannte Stundenart'], 422);
|
||||
}
|
||||
|
||||
$student = null;
|
||||
if (!empty($payload['student_id'])) {
|
||||
// Reject student_id if lesson_type does not allow it
|
||||
if ((int) $lessonType['allows_student'] !== 1) {
|
||||
Response::json(['message' => 'Bei dieser Stundenart kann kein Fahrschüler ausgewählt werden'], 422);
|
||||
}
|
||||
|
||||
$student = (new StudentRepository())->find($tenantId, (int) $payload['student_id']);
|
||||
if ($student === null) {
|
||||
Response::json(['message' => 'Unbekannter Fahrschueler'], 422);
|
||||
}
|
||||
|
||||
if (
|
||||
$lessonType['category'] === 'student'
|
||||
&& !$referenceRepository->isLessonTypeVisibleForTemplate($tenantId, (int) $lessonType['id'], (int) $student['template_id'])
|
||||
) {
|
||||
Response::json(['message' => 'Diese Stundenart ist fuer die Fuehrerscheinklasse nicht freigegeben'], 422);
|
||||
}
|
||||
}
|
||||
|
||||
$startAt = new \DateTimeImmutable((string) $payload['start_at']);
|
||||
$baseDuration = max(1, (int) $lessonType['default_duration']);
|
||||
$providedEndAt = !empty($payload['end_at']) ? new \DateTimeImmutable((string) $payload['end_at']) : null;
|
||||
|
||||
if ($providedEndAt instanceof \DateTimeImmutable && $providedEndAt > $startAt) {
|
||||
$endAt = $providedEndAt;
|
||||
$duration = max(1, (int) round(($endAt->getTimestamp() - $startAt->getTimestamp()) / 60));
|
||||
$units = (int) $lessonType['fixed_duration'] === 1
|
||||
? 1
|
||||
: max(1, (int) round($duration / $baseDuration));
|
||||
} else {
|
||||
$units = max(1, (int) ($payload['units'] ?? 1));
|
||||
$duration = (int) $lessonType['fixed_duration'] === 1
|
||||
? $baseDuration
|
||||
: $baseDuration * $units;
|
||||
$endAt = $startAt->modify(sprintf('+%d minutes', $duration));
|
||||
}
|
||||
|
||||
$title = $payload['title'] ?? $lessonType['name'] ?? 'Termin';
|
||||
$unitsBreakdown = [];
|
||||
|
||||
if ($lessonType && (int) $lessonType['is_counted'] === 1 && !empty($payload['student_id'])) {
|
||||
$requirementKey = $lessonType['effective_requirement_key'] ?? $lessonType['requirement_key'];
|
||||
if ($student !== null && (int) ($student['template_is_combination'] ?? 0) === 1) {
|
||||
$requirementKey = $this->requirementKeyForPhase($student['requirements'] ?? [], $requirementKey, (string) ($payload['requirement_phase'] ?? ''));
|
||||
}
|
||||
|
||||
$unitsBreakdown[] = [
|
||||
'requirement_key' => $requirementKey,
|
||||
'label' => $lessonType['name'],
|
||||
'units_counted' => $units,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'student_id' => $payload['student_id'] ?? null,
|
||||
'instructor_id' => (int) $payload['instructor_id'],
|
||||
'lesson_type_id' => (int) $payload['lesson_type_id'],
|
||||
'title' => $title,
|
||||
'category' => $lessonType['category'] ?? ($payload['category'] ?? 'student'),
|
||||
'start_at' => $startAt->format(DATE_ATOM),
|
||||
'end_at' => $endAt->format(DATE_ATOM),
|
||||
'units' => $units,
|
||||
'status' => $payload['status'] ?? 'planned',
|
||||
'notes' => $payload['notes'] ?? '',
|
||||
'notes_private' => $payload['notes_private'] ?? '',
|
||||
'warning_acknowledged' => !empty($payload['warning_acknowledged']),
|
||||
'units_breakdown' => $unitsBreakdown,
|
||||
];
|
||||
}
|
||||
|
||||
private function hasWarnings(array $warnings): bool
|
||||
{
|
||||
return !empty($warnings['conflicts']) || !empty($warnings['rest_period_warnings']);
|
||||
}
|
||||
|
||||
private function requirementKeyForPhase(array $requirements, string $baseRequirementKey, string $phaseLabel): string
|
||||
{
|
||||
$matchingRows = array_values(array_filter(
|
||||
$requirements,
|
||||
static fn (array $row): bool => str_ends_with((string) $row['requirement_key'], '::' . $baseRequirementKey)
|
||||
|| (string) $row['requirement_key'] === $baseRequirementKey
|
||||
));
|
||||
|
||||
if ($matchingRows === []) {
|
||||
return $baseRequirementKey;
|
||||
}
|
||||
|
||||
foreach ($matchingRows as $row) {
|
||||
if ((string) ($row['phase_label'] ?? '') === $phaseLabel) {
|
||||
return (string) $row['requirement_key'];
|
||||
}
|
||||
}
|
||||
|
||||
return (string) $matchingRows[0]['requirement_key'];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user