Files
SIRH/docs/superpowers/plans/2026-05-26-en-cours-acquisition-net-brut.md
T
tristan cf2e12c8ba
Auto Tag Develop / tag (push) Successful in 9s
[#SIRH-32] Ajouter l'exercice 2026/2027 dans les congés/RTT (#20)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|                  |                 |

## Description de la PR

## Modification du .env

## Check list

- [x] Pas de régression
- [x] TU/TI/TF rédigée
- [x] TU/TI/TF OK
- [ ] CHANGELOG modifié

Reviewed-on: #20
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-05-26 14:09:02 +00:00

12 KiB

En-cours d'acquisition « net / brut » — Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Sur l'onglet Congés de la fiche employé, afficher l'en-cours d'acquisition au format {net} / {brut généré à ce jour} pour les non-forfait, afin que la RH voie le total acquis même quand des congés ont été pris en anticipé.

Architecture : Exposition d'une valeur déjà calculée côté backend (generatedDays + generatedSaturdays) via un nouveau champ accruingDaysTotal sur EmployeeLeaveSummary, puis affichage en fraction côté Nuxt. Aucune nouvelle règle métier ; accruingDays (net) reste le numérateur inchangé.

Tech Stack : Backend Symfony / API Platform (State Provider + ApiResource DTO). Frontend Nuxt 4 / Vue 3 / TypeScript. Tests : PHPUnit (backend) ; pas de harnais frontend → vérification manuelle.

Référence spec : docs/superpowers/specs/2026-05-26-en-cours-acquisition-net-brut-design.md


File Structure

  • src/State/EmployeeLeaveSummaryProvider.php — calcule accruingDaysTotal dans computeYearSummary et le recopie sur le DTO.
  • src/ApiResource/EmployeeLeaveSummary.php — nouvelle propriété exposée accruingDaysTotal.
  • frontend/services/dto/employee-leave-summary.ts — champ TS accruingDaysTotal.
  • frontend/components/employees/LeaveTab.vue — affichage net / brut (non-forfait).
  • doc/leave-tab.md + frontend/data/documentation-content.ts — documentation.

Aucun fichier créé ; 6 fichiers modifiés.


Task 1 : Backend — exposer accruingDaysTotal

Files:

  • Modify: src/State/EmployeeLeaveSummaryProvider.php

  • Modify: src/ApiResource/EmployeeLeaveSummary.php

  • Step 1 : Calculer accruingDaysTotal dans les deux branches de computeYearSummary

Dans src/State/EmployeeLeaveSummaryProvider.php, branche non-forfait, remplacer :

                $acquiredDays       = $carryDays;
                $accruingDays       = $remainingGenerated + $remainingGeneratedSaturdays;

par :

                $acquiredDays       = $carryDays;
                $accruingDays       = $remainingGenerated + $remainingGeneratedSaturdays;
                // Brut généré à ce jour, AVANT imputation des congés pris en anticipé
                // (dénominateur de l'affichage « net / brut » sur l'onglet Congés).
                $accruingDaysTotal  = $generatedDays + $generatedSaturdays;

Puis, branche forfait, remplacer :

                $acquiredDays       = $leavePolicy['acquiredDays'];
                $accruingDays       = 0.0;

par :

                $acquiredDays       = $leavePolicy['acquiredDays'];
                $accruingDays       = 0.0;
                $accruingDaysTotal  = 0.0;
  • Step 2 : Ajouter la clé au tableau targetSummary

Toujours dans computeYearSummary, remplacer :

                    'accruingDays'              => $accruingDays,

par :

                    'accruingDays'              => $accruingDays,
                    'accruingDaysTotal'         => $accruingDaysTotal,
  • Step 3 : Déclarer la clé dans le PHPDoc de retour

Dans le bloc @return null|array{ ... } de computeYearSummary, remplacer :

     *   accruingDays: float,

par :

     *   accruingDays: float,
     *   accruingDaysTotal: float,
  • Step 4 : Recopier la valeur sur le DTO dans provide()

Remplacer :

        $summary->accruingDays              = $yearSummary['accruingDays'];

par :

        $summary->accruingDays              = $yearSummary['accruingDays'];
        $summary->accruingDaysTotal         = $yearSummary['accruingDaysTotal'];
  • Step 5 : Ajouter la propriété sur l'ApiResource

Dans src/ApiResource/EmployeeLeaveSummary.php, remplacer :

    public float $accruingDays              = 0.0;

par :

    public float $accruingDays              = 0.0;
    /** Brut généré sur l'exercice à ce jour (= accruingDays + congés pris en anticipé). Dénominateur de l'affichage « net / brut ». */
    public float $accruingDaysTotal         = 0.0;
  • Step 6 : Lancer la suite PHPUnit (non-régression)

Run: docker exec -t -u www-data php-sirh-fpm php vendor/bin/phpunit Expected: OK (151 tests, ...) — vert. (Le champ est une exposition pure ; aucun test existant ne doit casser. Le service n'est pas unit-testable en isolation à cause des dépôts final, cf. note spec.)

  • Step 7 : Vérification sur données réelles (jetable, non commitée)

Créer src/Command/TmpVerifyAccruingCommand.php :

<?php

declare(strict_types=1);

namespace App\Command;

use App\Entity\Employee;
use App\State\EmployeeLeaveSummaryProvider;
use Doctrine\ORM\EntityManagerInterface;
use ReflectionMethod;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(name: 'app:tmp-verify-accruing')]
final class TmpVerifyAccruingCommand extends Command
{
    public function __construct(
        private readonly EntityManagerInterface $em,
        private readonly EmployeeLeaveSummaryProvider $provider,
    ) {
        parent::__construct();
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $m = new ReflectionMethod(EmployeeLeaveSummaryProvider::class, 'computeYearSummary');
        foreach ($this->em->getRepository(Employee::class)->findAll() as $e) {
            $s = $m->invoke($this->provider, $e, 2026, 0.0, null, null);
            if (null === $s || 'CDI_CDD_NON_FORFAIT' !== $s['ruleCode']) {
                continue;
            }
            $output->writeln(sprintf(
                '#%d %s : en-cours net=%.2f / brut=%.2f',
                $e->getId(),
                $e->getLastName(),
                $s['accruingDays'],
                $s['accruingDaysTotal'],
            ));
        }

        return Command::SUCCESS;
    }
}

Run: docker exec -t php-sirh-fpm php /var/www/html/bin/console app:tmp-verify-accruing --env=dev Expected: chaque ligne affiche net=… / brut=… avec net ≤ brut. Pour un salarié sans congé anticipé, net == brut ; pour un salarié ayant débordé, net < brut.

Puis supprimer le fichier :

rm src/Command/TmpVerifyAccruingCommand.php
  • Step 8 : Commit
git add src/State/EmployeeLeaveSummaryProvider.php src/ApiResource/EmployeeLeaveSummary.php
git commit -m "feat : exposer accruingDaysTotal (brut généré) sur le récap congés

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"

Task 2 : Frontend — afficher « net / brut »

Files:

  • Modify: frontend/services/dto/employee-leave-summary.ts

  • Modify: frontend/components/employees/LeaveTab.vue

  • Step 1 : Ajouter le champ au DTO TypeScript

Dans frontend/services/dto/employee-leave-summary.ts, remplacer :

  accruingDays: number

par :

  accruingDays: number
  accruingDaysTotal: number
  • Step 2 : Afficher la fraction dans la case « En cours d'acquisition »

Dans frontend/components/employees/LeaveTab.vue, remplacer :

            <p class="col-start-4 p-[10px] border-b border-primary-500"><strong class="uppercase font-semibold">En cours d'acquisition :</strong>
                {{ formatCount(summary?.accruingDays) }} Jours
            </p>

par :

            <p class="col-start-4 p-[10px] border-b border-primary-500"><strong class="uppercase font-semibold">En cours d'acquisition :</strong>
                <template v-if="!isForfaitRule">{{ formatCount(summary?.accruingDays) }} / {{ formatCount(summary?.accruingDaysTotal) }} Jours</template>
                <template v-else>{{ formatCount(summary?.accruingDays) }} Jours</template>
            </p>
  • Step 3 : Vérification manuelle (dev server)

Run: make dev-nuxt puis ouvrir la fiche d'un employé non-forfait. Attendu :

  • La case « En cours d'acquisition » affiche deux nombres séparés par / (ex. 14,50 / 17,50 ou 17,50 / 17,50 si aucun congé anticipé).
  • Sur un employé forfait, la case affiche un seul nombre (0), inchangé.

(Ne pas lancer npm run build.)

  • Step 4 : Commit
git add frontend/services/dto/employee-leave-summary.ts frontend/components/employees/LeaveTab.vue
git commit -m "feat : afficher l'en-cours d'acquisition au format net / brut (onglet Congés)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"

Task 3 : Documentation

Files:

  • Modify: doc/leave-tab.md

  • Modify: frontend/data/documentation-content.ts

  • Step 1 : doc/leave-tab.md

Repérer la section décrivant les compteurs du header (recherche : grep -n "acquisition\|En cours\|compteur" doc/leave-tab.md). Ajouter (ou compléter la puce correspondante) avec ce texte :

- **En cours d'acquisition** (non-forfait) : affiché au format `net / brut`.
  - `net` (`accruingDays`) : généré de l'exercice restant, déduit des congés posés en anticipé (au-delà du report acquis).
  - `brut` (`accruingDaysTotal` = `generatedDays + generatedSaturdays`) : total généré sur l'exercice à ce jour, avant cette déduction.
  - La RH voit ainsi le total réellement acquis même si une partie a déjà été consommée en anticipé. Forfait : pas d'en-cours (affiche `0`, sans fraction).
  • Step 2 : frontend/data/documentation-content.ts

Repérer le paragraphe de l'article « Onglet Congés » décrivant les compteurs (recherche : grep -n "en cours d.acquisition\|En cours\|acquis" frontend/data/documentation-content.ts). Ajouter un bloc note dans le tableau blocks de cet article :

          { type: 'note', content: 'La case « En cours d\'acquisition » affiche deux valeurs : à gauche les jours encore à acquérir (déduction faite des congés déjà posés en anticipé), à droite le total brut acquis sur l\'exercice à ce jour. Exemple : « 14,50 / 17,50 » signifie 17,50 jours acquis dont 3 déjà pris en anticipé.' },

Insérer ce bloc juste après le paragraphe qui présente les compteurs de l'exercice de congés (celui mentionnant l'exercice Juin→Mai / les jours acquis). Respecter l'indentation existante (10 espaces) et l'échappement des apostrophes (\').

  • Step 3 : Vérifier la cohérence

Run: grep -rn "accruingDaysTotal\|net / brut\|14,50 / 17,50" doc/leave-tab.md frontend/data/documentation-content.ts Expected : la doc fonctionnelle mentionne le format net / brut et la doc in-app contient la note d'exemple.

  • Step 4 : Commit
git add doc/leave-tab.md frontend/data/documentation-content.ts
git commit -m "docs : en-cours d'acquisition affiché net / brut sur l'onglet Congés

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"

Self-Review

1. Couverture de la spec :

  • Nouveau champ accruingDaysTotal = generatedDays + generatedSaturdays (non-forfait), 0 (forfait) → Task 1 Steps 1-2. ✓
  • Exposition DTO PHP + recopie provider → Task 1 Steps 3-5. ✓
  • DTO TS → Task 2 Step 1. ✓
  • Affichage net / brut non-forfait, inchangé forfait → Task 2 Step 2. ✓
  • Docs doc/leave-tab.md + in-app → Task 3. ✓
  • Invariant accruingDays ≤ accruingDaysTotal → vérifié en Task 1 Step 7. ✓
  • Hors périmètre (RTT, récap, header) → aucun fichier de ces zones touché. ✓

2. Placeholders : aucun « TBD/TODO » ; tout le code est fourni. Les Steps 1-2 de Task 3 demandent un grep pour localiser l'ancre exacte (le texte à insérer est fourni intégralement) car la position dans documentation-content.ts dépend de l'article ; c'est une instruction d'insertion, pas un placeholder de contenu.

3. Cohérence des types/noms : accruingDaysTotal (float PHP / number TS) utilisé identiquement dans le provider, le tableau targetSummary, le PHPDoc, l'ApiResource, le DTO TS et le template. accruingDays (numérateur) reste inchangé. La variable $accruingDaysTotal est définie dans les deux branches avant la construction de targetSummary (comme $accruingDays).