import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core"; import type { Transaction } from "../generated/kysely-tailordb"; import { DuplicateNodeCodeError, MaxDepthExceededError, ParentNodeNotFoundError, } from "../lib/errors.generated"; export interface CreateTaxonomyNodeInput { code: string; name: string; parentId?: string; } /** * Function: createTaxonomyNode * * Creates a new root or child taxonomy node with a unique code. */ export async function run>( db: Transaction, input: CreateTaxonomyNodeInput & CF, _ctx: CommandContext, deps: { maxDepth: number }, ) { const { maxDepth } = deps; const { code, name, parentId, ...customFields } = input; // 1. Check code uniqueness (uniqueness before insert — lock) const existingCode = await db .selectFrom("TaxonomyNode") .selectAll() .where("code", "=", code) .forUpdate() .executeTakeFirst(); if (existingCode) { return err(new DuplicateNodeCodeError(code)); } // 2. If parent specified, check it exists and validate depth if (parentId) { const parent = await db .selectFrom("TaxonomyNode") .selectAll() .where("id", "=", parentId) .executeTakeFirst(); if (!parent) { return err(new ParentNodeNotFoundError(parentId)); } // Calculate depth: walk up from parent to root let depth = 1; let currentId: string | null = parentId; while (currentId) { const node = await db .selectFrom("TaxonomyNode") .selectAll() .where("id", "=", currentId) .executeTakeFirst(); if (!node?.parentId) break; depth++; currentId = node.parentId; } // Add 1 for the new node const newNodeDepth = depth + 1; if (newNodeDepth > maxDepth) { return err(new MaxDepthExceededError(`${newNodeDepth} exceeds ${maxDepth}`)); } } // 3. Create node const node = await db .insertInto("TaxonomyNode") .values({ ...(customFields as Record), code, name, parentId: parentId ?? null, }) .returningAll() .executeTakeFirstOrThrow(); return ok({ taxonomyNode: node }); }