meta { name: 01 KPIs type: http seq: 1 } get { url: {{baseUrl}}/api/v1/dashboard/kpis body: none auth: inherit } tests { test("200 OK", function () { expect(res.getStatus()).to.equal(200); }); test("KPIs shape", function () { const k = res.getBody().data; for (const key of [ "rubisCount", "rubisThisMonth", "hoursLiberatedThisMonth", "encaisseCents", "encaisseDeltaCents", "dsoDays", "dsoDeltaDays", "factureToRelance", "factureInRelance", "factureNewToday", "miseEnDemeurePending", "monthlyGoalProgress" ]) { expect(k).to.have.property(key); } }); } docs { GET /api/v1/dashboard/kpis Calculs agrégés sur les invoices de l'org : - `rubisCount` lu sur Organization (compteur cumulé) - `rubisThisMonth` = sum(rubis_earned where paid_at >= startOfMonth) - `hoursLiberatedThisMonth` = rubisThisMonth × 10 (1 rubis = 10 min) - `encaisseCents` = sum(amount_ttc_cents where paid this month) - `encaisseDeltaCents` = ce mois − mois précédent - `dsoDays` = avg(paid_at − issue_date) en jours, sur factures payées ce mois - `dsoDeltaDays` = idem delta vs mois précédent - `factureToRelance` = count(status='pending') - `factureInRelance` = count(status='in_relance') - `factureNewToday` = count(created_at >= today) - `miseEnDemeurePending` = 0 (à brancher quand RelanceTask sera là) - `monthlyGoalProgress` = clamp(rubisThisMonth/25*100, 0, 100) — placeholder - `percentile` = undefined V1 }