Planificateur « Suggérer » : 4 stratégies (smart / meilleurs d'abord / équilibré / juste ce qu'il faut), compétences+niveaux par tech (édition inline), niveau requis par compétence + par job, carte des tournées (1 couleur/tech, domicile→arrêts, sélecteur de jour), fenêtre de dispatch auj.+demain (dates sélectionnées), règle week-end + placeholder « en attente du quart », clustering + lasso + filtre-date sur la carte, accès rapide « À assigner » (badge). Boîte : liste /conversations allégée (45 Mo → ~1 Mo, 17×) + messages chargés à l'ouverture. Rapports : cache SWR sur revenue-explorer (22×). Session : keep-alive + timeout fetch global + authFetch durci → fin des rechargements manuels. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7.0 KiB
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 = totalsur 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+ envPRORATION_WRITE=on, OFF par défaut). EndpointsPOST /billing/prorate-previewetPOST /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 strictEn 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èglentbilled_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) →
POSThub. 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 depayment_apply.php, mais scoped à UNE facture (le prorata) au lieu du solde complet. - Clé d'idempotence Stripe par facture ;
billed_amt = total+billing_status = 1sur succès.
D. Push facture (hub/ERPNext → F)
- Le prorata DOIT exister dans la table
invoicede F (sinon ni chargé, ni balayé). Relié au chantierproject_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_WRITEoff 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_amtinchangé) → 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.