import { ok, err } from "@tailor-platform/erp-kit/core"; import type { Transaction } from "../generated/kysely-tailordb"; import { CategoryNotActiveError, DuplicateUnitSymbolError, InvalidConversionFactorError, InvalidRoundingPrecisionError, UomCategoryNotFoundError, } from "../lib/errors.generated"; interface CreateUnitInput { categoryId: string; name: string; symbol: string; conversionFactor: number; roundingPrecision: number; } /** * Function: createUnit * * Adds a new unit of measure to an existing category with its symbol, * display name, and conversion factor relative to the reference unit. */ export async function run>( db: Transaction, input: CreateUnitInput & CF, ) { const { categoryId, name, symbol, conversionFactor, roundingPrecision, ...customFields } = input; // 1. Check category exists const uoMCategory = await db .selectFrom("UoMCategory") .selectAll() .where("id", "=", categoryId) .executeTakeFirst(); if (!uoMCategory) { return err(new UomCategoryNotFoundError(categoryId)); } // 2. Check category is active if (uoMCategory.status !== "ACTIVE") { return err(new CategoryNotActiveError(categoryId)); } // 3. Check symbol uniqueness within category const existingUnit = await db .selectFrom("Unit") .selectAll() .where("categoryId", "=", categoryId) .where("symbol", "=", symbol) .forUpdate() .executeTakeFirst(); if (existingUnit) { return err(new DuplicateUnitSymbolError(`${symbol} in ${categoryId}`)); } // 4. Validate conversion factor if (conversionFactor <= 0) { return err(new InvalidConversionFactorError(String(conversionFactor))); } // 5. Validate rounding precision if (roundingPrecision < 0) { return err(new InvalidRoundingPrecisionError(String(roundingPrecision))); } // 6. Create unit const unit = await db .insertInto("Unit") .values({ ...(customFields as Record), name, symbol, categoryId, conversionFactor, roundingPrecision, status: "ACTIVE" as const, }) .returningAll() .executeTakeFirstOrThrow(); return ok({ unit }); }