import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core"; import { productionOrderLifecycle } from "../db/productionOrder.lifecycle.generated"; import type { Transaction } from "../generated/kysely-tailordb"; import { ProductionOrderNotFoundError, ProductionOrderNotReopenableError, ReopenReasonRequiredError, CostSummaryNotReopenableError, OrderAlreadyClosedError, } from "../lib/errors.generated"; export interface ReopenProductionOrderInput { id: string; reason: string; } /** * Function: reopenProductionOrder * * Re-enables execution after a technically complete order needs more * shop-floor work. It reverses the execution freeze and returns the linked * cost summary to active collection. */ export async function run( db: Transaction, input: ReopenProductionOrderInput, _ctx: CommandContext, ) { const { id, reason } = input; // 1. Fetch production order with lock const order = await db .selectFrom("ProductionOrder") .selectAll() .where("id", "=", id) .forUpdate() .executeTakeFirst(); if (!order) { return err(new ProductionOrderNotFoundError(id)); } // 2. Check if order is already closed if (order.status === "CLOSED") { return err(new OrderAlreadyClosedError(id)); } // 3. Validate status is reopenable const nextStatus = productionOrderLifecycle.tryTransition(order.status, "reopen"); if (!nextStatus) { return err(new ProductionOrderNotReopenableError(id)); } // 4. Validate reopen reason if (!reason || reason.trim() === "") { return err(new ReopenReasonRequiredError(id)); } // 5. Find and validate cost summary const costSummary = await db .selectFrom("ManufacturingCostSummary") .selectAll() .where("productionOrderId", "=", id) .forUpdate() .executeTakeFirst(); if ( !costSummary || (costSummary.status !== "PENDING_VARIANCE_REVIEW" && costSummary.status !== "VARIANCE_REVIEWED") ) { return err(new CostSummaryNotReopenableError(id)); } // 6. Return cost summary to COLLECTING await db .updateTable("ManufacturingCostSummary") .set({ status: "COLLECTING", }) .where("id", "=", costSummary.id) .execute(); // 7. Set order status to IN_PROGRESS const reopenedOrder = await db .updateTable("ProductionOrder") .set({ status: nextStatus, }) .where("id", "=", id) .returningAll() .executeTakeFirstOrThrow(); return ok({ productionOrder: reopenedOrder }); }