mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 12:41:54 +00:00
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:
@@ -186,10 +186,63 @@ model MeterReading { id, meterId (cascade), value Int, readById? → User, creat
|
||||
| Relevé de compteur strictement croissant | service meters |
|
||||
| Génération mensuelle idempotente ; appareil sans historique ⇒ premier contrôle (toutes tâches) | service préventif (R2.2, + tests) |
|
||||
|
||||
## R3 — Gestion
|
||||
|
||||
```prisma
|
||||
enum PartnerKind { SUPPLIER CLIENT } // fournisseur / syndic
|
||||
enum PurchaseOrderStatus { DRAFT SENT RECEIVED CANCELLED }
|
||||
enum StockMovementKind { RECEIPT ENTRY CONSUMPTION ADJUSTMENT }
|
||||
enum DocumentKind { NOTICE CERTIFICATE PHOTO OTHER }
|
||||
|
||||
model Partner { id, name @unique, kind, contact…, isActive }
|
||||
|
||||
model Part { // AUCUNE colonne de quantité : le stock EST la
|
||||
id, reference @unique // somme des mouvements (décision v1 éprouvée)
|
||||
designation, threshold Int // seuil d'alerte
|
||||
lastUnitPrice Decimal? // dernier prix d'achat (mis à jour à la réception)
|
||||
supplierId? → Partner
|
||||
}
|
||||
|
||||
model StockMovement { // + entrée / − sortie, TOUJOURS tracé
|
||||
id, partId (cascade), kind, quantity Int (signé)
|
||||
unitPrice Decimal? // FIGÉ (réception : PU du BC ; consommation : lastUnitPrice)
|
||||
reason String? // ADJUSTMENT : motif REQUIS (service)
|
||||
workOrderId? → WorkOrder // consommation
|
||||
purchaseOrderId? → PurchaseOrder // réception
|
||||
byId? → User
|
||||
}
|
||||
|
||||
model PurchaseOrder { // DRAFT → SENT → RECEIVED ; annulable avant réception
|
||||
id, reference @unique, status, supplierId → Partner
|
||||
lines PurchaseOrderLine[] // partId, quantity, unitPrice Decimal
|
||||
// la RÉCEPTION crée un mouvement RECEIPT par ligne et met à jour lastUnitPrice
|
||||
}
|
||||
|
||||
model LaborTime { // heures × taux FIGÉ à la saisie
|
||||
id, workOrderId (cascade), userId → User, minutes Int, hourlyRate Decimal, note?
|
||||
}
|
||||
// User reçoit hourlyRate Decimal? (taux COURANT, administrable)
|
||||
|
||||
model Document { // MinIO derrière FileStorage — jamais d'accès direct
|
||||
id, kind, fileName, storageKey @unique, size, contentType
|
||||
assetId? / workOrderId? // rattachement REQUIS à l'un des deux (service)
|
||||
}
|
||||
```
|
||||
|
||||
**Invariants R3** :
|
||||
|
||||
| Invariant | Où il vit |
|
||||
| --- | --- |
|
||||
| Stock = Σ mouvements ; jamais de saisie directe de quantité | par construction (pas de colonne) |
|
||||
| Un ajustement exige un motif ; le stock ne devient jamais négatif | service parts (+ tests) |
|
||||
| Consommation : PU figé = `lastUnitPrice` au moment T ; stock suffisant exigé | service (transaction) |
|
||||
| Réception : BC `SENT` uniquement ; crée les RECEIPT + met à jour `lastUnitPrice` | service (transaction) |
|
||||
| Main-d'œuvre : taux figé = `User.hourlyRate` au moment T (refus si non défini) | service |
|
||||
| Coût total d'un OT = Σ consommations + Σ main-d'œuvre — IMMUABLE après coup | dérivé des lignes figées |
|
||||
| Document : rattaché à un appareil OU un OT ; types fermés ; 20 Mo max | service documents (R3.2) |
|
||||
|
||||
## À venir (référence v1 éprouvée, sera réintroduit release par release)
|
||||
|
||||
- **R3** : `Part`/`StockMovement` (stock **dérivé des mouvements**), `PurchaseOrder`,
|
||||
`Partner`, `LaborTime` (taux figé), `Document`.
|
||||
- **R4** : `WorkOrder.version` (verrou optimiste de la synchro mobile).
|
||||
- **R5** : tables d'embeddings pgvector (côté service IA).
|
||||
|
||||
|
||||
@@ -4,6 +4,24 @@ Trace chronologique des sessions (la plus récente en premier). Le **playbook**
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-16 — Pr. Daaif (+ Claude) — R3.1 : socle backend de la gestion
|
||||
|
||||
**Actions**
|
||||
|
||||
- **Maquettes R3 et les 4 décisions VALIDÉES par le référent** → lancement du socle.
|
||||
- **Modèle** (migration `r3_gestion`, 7 tables) : Partner, Part (SANS colonne de quantité), StockMovement (signé, tracé, PU figé), PurchaseOrder/Line, LaborTime (taux figé), Document (préparé pour R3.2) ; `User.hourlyRate` (taux courant, administrable dans Personnes).
|
||||
- **API** (66 opérations au contrat) : tiers, pièces (stock = Σ mouvements calculé en `groupBy`, alerte sous seuil), entrée/ajustement (motif requis, **stock jamais négatif** — vérifié 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é**) et main-d'œuvre (**taux figé**, refus motivé si taux non défini) ; `WorkOrderDetail.costs` (lignes + total immuables) ; coûts verrouillés après clôture/annulation (409).
|
||||
- **Seed** : 5 tiers, 5 pièces de la maquette (P-0113 sous seuil via son histoire de mouvements), BC reçu + BC envoyé, taux horaires, et **la carte maquette rejouée : OT-0341 = 505 MAD** (240 + 85 + 180) — vérifiée par test.
|
||||
- **55 tests verts** (92 % / 74,9 %) dont la recette officielle : consommer sous seuil → alerte → BC → réception → réappro, prix d'hier intact sur l'OT pendant que le prix courant change ; hausse du taux d'Ahmed sans effet sur les OT passés.
|
||||
|
||||
**Leçon majeure (durcissement)**
|
||||
|
||||
- Les références « max+1 » (OT/DEM/BC/P) étaient une **course sous charge parallèle** (500 sporadiques malgré les retries). Remplacées par des **séquences Postgres** (`nextval`) initialisées au max existant : la classe de bugs disparaît — 3 runs Jest complets consécutifs verts. La numérotation ne se remet pas à zéro chaque année (l'unicité prime), consigné.
|
||||
|
||||
**Prochaine étape** : R3.2 — bibliothèque de documents (upload multipart 20 Mo → `FileStorage.putObject`, téléchargement streamé via l'API — MinIO jamais exposé) + analytics (`GET /analytics/summary`), puis R3.3 écrans web.
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-16 — Pr. Daaif (+ Claude) — R3 ouverte : maquettes de la gestion à valider
|
||||
|
||||
**Actions**
|
||||
|
||||
2840
docs/openapi.json
2840
docs/openapi.json
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user