import { EmpruntAvecMateriel } from '../repositories/emprunt.repository'; import { MaterielResponse, toMaterielResponse } from './materiel.dto'; import { AppError } from '../errors/app-error'; import { ETAT_CHECKLIST_ELEMENT, MODE_IDENTIFICATION } from '../models/enums'; export interface EmpruntResponse { id: number; dateEmprunt: Date; dateRetourPrevue: Date; dateRetourReelle: Date | null; statut: string; materiel: MaterielResponse; checklistDepart: ChecklistElementResponse[]; } export interface ChecklistElementResponse { accessoireId: number | null; nomElement: string; etat: string; quantiteConstatee: number; } export function toEmpruntResponse(emprunt: EmpruntAvecMateriel): EmpruntResponse { const checklistDepart = emprunt.checklists.find((checklist) => checklist.type === 'DEPART'); return { id: emprunt.id, dateEmprunt: emprunt.dateEmprunt, dateRetourPrevue: emprunt.dateRetourPrevue, dateRetourReelle: emprunt.dateRetourReelle, statut: emprunt.statut, materiel: toMaterielResponse(emprunt.materiel), checklistDepart: checklistDepart?.elements.map((element) => ({ accessoireId: element.accessoireId, nomElement: element.nomElement, etat: element.etat, quantiteConstatee: element.quantiteConstatee, })) ?? [], }; } export interface ChecklistElementRequest { accessoireId?: number; nomElement: string; etat: string; quantiteConstatee: number; commentaire?: string; } export interface CreerEmpruntRequest { materielId: number; posteEmpruntId: number; modeIdentification: string; commentaireDepart?: string; elements: ChecklistElementRequest[]; } function asObject(value: unknown, message: string): Record { if (typeof value !== 'object' || value === null) { throw new AppError(400, message); } return value as Record; } function asPositiveInt(value: unknown, champ: string): number { if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0) { throw new AppError(400, `${champ} doit etre un entier positif`); } return value; } function asIntOrZero(value: unknown, champ: string): number { if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) { throw new AppError(400, `${champ} doit etre un entier positif ou nul`); } return value; } function asEnum(value: unknown, autorisees: readonly T[], champ: string): T { if (typeof value !== 'string' || !(autorisees as readonly string[]).includes(value)) { throw new AppError(400, `${champ} invalide`); } return value as T; } function parseElement(value: unknown, index: number): ChecklistElementRequest { const obj = asObject(value, `elements[${index}] invalide`); if (typeof obj.nomElement !== 'string' || obj.nomElement.trim() === '') { throw new AppError(400, `elements[${index}].nomElement requis`); } const element: ChecklistElementRequest = { nomElement: obj.nomElement, etat: asEnum(obj.etat, ETAT_CHECKLIST_ELEMENT, `elements[${index}].etat`), quantiteConstatee: asIntOrZero(obj.quantiteConstatee, `elements[${index}].quantiteConstatee`), }; if (obj.accessoireId !== undefined) { element.accessoireId = asPositiveInt(obj.accessoireId, `elements[${index}].accessoireId`); } if (obj.commentaire !== undefined) { if (typeof obj.commentaire !== 'string') { throw new AppError(400, `elements[${index}].commentaire invalide`); } element.commentaire = obj.commentaire; } return element; } export function parseCreerEmpruntRequest(body: unknown): CreerEmpruntRequest { const obj = asObject(body, 'Corps de requete invalide'); if (!Array.isArray(obj.elements) || obj.elements.length === 0) { throw new AppError(400, 'La checklist de depart est obligatoire (RG11)'); } const request: CreerEmpruntRequest = { materielId: asPositiveInt(obj.materielId, 'materielId'), posteEmpruntId: asPositiveInt(obj.posteEmpruntId, 'posteEmpruntId'), modeIdentification: asEnum(obj.modeIdentification, MODE_IDENTIFICATION, 'modeIdentification'), elements: obj.elements.map((element, index) => parseElement(element, index)), }; if (obj.commentaireDepart !== undefined) { if (typeof obj.commentaireDepart !== 'string') { throw new AppError(400, 'commentaireDepart invalide'); } request.commentaireDepart = obj.commentaireDepart; } return request; } export interface RestituerEmpruntRequest { modeIdentification: string; commentaireRetour?: string; elements: ChecklistElementRequest[]; } export interface SignalerTentativeRetourRequest { nomElement: string; etatInitial: string; etatDemande: string; } export function parseSignalerTentativeRetourRequest(body: unknown): SignalerTentativeRetourRequest { const obj = asObject(body, 'Corps de requete invalide'); if (typeof obj.nomElement !== 'string' || obj.nomElement.trim() === '') { throw new AppError(400, 'nomElement requis'); } return { nomElement: obj.nomElement.trim(), etatInitial: asEnum(obj.etatInitial, ETAT_CHECKLIST_ELEMENT, 'etatInitial'), etatDemande: asEnum(obj.etatDemande, ETAT_CHECKLIST_ELEMENT, 'etatDemande'), }; } export function parseRestituerEmpruntRequest(body: unknown): RestituerEmpruntRequest { const obj = asObject(body, 'Corps de requete invalide'); if (!Array.isArray(obj.elements) || obj.elements.length === 0) { throw new AppError(400, 'La checklist de retour est obligatoire (RG16)'); } const request: RestituerEmpruntRequest = { modeIdentification: asEnum(obj.modeIdentification, MODE_IDENTIFICATION, 'modeIdentification'), elements: obj.elements.map((element, index) => parseElement(element, index)), }; if (obj.commentaireRetour !== undefined) { if (typeof obj.commentaireRetour !== 'string') { throw new AppError(400, 'commentaireRetour invalide'); } request.commentaireRetour = obj.commentaireRetour; } return request; }