feat(projects): add createProject service (manual, shared numbering)

Introduces CreateProjectSchema (Zod 4, NaN-guarded coercions), the
createProject() service function (atomic shared-number generation inside
a $transaction), getNextProjectNumber() preview helper, and two Vitest
tests that verify standalone project creation and distinct sequential
numbers. Routes are deferred to a subsequent task.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-06-06 11:01:10 +02:00
parent fdf180809d
commit 0c5d0ee2f7
3 changed files with 125 additions and 1 deletions

View File

@@ -1,5 +1,27 @@
import { z } from "zod";
export const CreateProjectSchema = z.object({
name: z.string().min(1, "Zadejte název projektu"),
customer_id: z
.union([z.number(), z.string(), z.null()])
.transform((v) => (v === null || v === "" ? null : Number(v)))
.refine((v) => v === null || !Number.isNaN(v), {
message: "Neplatný zákazník",
})
.optional(),
responsible_user_id: z
.union([z.number(), z.string(), z.null()])
.transform((v) => (v === null || v === "" ? null : Number(v)))
.refine((v) => v === null || !Number.isNaN(v), {
message: "Neplatná zodpovědná osoba",
})
.optional(),
status: z.string().optional().default("aktivni"),
start_date: z.union([z.string(), z.null()]).optional(),
end_date: z.union([z.string(), z.null()]).optional(),
notes: z.string().nullish(),
});
export const UpdateProjectSchema = z.object({
name: z.string().nullish(),
status: z.string().optional(),
@@ -28,5 +50,6 @@ export const CreateProjectNoteSchema = z.object({
content: z.string().nullish(),
});
export type CreateProjectInput = z.infer<typeof CreateProjectSchema>;
export type UpdateProjectInput = z.infer<typeof UpdateProjectSchema>;
export type CreateProjectNoteInput = z.infer<typeof CreateProjectNoteSchema>;