Files
pta/app/Controllers/VacationController.php
Loic Masi 5fbf76868f pour prod
2026-08-07 16:13:43 +02:00

171 lines
6.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace App\Controllers;
use App\Core\Access;
use App\Core\Csrf;
use App\Core\JsonResponse;
use App\Core\Request;
use App\Models\VacationModel;
use App\Services\SchoolHolidayApiService;
use DateTimeImmutable;
use PDO;
use Throwable;
final class VacationController
{
private VacationModel $vacationModel;
private Access $access;
public function __construct(private PDO $pdo)
{
$this->vacationModel = new VacationModel($pdo);
$this->access = new Access($pdo);
}
public function index(): void
{
$this->access->requireService();
$schoolYear = trim((string) ($_GET['school_year'] ?? ''));
if ($schoolYear !== '' && !$this->isSchoolYear($schoolYear)) {
JsonResponse::send(422, ['error' => 'Année scolaire invalide. Format attendu : 2026-2027.']);
}
JsonResponse::send(200, [
'periods' => $this->vacationModel->all($schoolYear !== '' ? $schoolYear : null),
]);
}
public function preview(): void
{
$this->access->requireService();
$academy = trim((string) ($_GET['academy'] ?? ''));
$calendarYearRaw = trim((string) ($_GET['year'] ?? ''));
if ($academy === '' || (function_exists('mb_strlen') ? mb_strlen($academy, 'UTF-8') : strlen($academy)) > 100) {
JsonResponse::send(422, ['error' => 'Renseignez une académie valide.']);
}
if (!preg_match('/^\d{4}$/', $calendarYearRaw)) {
JsonResponse::send(422, ['error' => 'Année civile invalide. Format attendu : 2026.']);
}
$calendarYear = (int) $calendarYearRaw;
if ($calendarYear < 2000 || $calendarYear > 2100) {
JsonResponse::send(422, ['error' => 'Année civile invalide.']);
}
try {
$result = (new SchoolHolidayApiService())->previewCalendarYearDetailed($academy, $calendarYear);
$periods = $result['periods'];
$warnings = $result['warnings'];
} catch (Throwable $e) {
JsonResponse::send(502, [
'error' => 'Impossible de charger les propositions depuis lAPI officielle.',
'details' => (getenv('PTA_DEBUG') ?: '1') !== '0' ? $e->getMessage() : null,
]);
}
JsonResponse::send(200, [
'academy' => $academy,
'year' => $calendarYear,
'periods' => $periods,
'warnings' => $warnings ?? [],
'message' => $periods === []
? 'Aucune période na été trouvée pour lannée ' . $calendarYear . '.'
: sprintf('%d période%s proposée%s pour toute lannée %d. Aucune donnée na été enregistrée en base.', count($periods), count($periods) > 1 ? 's' : '', count($periods) > 1 ? 's' : '', $calendarYear),
]);
}
public function save(): void
{
$this->access->requireService();
Request::requireMethod('POST');
$payload = Request::json();
Csrf::assertPayload($payload);
$rawPeriods = $payload['periods'] ?? [];
if (!is_array($rawPeriods) || $rawPeriods === []) {
JsonResponse::send(422, ['error' => 'Sélectionnez au moins une période à enregistrer.']);
}
if (count($rawPeriods) > 50) {
JsonResponse::send(422, ['error' => 'Trop de périodes ont été envoyées en une seule fois.']);
}
$periods = [];
foreach ($rawPeriods as $index => $raw) {
if (!is_array($raw)) {
JsonResponse::send(422, ['error' => 'Période invalide à la ligne ' . ($index + 1) . '.']);
}
$label = trim((string) ($raw['libelle'] ?? ''));
$start = trim((string) ($raw['date_debut'] ?? ''));
$end = trim((string) ($raw['date_fin'] ?? ''));
$schoolYear = trim((string) ($raw['annee_scolaire'] ?? ''));
$academy = trim((string) ($raw['academie'] ?? ''));
$zone = trim((string) ($raw['zone'] ?? ''));
$source = strtoupper(trim((string) ($raw['source'] ?? 'MANUEL')));
if ($label === '' || (function_exists('mb_strlen') ? mb_strlen($label, 'UTF-8') : strlen($label)) > 150 || !$this->isDate($start) || !$this->isDate($end) || $end < $start) {
JsonResponse::send(422, ['error' => 'Dates ou libellé invalides à la ligne ' . ($index + 1) . '.']);
}
if (!$this->isSchoolYear($schoolYear)) {
JsonResponse::send(422, ['error' => 'Année scolaire invalide à la ligne ' . ($index + 1) . '.']);
}
if (!in_array($source, ['MANUEL', 'DATA_GOUV'], true)) {
$source = 'MANUEL';
}
$periods[] = [
'libelle' => $label,
'date_debut' => $start,
'date_fin' => $end,
'annee_scolaire' => $schoolYear,
'academie' => function_exists('mb_substr') ? mb_substr($academy, 0, 100, 'UTF-8') : substr($academy, 0, 100),
'zone' => function_exists('mb_substr') ? mb_substr($zone, 0, 50, 'UTF-8') : substr($zone, 0, 50),
'source' => $source,
];
}
$count = $this->vacationModel->saveMany($periods);
JsonResponse::send(200, [
'success' => true,
'count' => $count,
'message' => sprintf('%d période%s de vacances enregistrée%s après validation manuelle.', $count, $count > 1 ? 's' : '', $count > 1 ? 's' : ''),
]);
}
public function delete(): void
{
$this->access->requireService();
Request::requireMethod('POST');
$payload = Request::json();
Csrf::assertPayload($payload);
$id = filter_var($payload['period_id'] ?? null, FILTER_VALIDATE_INT);
if (!$id) {
JsonResponse::send(422, ['error' => 'Période invalide.']);
}
if (!$this->vacationModel->delete((int) $id)) {
JsonResponse::send(404, ['error' => 'Période introuvable.']);
}
JsonResponse::send(200, ['success' => true, 'message' => 'Période supprimée.']);
}
private function isDate(string $value): bool
{
$date = DateTimeImmutable::createFromFormat('!Y-m-d', $value);
return $date !== false && $date->format('Y-m-d') === $value;
}
private function isSchoolYear(string $value): bool
{
if (!preg_match('/^(\d{4})-(\d{4})$/', $value, $matches)) {
return false;
}
return (int) $matches[2] === (int) $matches[1] + 1;
}
}