diff --git a/artifacts/api-server/src/routes/timesheets.ts b/artifacts/api-server/src/routes/timesheets.ts index 2275159..66a0d79 100644 --- a/artifacts/api-server/src/routes/timesheets.ts +++ b/artifacts/api-server/src/routes/timesheets.ts @@ -54,6 +54,20 @@ router.post("/timesheets", async (req, res): Promise => { return; } + const [existing] = await db + .select() + .from(timesheetsTable) + .where(and( + eq(timesheetsTable.year, parsed.data.year), + eq(timesheetsTable.month, parsed.data.month), + eq(timesheetsTable.collaborator, parsed.data.collaborator) + )); + + if (existing) { + res.status(409).json({ error: "Un CRA existe déjà pour ce mois et ce collaborateur" }); + return; + } + const [timesheet] = await db.insert(timesheetsTable).values(parsed.data).returning(); res.status(201).json(GetTimesheetResponse.parse({ ...timesheet, lines: [] })); }); diff --git a/artifacts/cra-app/src/pages/timesheets.tsx b/artifacts/cra-app/src/pages/timesheets.tsx index 99dc515..aae4e76 100644 --- a/artifacts/cra-app/src/pages/timesheets.tsx +++ b/artifacts/cra-app/src/pages/timesheets.tsx @@ -173,7 +173,7 @@ function CreateTimesheetDialog({ open, onOpenChange }: { open: boolean; onOpenCh data: { year: parseInt(year), month: parseInt(month), - collaborator: "Jean Dupont" // In a real app, from auth context + collaborator: "PHAM Sylvain" } }, { onSuccess: (data) => { diff --git a/attached_assets/image_1776154810677.png b/attached_assets/image_1776154810677.png new file mode 100644 index 0000000..7028768 Binary files /dev/null and b/attached_assets/image_1776154810677.png differ diff --git a/lib/db/src/schema/timesheets.ts b/lib/db/src/schema/timesheets.ts index 175e82e..54528a7 100644 --- a/lib/db/src/schema/timesheets.ts +++ b/lib/db/src/schema/timesheets.ts @@ -1,4 +1,4 @@ -import { pgTable, text, serial, timestamp, integer, real } from "drizzle-orm/pg-core"; +import { pgTable, text, serial, timestamp, integer, real, unique } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { z } from "zod/v4"; @@ -11,7 +11,9 @@ export const timesheetsTable = pgTable("timesheets", { totalHours: real("total_hours").notNull().default(0), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow().$onUpdate(() => new Date()), -}); +}, (table) => [ + unique("timesheets_year_month_collaborator_unique").on(table.year, table.month, table.collaborator), +]); export const insertTimesheetSchema = createInsertSchema(timesheetsTable).omit({ id: true, totalHours: true, createdAt: true, updatedAt: true }); export type InsertTimesheet = z.infer;