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();