{
  "plan": "mostajs-devtest/1",
  "project": {
    "key": "school-billing",
    "name": "@mostajs/school-billing"
  },
  "specs": [
    {
      "ref": "SPEC-SB-01",
      "title": "Une facture est la somme de ses frais, et porte un numéro de reçu",
      "priority": "critical",
      "status": "verified"
    },
    {
      "ref": "SPEC-SB-02",
      "title": "Remise en pourcentage et remise en montant se calculent différemment",
      "priority": "critical",
      "status": "verified"
    },
    {
      "ref": "SPEC-SB-03",
      "title": "L'encaissement compose @mostajs/payment et fait passer le statut",
      "priority": "critical",
      "status": "verified"
    },
    {
      "ref": "SPEC-SB-04",
      "title": "L'encours ne compte que l'impayé, et le revenu que l'encaissé",
      "priority": "critical",
      "status": "verified"
    },
    {
      "ref": "SPEC-SB-05",
      "title": "Le reçu compose @mostajs/file-export, sans connaître les formats",
      "priority": "high",
      "status": "verified"
    },
    {
      "ref": "SPEC-SB-06",
      "title": "Le mensuel compose @mostajs/subscriptions-plan, avec la portée ÉLÈVE",
      "priority": "high",
      "status": "verified"
    },
    {
      "ref": "SPEC-SB-07",
      "title": "Le MOYEN de règlement et son détail sont conservés sur la facture",
      "priority": "critical",
      "status": "verified"
    },
    {
      "ref": "SPEC-CTR-01",
      "priority": "critical",
      "status": "verified",
      "title": "Le module CONTRIBUE ses schémas et ses permissions par le contrat ; l'app n'en tient aucune liste"
    }
  ],
  "realisations": [
    {
      "ref": "REL-SB-01",
      "specRef": "SPEC-SB-01",
      "kind": "feature",
      "title": "`invoices.create` ; numérotation déléguée à @mostajs/numbering",
      "artifact": "src/school-billing.js",
      "progress": 100,
      "status": "done"
    },
    {
      "ref": "REL-SB-02",
      "specRef": "SPEC-SB-02",
      "kind": "feature",
      "title": "`applyDiscount` : proportion d'un côté, soustraction plafonnée de l'autre",
      "artifact": "src/school-billing.js",
      "progress": 100,
      "status": "done"
    },
    {
      "ref": "REL-SB-03",
      "specRef": "SPEC-SB-03",
      "kind": "feature",
      "title": "`invoices.pay` — le module n'encaisse pas lui-même, il délègue",
      "artifact": "src/school-billing.js",
      "progress": 100,
      "status": "done"
    },
    {
      "ref": "REL-SB-04",
      "specRef": "SPEC-SB-04",
      "kind": "feature",
      "title": "`outstanding` et `revenue` — deux lectures distinctes de la même table",
      "artifact": "src/school-billing.js",
      "progress": 100,
      "status": "done"
    },
    {
      "ref": "REL-SB-05",
      "specRef": "SPEC-SB-05",
      "kind": "feature",
      "title": "`invoices.receipt` — le format demandé est transmis tel quel",
      "artifact": "src/school-billing.js",
      "progress": 100,
      "status": "done"
    },
    {
      "ref": "REL-SB-06",
      "specRef": "SPEC-SB-06",
      "kind": "feature",
      "title": "`subscribeMonthly` — portée `{scopeType:'student'}`",
      "artifact": "src/school-billing.js",
      "progress": 100,
      "status": "done"
    },
    {
      "ref": "REL-SB-07",
      "specRef": "SPEC-SB-07",
      "kind": "feature",
      "title": "`method` (vocabulaire @mostajs/paiement-contrat) et `paymentDetails` au SCHÉMA",
      "artifact": "src/school-billing.js",
      "progress": 100,
      "status": "done"
    },
    {
      "ref": "REL-CTR-01",
      "specRef": "SPEC-CTR-01",
      "kind": "feature",
      "title": "`./register` — manifeste, permissions {permissions, definitions, categories}, schémas en enveloppe opaque",
      "artifact": "src/register.js",
      "progress": 100,
      "status": "done"
    }
  ],
  "tests": [
    {
      "ref": "T-SB-1",
      "specRef": "SPEC-SB-01",
      "title": "T-SB-1 — une facture est la SOMME de ses frais, et porte un numéro de reçu",
      "type": "automated",
      "priority": "critical",
      "autoRef": "test-scripts/unit/school-billing.test.mjs::T-SB-1",
      "steps": [
        {
          "action": "facturer deux frais",
          "expected": "sous-total = somme, total identique sans remise, numéro de reçu attribué"
        }
      ]
    },
    {
      "ref": "T-SB-2",
      "specRef": "SPEC-SB-02",
      "title": "T-SB-2 — remise en POURCENTAGE et remise en MONTANT ne se calculent pas pareil",
      "type": "automated",
      "priority": "critical",
      "autoRef": "test-scripts/unit/school-billing.test.mjs::T-SB-2",
      "steps": [
        {
          "action": "appliquer 10 %, puis un montant fixe",
          "expected": "le pourcentage proportionne, le montant soustrait"
        }
      ]
    },
    {
      "ref": "T-SB-3",
      "specRef": "SPEC-SB-03",
      "title": "T-SB-3 — l’encaissement compose @mostajs/payment et fait passer le statut",
      "type": "automated",
      "priority": "critical",
      "autoRef": "test-scripts/unit/school-billing.test.mjs::T-SB-3",
      "steps": [
        {
          "action": "encaisser avec un dialecte injecté",
          "expected": "statut `paid`, motif `school-fee` transmis"
        }
      ]
    },
    {
      "ref": "T-SB-4",
      "specRef": "SPEC-SB-04",
      "title": "T-SB-4 — l’ENCOURS ne compte que l’impayé, et le revenu que l’encaissé",
      "type": "automated",
      "priority": "critical",
      "autoRef": "test-scripts/unit/school-billing.test.mjs::T-SB-4",
      "steps": [
        {
          "action": "deux factures, une réglée",
          "expected": "l'encours retombe, le revenu ne retient que l'encaissé"
        }
      ]
    },
    {
      "ref": "T-SB-5",
      "specRef": "SPEC-SB-05",
      "title": "T-SB-5 — le reçu compose @mostajs/file-export",
      "type": "automated",
      "priority": "high",
      "autoRef": "test-scripts/unit/school-billing.test.mjs::T-SB-5",
      "steps": [
        {
          "action": "demander un reçu",
          "expected": "le document est produit par l'export injecté, le format est transmis"
        }
      ]
    },
    {
      "ref": "T-SB-6",
      "specRef": "SPEC-SB-06",
      "title": "T-SB-6 — le mensuel compose @mostajs/subscriptions-plan, avec la portée élève",
      "type": "automated",
      "priority": "high",
      "autoRef": "test-scripts/unit/school-billing.test.mjs::T-SB-6",
      "steps": [
        {
          "action": "abonner un élève",
          "expected": "la portée désigne l'élève — sans quoi deux enfants partagent un abonnement"
        }
      ]
    },
    {
      "ref": "T-PAY-1",
      "specRef": "SPEC-SB-07",
      "title": "T-PAY-1 — le MOYEN et son détail sont conservés sur la facture (0.2.0)",
      "type": "automated",
      "priority": "critical",
      "autoRef": "test-scripts/unit/school-billing.test.mjs::T-PAY-1",
      "steps": [
        {
          "action": "encaisser par chèque avec numéro et banque, puis sans moyen déclaré",
          "expected": "les deux sont persistés ; sans moyen déclaré, `null` — on n'invente rien"
        }
      ]
    },
    {
      "ref": "T-CTR-1",
      "specRef": "SPEC-CTR-01",
      "type": "automated",
      "priority": "critical",
      "title": "T-CTR-1 — le module CONTRIBUE ses schémas et ses permissions par le contrat",
      "autoRef": "test-scripts/unit/school-billing.test.mjs::T-CTR-1",
      "steps": [
        {
          "action": "appeler `register(registry)` et lire ce qui est contribué",
          "expected": "schémas en enveloppe {name, schema} ; permissions en TABLE (un tableau rend {} en SILENCE) ; codes aux deux-points ; manifeste = package.json"
        }
      ]
    }
  ],
  "_comment": "Plan écrit lors du passage à @mostajs/mjs-unit (0.2.0, 2026-09-01). Le module n'en avait aucun : ses essais tournaient sous un harnais fait main qui interrompait le fichier au premier échec — les suivants n'étaient pas rouges, ils n'étaient pas JOUÉS — et ne produisait aucun rapport JSON à importer. Les exigences ci-dessous sont la rétro-documentation de ce que les essais vérifiaient déjà, plus SPEC-SB-07, neuve, demandée par ATC · I4 (carte, chèque, espèces). CONTRAT AJOUTÉ (0.3.0, 2026-09-01). Le module n'avait pas de `./register` : chaque application devait écrire ses schémas dans une liste tenue à la main — et une liste tenue à la main ne sait pas qu'un module a gagné une entité. La table n'est alors jamais créée, et rien ne le dit jusqu'à la première écriture en production (DEVRULES §10)."
}
