feat(absences) : mise en conformité légale (événements familiaux, demi-journée, CCN)
Périmètre 1-6 du design 2026-05-22-absence-legal-compliance-fixes (points lourds — ancienneté, CP pendant maladie, rétention — reportés en backlog). - Événements familiaux sans solde : AbsenceType::decrementsBalance() ne vaut true que pour les CP. Mariage/PACS, naissance, décès = droits par événement ; congé parental = suspension ; maladie = Sécu. Plus de solde fantôme. - Décès : daysPerEvent = null (selon lien de parenté) + motif obligatoire à la création (REST + MCP), les minimums légaux étant rappelés dans l'aide. - Ajout du congé naissance (type, policy 3 j, justificatif, libellés/couleur front). - Garde-fou demi-journée : -0,5 appliqué uniquement si le jour-borne est réellement décompté (corrige un sous-décompte week-end/férié) — TDD. - CCN documentée : paramètre app.absence.convention = "Syntec (IDCC 1486)", rappelée en sous-titre admin et dans l'aide /help. Tests : AbsenceDayCalculatorTest (garde-fou demi-journée), AbsenceRequestLifecycle (motif décès obligatoire + aucun solde touché). make test 52/52, build Nuxt OK. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,7 @@ use App\Repository\UserRepository;
|
||||
use App\Service\AbsenceBalanceService;
|
||||
use App\Service\AbsenceDayCalculator;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
|
||||
@@ -127,6 +128,42 @@ class AbsenceRequestLifecycleTest extends KernelTestCase
|
||||
self::assertSame(0.0, $balance->getPending());
|
||||
}
|
||||
|
||||
public function testBereavementRequiresReasonAndDoesNotTouchBalance(): void
|
||||
{
|
||||
// Ensure an active bereavement policy exists (no fixed entitlement).
|
||||
$policy = $this->em->getRepository(AbsencePolicy::class)->findOneBy(['type' => AbsenceType::Bereavement]);
|
||||
if (null === $policy) {
|
||||
$policy = new AbsencePolicy();
|
||||
$policy->setType(AbsenceType::Bereavement);
|
||||
$policy->setCountWorkingDaysOnly(true);
|
||||
$this->em->persist($policy);
|
||||
}
|
||||
$policy->setActive(true);
|
||||
$this->em->flush();
|
||||
|
||||
$tool = $this->createTool($this->admin);
|
||||
|
||||
// Without a reason → rejected.
|
||||
try {
|
||||
($tool)($this->employee->getId(), 'deces', '2026-06-10', '2026-06-12');
|
||||
self::fail('Expected an exception for bereavement without a reason.');
|
||||
} catch (InvalidArgumentException $e) {
|
||||
self::assertStringContainsString('reason', $e->getMessage());
|
||||
}
|
||||
|
||||
// With a reason → accepted, and no balance row is created (per-event right).
|
||||
$data = json_decode(
|
||||
($tool)($this->employee->getId(), 'deces', '2026-06-10', '2026-06-12', null, null, 'Décès grand-parent'),
|
||||
true,
|
||||
);
|
||||
self::assertSame('pending', $data['status']);
|
||||
|
||||
$balance = self::getContainer()->get(AbsenceBalanceRepository::class)
|
||||
->findOneForPeriod($this->employee, AbsenceType::Bereavement, '2026')
|
||||
;
|
||||
self::assertNull($balance, 'Bereavement must not create or touch any balance.');
|
||||
}
|
||||
|
||||
private function securityFor(User $user): Security
|
||||
{
|
||||
$security = $this->createMock(Security::class);
|
||||
|
||||
@@ -77,6 +77,29 @@ class AbsenceDayCalculatorTest extends TestCase
|
||||
));
|
||||
}
|
||||
|
||||
public function testHalfDayOnNonCountedStartIsIgnored(): void
|
||||
{
|
||||
// Sat 2026-06-06 → Mon 2026-06-08, jours ouvrés : Sat & Sun skipped, Mon = 1.
|
||||
// startHalfDay sits on Saturday (not counted) => must NOT subtract 0.5.
|
||||
self::assertSame(1.0, $this->calculator->countWorkingDays(
|
||||
new DateTimeImmutable('2026-06-06'),
|
||||
new DateTimeImmutable('2026-06-08'),
|
||||
HalfDay::Afternoon,
|
||||
));
|
||||
}
|
||||
|
||||
public function testHalfDayOnNonCountedEndIsIgnored(): void
|
||||
{
|
||||
// Fri 2026-06-05 → Sun 2026-06-07, jours ouvrés : Fri = 1, weekend skipped.
|
||||
// endHalfDay sits on Sunday (not counted) => must NOT subtract 0.5.
|
||||
self::assertSame(1.0, $this->calculator->countWorkingDays(
|
||||
new DateTimeImmutable('2026-06-05'),
|
||||
new DateTimeImmutable('2026-06-07'),
|
||||
null,
|
||||
HalfDay::Morning,
|
||||
));
|
||||
}
|
||||
|
||||
public function testWorkingDaysVsOpenDays(): void
|
||||
{
|
||||
// Fri 2026-06-05 to Mon 2026-06-08, "ouvrables" includes Saturday
|
||||
|
||||
Reference in New Issue
Block a user