import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core"; import { routingLifecycle } from "../db/routing.lifecycle.generated"; import type { Transaction } from "../generated/kysely-tailordb"; import { RoutingNotFoundError, RoutingNotDeactivatableError, ReplacementRequiredError, } from "../lib/errors.generated"; export interface DeactivateRoutingInput { id: string; } /** * Function: deactivateRouting * * Removes an active routing from future production-order selection. * Released production orders keep their frozen operation plan. */ export async function run(db: Transaction, input: DeactivateRoutingInput, _ctx: CommandContext) { const { id } = input; // 1. Fetch routing const routing = await db .selectFrom("Routing") .selectAll() .where("id", "=", id) .forUpdate() .executeTakeFirst(); if (!routing) { return err(new RoutingNotFoundError(id)); } // 2. Verify allowed source status const nextStatus = routingLifecycle.tryTransition(routing.status, "deactivate"); if (!nextStatus) { return err(new RoutingNotDeactivatableError(id)); } // 3. Replacement policy: check if another active routing exists for same item+company const replacement = await db .selectFrom("Routing") .selectAll() .where("parentItemId", "=", routing.parentItemId) .where("companyId", "=", routing.companyId) .where("status", "=", "ACTIVE") .where("id", "!=", id) .executeTakeFirst(); if (!replacement) { // Check if any production orders reference this routing in released/in-progress state const dependentOrder = await db .selectFrom("ProductionOrder") .selectAll() .where("selectedRoutingRevisionId", "=", id) .where("status", "in", ["RELEASED", "IN_PROGRESS"]) .executeTakeFirst(); if (dependentOrder) { return err(new ReplacementRequiredError(id)); } } // 4. Set status to INACTIVE const updatedRouting = await db .updateTable("Routing") .set({ status: nextStatus, }) .where("id", "=", id) .returningAll() .executeTakeFirstOrThrow(); return ok({ routing: updatedRouting }); }