Add operational planning view for vehicle and guard availability

Implement GET /api/operational-planning/availability endpoint to fetch and sort vehicles and guards based on their availability and CCNL rules for a given date.

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/EEOXc3D
This commit is contained in:
marco370 2025-10-17 10:24:01 +00:00
parent 278419c4ff
commit 181de6a028
2 changed files with 125 additions and 0 deletions

View File

@ -19,6 +19,10 @@ externalPort = 80
localPort = 33035 localPort = 33035
externalPort = 3001 externalPort = 3001
[[ports]]
localPort = 34977
externalPort = 4200
[[ports]] [[ports]]
localPort = 41343 localPort = 41343
externalPort = 3000 externalPort = 3000

View File

@ -534,6 +534,127 @@ export async function registerRoutes(app: Express): Promise<Server> {
} }
}); });
// ============= OPERATIONAL PLANNING ROUTES =============
app.get("/api/operational-planning/availability", isAuthenticated, async (req, res) => {
try {
const { getGuardAvailabilityReport } = await import("./ccnlRules");
const date = req.query.date ? new Date(req.query.date as string) : new Date();
// Imposta inizio e fine giornata
const startOfDay = new Date(date);
startOfDay.setHours(0, 0, 0, 0);
const endOfDay = new Date(date);
endOfDay.setHours(23, 59, 59, 999);
// Ottieni tutti i veicoli
const allVehicles = await storage.getAllVehicles();
// Ottieni turni del giorno per trovare veicoli assegnati
const dayShifts = await db
.select()
.from(shifts)
.where(
and(
gte(shifts.startTime, startOfDay),
lte(shifts.startTime, endOfDay)
)
);
// Mappa veicoli con disponibilità
const vehiclesWithAvailability = await Promise.all(
allVehicles.map(async (vehicle) => {
const assignedShift = dayShifts.find((shift: any) => shift.vehicleId === vehicle.id);
return {
...vehicle,
isAvailable: !assignedShift,
assignedShift: assignedShift ? {
id: assignedShift.id,
startTime: assignedShift.startTime,
endTime: assignedShift.endTime,
siteId: assignedShift.siteId
} : null
};
})
);
// Ottieni tutte le guardie
const allGuards = await storage.getAllGuards();
// Ottieni assegnazioni turni del giorno
const dayShiftAssignments = await db
.select()
.from(shiftAssignments)
.innerJoin(shifts, eq(shiftAssignments.shiftId, shifts.id))
.where(
and(
gte(shifts.startTime, startOfDay),
lte(shifts.startTime, endOfDay)
)
);
// Calcola disponibilità agenti con report CCNL
const guardsWithAvailability = await Promise.all(
allGuards.map(async (guard) => {
const assignedShift = dayShiftAssignments.find(
(assignment: any) => assignment.shift_assignments.guardId === guard.id
);
// Calcola report disponibilità CCNL
const availabilityReport = await getGuardAvailabilityReport(
guard.id,
startOfDay,
endOfDay
);
return {
...guard,
isAvailable: !assignedShift,
assignedShift: assignedShift ? {
id: assignedShift.shifts.id,
startTime: assignedShift.shifts.startTime,
endTime: assignedShift.shifts.endTime,
siteId: assignedShift.shifts.siteId
} : null,
availability: {
weeklyHours: availabilityReport.weeklyHours.current,
remainingWeeklyHours: availabilityReport.remainingWeeklyHours,
remainingMonthlyHours: availabilityReport.remainingMonthlyHours,
consecutiveDaysWorked: availabilityReport.consecutiveDaysWorked
}
};
})
);
// Ordina veicoli: disponibili prima, poi per targa
const sortedVehicles = vehiclesWithAvailability.sort((a, b) => {
if (a.isAvailable && !b.isAvailable) return -1;
if (!a.isAvailable && b.isAvailable) return 1;
return a.licensePlate.localeCompare(b.licensePlate);
});
// Ordina agenti: disponibili prima, poi per ore settimanali (meno ore = più disponibili)
const sortedGuards = guardsWithAvailability.sort((a, b) => {
if (a.isAvailable && !b.isAvailable) return -1;
if (!a.isAvailable && b.isAvailable) return 1;
// Se entrambi disponibili, ordina per ore settimanali (meno ore = prima)
if (a.isAvailable && b.isAvailable) {
return a.availability.weeklyHours - b.availability.weeklyHours;
}
return 0;
});
res.json({
date: date.toISOString(),
vehicles: sortedVehicles,
guards: sortedGuards
});
} catch (error) {
console.error("Error fetching operational planning availability:", error);
res.status(500).json({ message: "Failed to fetch availability" });
}
});
// ============= CERTIFICATION ROUTES ============= // ============= CERTIFICATION ROUTES =============
app.post("/api/certifications", isAuthenticated, async (req, res) => { app.post("/api/certifications", isAuthenticated, async (req, res) => {
try { try {