import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core"; import type { Transaction } from "../generated/kysely-tailordb"; import { SiteNotFoundError, SiteInactiveError, DuplicateLocationNameError, } from "../lib/errors.generated"; import type { OrganizationQueries } from "../module"; export interface CreateStorageLocationInput { name: string; code?: string | null; siteId: string; storageCondition?: string | null; capacity?: number | null; } /** * Function: createStorageLocation * * Creates a new logical storage location within a site. Storage locations * are flat — there is no zone/bin hierarchy. */ export async function run>( db: Transaction, input: CreateStorageLocationInput & CF, ctx: CommandContext, organizationQueries: Pick, ) { const { name, code, siteId, storageCondition, capacity, ...customFields } = input; // 1. Validate site exists and is ACTIVE const { site } = (await organizationQueries.getSite(db, { id: siteId }, ctx)).value; if (!site) { return err(new SiteNotFoundError(siteId)); } if (site.status !== "ACTIVE") { return err(new SiteInactiveError(siteId)); } // 2. Validate name/code uniqueness within the site const duplicate = await db .selectFrom("StorageLocation") .selectAll() .where("siteId", "=", siteId) .where((eb) => { const conditions = [eb("name", "=", name)]; if (code != null) { conditions.push(eb("code", "=", code)); } return eb.or(conditions); }) .forUpdate() .executeTakeFirst(); if (duplicate) { return err(new DuplicateLocationNameError(name)); } // 3. Create storage location const storageLocation = await db .insertInto("StorageLocation") .values({ ...(customFields as Record), name, code: code ?? null, siteId, storageCondition: storageCondition ?? null, capacity: capacity != null ? String(capacity) : null, status: "ACTIVE", }) .returningAll() .executeTakeFirstOrThrow(); return ok({ storageLocation }); }