import { ok, err } from "@tailor-platform/erp-kit/core"; import type { Transaction } from "../generated/kysely-tailordb"; import { UnitNotFoundError, UnitNotInCategoryError } from "../lib/errors.generated"; export interface SetReferenceUnitInput { unitId: string; categoryId: string; } /** * Function: setReferenceUnit * * Changes the reference unit for a UoM category. All conversion factors * are recalculated relative to the new reference unit. */ export async function run(db: Transaction, input: SetReferenceUnitInput) { // 1. Find unit by ID const unit = await db .selectFrom("Unit") .selectAll() .where("id", "=", input.unitId) .forUpdate() .executeTakeFirst(); if (!unit) { return err(new UnitNotFoundError(input.unitId)); } // 2. Check unit belongs to specified category if (unit.categoryId !== input.categoryId) { return err(new UnitNotInCategoryError(`${input.unitId} in ${input.categoryId}`)); } // 3. Get category const uoMCategory = await db .selectFrom("UoMCategory") .selectAll() .where("id", "=", input.categoryId) .forUpdate() .executeTakeFirst(); // 4. If already reference unit, return (idempotent) if (uoMCategory?.referenceUnitId === input.unitId && uoMCategory) { return ok({ uoMCategory }); } // 5. Get all units in category for recalculation const units = await db .selectFrom("Unit") .selectAll() .where("categoryId", "=", input.categoryId) .forUpdate() .execute(); // 6. Recalculate all conversion factors // new_factor = old_factor / new_reference_old_factor const newReferenceFactor = unit.conversionFactor; for (const u of units) { const newFactor = u.conversionFactor / newReferenceFactor; await db .updateTable("Unit") .set({ conversionFactor: newFactor, }) .where("id", "=", u.id) .returningAll() .executeTakeFirst(); } // 7. Update category reference unit const updatedCategory = await db .updateTable("UoMCategory") .set({ referenceUnitId: input.unitId, }) .where("id", "=", input.categoryId) .returningAll() .executeTakeFirstOrThrow(); return ok({ uoMCategory: updatedCategory }); }