mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 12:41:54 +00:00
feat(r2.1): socle backend exploitation — machine à états, bilan codé, demandes 1-1
- migration r2_exploitation (10 tables) : WorkOrder (référence séquentielle, horodatages), WorkOrderEvent, Request (1-1, motif de rejet), ReferenceValue, InterventionReport (6 FK), TaskTemplate/ChecklistItem, Meter/MeterReading - contrat : 15 opérations (41 total) ; la table des transitions et les champs requis du bilan vivent dans @siop/shared ; la fiche OT expose allowedTransitions + closureBlockers (messages métier) - API : machine à états stricte ; garde de clôture (bilan 3 champs requis + checklist sans tâche en attente) ; approbation → OT lié 1-1 (409 si déjà traitée) ; rejet à motif obligatoire ; scoping « voir autre » sur listes et accès directs (404 sans fuite) ; validation des valeurs de bilan par champ ; « personne bloquée » triée en tête côté API - seed : 31 valeurs de référentiels, 8 gabarits (parachute réglementaire), OT/demandes/compteurs de la maquette — idempotent - 45 tests verts (95 % stmts / 79 % branches) dont la recette officielle rejouée de bout en bout ; smoke test sur build de prod Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -37,6 +37,26 @@ import {
|
||||
UsersResponseSchema,
|
||||
UserUpdateSchema,
|
||||
} from './schemas/users-admin';
|
||||
import {
|
||||
AssigneesUpdateSchema,
|
||||
ChecklistItemSchema,
|
||||
ChecklistPatchSchema,
|
||||
CommentCreateSchema,
|
||||
ReferenceValueCreateSchema,
|
||||
ReferenceValueSchema,
|
||||
ReferenceValuesResponseSchema,
|
||||
ReferenceValueUpdateSchema,
|
||||
ReportUpsertSchema,
|
||||
RequestApproveSchema,
|
||||
RequestCreateSchema,
|
||||
RequestRejectSchema,
|
||||
RequestsResponseSchema,
|
||||
RequestSummarySchema,
|
||||
TransitionRequestSchema,
|
||||
WorkOrderCreateSchema,
|
||||
WorkOrderDetailSchema,
|
||||
WorkOrdersResponseSchema,
|
||||
} from './schemas/exploitation';
|
||||
|
||||
/**
|
||||
* Contrat d'API R0 — source unique de vérité (règle d'or ADR-001).
|
||||
@@ -367,6 +387,189 @@ export const API_CONTRACT: ApiOperation[] = [
|
||||
404: { description: 'Inconnue' },
|
||||
},
|
||||
},
|
||||
// ————— R2 · Exploitation —————
|
||||
{
|
||||
operationId: 'listWorkOrders',
|
||||
method: 'get',
|
||||
path: '/work-orders',
|
||||
summary: 'Ordres de travail (sans « voir autre » : seulement les siens)',
|
||||
tags: ['work-orders'],
|
||||
responses: {
|
||||
200: { description: 'Liste', name: 'WorkOrdersResponse', schema: WorkOrdersResponseSchema },
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'getWorkOrder',
|
||||
method: 'get',
|
||||
path: '/work-orders/{id}',
|
||||
summary: 'Fiche OT (activité, checklist, bilan, transitions autorisées, blocages de clôture)',
|
||||
tags: ['work-orders'],
|
||||
pathParams: ['id'],
|
||||
responses: {
|
||||
200: { description: 'Fiche', name: 'WorkOrderDetail', schema: WorkOrderDetailSchema },
|
||||
404: { description: 'Inconnu (ou hors de son périmètre)' },
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'createWorkOrder',
|
||||
method: 'post',
|
||||
path: '/work-orders',
|
||||
summary: 'Créer un OT (statut Ouvert)',
|
||||
tags: ['work-orders'],
|
||||
request: { name: 'WorkOrderCreate', schema: WorkOrderCreateSchema },
|
||||
responses: {
|
||||
201: { description: 'Créé', name: 'WorkOrderDetail', schema: WorkOrderDetailSchema },
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'transitionWorkOrder',
|
||||
method: 'post',
|
||||
path: '/work-orders/{id}/transition',
|
||||
summary: 'Changer l’état (machine à états stricte ; DONE exige bilan + checklist)',
|
||||
tags: ['work-orders'],
|
||||
pathParams: ['id'],
|
||||
request: { name: 'TransitionRequest', schema: TransitionRequestSchema },
|
||||
responses: {
|
||||
200: { description: 'État changé', name: 'WorkOrderDetail', schema: WorkOrderDetailSchema },
|
||||
409: { description: 'Transition interdite ou clôture bloquée (garde)' },
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'commentWorkOrder',
|
||||
method: 'post',
|
||||
path: '/work-orders/{id}/comments',
|
||||
summary: 'Commenter (activité chronologique)',
|
||||
tags: ['work-orders'],
|
||||
pathParams: ['id'],
|
||||
request: { name: 'CommentCreate', schema: CommentCreateSchema },
|
||||
responses: {
|
||||
201: { description: 'Commentaire ajouté', name: 'WorkOrderDetail', schema: WorkOrderDetailSchema },
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'setWorkOrderAssignees',
|
||||
method: 'put',
|
||||
path: '/work-orders/{id}/assignees',
|
||||
summary: 'Assigner (remplace la liste)',
|
||||
tags: ['work-orders'],
|
||||
pathParams: ['id'],
|
||||
request: { name: 'AssigneesUpdate', schema: AssigneesUpdateSchema },
|
||||
responses: {
|
||||
200: { description: 'Assignés', name: 'WorkOrderDetail', schema: WorkOrderDetailSchema },
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'upsertWorkOrderReport',
|
||||
method: 'put',
|
||||
path: '/work-orders/{id}/report',
|
||||
summary: 'Renseigner le bilan codé (null efface un champ)',
|
||||
tags: ['work-orders'],
|
||||
pathParams: ['id'],
|
||||
request: { name: 'ReportUpsert', schema: ReportUpsertSchema },
|
||||
responses: {
|
||||
200: { description: 'Bilan enregistré', name: 'WorkOrderDetail', schema: WorkOrderDetailSchema },
|
||||
400: { description: 'Valeur hors référentiel du champ' },
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'patchChecklistItem',
|
||||
method: 'patch',
|
||||
path: '/work-orders/{id}/checklist/{itemId}',
|
||||
summary: 'Régler une tâche de la grille (Fait / N-A / à faire)',
|
||||
tags: ['work-orders'],
|
||||
pathParams: ['id', 'itemId'],
|
||||
request: { name: 'ChecklistPatch', schema: ChecklistPatchSchema },
|
||||
responses: {
|
||||
200: { description: 'Tâche réglée', name: 'ChecklistItem', schema: ChecklistItemSchema },
|
||||
404: { description: 'Tâche inconnue' },
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'listRequests',
|
||||
method: 'get',
|
||||
path: '/requests',
|
||||
summary: 'Demandes (sans « voir autre » : seulement les siennes)',
|
||||
tags: ['requests'],
|
||||
responses: {
|
||||
200: { description: 'Liste', name: 'RequestsResponse', schema: RequestsResponseSchema },
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'createRequest',
|
||||
method: 'post',
|
||||
path: '/requests',
|
||||
summary: 'Signaler (interne — le portail public QR arrive en R2.4)',
|
||||
tags: ['requests'],
|
||||
request: { name: 'RequestCreate', schema: RequestCreateSchema },
|
||||
responses: {
|
||||
201: { description: 'Demande créée', name: 'RequestSummary', schema: RequestSummarySchema },
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'approveRequest',
|
||||
method: 'post',
|
||||
path: '/requests/{id}/approve',
|
||||
summary: 'Approuver → crée l’OT lié (1-1, jamais de doublon)',
|
||||
tags: ['requests'],
|
||||
pathParams: ['id'],
|
||||
request: { name: 'RequestApprove', schema: RequestApproveSchema },
|
||||
responses: {
|
||||
201: { description: 'OT créé et lié', name: 'WorkOrderDetail', schema: WorkOrderDetailSchema },
|
||||
409: { description: 'Demande déjà traitée' },
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'rejectRequest',
|
||||
method: 'post',
|
||||
path: '/requests/{id}/reject',
|
||||
summary: 'Rejeter — motif obligatoire, lisible côté demandeur',
|
||||
tags: ['requests'],
|
||||
pathParams: ['id'],
|
||||
request: { name: 'RequestReject', schema: RequestRejectSchema },
|
||||
responses: {
|
||||
200: { description: 'Rejetée', name: 'RequestSummary', schema: RequestSummarySchema },
|
||||
409: { description: 'Demande déjà traitée' },
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'listReferenceValues',
|
||||
method: 'get',
|
||||
path: '/reference-values',
|
||||
summary: 'Référentiels du bilan codé (6 champs)',
|
||||
tags: ['reference-values'],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Liste',
|
||||
name: 'ReferenceValuesResponse',
|
||||
schema: ReferenceValuesResponseSchema,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'createReferenceValue',
|
||||
method: 'post',
|
||||
path: '/reference-values',
|
||||
summary: 'Ajouter une valeur de référentiel',
|
||||
tags: ['reference-values'],
|
||||
request: { name: 'ReferenceValueCreate', schema: ReferenceValueCreateSchema },
|
||||
responses: {
|
||||
201: { description: 'Créée', name: 'ReferenceValue', schema: ReferenceValueSchema },
|
||||
409: { description: 'Libellé déjà présent pour ce champ' },
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'updateReferenceValue',
|
||||
method: 'patch',
|
||||
path: '/reference-values/{id}',
|
||||
summary: 'Renommer ou (dés)activer — jamais de suppression si utilisée',
|
||||
tags: ['reference-values'],
|
||||
pathParams: ['id'],
|
||||
request: { name: 'ReferenceValueUpdate', schema: ReferenceValueUpdateSchema },
|
||||
responses: {
|
||||
200: { description: 'Mise à jour', name: 'ReferenceValue', schema: ReferenceValueSchema },
|
||||
404: { description: 'Inconnue' },
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'getHealth',
|
||||
method: 'get',
|
||||
|
||||
Reference in New Issue
Block a user