From d6b9811c2bd0f023bb5011e33b4d33140c5b5ef1 Mon Sep 17 00:00:00 2001 From: marco370 <48531002-marco370@users.noreply.replit.com> Date: Thu, 23 Oct 2025 15:01:18 +0000 Subject: [PATCH] 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 --- server/routes.ts | 73 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/server/routes.ts b/server/routes.ts index 1d2650a..84ad193 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -2646,6 +2646,32 @@ export async function registerRoutes(app: Express): Promise { // ============= SHIFT ASSIGNMENT ROUTES ============= app.post("/api/shift-assignments", isAuthenticated, async (req, res) => { 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); res.json(assignment); } catch (error) { @@ -2688,6 +2714,30 @@ export async function registerRoutes(app: Express): Promise { 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 const assignment = await storage.createShiftAssignment({ shiftId, @@ -3402,6 +3452,29 @@ export async function registerRoutes(app: Express): Promise { }); } + // 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 const [newRoute] = await db.insert(patrolRoutes).values(routeData).returning();