/** * Time Tracking. Source: `db/migrations/0001_init.sql`. */ import { pgTable, uuid, text, numeric, integer, boolean, timestamp, index, } from "drizzle-orm/pg-core"; import { brands } from "./brands"; export const timeTrackingSettings = pgTable( "time_tracking_settings", { id: uuid("id").primaryKey().defaultRandom(), brandId: uuid("brand_id") .notNull() .unique() .references(() => brands.id, { onDelete: "cascade" }), payPeriodStartDay: integer("pay_period_start_day").notNull().default(0), payPeriodLengthDays: integer("pay_period_length_days") .notNull() .default(7), dailyOvertimeThreshold: numeric("daily_overtime_threshold", { precision: 5, scale: 2, }).notNull().default("8.0"), weeklyOvertimeThreshold: numeric("weekly_overtime_threshold", { precision: 5, scale: 2, }).notNull().default("40.0"), overtimeMultiplier: numeric("overtime_multiplier", { precision: 3, scale: 2, }).notNull().default("1.50"), overtimeNotifications: boolean("overtime_notifications") .notNull() .default(true), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }) .notNull() .defaultNow(), }, ); export const timeTrackingWorkers = pgTable( "time_tracking_workers", { id: uuid("id").primaryKey().defaultRandom(), brandId: uuid("brand_id") .notNull() .references(() => brands.id, { onDelete: "cascade" }), name: text("name").notNull(), role: text("role", { enum: ["worker", "time_admin"] }) .notNull() .default("worker"), lang: text("lang").notNull().default("en"), pin: text("pin").notNull(), active: boolean("active").notNull().default(true), lastUsedAt: timestamp("last_used_at", { withTimezone: true }), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ brandIdx: index("time_tracking_workers_brand_idx").on(t.brandId), }), ); export const timeTrackingTasks = pgTable( "time_tracking_tasks", { id: uuid("id").primaryKey().defaultRandom(), brandId: uuid("brand_id") .notNull() .references(() => brands.id, { onDelete: "cascade" }), name: text("name").notNull(), nameEs: text("name_es"), unit: text("unit", { enum: ["hours", "pieces", "units"] }) .notNull() .default("hours"), sortOrder: integer("sort_order").notNull().default(0), active: boolean("active").notNull().default(true), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ brandIdx: index("time_tracking_tasks_brand_idx").on(t.brandId), }), ); export const timeTrackingLogs = pgTable( "time_tracking_logs", { id: uuid("id").primaryKey().defaultRandom(), brandId: uuid("brand_id") .notNull() .references(() => brands.id, { onDelete: "cascade" }), workerId: uuid("worker_id") .notNull() .references(() => timeTrackingWorkers.id, { onDelete: "cascade" }), taskId: uuid("task_id").references(() => timeTrackingTasks.id, { onDelete: "set null", }), taskName: text("task_name").notNull(), clockIn: timestamp("clock_in", { withTimezone: true }).notNull(), clockOut: timestamp("clock_out", { withTimezone: true }), lunchBreakMinutes: integer("lunch_break_minutes").notNull().default(0), notes: text("notes"), submittedVia: text("submitted_via", { enum: ["manual", "field", "import"], }).notNull().default("manual"), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ brandIdx: index("time_tracking_logs_brand_idx").on(t.brandId), workerIdx: index("time_tracking_logs_worker_idx").on(t.workerId), clockInIdx: index("time_tracking_logs_clock_in_idx").on(t.clockIn), }), ); export const timeTrackingNotificationLog = pgTable( "time_tracking_notification_log", { id: uuid("id").primaryKey().defaultRandom(), brandId: uuid("brand_id") .notNull() .references(() => brands.id, { onDelete: "cascade" }), workerId: uuid("worker_id").references( () => timeTrackingWorkers.id, { onDelete: "set null" }, ), notificationType: text("notification_type").notNull(), recipient: text("recipient").notNull(), subject: text("subject"), body: text("body").notNull(), status: text("status", { enum: ["pending", "sent", "failed"], }).notNull().default("pending"), sentAt: timestamp("sent_at", { withTimezone: true }), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), }, ); export type TimeTrackingSettings = typeof timeTrackingSettings.$inferSelect; export type TimeTrackingWorker = typeof timeTrackingWorkers.$inferSelect; export type TimeTrackingTask = typeof timeTrackingTasks.$inferSelect; export type TimeTrackingLog = typeof timeTrackingLogs.$inferSelect; export type TimeTrackingNotificationLog = typeof timeTrackingNotificationLog.$inferSelect;