import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core"; import { Decimal } from "decimal.js"; import type { StockReservationSourceType } from "../generated/enums"; import type { Transaction } from "../generated/kysely-tailordb"; import { calculateSiteAvailableExcludingReservation } from "../internal/stockReservation"; import { InsufficientAvailableStockError, InvalidReservationQuantitiesError, ItemNotFoundError, SiteNotFoundError, StorageLocationNotFoundError, } from "../lib/errors.generated"; import type { ItemManagementQueries, OrganizationQueries } from "../module"; export interface CreateStockReservationInput { sourceType: StockReservationSourceType; sourceId: string; sourceLineId: string; itemId: string; siteId: string; storageLocationId?: string | null; reservedQuantity: string; requiredDate: Date; } export async function run( db: Transaction, input: CreateStockReservationInput, ctx: CommandContext, itemManagementQueries: Pick, organizationQueries: Pick, ) { const { sourceType, sourceId, sourceLineId, itemId, siteId, storageLocationId, reservedQuantity: inputReservedQuantity, requiredDate, } = input; const reservedQuantity = new Decimal(inputReservedQuantity); if (reservedQuantity.lte(0)) { return err(new InvalidReservationQuantitiesError(inputReservedQuantity)); } const { item } = (await itemManagementQueries.getItem(db, { id: itemId }, ctx)).value; if (!item) { return err(new ItemNotFoundError(itemId)); } const { site } = (await organizationQueries.getSite(db, { id: siteId }, ctx)).value; if (!site) { return err(new SiteNotFoundError(siteId)); } if (storageLocationId) { const location = await db .selectFrom("StorageLocation") .selectAll() .where("id", "=", storageLocationId) .where("siteId", "=", siteId) .executeTakeFirst(); if (!location) { return err(new StorageLocationNotFoundError(storageLocationId)); } } const available = await calculateSiteAvailableExcludingReservation(db, itemId, siteId); if (!available || available.lt(reservedQuantity)) { return err(new InsufficientAvailableStockError(`${itemId}:${siteId}`)); } const stockReservation = await db .insertInto("StockReservation") .values({ sourceType, sourceId, sourceLineId, itemId, siteId, storageLocationId: storageLocationId ?? null, reservedQuantity: inputReservedQuantity, consumedQuantity: "0", status: "OPEN", requiredDate, }) .returningAll() .executeTakeFirstOrThrow(); return ok({ stockReservation }); }