Aller au contenu

Lot 1, séquence A3 — Numérotation

1er octobre 2026. Mise en œuvre de la séquence A3 du plan du Lot 1. Décisions : EC-010, EC-023 ; écart ouvert : EC-068.

  • Le rang d’un numéro vient de la base ; son format vient du domaine. La base garantit l’unicité et l’absence de trou, numeroOfficiel (packages/domain) compose la chaîne. Un seul endroit connaît le format.
  • Le format par défaut est celui de l’avenant n°1 : {AA}{code campagne}/{nature}/{produit}/{NNNN}, par exemple 26A/MP/ANA/0001. Le préfixe entreprise existe comme segment, désactivé.
  • Aucune route REST en A3 : le premier consommateur est le Lot 2 (validation d’un lot). L’API expose un service interne.
Table Contenu
app.modele_numerotation Une ligne par version du modèle d’une organisation, en ajout seul. segments est un tableau au format de numeroOfficiel, avec exactement un segment de séquence.
app.sequence_numero Un compteur par (organisation, campagne, nature, produit). derniere_valeur ne revient jamais en arrière. Non journalisée : le numéro attribué l’est avec le document qui le porte.

app.campagne pointe maintenant sur app.modele_numerotation (organisation_id, version) par une clé étrangère composée. Une campagne fige sa version à l’ouverture (déclencheur de A1) : publier une version 2 ne change donc pas les campagnes déjà ouvertes.

Modèle par défaut. La version 1 est créée avec l’organisation par un déclencheur AFTER INSERT (SECURITY DEFINER, car l’API écrit avec le rôle soumis à la RLS). La migration le crée aussi pour les organisations existantes.

Natures. MP (matière première), REC (réception), NET (nettoyage), PF (produit fini), d’après le cahier v1.2 §5.10. « Vente » a disparu (avenant n°1). Les codes sont à confirmer : voir EC-068.

app.attribuer_numero(campagne_id, nature, produit_id) renvoie le rang suivant :

  1. la campagne doit exister dans l’organisation et être ouverte ;
  2. la ligne de séquence est créée au besoin, puis incrémentée par un UPDATE qui la verrouille jusqu’à la fin de la transaction ;
  3. deux appels simultanés se suivent ; une transaction annulée rend son rang.

La fonction s’exécute avec le rôle de l’appelant : la RLS s’applique.

attribuerNumero(trx, …) (apps/api/src/numerotation/numerotation.service.ts) lit la campagne, le produit et le modèle, appelle la fonction SQL, puis numeroOfficiel. Il rend { ok: true, numero, rang } ou une erreur : campagne_introuvable, campagne_non_ouverte, produit_introuvable, modele_corrompu, sequence_hors_capacite, segment_manquant.

Règle pour l’appelant : sur une erreur, annuler la transaction. Le rang est déjà pris dans cette transaction ; seule l’annulation le rend. C’est vrai surtout pour sequence_hors_capacite, qui ne peut être décidé qu’après coup puisque la largeur de la séquence est dans le modèle, que la base ne lit pas.

Test Ce qu’il vérifie
db/tests/l1-a3.sql Modèle par défaut créé (y compris par le rôle de l’API), ajout seul, version unique, un seul segment de séquence, clé étrangère de la campagne, séquences indépendantes, nature « VTE » refusée, campagne non ouverte, rang rendu après annulation, isolation entre organisations
A3-01 Deux campagnes la même année : 26A/MP/ANA/0001 et 26B/MP/ANA/0001
A3-02 Cent transactions simultanées : rangs 1 à 100, aucun doublon ni trou ; transaction annulée, rang rendu
A3-03 Rang 10 000 refusé (sequence_hors_capacite), séquence laissée à 9 999
A3-04 Une version 2 du modèle ne touche pas une campagne ouverte sur la version 1

Route de lecture du modèle pour l’écran B3 de la numérotation ; publication d’une nouvelle version par l’utilisateur (route et permission) ; source du préfixe entreprise quand il sera actif (EC-068) ; règles PowerSync (A9).