import { ok, type ReadonlyDB } from "@tailor-platform/erp-kit/core"; import { Decimal } from "decimal.js"; import type { DB } from "../generated/kysely-tailordb"; import { sumOpenStockReservationQuantity } from "../internal/stockReservation"; export interface GetStockLevelInput { itemId: string; storageLocationId: string; } export async function run(db: ReadonlyDB, input: GetStockLevelInput) { const stockLevels = await db .selectFrom("StockLevel") .selectAll() .where("itemId", "=", input.itemId) .where("storageLocationId", "=", input.storageLocationId) .execute(); if (stockLevels.length === 0) { return ok({ stockLevel: null }); } const available = stockLevels.find((row) => row.stockType === "AVAILABLE"); const blocked = stockLevels.find((row) => row.stockType === "BLOCKED"); const inTransit = stockLevels.find((row) => row.stockType === "IN_TRANSIT"); const reservationsResult = await db .selectFrom("StockReservation") .selectAll() .where("itemId", "=", input.itemId) .where("storageLocationId", "=", input.storageLocationId) .where("status", "=", "OPEN") .execute(); const reservations = Array.isArray(reservationsResult) ? reservationsResult : reservationsResult ? [reservationsResult] : []; const onHand = new Decimal(available?.quantity ?? 0).plus(blocked?.quantity ?? 0); const reservedQuantity = sumOpenStockReservationQuantity(reservations); const blockedQuantity = new Decimal(blocked?.quantity ?? 0); const availableQuantity = new Decimal(available?.quantity ?? 0) .minus(reservedQuantity) .toString(); return ok({ stockLevel: { itemId: input.itemId, storageLocationId: input.storageLocationId, onHand: onHand.toString(), reserved: reservedQuantity.toString(), blocked: blockedQuantity.toString(), inTransit: new Decimal(inTransit?.quantity ?? 0).toString(), availableQuantity, stockLevels, reservation: reservations[0] ?? null, }, }); }