mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 20:51:53 +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:
46
apps/api/src/reference-values/reference-values.controller.ts
Normal file
46
apps/api/src/reference-values/reference-values.controller.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
ParseUUIDPipe,
|
||||
Patch,
|
||||
Post,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
ReferenceValueCreateSchema,
|
||||
ReferenceValueUpdateSchema,
|
||||
type ReferenceValueCreate,
|
||||
type ReferenceValueUpdate,
|
||||
} from '@siop/shared';
|
||||
import { ZodValidationPipe } from '../common/zod-validation.pipe';
|
||||
import { RequirePermission } from '../permissions/require-permission.decorator';
|
||||
import { ReferenceValuesService } from './reference-values.service';
|
||||
|
||||
@Controller('reference-values')
|
||||
export class ReferenceValuesController {
|
||||
constructor(private readonly referenceValues: ReferenceValuesService) {}
|
||||
|
||||
/** Lu par le formulaire de bilan — authentification seule. */
|
||||
@Get()
|
||||
list() {
|
||||
return this.referenceValues.list();
|
||||
}
|
||||
|
||||
@Post()
|
||||
@RequirePermission('SETTINGS', 'create')
|
||||
create(
|
||||
@Body(new ZodValidationPipe(ReferenceValueCreateSchema)) body: ReferenceValueCreate,
|
||||
) {
|
||||
return this.referenceValues.create(body);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
@RequirePermission('SETTINGS', 'edit')
|
||||
update(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Body(new ZodValidationPipe(ReferenceValueUpdateSchema)) body: ReferenceValueUpdate,
|
||||
) {
|
||||
return this.referenceValues.update(id, body);
|
||||
}
|
||||
}
|
||||
10
apps/api/src/reference-values/reference-values.module.ts
Normal file
10
apps/api/src/reference-values/reference-values.module.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ReferenceValuesController } from './reference-values.controller';
|
||||
import { ReferenceValuesService } from './reference-values.service';
|
||||
|
||||
@Module({
|
||||
controllers: [ReferenceValuesController],
|
||||
providers: [ReferenceValuesService],
|
||||
exports: [ReferenceValuesService],
|
||||
})
|
||||
export class ReferenceValuesModule {}
|
||||
93
apps/api/src/reference-values/reference-values.service.ts
Normal file
93
apps/api/src/reference-values/reference-values.service.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import {
|
||||
ConflictException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import type {
|
||||
ReferenceValueCreate,
|
||||
ReferenceValueDto,
|
||||
ReferenceValuesResponse,
|
||||
ReferenceValueUpdate,
|
||||
} from '@siop/shared';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
const usageInclude = {
|
||||
_count: {
|
||||
select: {
|
||||
doorStates: true,
|
||||
cabinPositions: true,
|
||||
anomalies: true,
|
||||
externalCauses: true,
|
||||
actionsTaken: true,
|
||||
componentsConcerned: true,
|
||||
},
|
||||
},
|
||||
} satisfies Prisma.ReferenceValueInclude;
|
||||
|
||||
type Row = Prisma.ReferenceValueGetPayload<{ include: typeof usageInclude }>;
|
||||
|
||||
@Injectable()
|
||||
export class ReferenceValuesService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async list(): Promise<ReferenceValuesResponse> {
|
||||
const rows = await this.prisma.referenceValue.findMany({
|
||||
include: usageInclude,
|
||||
orderBy: [{ field: 'asc' }, { label: 'asc' }],
|
||||
});
|
||||
return { referenceValues: rows.map((r) => this.toDto(r)) };
|
||||
}
|
||||
|
||||
async create(dto: ReferenceValueCreate): Promise<ReferenceValueDto> {
|
||||
try {
|
||||
const created = await this.prisma.referenceValue.create({
|
||||
data: dto,
|
||||
include: usageInclude,
|
||||
});
|
||||
return this.toDto(created);
|
||||
} catch (e) {
|
||||
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2002') {
|
||||
throw new ConflictException('Ce libellé existe déjà pour ce champ');
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/** Renommage / (dés)activation — jamais de suppression (même règle que Category). */
|
||||
async update(id: string, dto: ReferenceValueUpdate): Promise<ReferenceValueDto> {
|
||||
try {
|
||||
const updated = await this.prisma.referenceValue.update({
|
||||
where: { id },
|
||||
data: dto,
|
||||
include: usageInclude,
|
||||
});
|
||||
return this.toDto(updated);
|
||||
} catch (e) {
|
||||
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2025') {
|
||||
throw new NotFoundException('Valeur inconnue');
|
||||
}
|
||||
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2002') {
|
||||
throw new ConflictException('Ce libellé existe déjà pour ce champ');
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private toDto(row: Row): ReferenceValueDto {
|
||||
const c = row._count;
|
||||
return {
|
||||
id: row.id,
|
||||
field: row.field,
|
||||
label: row.label,
|
||||
isActive: row.isActive,
|
||||
usageCount:
|
||||
c.doorStates +
|
||||
c.cabinPositions +
|
||||
c.anomalies +
|
||||
c.externalCauses +
|
||||
c.actionsTaken +
|
||||
c.componentsConcerned,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user