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

226 lines
8.9 KiB
PHP
Raw Permalink 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\PdfResponse;
use App\Core\Request;
use App\Models\CoverageModel;
use App\Models\QuotaModel;
use App\Models\StructureModel;
use App\Services\PlanningPdfService;
use DateTimeImmutable;
use PDO;
use Throwable;
final class StructureController
{
private StructureModel $structureModel;
private QuotaModel $quotaModel;
private CoverageModel $coverageModel;
private Access $access;
public function __construct(private PDO $pdo)
{
$this->structureModel = new StructureModel($pdo);
$this->quotaModel = new QuotaModel($pdo);
$this->coverageModel = new CoverageModel($pdo);
$this->access = new Access($pdo);
}
public function overview(): void
{
$structureId = filter_input(INPUT_GET, 'structure_id', FILTER_VALIDATE_INT);
$weekValue = trim((string) ($_GET['week'] ?? ''));
$week = $this->parseWeek($weekValue);
if (!$structureId || $week === null) {
JsonResponse::send(400, ['error' => 'Lieu daffectation ou semaine invalide.']);
}
$this->access->requireStructureView((int) $structureId);
$structure = $this->structureModel->findActive((int) $structureId);
if ($structure === null) {
JsonResponse::send(404, ['error' => 'Lieu daffectation introuvable.']);
}
[$year, $weekNumber] = $week;
$weekStart = (new DateTimeImmutable())->setISODate($year, $weekNumber, 1)->format('Y-m-d');
$weekEnd = (new DateTimeImmutable())->setISODate($year, $weekNumber, 7)->format('Y-m-d');
$entries = $this->structureModel->entriesForOverview((int) $structureId, $year, $weekNumber);
$entriesByAgent = [];
foreach ($entries as $entry) {
$entriesByAgent[(int) $entry['id_agent']][] = $entry;
}
$agents = [];
foreach ($this->structureModel->agentsForOverview((int) $structureId, $year, $weekNumber) as $agent) {
$agentId = (int) $agent['id_agent'];
$agents[] = [
'id_agent' => $agentId,
'matricule' => $agent['matricule'],
'nom' => $agent['nom'],
'prenom' => $agent['prenom'],
'lieu_principal' => (int) ($agent['id_structure_principale'] ?? 0) === (int) $structureId,
'quota' => $this->quotaModel->forDateForApi($agentId, $weekStart),
'entries' => $entriesByAgent[$agentId] ?? [],
];
}
JsonResponse::send(200, [
'structure' => $structure,
'week' => ['value' => $weekValue, 'date_debut' => $weekStart, 'date_fin' => $weekEnd],
'agents' => $agents,
'coverage' => $this->coverageModel->reportForStructure((int) $structureId, $year, $weekNumber),
]);
}
public function pdf(): void
{
$structureId = filter_input(INPUT_GET, 'structure_id', FILTER_VALIDATE_INT);
$weekValue = trim((string) ($_GET['week'] ?? ''));
$week = $this->parseWeek($weekValue);
if (!$structureId || $week === null) {
http_response_code(400);
echo 'Lieu daffectation ou semaine invalide.';
return;
}
$this->access->requireStructureView((int) $structureId);
$structure = $this->structureModel->findActive((int) $structureId);
if ($structure === null) {
http_response_code(404);
echo 'Lieu daffectation introuvable.';
return;
}
[$year, $weekNumber] = $week;
$weekStart = (new DateTimeImmutable())->setISODate($year, $weekNumber, 1)->format('Y-m-d');
$weekEnd = (new DateTimeImmutable())->setISODate($year, $weekNumber, 7)->format('Y-m-d');
$weekData = ['value' => $weekValue, 'date_debut' => $weekStart, 'date_fin' => $weekEnd];
$entries = $this->structureModel->entriesForOverview((int) $structureId, $year, $weekNumber);
$entriesByAgent = [];
foreach ($entries as $entry) {
$entriesByAgent[(int) $entry['id_agent']][] = $entry;
}
$agents = [];
foreach ($this->structureModel->agentsForOverview((int) $structureId, $year, $weekNumber) as $agent) {
$agentId = (int) $agent['id_agent'];
$agents[] = [
'id_agent' => $agentId,
'matricule' => $agent['matricule'],
'nom' => $agent['nom'],
'prenom' => $agent['prenom'],
'lieu_principal' => (int) ($agent['id_structure_principale'] ?? 0) === (int) $structureId,
'quota' => $this->quotaModel->forDateForApi($agentId, $weekStart),
'entries' => $entriesByAgent[$agentId] ?? [],
];
}
$pdf = (new PlanningPdfService())->structureWeek($structure, $agents, $weekData);
PdfResponse::inline(
$pdf,
sprintf('planning_lieu_%s_%s.pdf', $structure['code'] ?: $structure['id_structure'], str_replace('-W', '_S', $weekValue))
);
}
public function create(): void
{
$this->access->requireService();
Request::requireMethod('POST');
$payload = Request::json();
Csrf::assertPayload($payload);
$rawCode = trim((string) ($payload['code'] ?? ''));
$code = strtoupper((string) preg_replace('/\s+/', '_', $rawCode));
$name = trim((string) ($payload['nom'] ?? ''));
$address = trim((string) ($payload['adresse'] ?? ''));
$typeAffectation = strtoupper(trim((string) ($payload['type_affectation'] ?? '')));
if ($code === '' || $name === '') {
JsonResponse::send(422, ['error' => 'Le code et le nom du lieu daffectation sont obligatoires.']);
}
if (!in_array($typeAffectation, ['PERISCOLAIRE', 'EXTRASCOLAIRE'], true)) {
JsonResponse::send(422, ['error' => 'Le type du lieu doit être Périscolaire ou Extrascolaire.']);
}
if (!preg_match('/^[A-Z0-9_-]{2,50}$/', $code)) {
JsonResponse::send(422, ['error' => 'Le code doit contenir uniquement des lettres, chiffres, tirets ou underscores.']);
}
if (strlen($name) > 150 || strlen($address) > 255) {
JsonResponse::send(422, ['error' => 'Un des champs dépasse la longueur autorisée.']);
}
if ($this->structureModel->codeExists($code)) {
JsonResponse::send(409, ['error' => 'Un lieu daffectation utilise déjà ce code.']);
}
try {
$structure = $this->structureModel->create($code, $name, $address !== '' ? $address : null, $typeAffectation);
} catch (Throwable $e) {
JsonResponse::send(500, [
'error' => 'Impossible de créer le lieu daffectation.',
'details' => (getenv('PTA_DEBUG') ?: '1') !== '0' ? $e->getMessage() : null,
]);
}
JsonResponse::send(201, [
'message' => sprintf('Le lieu daffectation « %s » a été ajouté.', $name),
'structure' => $structure,
]);
}
public function updateType(): void
{
$this->access->requireService();
Request::requireMethod('POST');
$payload = Request::json();
Csrf::assertPayload($payload);
$structureId = filter_var($payload['structure_id'] ?? null, FILTER_VALIDATE_INT);
$typeAffectation = strtoupper(trim((string) ($payload['type_affectation'] ?? '')));
if (!$structureId) {
JsonResponse::send(422, ['error' => 'Lieu daffectation invalide.']);
}
if (!in_array($typeAffectation, ['PERISCOLAIRE', 'EXTRASCOLAIRE'], true)) {
JsonResponse::send(422, ['error' => 'Le type du lieu doit être Périscolaire ou Extrascolaire.']);
}
$structure = $this->structureModel->findActive((int) $structureId);
if ($structure === null) {
JsonResponse::send(404, ['error' => 'Lieu daffectation introuvable.']);
}
try {
$this->structureModel->updateType((int) $structureId, $typeAffectation);
} catch (Throwable $e) {
JsonResponse::send(500, [
'error' => 'Impossible de modifier le type du lieu daffectation.',
'details' => (getenv('PTA_DEBUG') ?: '1') !== '0' ? $e->getMessage() : null,
]);
}
JsonResponse::send(200, [
'message' => sprintf('Le type du lieu « %s » a été mis à jour.', $structure['nom']),
'structure' => [
'id_structure' => (int) $structureId,
'type_affectation' => $typeAffectation,
],
]);
}
private function parseWeek(string $value): ?array
{
if (!preg_match('/^(\d{4})-W(\d{2})$/', $value, $matches)) {
return null;
}
$week = (int) $matches[2];
return $week >= 1 && $week <= 53 ? [(int) $matches[1], $week] : null;
}
}