import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core"; import { Decimal } from "decimal.js"; import { inventorySupplyPlanLifecycle } from "../db/inventorySupplyPlan.lifecycle.generated"; import type { InventorySupplyPlanSourceType } from "../generated/enums"; import type { Transaction } from "../generated/kysely-tailordb"; import { InvalidSupplyPlanQuantitiesError, InventorySupplyPlanNotFoundError, ItemNotFoundError, SiteNotFoundError, SupplyPlanNotOpenError, SupplyPlanUnitMismatchError, } from "../lib/errors.generated"; import type { ItemManagementQueries, OrganizationQueries } from "../module"; export interface UpdateInventorySupplyPlanInput { id: string; sourceType?: InventorySupplyPlanSourceType; sourceLineId?: string; sourceId?: string; itemId?: string; siteId?: string; expectedQuantity?: string; unitId?: string; expectedDate?: Date; } export async function run( db: Transaction, input: UpdateInventorySupplyPlanInput & Omit, "status" | "receivedQuantity">, ctx: CommandContext, itemManagementQueries: Pick, organizationQueries: Pick, ) { const { id, sourceType, sourceLineId, sourceId, itemId, siteId, expectedQuantity: inputExpectedQuantity, unitId, expectedDate, ...customFields } = input; const existing = await db .selectFrom("InventorySupplyPlan") .selectAll() .where("id", "=", id) .forUpdate() .executeTakeFirst(); if (!existing) { return err(new InventorySupplyPlanNotFoundError(id)); } const nextStatus = inventorySupplyPlanLifecycle.tryTransition(existing.status, "update"); if (!nextStatus) { return err(new SupplyPlanNotOpenError(existing.id)); } const updateSet: Record = { ...customFields, status: nextStatus }; if (sourceId !== undefined) { updateSet.sourceId = sourceId; } if (sourceType !== undefined) { updateSet.sourceType = sourceType; } if (sourceLineId !== undefined) { updateSet.sourceLineId = sourceLineId; } const effectiveItemId = itemId ?? existing.itemId; if (itemId !== undefined || unitId !== undefined) { const { item } = (await itemManagementQueries.getItem(db, { id: effectiveItemId }, ctx)).value; if (!item) { return err(new ItemNotFoundError(effectiveItemId)); } if (unitId !== undefined && unitId !== item.unitId) { return err(new SupplyPlanUnitMismatchError(`${unitId} expected ${item.unitId}`)); } if (itemId !== undefined) { updateSet.itemId = itemId; } } if (siteId !== undefined) { const { site } = (await organizationQueries.getSite(db, { id: siteId }, ctx)).value; if (!site) { return err(new SiteNotFoundError(siteId)); } updateSet.siteId = siteId; } const effectiveExpectedQuantity = inputExpectedQuantity ?? existing.expectedQuantity; const expectedQuantity = new Decimal(effectiveExpectedQuantity); if (expectedQuantity.lt(0)) { return err(new InvalidSupplyPlanQuantitiesError(effectiveExpectedQuantity)); } if (inputExpectedQuantity !== undefined) { updateSet.expectedQuantity = inputExpectedQuantity; } if (expectedDate !== undefined) { updateSet.expectedDate = expectedDate; } const supplyPlan = await db .updateTable("InventorySupplyPlan") .set(updateSet) .where("id", "=", existing.id) .returningAll() .executeTakeFirstOrThrow(); return ok({ supplyPlan }); }