import { ok, err } from "@tailor-platform/erp-kit/core"; import type { Transaction } from "../generated/kysely-tailordb"; import { DuplicateCurrencyCodeError, InvalidDecimalPlacesError, InvalidIsoCodeError, } from "../lib/errors.generated"; interface CreateCurrencyInput { code: string; name: string; symbol: string; decimalPlaces: number; } const ISO_CODE_PATTERN = /^[A-Z]{3}$/; /** * Function: createCurrency * * Establishes a new monetary unit with its ISO 4217 code, display symbol, * name, and decimal precision. The first currency becomes the base currency. */ export async function run>( db: Transaction, input: CreateCurrencyInput & CF, ) { const { code, name, symbol, decimalPlaces, ...customFields } = input; // 1. Validate ISO code format if (!ISO_CODE_PATTERN.test(code)) { return err(new InvalidIsoCodeError(code)); } // 2. Check code uniqueness const existingCurrency = await db .selectFrom("Currency") .selectAll() .where("code", "=", code) .forUpdate() .executeTakeFirst(); if (existingCurrency) { return err(new DuplicateCurrencyCodeError(code)); } // 3. Validate decimal places if (decimalPlaces < 0 || decimalPlaces > 4) { return err(new InvalidDecimalPlacesError(String(decimalPlaces))); } // 4. Check if this is the first currency const anyCurrency = await db.selectFrom("Currency").selectAll().executeTakeFirst(); const isBaseCurrency = !anyCurrency; // 5. Create currency const currency = await db .insertInto("Currency") .values({ ...(customFields as Record), code, name, symbol, decimalPlaces, isBaseCurrency, status: "ACTIVE" as const, }) .returningAll() .executeTakeFirstOrThrow(); return ok({ currency }); }