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, RoutingNotActivatableError, OperationRequiredError, DuplicateOperationSequenceError, WorkCenterNotActiveError, CrossCompanyWorkCenterError, } from "../lib/errors.generated"; export interface ActivateRoutingInput { id: string; } /** * Function: activateRouting * * Validates a draft routing and transitions it to ACTIVE status. * Enforces operation completeness, sequence integrity, and active * work-center readiness. */ export async function run(db: Transaction, input: ActivateRoutingInput, _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, "activate"); if (!nextStatus) { return err(new RoutingNotActivatableError(id)); } // 3. Fetch operations const operations = await db .selectFrom("RoutingOperation") .selectAll() .where("routingId", "=", id) .execute(); // 4. At least one operation required if (operations.length === 0) { return err(new OperationRequiredError(id)); } // 5. Validate unique sequence numbers const seqNumbers = operations.map((op) => op.sequenceNumber); const uniqueSeqs = new Set(seqNumbers); if (uniqueSeqs.size !== seqNumbers.length) { return err(new DuplicateOperationSequenceError(id)); } // 6. Validate work centers are active and in same company for (const op of operations) { const workCenter = await db .selectFrom("WorkCenter") .selectAll() .where("id", "=", op.workCenterId) .executeTakeFirst(); if (workCenter?.status !== "ACTIVE") { return err(new WorkCenterNotActiveError(op.workCenterId)); } if (workCenter.companyId !== routing.companyId) { return err(new CrossCompanyWorkCenterError(op.workCenterId)); } } // 7. Set status to ACTIVE const updatedRouting = await db .updateTable("Routing") .set({ status: nextStatus, }) .where("id", "=", id) .returningAll() .executeTakeFirstOrThrow(); return ok({ routing: updatedRouting }); }