Enforce exclusive assignments for guards across shift types

Adds checks to prevent guards from being assigned to both fixed shifts and mobile patrol routes on the same date by validating against existing assignments in the database for the specified shift and guard.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: e5565357-90e1-419f-b9a8-6ee8394636df
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/6d543d2c-20b9-4ea6-93fe-70fe9b1d9f80/e5565357-90e1-419f-b9a8-6ee8394636df/ZaT6tFl
This commit is contained in:
marco370 2025-10-23 15:01:18 +00:00
parent 897a674eee
commit d6b9811c2b

View File

@ -2646,6 +2646,32 @@ export async function registerRoutes(app: Express): Promise<Server> {
// ============= SHIFT ASSIGNMENT ROUTES ============= // ============= SHIFT ASSIGNMENT ROUTES =============
app.post("/api/shift-assignments", isAuthenticated, async (req, res) => { app.post("/api/shift-assignments", isAuthenticated, async (req, res) => {
try { try {
const { shiftId, guardId } = req.body;
// Recupera il shift per ottenere la data
const [shift] = await db.select().from(shifts).where(eq(shifts.id, shiftId)).limit(1);
if (!shift) {
return res.status(404).json({ message: "Turno non trovato" });
}
// VINCOLO ESCLUSIVITÀ: Verifica che la guardia NON abbia patrol routes (turni mobile) nella stessa data
const existingMobileShifts = await db
.select()
.from(patrolRoutes)
.where(
and(
eq(patrolRoutes.guardId, guardId),
eq(patrolRoutes.shiftDate, shift.shiftDate)
)
)
.limit(1);
if (existingMobileShifts.length > 0) {
return res.status(400).json({
message: `Vincolo esclusività: la guardia è già assegnata a un turno pattuglia mobile in questa data. Una guardia non può essere assegnata contemporaneamente a turni fissi e mobile.`
});
}
const assignment = await storage.createShiftAssignment(req.body); const assignment = await storage.createShiftAssignment(req.body);
res.json(assignment); res.json(assignment);
} catch (error) { } catch (error) {
@ -2688,6 +2714,30 @@ export async function registerRoutes(app: Express): Promise<Server> {
return res.status(400).json({ message: "plannedEndTime must be after plannedStartTime" }); return res.status(400).json({ message: "plannedEndTime must be after plannedStartTime" });
} }
// Recupera il shift per ottenere la data
const [shift] = await db.select().from(shifts).where(eq(shifts.id, shiftId)).limit(1);
if (!shift) {
return res.status(404).json({ message: "Turno non trovato" });
}
// VINCOLO ESCLUSIVITÀ: Verifica che la guardia NON abbia patrol routes (turni mobile) nella stessa data
const existingMobileShifts = await db
.select()
.from(patrolRoutes)
.where(
and(
eq(patrolRoutes.guardId, guardId),
eq(patrolRoutes.shiftDate, shift.shiftDate)
)
)
.limit(1);
if (existingMobileShifts.length > 0) {
return res.status(400).json({
message: `Vincolo esclusività: la guardia è già assegnata a un turno pattuglia mobile in questa data. Una guardia non può essere assegnata contemporaneamente a turni fissi e mobile.`
});
}
// Create assignment // Create assignment
const assignment = await storage.createShiftAssignment({ const assignment = await storage.createShiftAssignment({
shiftId, shiftId,
@ -3402,6 +3452,29 @@ export async function registerRoutes(app: Express): Promise<Server> {
}); });
} }
// VINCOLO ESCLUSIVITÀ: Verifica che la guardia NON abbia shift assignments (turni fissi) nella stessa data
const existingFixedShifts = await db
.select({
shiftId: shifts.id,
siteName: sites.name,
})
.from(shiftAssignments)
.innerJoin(shifts, eq(shiftAssignments.shiftId, shifts.id))
.innerJoin(sites, eq(shifts.siteId, sites.id))
.where(
and(
eq(shiftAssignments.guardId, routeData.guardId),
eq(shifts.shiftDate, routeData.shiftDate)
)
)
.limit(1);
if (existingFixedShifts.length > 0) {
return res.status(400).json({
message: `Vincolo esclusività: la guardia è già assegnata a un turno fisso (${existingFixedShifts[0].siteName}) in questa data. Una guardia non può essere assegnata contemporaneamente a turni fissi e mobile.`
});
}
// Crea patrol route // Crea patrol route
const [newRoute] = await db.insert(patrolRoutes).values(routeData).returning(); const [newRoute] = await db.insert(patrolRoutes).values(routeData).returning();