import { ok, err, type ReadonlyDB, type CallerContext } from "@tailor-platform/erp-kit/core"; import type { DB } from "../generated/kysely-tailordb"; import { InactiveUnitError, IncompatibleUnitsError, UnitNotFoundError, } from "../lib/errors.generated"; import { getUnit } from "./getUnit.generated"; export interface ConvertQuantityInput { quantity: number; sourceUnitSymbol: string; targetUnitSymbol: string; } /** * Converts a quantity from one unit of measure to another within the same category. * Units are identified by their symbol (e.g., "kg", "lb", "g"). * The conversion uses each unit's conversion factor relative to the category's reference unit. * Result is rounded to the target unit's precision setting. */ export async function run(db: ReadonlyDB, input: ConvertQuantityInput, ctx: CallerContext) { // Validate source unit exists const { unit: sourceUnit } = (await getUnit(db, { symbol: input.sourceUnitSymbol }, ctx)).value; if (!sourceUnit) { return err(new UnitNotFoundError(input.sourceUnitSymbol)); } // Validate target unit exists const { unit: targetUnit } = (await getUnit(db, { symbol: input.targetUnitSymbol }, ctx)).value; if (!targetUnit) { return err(new UnitNotFoundError(input.targetUnitSymbol)); } // Validate both units are active if (sourceUnit.status !== "ACTIVE") { return err(new InactiveUnitError(input.sourceUnitSymbol)); } if (targetUnit.status !== "ACTIVE") { return err(new InactiveUnitError(input.targetUnitSymbol)); } // Validate units belong to the same category if (sourceUnit.categoryId !== targetUnit.categoryId) { return err( new IncompatibleUnitsError(`${input.sourceUnitSymbol} and ${input.targetUnitSymbol}`), ); } // Perform conversion: result = quantity * sourceConversionFactor / targetConversionFactor const rawResult = (input.quantity * sourceUnit.conversionFactor) / targetUnit.conversionFactor; // Apply rounding to target unit's precision const roundingFactor = Math.pow(10, targetUnit.roundingPrecision); const convertedQuantity = Math.round(rawResult * roundingFactor) / roundingFactor; return ok({ convertedQuantity, sourceUnit, targetUnit, }); }