# Rapport — v3.13.4 (encaisser une facture « pour compte de tiers » sans toucher la trésorerie)

**Micro-lot du socle, un commit : se livre aux DEUX installations, à l'identique.
Aucune migration, aucune dépendance.**

## Le bug (constaté en prod, avec dégât réel)

`InvoiceService::markCollected()` ignorait le drapeau `hors_tresorerie` : encaisser une facture
« pour compte de tiers » créait une vraie transaction (flow `milestone_collection`, catégorie
CA/encaissements). Constaté : 6 encaissements fictifs (50 593,20 DH) entrés en banque le
31/03/2026 pour les factures 1000 FIKRA — annulés à la main par l'exploitant via « Annuler
l'encaissement ». Ce lot empêche la récidive.

## Le correctif

- **`markCollected()`** (signature : retourne désormais `?Transaction`) : quand
  `estHorsTresorerie()`, la facture passe « Encaissée » — `collected_at`, `payment_method` sur
  la facture s'il est fourni, `payment_reference` consignée dans les notes internes (pas de
  colonne dédiée, pas de migration) — **sans créer aucune transaction**
  (`collection_transaction_id` reste null) et sans toucher aucun compte. Le décompte lié suit
  le **même passage d'état** qu'aujourd'hui (Encaissé, primes/parts déclenchées), avec
  `transaction_id` null ; l'événement `MilestoneCollected` accepte désormais une transaction
  **nullable** (ses écouteurs — primes, parts investisseurs, notification — ne consomment que
  le décompte).
- **Modale « Marquer encaissée »** (table ; la page d'édition n'a pas cette action) : pour une
  facture hors trésorerie, le champ « Compte crédité » disparaît (ni affiché ni requis) et une
  phrase l'annonce : « Facture pour compte de tiers : aucun mouvement de trésorerie ne sera
  créé. » La notification de succès le redit. Facture normale : modale et comportement
  strictement inchangés.
- **`markUncollected()`** : déjà sûr (gardes null) — le retour « Émise » d'une facture hors
  trésorerie ne cherche aucune transaction à supprimer ; le décompte est re-basculé « Facturé »
  et les primes/parts non versées reversées, comme pour un encaissement normal.

## Recette (tout au vert — garde-fou §4 inclus)

- Hors trésorerie : encaisser → Encaissée, `collection_transaction_id` null, **zéro ligne dans
  `transactions` (corbeille comprise)**, **solde de compte strictement intact** ; annuler →
  retour Émise sans erreur ; décompte lié → Encaissé puis re-Facturé, transaction nulle dans
  l'événement. — `InvoiceServiceTest`
- Modale : passe sans compte pour une facture hors trésorerie (aucune transaction créée) ;
  bloque toujours sans compte pour une facture normale (statut inchangé, rien créé). —
  `InvoiceResourceTest`
- Non-régression : facture normale strictement inchangée (transaction créée/supprimée, soldes
  recalculés) — suite complète verte + `pint` + `npm run build`.

## Fichiers touchés

- `app/Services/Invoices/InvoiceService.php` — branche hors trésorerie de `markCollected()`,
  commentaire de symétrie dans `markUncollected()`
- `app/Events/MilestoneCollected.php` — transaction nullable
- `app/Filament/Admin/Resources/Invoices/Tables/InvoicesTable.php` — modale adaptée
- `tests/Feature/Invoices/{InvoiceServiceTest,InvoiceResourceTest}.php`
