feat : Ajout du système de RTT sur la page employé avec le repport annuel des heures
All checks were successful
Auto Tag Develop / tag (push) Successful in 6s
All checks were successful
Auto Tag Develop / tag (push) Successful in 6s
This commit is contained in:
563
docs/superpowers/plans/2026-03-13-rtt-tab-redesign.md
Normal file
563
docs/superpowers/plans/2026-03-13-rtt-tab-redesign.md
Normal file
@@ -0,0 +1,563 @@
|
||||
# Refonte onglet RTT — Plan d'implémentation
|
||||
|
||||
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Remplacer la vue annuelle RTT par une vue mensuelle avec tableau détaillé par semaine (base/25%/50%) et un système de paiement à 4 champs.
|
||||
|
||||
**Architecture:** Enrichir `RttRecoveryComputationService` pour retourner le détail base/bonus par palier. Modifier l'entité `EmployeeRttPayment` pour stocker 4 valeurs. Réécrire le composant `RttTab.vue` avec navigation mensuelle et tableau 7 colonnes.
|
||||
|
||||
**Tech Stack:** Symfony + API Platform + Doctrine (backend), Nuxt 4 + Vue 3 + TypeScript + Tailwind (frontend), PostgreSQL.
|
||||
|
||||
**Spec:** `docs/superpowers/specs/2026-03-13-rtt-tab-redesign.md`
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Enrichir le retour de `RttRecoveryComputationService::computeRecoveryByWeek`
|
||||
|
||||
**Files:**
|
||||
- Create: `src/Dto/Rtt/WeekRecoveryDetail.php`
|
||||
- Modify: `src/Service/Rtt/RttRecoveryComputationService.php:97-206`
|
||||
|
||||
Actuellement `computeRecoveryByWeek` retourne `array<string, int>` (weekKey => totalMinutes). Il faut retourner `array<string, WeekRecoveryDetail>` avec le détail ventilé.
|
||||
|
||||
- [ ] **Step 1: Créer le DTO `WeekRecoveryDetail`**
|
||||
|
||||
```php
|
||||
// src/Dto/Rtt/WeekRecoveryDetail.php
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Rtt;
|
||||
|
||||
final class WeekRecoveryDetail
|
||||
{
|
||||
public function __construct(
|
||||
public int $overtimeMinutes = 0,
|
||||
public int $base25Minutes = 0,
|
||||
public int $bonus25Minutes = 0,
|
||||
public int $base50Minutes = 0,
|
||||
public int $bonus50Minutes = 0,
|
||||
public int $totalMinutes = 0,
|
||||
) {}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Modifier `computeRecoveryByWeek` pour retourner `array<string, WeekRecoveryDetail>`**
|
||||
|
||||
Changer le retour de la méthode. Les variables internes existent déjà (`weeklyOvertimeTotalMinutes`, `weeklyOvertime25Minutes`, `weeklyOvertime50Minutes`). Il faut calculer en plus les bases séparées.
|
||||
|
||||
La logique de ventilation des heures de base entre palier 25% et palier 50% :
|
||||
- `base25Minutes` = heures sup dans la tranche 25% = `min(overtimeMinutes, max(0, overtime25StartMinutes - overtimeReferenceMinutes))`... En fait, c'est plus simple :
|
||||
- `base25Minutes` = `min(weeklyOvertimeTotalMinutes, max(0, 43*60 - overtime25StartMinutes))` quand overtimeTotal > 0
|
||||
- Plus simplement : `base25Minutes` = heures entre le seuil 25% et 43h, `base50Minutes` = heures au-dessus de 43h
|
||||
|
||||
Reprenons la logique existante (lignes 189-202) :
|
||||
- `overtimeReferenceMinutes` = seuil à partir duquel on compte les heures sup (max(35, weeklyHours) * 60 réparti sur les jours)
|
||||
- `overtime25StartMinutes` = seuil à partir duquel les heures sup sont à 25% (39h si contrat >= 39h, sinon 35h)
|
||||
- `weeklyOvertimeTotalMinutes` = max(0, worked - overtimeReference) — total heures sup brutes
|
||||
- `weeklyOvertime25Minutes` = bonus 25% = round(min(worked, 43*60) - overtime25Start) * 0.25
|
||||
- `weeklyOvertime50Minutes` = bonus 50% = round(max(0, worked - 43*60)) * 0.5
|
||||
|
||||
Pour la ventilation :
|
||||
- `base25Minutes` = min(weeklyOvertimeTotalMinutes, max(0, 43*60 - overtime25StartMinutes)) — Non, c'est la tranche 25% en termes d'heures travaillées, pas en termes d'heures sup.
|
||||
|
||||
En fait :
|
||||
- Les heures sup brutes = `weeklyOvertimeTotalMinutes` = `worked - overtimeReference`
|
||||
- Les heures dans le palier 25% = heures entre `overtime25Start` et `min(worked, 43*60)` = c'est `max(0, min(worked, 43*60) - overtime25Start)`. C'est la base sur laquelle le 25% est calculé.
|
||||
- Les heures dans le palier 50% = heures au-dessus de 43h = `max(0, worked - 43*60)`. C'est la base sur laquelle le 50% est calculé.
|
||||
|
||||
Modifier les lignes 191-202 :
|
||||
|
||||
```php
|
||||
$weeklyOvertimeTotalMinutes = $isWeekPresenceTracking
|
||||
? 0
|
||||
: max(0, $weeklyTotalMinutes - $overtimeReferenceMinutes);
|
||||
|
||||
$base25 = ($isWeekPresenceTracking || $disableOvertimeBonuses) ? 0 : max(0, min($weeklyTotalMinutes, 43 * 60) - $overtime25StartMinutes);
|
||||
$bonus25 = ($isWeekPresenceTracking || $disableOvertimeBonuses) ? 0 : (int) round($base25 * 0.25);
|
||||
$base50 = ($isWeekPresenceTracking || $disableOvertimeBonuses) ? 0 : max(0, $weeklyTotalMinutes - 43 * 60);
|
||||
$bonus50 = ($isWeekPresenceTracking || $disableOvertimeBonuses) ? 0 : (int) round($base50 * 0.5);
|
||||
|
||||
$results[$weekKey] = new WeekRecoveryDetail(
|
||||
overtimeMinutes: $weeklyOvertimeTotalMinutes,
|
||||
base25Minutes: $base25,
|
||||
bonus25Minutes: $bonus25,
|
||||
base50Minutes: $base50,
|
||||
bonus50Minutes: $bonus50,
|
||||
totalMinutes: ($isWeekPresenceTracking || $disableOvertimeBonuses)
|
||||
? 0
|
||||
: $weeklyOvertimeTotalMinutes + $bonus25 + $bonus50,
|
||||
);
|
||||
```
|
||||
|
||||
Les cas "zéro" (weekStart vide, limitDate dépassée, etc.) retournent `new WeekRecoveryDetail()` (tout à 0).
|
||||
|
||||
- [ ] **Step 3: Adapter `computeTotalRecoveryForExercise` pour retourner un `WeekRecoveryDetail` agrégé**
|
||||
|
||||
Cette méthode retournait `int`. Elle doit maintenant retourner un `WeekRecoveryDetail` qui agrège toutes les semaines (somme par champ). Le rollover et le provider en ont besoin pour la ventilation du carry-over.
|
||||
|
||||
```php
|
||||
public function computeTotalRecoveryForExercise(Employee $employee, int $exerciseYear): WeekRecoveryDetail
|
||||
{
|
||||
[$from, $to] = $this->resolveExerciseBounds($exerciseYear);
|
||||
$weeks = $this->buildWeeksForExercise($from, $to);
|
||||
$weekRanges = array_map(
|
||||
static fn (array $week): array => [
|
||||
'month' => (int) $week['month'],
|
||||
'weekNumber' => (int) $week['weekNumber'],
|
||||
'start' => $week['start'],
|
||||
'end' => $week['end'],
|
||||
],
|
||||
$weeks
|
||||
);
|
||||
|
||||
$byWeek = $this->computeRecoveryByWeek($employee, $weekRanges, $from, $to, null);
|
||||
|
||||
$total = new WeekRecoveryDetail();
|
||||
foreach ($byWeek as $detail) {
|
||||
$total = new WeekRecoveryDetail(
|
||||
overtimeMinutes: $total->overtimeMinutes + $detail->overtimeMinutes,
|
||||
base25Minutes: $total->base25Minutes + $detail->base25Minutes,
|
||||
bonus25Minutes: $total->bonus25Minutes + $detail->bonus25Minutes,
|
||||
base50Minutes: $total->base50Minutes + $detail->base50Minutes,
|
||||
bonus50Minutes: $total->bonus50Minutes + $detail->bonus50Minutes,
|
||||
totalMinutes: $total->totalMinutes + $detail->totalMinutes,
|
||||
);
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Vérifier que le code compile**
|
||||
|
||||
Run: `docker exec php-sirh-fpm php bin/console cache:clear`
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Modifier l'entité `EmployeeRttBalance` (carry-over ventilé) + rollover
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/Entity/EmployeeRttBalance.php`
|
||||
- Modify: `src/Repository/EmployeeRttBalanceRepository.php`
|
||||
- Modify: `src/Command/RttRolloverCommand.php`
|
||||
|
||||
Le carry-over doit être ventilé sur les mêmes 4 colonnes que le tableau (base25, bonus25, base50, bonus50) pour pouvoir afficher une ligne "Report" dans le mois de juin.
|
||||
|
||||
- [ ] **Step 1: Remplacer `openingMinutes` par 4 champs dans `EmployeeRttBalance`**
|
||||
|
||||
Remplacer la propriété `$openingMinutes` par :
|
||||
|
||||
```php
|
||||
#[ORM\Column(type: 'integer', options: ['comment' => 'Report N-1 base 25% en minutes.', 'default' => 0])]
|
||||
private int $openingBase25Minutes = 0;
|
||||
|
||||
#[ORM\Column(type: 'integer', options: ['comment' => 'Report N-1 bonus 25% en minutes.', 'default' => 0])]
|
||||
private int $openingBonus25Minutes = 0;
|
||||
|
||||
#[ORM\Column(type: 'integer', options: ['comment' => 'Report N-1 base 50% en minutes.', 'default' => 0])]
|
||||
private int $openingBase50Minutes = 0;
|
||||
|
||||
#[ORM\Column(type: 'integer', options: ['comment' => 'Report N-1 bonus 50% en minutes.', 'default' => 0])]
|
||||
private int $openingBonus50Minutes = 0;
|
||||
```
|
||||
|
||||
Ajouter les getters/setters. Supprimer `getOpeningMinutes`/`setOpeningMinutes`. Ajouter un helper `getTotalOpeningMinutes()` qui retourne la somme des 4 champs.
|
||||
|
||||
- [ ] **Step 2: Adapter `RttRolloverCommand`**
|
||||
|
||||
`computeTotalRecoveryForExercise` retourne maintenant un `WeekRecoveryDetail`. Utiliser les 4 champs :
|
||||
|
||||
```php
|
||||
$carry = $this->rttRecoveryService->computeTotalRecoveryForExercise($employee, $previousYear);
|
||||
|
||||
$balance = new EmployeeRttBalance()
|
||||
->setEmployee($employee)
|
||||
->setYear($targetYear)
|
||||
->setOpeningBase25Minutes($carry->base25Minutes)
|
||||
->setOpeningBonus25Minutes($carry->bonus25Minutes)
|
||||
->setOpeningBase50Minutes($carry->base50Minutes)
|
||||
->setOpeningBonus50Minutes($carry->bonus50Minutes)
|
||||
->setIsLocked(false)
|
||||
;
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Adapter `EmployeeRttSummaryProvider::resolveCarryMinutes`**
|
||||
|
||||
Cette méthode retournait `int`. La renommer en `resolveCarry` et retourner un `WeekRecoveryDetail` :
|
||||
|
||||
```php
|
||||
private function resolveCarry(Employee $employee, int $year): WeekRecoveryDetail
|
||||
{
|
||||
$balance = $this->rttBalanceRepository->findOneByEmployeeAndYear($employee, $year);
|
||||
if (null !== $balance) {
|
||||
return new WeekRecoveryDetail(
|
||||
base25Minutes: $balance->getOpeningBase25Minutes(),
|
||||
bonus25Minutes: $balance->getOpeningBonus25Minutes(),
|
||||
base50Minutes: $balance->getOpeningBase50Minutes(),
|
||||
bonus50Minutes: $balance->getOpeningBonus50Minutes(),
|
||||
totalMinutes: $balance->getTotalOpeningMinutes(),
|
||||
);
|
||||
}
|
||||
|
||||
return $this->rttRecoveryService->computeTotalRecoveryForExercise($employee, $year - 1);
|
||||
}
|
||||
```
|
||||
|
||||
Adapter le provider pour utiliser le carry ventilé dans le summary :
|
||||
- `carryFromPreviousYearMinutes` = carry->totalMinutes
|
||||
- Ajouter les 4 champs de carry dans `EmployeeRttSummary` pour le frontend
|
||||
|
||||
- [ ] **Step 4: Ajouter les champs carry dans `EmployeeRttSummary`**
|
||||
|
||||
```php
|
||||
public int $carryBase25Minutes = 0;
|
||||
public int $carryBonus25Minutes = 0;
|
||||
public int $carryBase50Minutes = 0;
|
||||
public int $carryBonus50Minutes = 0;
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Générer et exécuter la migration**
|
||||
|
||||
Run: `docker exec php-sirh-fpm php bin/console doctrine:migrations:diff`
|
||||
Run: `docker exec php-sirh-fpm php bin/console doctrine:migrations:migrate --no-interaction`
|
||||
|
||||
Note : faire la migration après la Task 3 (EmployeeRttPayment) pour regrouper les changements dans une seule migration.
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Modifier l'entité `EmployeeRttPayment` et la migration
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/Entity/EmployeeRttPayment.php`
|
||||
- Modify: `src/Repository/EmployeeRttPaymentRepository.php`
|
||||
|
||||
- [ ] **Step 1: Remplacer `minutes` + `rate` par 4 champs dans l'entité**
|
||||
|
||||
Remplacer les propriétés `$minutes` et `$rate` par :
|
||||
|
||||
```php
|
||||
#[ORM\Column(type: 'integer', options: ['comment' => 'Base heures palier 25% en minutes.', 'default' => 0])]
|
||||
private int $base25Minutes = 0;
|
||||
|
||||
#[ORM\Column(type: 'integer', options: ['comment' => 'Bonus 25% en minutes.', 'default' => 0])]
|
||||
private int $bonus25Minutes = 0;
|
||||
|
||||
#[ORM\Column(type: 'integer', options: ['comment' => 'Base heures palier 50% en minutes.', 'default' => 0])]
|
||||
private int $base50Minutes = 0;
|
||||
|
||||
#[ORM\Column(type: 'integer', options: ['comment' => 'Bonus 50% en minutes.', 'default' => 0])]
|
||||
private int $bonus50Minutes = 0;
|
||||
```
|
||||
|
||||
Ajouter les getters/setters correspondants. Supprimer `getMinutes`/`setMinutes`/`getRate`/`setRate`.
|
||||
|
||||
- [ ] **Step 2: Adapter le repository**
|
||||
|
||||
Remplacer `findOneByEmployeeYearMonthRate` par `findOneByEmployeeYearMonth` (plus besoin du rate) :
|
||||
|
||||
```php
|
||||
public function findOneByEmployeeYearMonth(Employee $employee, int $year, int $month): ?EmployeeRttPayment
|
||||
{
|
||||
return $this->findOneBy([
|
||||
'employee' => $employee,
|
||||
'year' => $year,
|
||||
'month' => $month,
|
||||
]);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Générer et vérifier la migration**
|
||||
|
||||
Run: `docker exec php-sirh-fpm php bin/console doctrine:migrations:diff`
|
||||
|
||||
Vérifier que la migration :
|
||||
- Ajoute `base25_minutes`, `bonus25_minutes`, `base50_minutes`, `bonus50_minutes`
|
||||
- Supprime `minutes` et `rate`
|
||||
|
||||
Run: `docker exec php-sirh-fpm php bin/console doctrine:migrations:migrate --no-interaction`
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Adapter le DTO `RttMonthPayment` et `EmployeeRttWeekSummary`
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/Dto/Rtt/RttMonthPayment.php`
|
||||
- Modify: `src/Dto/Rtt/EmployeeRttWeekSummary.php`
|
||||
|
||||
- [ ] **Step 1: Modifier `RttMonthPayment`**
|
||||
|
||||
Remplacer `paidMinutes25` et `paidMinutes50` par les 4 champs :
|
||||
|
||||
```php
|
||||
final class RttMonthPayment
|
||||
{
|
||||
public function __construct(
|
||||
public int $month,
|
||||
public int $paidBase25Minutes = 0,
|
||||
public int $paidBonus25Minutes = 0,
|
||||
public int $paidBase50Minutes = 0,
|
||||
public int $paidBonus50Minutes = 0,
|
||||
) {}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Enrichir `EmployeeRttWeekSummary`**
|
||||
|
||||
Ajouter les champs de détail :
|
||||
|
||||
```php
|
||||
final class EmployeeRttWeekSummary
|
||||
{
|
||||
public function __construct(
|
||||
public int $month,
|
||||
public int $weekNumber,
|
||||
public string $weekStart,
|
||||
public string $weekEnd,
|
||||
public int $overtimeMinutes = 0,
|
||||
public int $base25Minutes = 0,
|
||||
public int $bonus25Minutes = 0,
|
||||
public int $base50Minutes = 0,
|
||||
public int $bonus50Minutes = 0,
|
||||
public int $totalMinutes = 0,
|
||||
) {}
|
||||
}
|
||||
```
|
||||
|
||||
Supprimer l'ancien champ `recoveryMinutes`.
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Adapter le provider et le processor backend
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/State/EmployeeRttSummaryProvider.php`
|
||||
- Modify: `src/ApiResource/EmployeeRttSummary.php`
|
||||
- Modify: `src/ApiResource/EmployeeRttPaymentInput.php`
|
||||
- Modify: `src/State/EmployeeRttPaymentProcessor.php`
|
||||
|
||||
- [ ] **Step 1: Adapter `EmployeeRttSummaryProvider::provide`**
|
||||
|
||||
Le mapping des semaines (ligne 87-96) doit utiliser les nouveaux champs du `WeekRecoveryDetail` :
|
||||
|
||||
```php
|
||||
$summary->weeks = array_map(
|
||||
static function (array $week) use ($currentByWeekStart) {
|
||||
$detail = $currentByWeekStart[$week['start']->format('Y-m-d')] ?? new WeekRecoveryDetail();
|
||||
|
||||
return new EmployeeRttWeekSummary(
|
||||
month: (int) $week['month'],
|
||||
weekNumber: (int) $week['weekNumber'],
|
||||
weekStart: $week['start']->format('Y-m-d'),
|
||||
weekEnd: $week['end']->format('Y-m-d'),
|
||||
overtimeMinutes: $detail->overtimeMinutes,
|
||||
base25Minutes: $detail->base25Minutes,
|
||||
bonus25Minutes: $detail->bonus25Minutes,
|
||||
base50Minutes: $detail->base50Minutes,
|
||||
bonus50Minutes: $detail->bonus50Minutes,
|
||||
totalMinutes: $detail->totalMinutes,
|
||||
);
|
||||
},
|
||||
$weekRanges
|
||||
);
|
||||
```
|
||||
|
||||
Le `currentYearRecoveryMinutes` doit sommer les `totalMinutes` :
|
||||
|
||||
```php
|
||||
$summary->currentYearRecoveryMinutes = array_sum(
|
||||
array_map(static fn (WeekRecoveryDetail $d) => $d->totalMinutes, $currentByWeekStart)
|
||||
);
|
||||
```
|
||||
|
||||
Adapter l'agrégation des paiements (lignes 98-121) pour les 4 champs :
|
||||
|
||||
```php
|
||||
foreach ($payments as $payment) {
|
||||
$m = $payment->getMonth();
|
||||
if (!isset($monthBuckets[$m])) {
|
||||
$monthBuckets[$m] = ['base25' => 0, 'bonus25' => 0, 'base50' => 0, 'bonus50' => 0];
|
||||
}
|
||||
$monthBuckets[$m]['base25'] += $payment->getBase25Minutes();
|
||||
$monthBuckets[$m]['bonus25'] += $payment->getBonus25Minutes();
|
||||
$monthBuckets[$m]['base50'] += $payment->getBase50Minutes();
|
||||
$monthBuckets[$m]['bonus50'] += $payment->getBonus50Minutes();
|
||||
}
|
||||
|
||||
foreach ($monthBuckets as $m => $bucket) {
|
||||
$monthPayments[] = new RttMonthPayment($m, $bucket['base25'], $bucket['bonus25'], $bucket['base50'], $bucket['bonus50']);
|
||||
$totalPaidMinutes += $bucket['base25'] + $bucket['bonus25'] + $bucket['base50'] + $bucket['bonus50'];
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Adapter `EmployeeRttPaymentInput`**
|
||||
|
||||
```php
|
||||
final class EmployeeRttPaymentInput
|
||||
{
|
||||
public int $month = 0;
|
||||
public int $base25Minutes = 0;
|
||||
public int $bonus25Minutes = 0;
|
||||
public int $base50Minutes = 0;
|
||||
public int $bonus50Minutes = 0;
|
||||
public ?int $year = null;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Adapter `EmployeeRttPaymentProcessor`**
|
||||
|
||||
Supprimer la validation du `rate`. Adapter le upsert :
|
||||
|
||||
```php
|
||||
$payment = $this->rttPaymentRepository->findOneByEmployeeYearMonth($employee, $year, $data->month);
|
||||
|
||||
if (null === $payment) {
|
||||
$payment = new EmployeeRttPayment();
|
||||
$payment->setEmployee($employee);
|
||||
$payment->setYear($year);
|
||||
$payment->setMonth($data->month);
|
||||
$this->entityManager->persist($payment);
|
||||
}
|
||||
|
||||
$payment->setBase25Minutes($data->base25Minutes);
|
||||
$payment->setBonus25Minutes($data->bonus25Minutes);
|
||||
$payment->setBase50Minutes($data->base50Minutes);
|
||||
$payment->setBonus50Minutes($data->bonus50Minutes);
|
||||
$payment->touch();
|
||||
$this->entityManager->flush();
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Vérifier**
|
||||
|
||||
Run: `docker exec php-sirh-fpm php bin/console cache:clear`
|
||||
|
||||
---
|
||||
|
||||
## Task 5: Adapter le frontend — DTOs et service
|
||||
|
||||
**Files:**
|
||||
- Modify: `frontend/services/dto/employee-rtt-summary.ts`
|
||||
- Modify: `frontend/services/employee-rtt-summary.ts`
|
||||
|
||||
- [ ] **Step 1: Mettre à jour les types TS**
|
||||
|
||||
```typescript
|
||||
export type EmployeeRttWeekSummary = {
|
||||
month: number
|
||||
weekNumber: number
|
||||
weekStart: string
|
||||
weekEnd: string
|
||||
overtimeMinutes: number
|
||||
base25Minutes: number
|
||||
bonus25Minutes: number
|
||||
base50Minutes: number
|
||||
bonus50Minutes: number
|
||||
totalMinutes: number
|
||||
}
|
||||
|
||||
export type RttMonthPayment = {
|
||||
month: number
|
||||
paidBase25Minutes: number
|
||||
paidBonus25Minutes: number
|
||||
paidBase50Minutes: number
|
||||
paidBonus50Minutes: number
|
||||
}
|
||||
|
||||
export type EmployeeRttSummary = {
|
||||
year: number
|
||||
carryFromPreviousYearMinutes: number
|
||||
carryBase25Minutes: number
|
||||
carryBonus25Minutes: number
|
||||
carryBase50Minutes: number
|
||||
carryBonus50Minutes: number
|
||||
currentYearRecoveryMinutes: number
|
||||
totalPaidMinutes: number
|
||||
availableMinutes: number
|
||||
weeks: EmployeeRttWeekSummary[]
|
||||
monthPayments: RttMonthPayment[]
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Adapter le service `createRttPayment`**
|
||||
|
||||
```typescript
|
||||
export const createRttPayment = async (
|
||||
employeeId: number,
|
||||
month: number,
|
||||
base25Minutes: number,
|
||||
bonus25Minutes: number,
|
||||
base50Minutes: number,
|
||||
bonus50Minutes: number,
|
||||
year?: number
|
||||
) => {
|
||||
const api = useApi()
|
||||
const body: Record<string, unknown> = { month, base25Minutes, bonus25Minutes, base50Minutes, bonus50Minutes }
|
||||
if (year) body.year = year
|
||||
return api.patch(`/employees/${employeeId}/rtt-payments`, body)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 6: Réécrire `RttTab.vue`
|
||||
|
||||
**Files:**
|
||||
- Modify: `frontend/components/employees/RttTab.vue`
|
||||
|
||||
- [ ] **Step 1: Réécrire le composant complet**
|
||||
|
||||
Structure du template :
|
||||
1. En-tête avec navigation mensuelle (flèches `<` `>`) et "RTT À LA DATE DU JOUR : X heure"
|
||||
2. Tableau 7 colonnes : Semaine | Heure | Base | 25% | Base | 50% | Total
|
||||
3. Si mois de juin (premier mois de l'exercice) et carry > 0 : ligne "Report" avec les 4 valeurs carry (colonne Heure = "-")
|
||||
4. 5 lignes semaines (padding si < 5)
|
||||
5. Ligne Total (somme par colonne, incluant le report si présent)
|
||||
6. Ligne Payé (valeurs négatives, "-" pour colonne Heure)
|
||||
7. Ligne Reste (Total - |Payé|, "-" pour colonne Heure)
|
||||
8. Bouton "+ Payer les RRT"
|
||||
9. Drawer de paiement avec 5 champs
|
||||
|
||||
Script setup :
|
||||
- `currentMonthIndex` : ref (0-11) pour la navigation dans `orderedMonthIndexes` (toujours [5,6,7,8,9,10,11,0,1,2,3,4] = juin à mai)
|
||||
- Initialiser `currentMonthIndex` au mois courant dans l'exercice
|
||||
- `currentMonth` : computed qui retourne le numéro de mois (1-12) basé sur l'index
|
||||
- `weeksForMonth` : computed filtrant les semaines du summary pour le mois courant, paddé à 5
|
||||
- `monthPayment` : computed trouvant le paiement du mois dans `summary.monthPayments`
|
||||
- Totaux par colonne : computed sommant les semaines
|
||||
- `formatMinutes` : existant, réutiliser (format `Xh` ou `Xh Ym`)
|
||||
- Navigation : `prevMonth` / `nextMonth` modifiant `currentMonthIndex` avec bornes [0, 11]
|
||||
|
||||
Drawer de paiement :
|
||||
- Champs : Mois (select), Base 25% (number en heures), Heures 25% (number en heures), Base 50% (number en heures), Heures 50% (number en heures)
|
||||
- Si paiement existant pour le mois sélectionné : pré-remplir en convertissant minutes → heures
|
||||
- Emit : `submit-rtt-payment` avec les 4 valeurs converties en minutes + le mois
|
||||
|
||||
- [ ] **Step 2: Adapter le composant parent**
|
||||
|
||||
Chercher où `RttTab` est utilisé et adapter l'event handler `submit-rtt-payment` pour passer les 4 champs au lieu de `(month, minutes, rate)`.
|
||||
|
||||
Run: `grep -rn "submit-rtt-payment" frontend/` pour trouver le parent.
|
||||
|
||||
---
|
||||
|
||||
## Task 7: Test de bout en bout
|
||||
|
||||
- [ ] **Step 1: Vérifier le cache et la migration**
|
||||
|
||||
```bash
|
||||
docker exec php-sirh-fpm php bin/console cache:clear
|
||||
docker exec php-sirh-fpm php bin/console doctrine:migrations:migrate --no-interaction
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Tester l'API**
|
||||
|
||||
Vérifier que `GET /api/employees/{id}/rtt-summary` retourne les nouveaux champs par semaine.
|
||||
Vérifier que `PATCH /api/employees/{id}/rtt-payments` accepte les 4 champs.
|
||||
|
||||
- [ ] **Step 3: Tester le frontend**
|
||||
|
||||
- Navigation mensuelle (flèches, mois courant par défaut)
|
||||
- Tableau : vérifier les valeurs par semaine
|
||||
- Paiement : créer, modifier, vérifier pré-remplissage
|
||||
- "RTT À LA DATE DU JOUR" : vérifier le cumul
|
||||
Reference in New Issue
Block a user