// P1 — Porte d'APPROBATION de facturation (chaîne installation → approbation → facturation). // Quand le job terminal est complété (et que BILLING_APPROVAL_GATE=on), l'activation est placée // « à approuver » au lieu d'activer/facturer directement. Un dispatcher/admin révise l'aperçu // prorata (lecture seule) puis approuve → on ACTIVE les abonnements. La FACTURATION reste gérée // par activateSubscriptionForJob (gatée PRORATION_WRITE) : en P1 (gate off) → active SANS facturer. // // Store : data/billing-approvals.json (même répertoire persistant que les autres données du hub). const fs = require('fs') const path = require('path') const STORE = path.join(__dirname, '..', 'data', 'billing-approvals.json') function _read () { try { return JSON.parse(fs.readFileSync(STORE, 'utf8')) } catch (_) { return {} } } function _write (o) { fs.mkdirSync(path.dirname(STORE), { recursive: true }); fs.writeFileSync(STORE, JSON.stringify(o, null, 2)) } // Appelé à la complétion du job terminal (gaté). Marque « à approuver ». N'ACTIVE PAS, NE FACTURE PAS. // Idempotent : ne rétrograde jamais un job déjà approuvé. function markPending (job, completed_at) { if (!job) return null const all = _read() if (all[job] && all[job].status === 'approved') return all[job] all[job] = { job, completed_at: completed_at || (all[job] && all[job].completed_at) || new Date().toISOString(), status: 'pending', approved_by: null, approved_at: null, } _write(all) return all[job] } function listPending () { return Object.values(_read()).filter(a => a.status === 'pending') } function get (job) { return _read()[job] || null } // Approbation : enregistre la décision PUIS active les abonnements via dispatch.activateSubscriptionForJob. // La facturation (création de la facture consolidée) est gérée DANS activateSubscriptionForJob et reste // DOUBLE-GATÉE (PRORATION_WRITE) → en P1 (gate off) : active SANS écrire de facture. async function approve (job, approver) { if (!job) throw new Error('job requis') const all = _read() const rec = all[job] if (!rec) throw new Error('aucune approbation en attente pour ' + job) if (rec.status === 'approved') return { ...rec, already: true } const result = await require('./dispatch').activateSubscriptionForJob(job) rec.status = 'approved' rec.approved_by = approver || 'inconnu' rec.approved_at = new Date().toISOString() rec.activation = { activated: result.activated || [], invoice: result.invoice || null, written: !!result.written, prorated_total: result.prorated_total != null ? result.prorated_total : null, } _write(all) return rec } module.exports = { markPending, listPending, get, approve }