From 62b5cb997f32d37124eecfb24212dab8aefdc3bd Mon Sep 17 00:00:00 2001 From: marco370 <48531002-marco370@users.noreply.replit.com> Date: Thu, 23 Oct 2025 14:51:16 +0000 Subject: [PATCH] Add patrol routes and related entities for guard scheduling Introduces new database tables and relations for `patrolRoutes`, `patrolRouteStops`, and updates `shiftAssignments` with new fields like `isArmedOnDuty` and `assignedVehicleId`. Also updates relations for `guards` and `sites`. 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 --- shared/schema.ts | 84 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/shared/schema.ts b/shared/schema.ts index 9b9bbd2..1517edb 100644 --- a/shared/schema.ts +++ b/shared/schema.ts @@ -297,6 +297,50 @@ export const shiftAssignments = pgTable("shift_assignments", { // Actual check-in/out times (recorded when guard clocks in/out) checkInTime: timestamp("check_in_time"), checkOutTime: timestamp("check_out_time"), + + // Dotazioni operative per questo turno specifico + isArmedOnDuty: boolean("is_armed_on_duty").default(false), // Guardia armata per questo turno + assignedVehicleId: varchar("assigned_vehicle_id").references(() => vehicles.id, { onDelete: "set null" }), // Automezzo assegnato +}); + +// ============= PATROL ROUTES (TURNI PATTUGLIA) ============= + +export const patrolRoutes = pgTable("patrol_routes", { + id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), + guardId: varchar("guard_id").notNull().references(() => guards.id, { onDelete: "cascade" }), + + // Data e orari del turno pattuglia + shiftDate: date("shift_date").notNull(), // Data del turno + startTime: varchar("start_time").notNull(), // Orario inizio (HH:MM) + endTime: varchar("end_time").notNull(), // Orario fine (HH:MM) + + status: shiftStatusEnum("status").notNull().default("planned"), + location: locationEnum("location").notNull(), // Sede di riferimento + + // Dotazioni + vehicleId: varchar("vehicle_id").references(() => vehicles.id, { onDelete: "set null" }), + isArmedRoute: boolean("is_armed_route").default(false), // Percorso con guardia armata + + notes: text("notes"), + createdAt: timestamp("created_at").defaultNow(), + updatedAt: timestamp("updated_at").defaultNow(), +}); + +export const patrolRouteStops = pgTable("patrol_route_stops", { + id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), + patrolRouteId: varchar("patrol_route_id").notNull().references(() => patrolRoutes.id, { onDelete: "cascade" }), + siteId: varchar("site_id").notNull().references(() => sites.id, { onDelete: "cascade" }), + + sequenceOrder: integer("sequence_order").notNull(), // Ordine nel percorso (1, 2, 3...) + estimatedArrivalTime: varchar("estimated_arrival_time"), // Orario stimato arrivo (HH:MM) + actualArrivalTime: timestamp("actual_arrival_time"), // Orario effettivo arrivo + + // Check completamento tappa + isCompleted: boolean("is_completed").default(false), + completedAt: timestamp("completed_at"), + + notes: text("notes"), // Note specifiche per questa tappa + createdAt: timestamp("created_at").defaultNow(), }); // ============= CCNL SETTINGS ============= @@ -518,6 +562,7 @@ export const guardsRelations = relations(guards, ({ one, many }) => ({ }), certifications: many(certifications), shiftAssignments: many(shiftAssignments), + patrolRoutes: many(patrolRoutes), constraints: one(guardConstraints), sitePreferences: many(sitePreferences), trainingCourses: many(trainingCourses), @@ -549,6 +594,7 @@ export const sitesRelations = relations(sites, ({ one, many }) => ({ references: [customers.id], }), shifts: many(shifts), + patrolRouteStops: many(patrolRouteStops), preferences: many(sitePreferences), })); @@ -573,6 +619,33 @@ export const shiftAssignmentsRelations = relations(shiftAssignments, ({ one }) = fields: [shiftAssignments.guardId], references: [guards.id], }), + assignedVehicle: one(vehicles, { + fields: [shiftAssignments.assignedVehicleId], + references: [vehicles.id], + }), +})); + +export const patrolRoutesRelations = relations(patrolRoutes, ({ one, many }) => ({ + guard: one(guards, { + fields: [patrolRoutes.guardId], + references: [guards.id], + }), + vehicle: one(vehicles, { + fields: [patrolRoutes.vehicleId], + references: [vehicles.id], + }), + stops: many(patrolRouteStops), +})); + +export const patrolRouteStopsRelations = relations(patrolRouteStops, ({ one }) => ({ + patrolRoute: one(patrolRoutes, { + fields: [patrolRouteStops.patrolRouteId], + references: [patrolRoutes.id], + }), + site: one(sites, { + fields: [patrolRouteStops.siteId], + references: [sites.id], + }), })); export const notificationsRelations = relations(notifications, ({ one }) => ({ @@ -731,6 +804,17 @@ export const insertShiftSchema = createInsertSchema(shifts).omit({ updatedAt: true, }); +export const insertPatrolRouteSchema = createInsertSchema(patrolRoutes).omit({ + id: true, + createdAt: true, + updatedAt: true, +}); + +export const insertPatrolRouteStopSchema = createInsertSchema(patrolRouteStops).omit({ + id: true, + createdAt: true, +}); + // Form schema that accepts datetime strings and transforms to Date export const insertShiftFormSchema = z.object({ siteId: z.string().min(1, "Sito obbligatorio"),