feat(r3.1): socle backend gestion — stock dérivé, BC, coûts figés sur OT

- migration r3_gestion (7 tables) : Partner, Part (SANS colonne de
  quantité), StockMovement (signé, tracé, PU figé), PurchaseOrder/Line,
  LaborTime (taux figé), Document (R3.2) ; User.hourlyRate administrable
- API (66 opérations) : tiers ; pièces (stock = Σ mouvements, alerte sous
  seuil) ; entrée/ajustement (motif requis, stock jamais négatif, en
  transaction) ; BC Brouillon→Envoyé→Reçu (la réception crée les RECEIPT
  et met à jour lastUnitPrice) ; consommation sur OT (stock suffisant,
  PRIX FIGÉ) ; main-d'œuvre (TAUX FIGÉ, refus si taux non défini) ;
  WorkOrderDetail.costs ; coûts verrouillés après clôture (409)
- seed : tiers/pièces/BC/taux de la maquette — OT-0341 = 505 MAD (testé)
- durcissement : références OT/DEM/BC/P par SÉQUENCES Postgres (nextval)
  — fin des courses « max+1 » (500 sporadiques sous charge parallèle) ;
  3 runs Jest complets consécutifs verts
- 55 tests (92 % stmts / 74,9 % branches) dont la recette officielle :
  consommer sous seuil → BC → réception → réappro, prix/taux figés prouvés

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
pr-daaif
2026-07-16 20:42:03 +01:00
parent d5041c6f05
commit dfdf8f7c18
33 changed files with 5361 additions and 717 deletions

View File

@@ -0,0 +1,16 @@
-- Références OT/DEM/BC/P : séquences Postgres — la concurrence ne peut plus
-- produire de collision (leçon des générations parallèles).
-- La numérotation ne se remet pas à zéro chaque année : l'unicité prime.
CREATE SEQUENCE IF NOT EXISTS "work_order_ref_seq";
CREATE SEQUENCE IF NOT EXISTS "request_ref_seq";
CREATE SEQUENCE IF NOT EXISTS "purchase_order_ref_seq";
CREATE SEQUENCE IF NOT EXISTS "part_ref_seq";
SELECT setval('work_order_ref_seq',
GREATEST((SELECT COALESCE(MAX(SUBSTRING("reference" FROM '\d+$')::int), 0) FROM "WorkOrder"), 1000));
SELECT setval('request_ref_seq',
GREATEST((SELECT COALESCE(MAX(SUBSTRING("reference" FROM '\d+$')::int), 0) FROM "Request"), 1000));
SELECT setval('purchase_order_ref_seq',
GREATEST((SELECT COALESCE(MAX(SUBSTRING("reference" FROM '\d+$')::int), 0) FROM "PurchaseOrder"), 1000));
SELECT setval('part_ref_seq',
GREATEST((SELECT COALESCE(MAX(SUBSTRING("reference" FROM '\d+$')::int), 0) FROM "Part"), 1000));