# RTT : Affichage en heures + Paiement RTT

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**Goal:** Afficher les RTT en heures (plus en jours), permettre le paiement RTT via drawer avec majoration 25%/50%, stocker en BDD et afficher par mois.

**Architecture:** Nouvelle entity `EmployeeRttPayment` (employee, year, month, minutes, rate). Le provider RTT agrège les paiements par mois et les soustrait du disponible. Le frontend ajoute un drawer de saisie et deux lignes par mois (25% / 50%).

**Tech Stack:** Symfony + Doctrine + API Platform (backend), Nuxt 4 + Vue 3 + TypeScript + Tailwind (frontend)

---

## Contexte existant

- **Entity:** `EmployeeRttBalance` dans `src/Entity/EmployeeRttBalance.php` - stocke `openingMinutes` par exercice
- **Provider:** `src/State/EmployeeRttSummaryProvider.php` - calcule `availableMinutes = carry + currentYearRecovery`
- **Service:** `src/Service/Rtt/RttRecoveryComputationService.php` - calcul semaine par semaine
- **Frontend:** `frontend/components/employees/RttTab.vue` - affichage grille mois/semaines
- **DTO backend:** `src/ApiResource/EmployeeRttSummary.php` - champs `availableMinutes`, `weeks[]`
- **DTO frontend:** `frontend/services/dto/employee-rtt-summary.ts`
- Les minutes sont la base de calcul. 1 jour = 7h = 420 minutes.
- Exercice RTT : 1er juin N-1 -> 31 mai N (year = N)

---

### Task 1: Migration - Créer la table `employee_rtt_payments`

**Files:**
- Create: `migrations/Version20260309140000.php`

**Step 1: Créer la migration**

```php
<?php
declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20260309140000 extends AbstractMigration
{
    public function getDescription(): string
    {
        return 'Create employee_rtt_payments table for RTT paid hours tracking.';
    }

    public function up(Schema $schema): void
    {
        $this->addSql(<<<'SQL'
            CREATE TABLE employee_rtt_payments (
                id SERIAL PRIMARY KEY,
                employee_id INT NOT NULL REFERENCES employees(id) ON DELETE CASCADE,
                year INT NOT NULL,
                month INT NOT NULL,
                minutes INT NOT NULL,
                rate VARCHAR(10) NOT NULL,
                created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),
                updated_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL DEFAULT NOW()
            )
        SQL);
        $this->addSql('CREATE INDEX idx_rtt_payment_employee_year ON employee_rtt_payments (employee_id, year)');
        $this->addSql("COMMENT ON TABLE employee_rtt_payments IS 'Paiements RTT par employe, mois et taux de majoration.'");
        $this->addSql("COMMENT ON COLUMN employee_rtt_payments.rate IS 'Taux de majoration: 25 ou 50.'");
        $this->addSql("COMMENT ON COLUMN employee_rtt_payments.minutes IS 'Minutes RTT payees pour ce mois et ce taux.'");
    }

    public function down(Schema $schema): void
    {
        $this->addSql('DROP TABLE employee_rtt_payments');
    }
}
```

**Step 2: Exécuter la migration**

Run: `make migrate` ou `docker compose exec php php bin/console doctrine:migrations:migrate --no-interaction`
Expected: Migration exécutée sans erreur

**Step 3: Commit**

```bash
git add migrations/Version20260309140000.php
git commit -m "feat(rtt): add employee_rtt_payments table"
```

---

### Task 2: Entity + Repository `EmployeeRttPayment`

**Files:**
- Create: `src/Entity/EmployeeRttPayment.php`
- Create: `src/Repository/EmployeeRttPaymentRepository.php`

**Step 1: Créer l'entity**

```php
<?php
declare(strict_types=1);

namespace App\Entity;

use App\Repository\EmployeeRttPaymentRepository;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: EmployeeRttPaymentRepository::class)]
#[ORM\Table(name: 'employee_rtt_payments')]
#[ORM\Index(columns: ['employee_id', 'year'], name: 'idx_rtt_payment_employee_year')]
class EmployeeRttPayment
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private ?int $id = null;

    #[ORM\ManyToOne(targetEntity: Employee::class)]
    #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
    private ?Employee $employee = null;

    #[ORM\Column(type: 'integer')]
    private int $year = 0;

    #[ORM\Column(type: 'integer')]
    private int $month = 0;

    #[ORM\Column(type: 'integer', options: ['comment' => 'Minutes RTT payees pour ce mois et ce taux.'])]
    private int $minutes = 0;

    #[ORM\Column(length: 10, options: ['comment' => 'Taux de majoration: 25 ou 50.'])]
    private string $rate = '25';

    #[ORM\Column(type: 'datetime_immutable')]
    private DateTimeImmutable $createdAt;

    #[ORM\Column(type: 'datetime_immutable')]
    private DateTimeImmutable $updatedAt;

    public function __construct()
    {
        $now             = new DateTimeImmutable();
        $this->createdAt = $now;
        $this->updatedAt = $now;
    }

    // Getters & setters (getId, getEmployee/setEmployee, getYear/setYear,
    // getMonth/setMonth, getMinutes/setMinutes, getRate/setRate, touch)
    // Suivre le même pattern que EmployeeLeaveBalance
}
```

**Step 2: Créer le repository**

```php
<?php
declare(strict_types=1);

namespace App\Repository;

use App\Entity\Employee;
use App\Entity\EmployeeRttPayment;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

/**
 * @extends ServiceEntityRepository<EmployeeRttPayment>
 */
final class EmployeeRttPaymentRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, EmployeeRttPayment::class);
    }

    /**
     * @return list<EmployeeRttPayment>
     */
    public function findByEmployeeAndYear(Employee $employee, int $year): array
    {
        return $this->createQueryBuilder('p')
            ->andWhere('p.employee = :employee')
            ->andWhere('p.year = :year')
            ->setParameter('employee', $employee)
            ->setParameter('year', $year)
            ->orderBy('p.month', 'ASC')
            ->getQuery()
            ->getResult();
    }
}
```

**Step 3: Vérifier le lint**

Run: `docker compose exec php php -l src/Entity/EmployeeRttPayment.php && docker compose exec php php -l src/Repository/EmployeeRttPaymentRepository.php`
Expected: No syntax errors

**Step 4: Commit**

```bash
git add src/Entity/EmployeeRttPayment.php src/Repository/EmployeeRttPaymentRepository.php
git commit -m "feat(rtt): add EmployeeRttPayment entity and repository"
```

---

### Task 3: API Resource + Provider + Processor pour le paiement RTT

**Files:**
- Create: `src/ApiResource/EmployeeRttPaymentInput.php`
- Create: `src/State/EmployeeRttPaymentProvider.php`
- Create: `src/State/EmployeeRttPaymentProcessor.php`

**Step 1: Créer l'API Resource**

Endpoint: `PATCH /employees/{id}/rtt-payments` (ROLE_ADMIN)
Body: `{ "month": 3, "minutes": 120, "rate": "25" }`

```php
// src/ApiResource/EmployeeRttPaymentInput.php
#[ApiResource(
    operations: [
        new Patch(
            uriTemplate: '/employees/{id}/rtt-payments',
            security: "is_granted('ROLE_ADMIN')",
            provider: EmployeeRttPaymentProvider::class,
            processor: EmployeeRttPaymentProcessor::class
        ),
    ],
    paginationEnabled: false
)]
final class EmployeeRttPaymentInput
{
    public int $month    = 0;
    public int $minutes  = 0;
    public string $rate  = '25';
    public ?int $year    = null;
}
```

**Step 2: Créer le Provider** (retourne un DTO vide, même pattern que `EmployeeFractionedDaysProvider`)

**Step 3: Créer le Processor**

Logique:
- Valider `rate` in `['25', '50']`, `month` in `[1..12]`, `minutes >= 0`
- Résoudre l'année (même logique exercice RTT que le provider existant)
- Persister un `EmployeeRttPayment`

**Step 4: Vérifier le lint**

**Step 5: Commit**

```bash
git add src/ApiResource/EmployeeRttPaymentInput.php src/State/EmployeeRttPaymentProvider.php src/State/EmployeeRttPaymentProcessor.php
git commit -m "feat(rtt): add PATCH endpoint for RTT payment"
```

---

### Task 4: Modifier le DTO + Provider RTT pour inclure les paiements par mois

**Files:**
- Modify: `src/ApiResource/EmployeeRttSummary.php`
- Modify: `src/State/EmployeeRttSummaryProvider.php`
- Create: `src/Dto/Rtt/RttMonthPayment.php`
- Modify: `frontend/services/dto/employee-rtt-summary.ts`

**Step 1: Créer le DTO mois-paiement**

```php
// src/Dto/Rtt/RttMonthPayment.php
final class RttMonthPayment
{
    public function __construct(
        public int $month,
        public int $paidMinutes25 = 0,
        public int $paidMinutes50 = 0,
    ) {}
}
```

**Step 2: Ajouter au summary backend**

Dans `EmployeeRttSummary.php`, ajouter:
```php
public int $totalPaidMinutes = 0;

/** @var list<RttMonthPayment> */
public array $monthPayments = [];
```

Et modifier `availableMinutes` pour soustraire les paiements:
```php
$summary->availableMinutes = $summary->carryFromPreviousYearMinutes
    + $summary->currentYearRecoveryMinutes
    - $summary->totalPaidMinutes;
```

**Step 3: Modifier le provider** pour charger les paiements via le repository et les agréger par mois

Dans `EmployeeRttSummaryProvider::provide()`:
- Charger `$payments = $this->rttPaymentRepository->findByEmployeeAndYear($employee, $year)`
- Agréger par mois + rate pour construire les `RttMonthPayment`
- Calculer `totalPaidMinutes = sum(minutes)`
- Soustraire du `availableMinutes`

**Step 4: Mettre à jour le DTO frontend**

Dans `frontend/services/dto/employee-rtt-summary.ts`:
```typescript
export type RttMonthPayment = {
  month: number
  paidMinutes25: number
  paidMinutes50: number
}

export type EmployeeRttSummary = {
  // ... champs existants ...
  totalPaidMinutes: number
  monthPayments: RttMonthPayment[]
}
```

**Step 5: Commit**

```bash
git commit -m "feat(rtt): include paid hours in RTT summary by month"
```

---

### Task 5: Frontend - Afficher les RTT en heures + lignes payées

**Files:**
- Modify: `frontend/components/employees/RttTab.vue`

**Step 1: Changer l'affichage du header de jours en heures**

Ligne 4 actuelle:
```html
<p>...: {{ formatDays(summary?.availableMinutes ?? 0) }}</p>
```

Remplacer par:
```html
<p>...: {{ formatMinutes(summary?.availableMinutes ?? 0) }}</p>
```

**Step 2: Ajouter les 2 lignes de paiement par mois**

Après la ligne `Heure payée` existante (ligne 33-34), remplacer par 2 lignes distinctes:

```html
<div class="py-[6px] pl-3 border-r border-b border-primary-500">Heure payée 25%</div>
<div class="py-[6px] pl-3 border-b border-primary-500">{{ formatMinutes(getMonthPaid25(month.month)) }}</div>
<div class="py-[6px] pl-3 border-r border-primary-500">Heure payée 50%</div>
<div class="py-[6px] pl-3">{{ formatMinutes(getMonthPaid50(month.month)) }}</div>
```

**Step 3: Ajouter les helpers computed**

```typescript
const paymentsByMonth = computed(() => {
  const map = new Map<number, { paid25: number; paid50: number }>()
  for (const mp of props.summary?.monthPayments ?? []) {
    map.set(mp.month, { paid25: mp.paidMinutes25, paid50: mp.paidMinutes50 })
  }
  return map
})

const getMonthPaid25 = (month: number) => paymentsByMonth.value.get(month)?.paid25 ?? 0
const getMonthPaid50 = (month: number) => paymentsByMonth.value.get(month)?.paid50 ?? 0
```

**Step 4: Commit**

```bash
git commit -m "feat(rtt): display hours instead of days + paid hours per month"
```

---

### Task 6: Frontend - Drawer de paiement RTT

**Files:**
- Modify: `frontend/components/employees/RttTab.vue`
- Modify: `frontend/services/employee-rtt-summary.ts`
- Modify: `frontend/composables/useEmployeeDetailPage.ts`
- Modify: `frontend/pages/employees/[id].vue`

**Step 1: Ajouter le service API**

Dans `frontend/services/employee-rtt-summary.ts`:
```typescript
export const createRttPayment = async (
  employeeId: number,
  month: number,
  minutes: number,
  rate: '25' | '50',
  year?: number
) => {
  const api = useApi()
  const body: Record<string, unknown> = { month, minutes, rate }
  if (year) body.year = year
  return api.patch(`/employees/${employeeId}/rtt-payments`, body)
}
```

**Step 2: Ajouter au composable**

Dans `useEmployeeDetailPage.ts`:
- Import `createRttPayment`
- Ajouter `submitRttPayment(month, minutes, rate)` qui appelle l'API puis `loadEmployee()`
- Exposer dans le return

**Step 3: Passer l'event dans la page**

Dans `frontend/pages/employees/[id].vue`:
- Destructurer `submitRttPayment`
- Ajouter `@submit-rtt-payment="submitRttPayment"` sur `<EmployeesRttTab>`

**Step 4: Ajouter le drawer dans RttTab.vue**

Même pattern que le drawer fractionnés dans LeaveTab:
- Import `AppDrawer`
- State: `isPaymentDrawerOpen`, `paymentForm: { month, minutes, rate }`
- Bouton "Payer les RTT" ouvre le drawer
- Formulaire avec:
  - Select mois (Janvier..Décembre)
  - Input number "Nombre d'heures" (step 0.5, converti en minutes au submit)
  - Select rate: "25%" / "50%"
  - Boutons Annuler / Enregistrer
- Emit `submit-rtt-payment` au submit

**Step 5: Commit**

```bash
git commit -m "feat(rtt): add payment drawer with month/hours/rate fields"
```

---

### Task 7: Documentation fonctionnelle

**Files:**
- Modify: `doc/functional-rules.md`

**Step 1: Mettre à jour la section RTT**

Ajouter après les règles RTT existantes:
- Paiement RTT: saisie RH via `PATCH /employees/{id}/rtt-payments`
- Stocké dans `employee_rtt_payments` (employee, year, month, minutes, rate)
- Les heures payées sont soustraites du disponible RTT
- Affichage: 2 lignes par mois (25% et 50%)
- L'affichage global RTT est en heures (plus en jours)

**Step 2: Commit**

```bash
git commit -m "docs: update functional rules with RTT payment feature"
```

---

## Résumé des fichiers

| Action | Fichier |
|--------|---------|
| Create | `migrations/Version20260309140000.php` |
| Create | `src/Entity/EmployeeRttPayment.php` |
| Create | `src/Repository/EmployeeRttPaymentRepository.php` |
| Create | `src/ApiResource/EmployeeRttPaymentInput.php` |
| Create | `src/State/EmployeeRttPaymentProvider.php` |
| Create | `src/State/EmployeeRttPaymentProcessor.php` |
| Create | `src/Dto/Rtt/RttMonthPayment.php` |
| Modify | `src/ApiResource/EmployeeRttSummary.php` |
| Modify | `src/State/EmployeeRttSummaryProvider.php` |
| Modify | `frontend/services/dto/employee-rtt-summary.ts` |
| Modify | `frontend/services/employee-rtt-summary.ts` |
| Modify | `frontend/components/employees/RttTab.vue` |
| Modify | `frontend/composables/useEmployeeDetailPage.ts` |
| Modify | `frontend/pages/employees/[id].vue` |
| Modify | `doc/functional-rules.md` |
