import { z } from "zod"; import { prisma } from "./prisma"; export type AssetCategoryValueGroup = "high_value" | "low_value"; export interface AssetCategoryRecord { id: string; name: string; value_group: AssetCategoryValueGroup; created_at: string; } export interface CreateAssetCategoryInput { name: string; valueGroup: AssetCategoryValueGroup; } export type ValidationResult = | { ok: true; value: CreateAssetCategoryInput } | { ok: false; error: string }; // Mirrors src/lib/vendors.ts's nonEmptyString helper. function nonEmptyString(message: string) { return z.string({ required_error: message, invalid_type_error: message }).refine( (value) => value.trim() !== "", { message } ); } const createAssetCategorySchema = z.object({ name: nonEmptyString("name is required and must be a non-empty string"), // Fixed 2-value set (see docs/asset-license-management.md's "## Phân // loại tài sản" section) — mirrors src/lib/licenses.ts's licenseType // z.enum() pattern. valueGroup: z.enum(["high_value", "low_value"], { errorMap: () => ({ message: "valueGroup is required and must be either 'high_value' or 'low_value'", }), }), }); /** * Validates raw (untyped) asset-category-creation input. name must be a * non-empty string, valueGroup must be one of the two fixed enum values * (per the "## Phân loại tài sản" spec in docs/asset-license-management.md). */ export function validateCreateAssetCategoryInput(input: { name?: unknown; valueGroup?: unknown; }): ValidationResult { const result = createAssetCategorySchema.safeParse(input); if (!result.success) { const message = result.error.issues[0]?.message ?? "Invalid input"; return { ok: false, error: message }; } return { ok: true, value: result.data }; } function toRecord(row: { id: string; name: string; valueGroup: AssetCategoryValueGroup; createdAt: Date; }): AssetCategoryRecord { return { id: row.id, name: row.name, value_group: row.valueGroup, created_at: row.createdAt.toISOString(), }; } export type CreateAssetCategoryResult = | { ok: true; value: AssetCategoryRecord } | { ok: false; error: string }; /** * Creates an AssetCategory. No unique constraint on any field, no FKs * either (see docs/asset-license-management.md's "## Phân loại tài sản" * spec — standalone entity, same starting shape as Vendor before it grew * relations) — so there's no known Prisma error code to catch/translate * here today. Still returns the `{ok,value}|{ok,error}` shape (rather than * a bare Promise like createVendor()) to match the * convention used across the newer entities (AssetItem/License) — see the * code-reviewer finding on createLicense() in CLAUDE.md's "Trạng thái hiện * tại": adding this shape up front avoids a silent 500 if a unique * constraint is added later (e.g. on `name`) without remembering to also * add P2002 handling here. */ export async function createAssetCategory( input: CreateAssetCategoryInput ): Promise { const created = await prisma.assetCategory.create({ data: { name: input.name, valueGroup: input.valueGroup, }, }); return { ok: true, value: toRecord(created) }; } export async function listAssetCategories(): Promise { const rows = await prisma.assetCategory.findMany({ orderBy: [{ createdAt: "desc" }, { id: "desc" }], }); return rows.map(toRecord); }