feat(backend): add responsable stock endpoint

This commit is contained in:
SaidSoighiri94
2026-07-15 12:35:16 +02:00
parent 2c81bd7208
commit c76f546f2a
6 changed files with 162 additions and 3 deletions
@@ -2,12 +2,16 @@ import { Request, Response } from 'express';
import { AppError } from '../errors/app-error';
import {
getDashboardResponsable,
listerMaterielsResponsable,
listerEmpruntsResponsable,
parseCategorieId,
parseStatutMateriel,
parseStatutEmprunt,
} from '../services/responsable.service';
import {
toDashboardResponsableResponse,
toEmpruntResponsableResponse,
toMaterielResponsableResponse,
} from '../dtos/responsable.dto';
export async function getDashboard(req: Request, res: Response): Promise<void> {
@@ -30,3 +34,21 @@ export async function getEmprunts(req: Request, res: Response): Promise<void> {
const emprunts = await listerEmpruntsResponsable(user.roleCode, user.campusId, { statut });
res.json({ data: emprunts.map(toEmpruntResponsableResponse) });
}
export async function getMateriels(req: Request, res: Response): Promise<void> {
const user = req.user;
if (!user) {
throw new AppError(401, 'Authentification requise');
}
const statut = parseStatutMateriel(req.query.statut);
const categorieId = parseCategorieId(req.query.categorieId);
const recherche = typeof req.query.q === 'string' ? req.query.q : undefined;
const materiels = await listerMaterielsResponsable(user.roleCode, user.campusId, {
statut,
categorieId,
recherche,
});
res.json({ data: materiels.map(toMaterielResponsableResponse) });
}
+45
View File
@@ -1,6 +1,7 @@
import {
ActiviteResponsable,
EmpruntResponsable,
MaterielResponsable,
StatutCount,
} from '../repositories/responsable.repository';
import { DashboardResponsableData } from '../services/responsable.service';
@@ -63,6 +64,26 @@ export interface EmpruntResponsableResponse {
};
}
export interface MaterielResponsableResponse {
id: number;
nom: string;
marque: string;
modele: string;
reference: string;
statut: string;
etatGeneral: string;
categorie: {
id: number;
nom: string;
};
accessoires: Array<{
id: number;
nom: string;
quantiteAttendue: number;
obligatoire: boolean;
}>;
}
function toRepartition(rows: StatutCount[]): RepartitionStatutResponse {
const parStatut: Record<string, number> = {};
let total = 0;
@@ -139,3 +160,27 @@ export function toEmpruntResponsableResponse(
},
};
}
export function toMaterielResponsableResponse(
materiel: MaterielResponsable,
): MaterielResponsableResponse {
return {
id: materiel.id,
nom: materiel.nom,
marque: materiel.marque,
modele: materiel.modele,
reference: materiel.reference,
statut: materiel.statut,
etatGeneral: materiel.etatGeneral,
categorie: {
id: materiel.categorie.id,
nom: materiel.categorie.nom,
},
accessoires: materiel.accessoires.map((liaison) => ({
id: liaison.accessoire.id,
nom: liaison.accessoire.nom,
quantiteAttendue: liaison.quantiteAttendue,
obligatoire: liaison.obligatoire,
})),
};
}
@@ -21,10 +21,23 @@ export type EmpruntResponsable = Prisma.EmpruntGetPayload<{
};
}>;
export type MaterielResponsable = Prisma.MaterielGetPayload<{
include: {
categorie: true;
accessoires: { include: { accessoire: true } };
};
}>;
export interface EmpruntResponsableFiltres {
statut?: string;
}
export interface MaterielResponsableFiltres {
statut?: string;
categorieId?: number;
recherche?: string;
}
function mapStatutCounts(rows: Array<{ statut: string; _count: { _all: number } }>): StatutCount[] {
return rows.map((row) => ({ statut: row.statut, count: row._count._all }));
}
@@ -97,3 +110,32 @@ export function findEmpruntsResponsable(
orderBy: { dateEmprunt: 'desc' },
});
}
export function findMaterielsResponsable(
campusId: number,
filtres: MaterielResponsableFiltres,
): Promise<MaterielResponsable[]> {
return prisma.materiel.findMany({
where: {
campusId,
actif: true,
...(filtres.statut ? { statut: filtres.statut } : {}),
...(filtres.categorieId !== undefined ? { categorieId: filtres.categorieId } : {}),
...(filtres.recherche
? {
OR: [
{ nom: { contains: filtres.recherche } },
{ marque: { contains: filtres.recherche } },
{ modele: { contains: filtres.recherche } },
{ reference: { contains: filtres.recherche } },
],
}
: {}),
},
include: {
categorie: true,
accessoires: { include: { accessoire: true } },
},
orderBy: { nom: 'asc' },
});
}
+2 -1
View File
@@ -1,7 +1,8 @@
import { Router } from 'express';
import { getDashboard, getEmprunts } from '../controllers/responsable.controller';
import { getDashboard, getEmprunts, getMateriels } from '../controllers/responsable.controller';
export const responsableRoutes: Router = Router();
responsableRoutes.get('/dashboard', getDashboard);
responsableRoutes.get('/emprunts', getEmprunts);
responsableRoutes.get('/materiels', getMateriels);
@@ -6,11 +6,13 @@ import {
countNotificationsNonLues,
findActiviteRecente,
findEmpruntsResponsable,
findMaterielsResponsable,
StatutCount,
ActiviteResponsable,
EmpruntResponsable,
MaterielResponsable,
} from '../repositories/responsable.repository';
import { STATUT_EMPRUNT, StatutEmprunt } from '../models/enums';
import { STATUT_EMPRUNT, STATUT_MATERIEL, StatutEmprunt, StatutMateriel } from '../models/enums';
export interface DashboardResponsableData {
materiels: StatutCount[];
@@ -24,6 +26,12 @@ export interface ListerEmpruntsResponsableFiltres {
statut?: StatutEmprunt;
}
export interface ListerMaterielsResponsableFiltres {
statut?: StatutMateriel;
categorieId?: number;
recherche?: string;
}
function verifierResponsable(roleCode: string): void {
if (roleCode !== 'RESPONSABLE') {
throw new AppError(403, 'Acces reserve au responsable materiel');
@@ -65,6 +73,29 @@ export function parseStatutEmprunt(value: unknown): StatutEmprunt | undefined {
return value as StatutEmprunt;
}
export function parseStatutMateriel(value: unknown): StatutMateriel | undefined {
if (value === undefined) {
return undefined;
}
if (typeof value !== 'string' || !(STATUT_MATERIEL as readonly string[]).includes(value)) {
throw new AppError(400, 'statut invalide');
}
return value as StatutMateriel;
}
export function parseCategorieId(value: unknown): number | undefined {
if (value === undefined) {
return undefined;
}
const parsed = Number(value);
if (!Number.isInteger(parsed) || parsed <= 0) {
throw new AppError(400, 'categorieId invalide');
}
return parsed;
}
export function listerEmpruntsResponsable(
roleCode: string,
campusId: number,
@@ -73,3 +104,12 @@ export function listerEmpruntsResponsable(
verifierResponsable(roleCode);
return findEmpruntsResponsable(campusId, filtres);
}
export function listerMaterielsResponsable(
roleCode: string,
campusId: number,
filtres: ListerMaterielsResponsableFiltres,
): Promise<MaterielResponsable[]> {
verifierResponsable(roleCode);
return findMaterielsResponsable(campusId, filtres);
}