feat(loans): confirm declared material state changes
This commit is contained in:
@@ -5,7 +5,10 @@ import {
|
||||
countEmpruntsParStatut,
|
||||
countMaterielsParStatut,
|
||||
countNotificationsNonLues,
|
||||
deciderEcartResponsable,
|
||||
findActiviteRecente,
|
||||
findEcartResponsableById,
|
||||
findEcartsResponsable,
|
||||
findAnomalieResponsableById,
|
||||
findAnomaliesResponsable,
|
||||
findEmpruntsResponsable,
|
||||
@@ -21,6 +24,7 @@ import {
|
||||
HistoriqueResponsable,
|
||||
MaterielResponsable,
|
||||
NotificationResponsable,
|
||||
EcartResponsable,
|
||||
} from '../repositories/responsable.repository';
|
||||
import {
|
||||
STATUT_ANOMALIE,
|
||||
@@ -72,6 +76,20 @@ export interface ChangerStatutAnomalieResponsableRequest {
|
||||
observation?: string;
|
||||
}
|
||||
|
||||
export interface DeciderEcartResponsableRequest {
|
||||
decision: 'CONFIRMER' | 'REFUSER';
|
||||
observation?: string;
|
||||
statutMateriel?: StatutMateriel;
|
||||
}
|
||||
|
||||
const STATUTS_MATERIEL_RETOUR_CONFIRMES: readonly StatutMateriel[] = [
|
||||
'DISPONIBLE',
|
||||
'NON_CONFORME',
|
||||
'DETERIORE',
|
||||
'MAINTENANCE',
|
||||
'INDISPONIBLE',
|
||||
];
|
||||
|
||||
const TRANSITIONS_ANOMALIE: Record<StatutAnomalie, StatutAnomalie[]> = {
|
||||
DETECTEE: ['EN_COURS_TRAITEMENT'],
|
||||
EN_COURS_TRAITEMENT: ['RESOLUE'],
|
||||
@@ -180,6 +198,19 @@ export function parseAnomalieId(value: unknown): number {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export function parseEmpruntId(value: unknown): number {
|
||||
if (typeof value !== 'string') {
|
||||
throw new AppError(400, 'empruntId invalide');
|
||||
}
|
||||
|
||||
const parsed = Number(value);
|
||||
if (!Number.isInteger(parsed) || parsed <= 0) {
|
||||
throw new AppError(400, 'empruntId invalide');
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export function parsePositiveIntQuery(value: unknown, champ: string): number | undefined {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
@@ -234,6 +265,101 @@ export function listerEmpruntsResponsable(
|
||||
return findEmpruntsResponsable(campusId, filtres);
|
||||
}
|
||||
|
||||
export function listerEcartsResponsable(
|
||||
roleCode: string,
|
||||
campusId: number,
|
||||
): Promise<EcartResponsable[]> {
|
||||
verifierResponsable(roleCode);
|
||||
return findEcartsResponsable(campusId);
|
||||
}
|
||||
|
||||
function decrireChecklist(ecart: EcartResponsable, type: 'DEPART' | 'RETOUR'): string {
|
||||
const checklist = ecart.checklists.find((item) => item.type === type);
|
||||
if (!checklist) {
|
||||
throw new AppError(409, `Checklist de ${type.toLocaleLowerCase()} introuvable`);
|
||||
}
|
||||
|
||||
return checklist.elements
|
||||
.map((element) => `${element.nomElement}=${element.etat}/${element.quantiteConstatee}`)
|
||||
.join(', ');
|
||||
}
|
||||
|
||||
export async function deciderEcartResponsableService(
|
||||
responsableId: number,
|
||||
roleCode: string,
|
||||
campusId: number,
|
||||
empruntId: number,
|
||||
request: DeciderEcartResponsableRequest,
|
||||
): Promise<EcartResponsable> {
|
||||
verifierResponsable(roleCode);
|
||||
|
||||
const ecart = await findEcartResponsableById(campusId, empruntId);
|
||||
if (!ecart) {
|
||||
throw new AppError(404, 'Ecart en attente introuvable');
|
||||
}
|
||||
|
||||
const typeEcart = ecart.statut === 'EN_ATTENTE_VALIDATION_DEPART' ? 'DEPART' : 'RETOUR';
|
||||
if (
|
||||
typeEcart === 'RETOUR' &&
|
||||
request.decision === 'CONFIRMER' &&
|
||||
(!request.statutMateriel || !STATUTS_MATERIEL_RETOUR_CONFIRMES.includes(request.statutMateriel))
|
||||
) {
|
||||
throw new AppError(400, 'statutMateriel final requis pour confirmer le retour');
|
||||
}
|
||||
|
||||
const description =
|
||||
`Ecart ${typeEcart.toLocaleLowerCase()} ${request.decision.toLocaleLowerCase()} ` +
|
||||
`pour l'emprunt #${ecart.id} : ${decrireChecklist(ecart, typeEcart)}.`;
|
||||
|
||||
let elementsDepartCorriges: Array<{ id: number; quantiteConstatee: number }> | undefined;
|
||||
if (typeEcart === 'DEPART' && request.decision === 'REFUSER') {
|
||||
const checklistDepart = ecart.checklists.find((checklist) => checklist.type === 'DEPART');
|
||||
if (!checklistDepart) {
|
||||
throw new AppError(409, 'Checklist de depart introuvable');
|
||||
}
|
||||
elementsDepartCorriges = checklistDepart.elements.map((element) => {
|
||||
const accessoire = ecart.materiel.accessoires.find(
|
||||
(liaison) => liaison.accessoireId === element.accessoireId,
|
||||
);
|
||||
return {
|
||||
id: element.id,
|
||||
quantiteConstatee: accessoire?.quantiteAttendue ?? 1,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
return await deciderEcartResponsable({
|
||||
empruntId,
|
||||
campusId,
|
||||
responsableId,
|
||||
statutAttendu: ecart.statut,
|
||||
statutEmpruntFinal:
|
||||
typeEcart === 'DEPART'
|
||||
? 'EN_COURS'
|
||||
: request.decision === 'CONFIRMER'
|
||||
? 'RETOUR_NON_CONFORME'
|
||||
: 'CLOTURE',
|
||||
statutMaterielFinal:
|
||||
typeEcart === 'DEPART'
|
||||
? 'EMPRUNTE'
|
||||
: request.decision === 'CONFIRMER'
|
||||
? (request.statutMateriel as StatutMateriel)
|
||||
: 'DISPONIBLE',
|
||||
typeEcart,
|
||||
decision: request.decision,
|
||||
description,
|
||||
observation: request.observation,
|
||||
elementsDepartCorriges,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message === 'ECART_DEJA_TRAITE') {
|
||||
throw new AppError(409, 'Cet ecart a deja ete traite');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function listerMaterielsResponsable(
|
||||
roleCode: string,
|
||||
campusId: number,
|
||||
|
||||
Reference in New Issue
Block a user