update
This commit is contained in:
@@ -6,6 +6,7 @@ namespace App\Controllers;
|
||||
|
||||
use App\Core\Access;
|
||||
use App\Core\Csrf;
|
||||
use App\Core\Journal;
|
||||
use App\Core\JsonResponse;
|
||||
use App\Core\Request;
|
||||
use App\Core\Security;
|
||||
@@ -200,6 +201,14 @@ final class WeekTemplateController
|
||||
]);
|
||||
}
|
||||
|
||||
Journal::ecriture(
|
||||
'SEMAINE_TYPE_ENREGISTREE',
|
||||
'modele_semaine_agent',
|
||||
(int) $template['id_modele'],
|
||||
sprintf('Semaine type « %s » enregistrée depuis l’éditeur pour l’agent #%d.', (string) $template['nom'], (int) $agentId),
|
||||
['id_agent' => (int) $agentId, 'nombre_creneaux' => (int) $template['nombre_creneaux'], 'remplacement' => $replace]
|
||||
);
|
||||
|
||||
JsonResponse::send(200, [
|
||||
'success' => true,
|
||||
'template' => [
|
||||
@@ -259,6 +268,14 @@ final class WeekTemplateController
|
||||
]);
|
||||
}
|
||||
|
||||
Journal::ecriture(
|
||||
'SEMAINE_TYPE_ENREGISTREE',
|
||||
'modele_semaine_agent',
|
||||
isset($template['id_modele']) ? (int) $template['id_modele'] : null,
|
||||
sprintf('Semaine type « %s » enregistrée depuis une semaine planifiée pour l’agent #%d.', $name, (int) $agentId),
|
||||
['id_agent' => (int) $agentId, 'nombre_creneaux' => (int) $template['nombre_creneaux'], 'remplacement' => $replace]
|
||||
);
|
||||
|
||||
JsonResponse::send(200, [
|
||||
'success' => true,
|
||||
'template' => $template,
|
||||
@@ -290,12 +307,18 @@ final class WeekTemplateController
|
||||
if (!$templateId || !$agentId || $week === null || !in_array($mode, ['merge', 'replace'], true)) {
|
||||
JsonResponse::send(422, ['error' => 'Semaine type, agent, semaine ou mode d’import invalide.']);
|
||||
}
|
||||
if ($this->agentModel->findActive((int) $agentId) === null) {
|
||||
$agent = $this->agentModel->findActive((int) $agentId);
|
||||
if ($agent === null) {
|
||||
JsonResponse::send(404, ['error' => 'Agent introuvable ou inactif.']);
|
||||
}
|
||||
[$year, $weekNumber] = $week;
|
||||
$lundi = (new DateTimeImmutable())->setISODate($year, $weekNumber, 1);
|
||||
$this->assertDatesWithinContract($agent, array_map(
|
||||
static fn(int $offset): string => $lundi->modify('+' . $offset . ' day')->format('Y-m-d'),
|
||||
range(0, 4)
|
||||
));
|
||||
|
||||
try {
|
||||
[$year, $weekNumber] = $week;
|
||||
$result = $this->templateModel->applyToWeek((int) $templateId, (int) $agentId, $year, $weekNumber, $mode);
|
||||
} catch (DomainException $e) {
|
||||
JsonResponse::send(409, ['error' => $e->getMessage()]);
|
||||
@@ -318,6 +341,27 @@ final class WeekTemplateController
|
||||
$message .= ' Les plannings validés modifiés ont été repassés en brouillon.';
|
||||
}
|
||||
|
||||
Journal::ecriture(
|
||||
'SEMAINE_TYPE_APPLIQUEE',
|
||||
'planning',
|
||||
null,
|
||||
sprintf(
|
||||
'Semaine type « %s » appliquée à l’agent #%d sur la semaine %d de %d : %d créneau(x).',
|
||||
(string) $result['template']['nom'],
|
||||
(int) $agentId,
|
||||
$weekNumber,
|
||||
$year,
|
||||
(int) $result['inserted']
|
||||
),
|
||||
[
|
||||
'id_agent' => (int) $agentId,
|
||||
'id_modele' => (int) $templateId,
|
||||
'annee' => $year,
|
||||
'semaine' => $weekNumber,
|
||||
'mode' => $mode,
|
||||
]
|
||||
);
|
||||
|
||||
JsonResponse::send(200, [
|
||||
'success' => true,
|
||||
'message' => $message,
|
||||
@@ -368,9 +412,11 @@ final class WeekTemplateController
|
||||
) {
|
||||
JsonResponse::send(422, ['error' => 'Semaine type, agent, période, filtre de calendrier ou mode d’application invalide.']);
|
||||
}
|
||||
if ($this->agentModel->findActive((int) $agentId) === null) {
|
||||
$agent = $this->agentModel->findActive((int) $agentId);
|
||||
if ($agent === null) {
|
||||
JsonResponse::send(404, ['error' => 'Agent introuvable ou inactif.']);
|
||||
}
|
||||
$this->assertPeriodWithinContract($agent, $startDate, $endDate);
|
||||
|
||||
try {
|
||||
$preview = $this->templateModel->previewPeriod(
|
||||
@@ -435,9 +481,11 @@ final class WeekTemplateController
|
||||
) {
|
||||
JsonResponse::send(422, ['error' => 'Semaine type, agent, période, filtre de calendrier ou mode d’application invalide.']);
|
||||
}
|
||||
if ($this->agentModel->findActive((int) $agentId) === null) {
|
||||
$agent = $this->agentModel->findActive((int) $agentId);
|
||||
if ($agent === null) {
|
||||
JsonResponse::send(404, ['error' => 'Agent introuvable ou inactif.']);
|
||||
}
|
||||
$this->assertPeriodWithinContract($agent, $startDate, $endDate);
|
||||
|
||||
try {
|
||||
$result = $this->templateModel->applyToPeriod(
|
||||
@@ -499,6 +547,27 @@ final class WeekTemplateController
|
||||
$quotas[(string) $quota['date_debut_periode']] = $quota;
|
||||
}
|
||||
|
||||
Journal::ecriture(
|
||||
'SEMAINE_TYPE_APPLIQUEE_PERIODE',
|
||||
'planning',
|
||||
null,
|
||||
sprintf(
|
||||
'Semaine type #%d appliquée à l’agent #%d du %s au %s.',
|
||||
(int) $templateId,
|
||||
(int) $agentId,
|
||||
$startDate,
|
||||
$endDate
|
||||
),
|
||||
[
|
||||
'id_agent' => (int) $agentId,
|
||||
'id_modele' => (int) $templateId,
|
||||
'date_debut' => $startDate,
|
||||
'date_fin' => $endDate,
|
||||
'mode' => $mode,
|
||||
'filtre_periode' => $periodFilter,
|
||||
]
|
||||
);
|
||||
|
||||
JsonResponse::send(200, [
|
||||
'success' => true,
|
||||
'message' => $message,
|
||||
@@ -533,6 +602,14 @@ final class WeekTemplateController
|
||||
JsonResponse::send(404, ['error' => 'Semaine type introuvable.']);
|
||||
}
|
||||
|
||||
Journal::ecriture(
|
||||
'SEMAINE_TYPE_SUPPRIMEE',
|
||||
'modele_semaine_agent',
|
||||
(int) $templateId,
|
||||
sprintf('Semaine type #%d supprimée pour l’agent #%d.', (int) $templateId, (int) $agentId),
|
||||
['id_agent' => (int) $agentId]
|
||||
);
|
||||
|
||||
JsonResponse::send(200, ['success' => true, 'message' => 'Semaine type supprimée.']);
|
||||
}
|
||||
|
||||
@@ -541,6 +618,51 @@ final class WeekTemplateController
|
||||
*
|
||||
* @return array{0: int, 1: int}|null [année ISO, semaine], ou null.
|
||||
*/
|
||||
/**
|
||||
* Refuse toute affectation hors des dates de contrat de l'agent (V7.15).
|
||||
*
|
||||
* Copie assumée de PlanningController::assertDatesWithinContract() : les deux
|
||||
* contrôleurs n'ont pas de base commune, et la règle doit s'appliquer des
|
||||
* deux côtés. Le MESSAGE, lui, est construit une seule fois par AgentModel —
|
||||
* c'est lui qui doit rester unique, pas la garde.
|
||||
*
|
||||
* @param array<string, mixed> $agent
|
||||
* @param array<int, string> $dates
|
||||
*/
|
||||
private function assertDatesWithinContract(array $agent, array $dates): void
|
||||
{
|
||||
$hors = AgentModel::datesOutsideContract($agent, $dates);
|
||||
if ($hors === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
JsonResponse::send(422, [
|
||||
'error' => AgentModel::contractViolationMessage($agent, $hors),
|
||||
'code' => 'HORS_CONTRAT',
|
||||
'dates' => $hors,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Même règle, pour une PÉRIODE entière.
|
||||
*
|
||||
* Seules les bornes sont contrôlées, et c'est suffisant : le contrat est un
|
||||
* intervalle continu, donc une période dont les deux extrémités y tombent y
|
||||
* est entièrement contenue. Contrôler chacun des jours d'une duplication
|
||||
* annuelle coûterait 365 comparaisons pour la même réponse.
|
||||
*
|
||||
* Le refus porte volontairement sur la période DEMANDÉE et non sur les seuls
|
||||
* jours réellement écrits : personne ne doit avoir à deviner qu'une
|
||||
* duplication « du 1er janvier au 31 décembre » n'a rien produit en novembre
|
||||
* parce que le contrat s'arrêtait en octobre.
|
||||
*
|
||||
* @param array<string, mixed> $agent
|
||||
*/
|
||||
private function assertPeriodWithinContract(array $agent, string $startDate, string $endDate): void
|
||||
{
|
||||
$this->assertDatesWithinContract($agent, [$startDate, $endDate]);
|
||||
}
|
||||
|
||||
private function parseWeek(string $value): ?array
|
||||
{
|
||||
if (!preg_match('/^(\d{4})-W(\d{2})$/', $value, $matches)) {
|
||||
|
||||
Reference in New Issue
Block a user