Add functionality to duplicate and modify patrol routes

Adds a new POST endpoint `/api/patrol-routes/duplicate` to duplicate existing patrol routes to a new date, optionally assigning a different guard. If the target date is the same as the source date, it updates the guard for the existing route.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: e0b5b11c-5b75-4389-8ea9-5f3cd9332f88
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/6d543d2c-20b9-4ea6-93fe-70fe9b1d9f80/e0b5b11c-5b75-4389-8ea9-5f3cd9332f88/EDxr1e6
This commit is contained in:
marco370 2025-10-24 15:40:27 +00:00
parent 6366382753
commit 1bad21cf9e

View File

@ -4256,6 +4256,98 @@ export async function registerRoutes(app: Express): Promise<Server> {
}
});
// POST - Duplica o modifica patrol route
app.post("/api/patrol-routes/duplicate", isAuthenticated, async (req: any, res) => {
try {
const { sourceRouteId, targetDate, guardId } = req.body;
if (!sourceRouteId || !targetDate) {
return res.status(400).json({
message: "sourceRouteId e targetDate sono obbligatori"
});
}
// Carica patrol route sorgente con tutti gli stops
const sourceRoute = await db.query.patrolRoutes.findFirst({
where: eq(patrolRoutes.id, sourceRouteId),
with: {
stops: {
orderBy: (stops, { asc }) => [asc(stops.sequenceOrder)],
},
},
});
if (!sourceRoute) {
return res.status(404).json({ message: "Sequenza pattuglia sorgente non trovata" });
}
// Controlla se targetDate è uguale a sourceRoute.scheduledDate
const sourceDate = new Date(sourceRoute.scheduledDate).toISOString().split('T')[0];
const targetDateNormalized = new Date(targetDate).toISOString().split('T')[0];
if (sourceDate === targetDateNormalized) {
// UPDATE: stessa data, modifica solo guardia se fornita
if (guardId && guardId !== sourceRoute.guardId) {
const updated = await db
.update(patrolRoutes)
.set({ guardId })
.where(eq(patrolRoutes.id, sourceRouteId))
.returning();
return res.json({
action: "updated",
route: updated[0],
message: "Guardia assegnata alla sequenza esistente",
});
} else {
return res.status(400).json({
message: "Nessuna modifica da applicare (stessa data e stessa guardia)"
});
}
} else {
// CREATE: data diversa, duplica sequenza con stops
// Crea nuova patrol route
const newRoute = await db
.insert(patrolRoutes)
.values({
guardId: guardId || sourceRoute.guardId, // Usa nuova guardia o mantieni originale
scheduledDate: new Date(targetDate),
startTime: sourceRoute.startTime,
endTime: sourceRoute.endTime,
status: "scheduled", // Nuova sequenza sempre in stato scheduled
location: sourceRoute.location,
notes: sourceRoute.notes,
})
.returning();
const newRouteId = newRoute[0].id;
// Duplica tutti gli stops
if (sourceRoute.stops && sourceRoute.stops.length > 0) {
const stopsData = sourceRoute.stops.map((stop) => ({
patrolRouteId: newRouteId,
siteId: stop.siteId,
sequenceOrder: stop.sequenceOrder,
estimatedArrivalTime: stop.estimatedArrivalTime,
}));
await db.insert(patrolRouteStops).values(stopsData);
}
return res.json({
action: "created",
route: newRoute[0],
copiedStops: sourceRoute.stops?.length || 0,
message: "Sequenza pattuglia duplicata con successo",
});
}
} catch (error) {
console.error("Error duplicating patrol route:", error);
res.status(500).json({ message: "Errore durante duplicazione sequenza pattuglia" });
}
});
// ============= GEOCODING API (Nominatim/OSM) =============
// Rate limiter semplice per rispettare 1 req/sec di Nominatim