# Automatisation : Installation → Approbation → Facturation

> Statut : **PLAN** (2026-06-18). Touche F (live, autoritatif) → tout changement F = test-first.
> Décisions validées : (1) approbation **explicite dans Ops** ; (2) encaissement **immédiat à l'approbation**.

## Objectif

Chaîne **100 % automatisée après un seul point de contrôle humain** (l'approbation) :
installation complétée (tech) → **approuvée** (dispatcher/admin dans Ops) → facturation prorata
**consolidée, émise et encaissée immédiatement**, sans double-débit, F restant la source de vérité
de facturation.

## Principe de sécurité central — UN seul chargeur, DEUX déclencheurs

F ne charge pas « une facture » : `stripe_ppa.php` charge `sum(total_amt - billed_amt)` — **le reste
impayé, facture par facture**. Donc :

- **Déclencheur A — à l'approbation (immédiat)** : on charge le prorata off-session puis on pose
  `billed_amt = total` sur cette facture.
- **Déclencheur B — balayage du 15 (`stripe_ppa.php`)** : charge le reste impayé ; le prorata déjà
  réglé (`billed_amt = total`) contribue **0** → **jamais rechargé**.

Les deux écrivent le **même registre `billed_amt`** → le reste impayé d'une facture est chargé
exactement une fois. **Pas de double-débit par construction** — surtout ne PAS bâtir un 2ᵉ chargeur
OPS (cause de l'incident de juin 2026, cf `feedback_ppa_double_charge_incident`).

## Pipeline cible

```
[Tech] complète le job TERMINAL de la chaîne
        │  (existant : setJobStatusWithChain → _isChainTerminal → activateSubscriptionForJob)
        ▼
État « À approuver »  (abonnements restent 'En attente' ; aperçu prorata calculé, AUCUNE écriture)
        │
        ▼
[Dispatcher/Admin] révise l'install + l'aperçu prorata → clique « Approuver la facturation »
        │
        ▼  séquence atomique-idempotente (clé = job/approbation)
   a. Construire le prorata consolidé (moteur : récurrent proraté, ponctuel/install plein,
      rabais selon le type, arrondi plus-grand-reste)
   b. Créer la facture dans F (table `invoice`, date_orig = date d'approbation)   ← push-to-F
   c. Charger immédiatement off-session via Stripe (clé d'idempotence = id facture)
   d. Sur succès : poser billed_amt = total (règlement) → exclu du balayage du 15
   e. Activer les abonnements (Actif, start_date = lendemain — jour d'activation gratuit)
   f. Re-synchro miroir ERPNext
        │
        ▼
Récurrent : cycle normal de F (émission le 1er via task_charge_recurrent, balayage le 15)
```

## État actuel (acquis — P0)

- **Moteur** `services/targo-hub/lib/proration.js` : consolidation *type-aware* (récurrent proraté /
  ponctuel-install plein / rabais selon le type), arrondi **plus-grand-reste** (somme exacte, 0 résidu),
  écriture **brouillon double-gatée** (`commit:true` + env `PRORATION_WRITE=on`, OFF par défaut).
  Endpoints `POST /billing/prorate-preview` et `POST /billing/activation-preview` (lecture seule).
- **dispatch.js** `activateSubscriptionForJob` : déjà branché sur le moteur → **UNE** facture
  consolidée (au lieu d'une par abonnement) ; filtre strict `En attente` (ne re-facture jamais les
  actifs) ; gaté `PRORATION_WRITE` (n'écrit rien tant que non armé).
- **F** : `stripe_ppa.php` (balayage du 15, `sum(total_amt-billed_amt)`), `task_charge_recurrent.php`
  (émission le 1er, due_date 16), `payment_apply.php` / `credit_creance.php` (règlent `billed_amt`),
  `pay_*`/`payment_add.php` (paiement d'une facture précise).

## Composants à bâtir

### A. Ops — la porte d'approbation
- Nouvel état sur `Dispatch Job` (ou doctype léger « Approbation Facturation ») : `À approuver` → `Approuvé`.
- Vue Ops : installs complétées en attente, avec **aperçu de la facture prorata** (via
  `/billing/activation-preview`, lecture seule) — l'approbateur voit exactement ce qui sera facturé.
- Action « Approuver la facturation » (permission dispatcher/admin) → `POST` hub. Idempotente.

### B. Hub — l'orchestrateur
- `POST /billing/approve-activation { job | customer + service_location }` (staff-gated).
- Séquence b→f ci-dessus, **idempotente** (clé = job/approbation/mois), avec **reprise sur échec partiel**
  (ex. facture créée mais charge échouée → reprend à la charge, ne recrée pas la facture).

### C. F — charge immédiate *scoped* (test-first)
- Réutiliser la logique **off-session** de `stripe_ppa.php` + le **règlement** de `payment_apply.php`,
  mais **scoped à UNE facture** (le prorata) au lieu du solde complet.
- Clé d'idempotence Stripe par facture ; `billed_amt = total` + `billing_status = 1` sur succès.

### D. Push facture (hub/ERPNext → F)
- Le prorata DOIT exister dans la table `invoice` de F (sinon ni chargé, ni balayé). Relié au chantier
  `project_sales_sync_f` (push-to-F). Reco : créer en ERPNext (déjà bâti) **puis pousser vers F**.

## Garde-fous (non négociables)

- **Idempotence** : clé Stripe par facture + reprise. (Leçon juin : charge OK mais règlement échoue
  sous charge → recharge. Ici : clé idempotence + règlement fiable.)
- **`PRORATION_WRITE` off** jusqu'à validation ; brouillon → revue → armement.
- **Test-first** : Stripe **test mode** + client **C-LPB4**, puis **1 client réel**, puis rollout.
- **Pas de carte au dossier** → courriel (comme `stripe_ppa.php`), NE PAS marquer payé.
- **Échec de charge** → facture reste due (`billed_amt` inchangé) → rejoint le **balayage du 15** (filet)
  + alerte ; NE JAMAIS activer « payé » sans confirmation Stripe.
- **Dernier jour du mois** → prorata 0 (le récurrent du 1er couvre) — pas de facture prorata.
- **Ré-approbation / double-clic** → idempotent : ne pas créer 2 factures prorata pour la même
  activation (clé = service_location + job + mois).
- **CRTC 2026-43** : frais d'installation facturables (exception) — la ligne installation au plein
  est conforme (cf `project_crtc_2026_43`).

## Phasage (incrémental, réversible)

| Phase | Contenu | Risque facturation |
|---|---|---|
| **P0** ✅ | Moteur + facture consolidée brouillon (gaté, ERPNext) | nul |
| **P1** | Porte d'approbation Ops : état + **aperçu lecture seule** + action (sans facturer) | nul |
| **P2** | Push prorata → F (créer la facture dans F, **sans charger**) ; valider qu'elle serait balayée le 15 | faible (filet 15) |
| **P3** | Charge immédiate *scoped* dans F (test mode → C-LPB4 → 1 client) + règlement `billed_amt` + idempotence | contrôlé |
| **P4** | Câbler approbation → orchestrateur complet (auto) + monitoring/alertes | contrôlé |
| **P5** | Armer en prod (`PRORATION_WRITE=on`) après revue d'un cycle complet | go-live |

## Décisions ouvertes restantes

- **Locus de création de la facture** : F-direct vs ERPNext+push. Reco : ERPNext d'abord (déjà bâti) puis push-to-F.
- **Ce que l'approbateur révise** (checklist d'install, photos ?) — hors scope facturation pour l'instant.
- **Mapping client F↔ERPNext** pour le push (legacy_account_id / stripe_id) — à confirmer via `project_sales_sync_f`.
