fix(absences) : garde-fou solde négatif à l'approbation + cohérence fixture
- AbsenceBalanceService::availableForRequest() : jours disponibles (acquis N-1 + en cours N − pris) pour la période de la demande, null si type non suivi. - Blocage de l'approbation si countedDays > disponible, dans les deux chemins (REST AbsenceReviewProcessor + MCP ReviewAbsenceRequestTool), comme le motif décès. Les CP en cours d'acquisition restent posables, mais pas au-delà du droit total (plus de solde négatif silencieux à l'approbation). - Fixture : demande pending CP d'alice replacée dans sa période de référence 2025-2026 (26→29/05/2026, 4 j ouvrés) et solde pending aligné (5 → 4) ; plus de "en attente" orphelin non lié à une demande. - Test fonctionnel testApproveBeyondAvailableBalanceIsBlocked + employé de test doté d'un droit pour que les approbations existantes passent le garde-fou. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Functional\Mcp;
|
||||
|
||||
use App\Entity\AbsenceBalance;
|
||||
use App\Entity\AbsencePolicy;
|
||||
use App\Entity\User;
|
||||
use App\Enum\AbsenceType;
|
||||
@@ -62,6 +63,15 @@ class AbsenceRequestLifecycleTest extends KernelTestCase
|
||||
$policy->setActive(true);
|
||||
}
|
||||
|
||||
// Entitlement so CP requests can be approved without breaching the
|
||||
// no-negative-balance guard (period of the June 2026 test requests).
|
||||
$balance = new AbsenceBalance();
|
||||
$balance->setUser($this->employee);
|
||||
$balance->setType(AbsenceType::PaidLeave);
|
||||
$balance->setPeriod('2026-2027');
|
||||
$balance->setAcquired(25.0);
|
||||
$this->em->persist($balance);
|
||||
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
@@ -110,6 +120,33 @@ class AbsenceRequestLifecycleTest extends KernelTestCase
|
||||
self::assertSame(5.0, $balance->getTaken());
|
||||
}
|
||||
|
||||
public function testApproveBeyondAvailableBalanceIsBlocked(): void
|
||||
{
|
||||
$created = json_decode(
|
||||
($this->createTool($this->admin))($this->employee->getId(), 'cp', '2026-06-01', '2026-06-05'),
|
||||
true,
|
||||
);
|
||||
|
||||
// Shrink the entitlement below the 5 requested days.
|
||||
$balance = self::getContainer()->get(AbsenceBalanceRepository::class)
|
||||
->findOneForPeriod($this->employee, AbsenceType::PaidLeave, '2026-2027')
|
||||
;
|
||||
$balance->setAcquired(2.0);
|
||||
$balance->setAcquiring(0.0);
|
||||
$this->em->flush();
|
||||
|
||||
try {
|
||||
($this->reviewTool($this->admin))($created['id'], 'approve');
|
||||
self::fail('Expected approval to be blocked when it would breach the balance.');
|
||||
} catch (InvalidArgumentException $e) {
|
||||
self::assertStringContainsString('below zero', $e->getMessage());
|
||||
}
|
||||
|
||||
// Approval bailed out before mutating: nothing moved to taken, days stay reserved.
|
||||
self::assertSame(0.0, $balance->getTaken());
|
||||
self::assertSame(5.0, $balance->getPending());
|
||||
}
|
||||
|
||||
public function testAdminCancelApprovedReleasesTaken(): void
|
||||
{
|
||||
$created = json_decode(
|
||||
|
||||
Reference in New Issue
Block a user