import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core"; import type { Transaction } from "../generated/kysely-tailordb"; import { PipelineNotFoundError, DuplicatePipelineNameError, InvalidNameError, } from "../lib/errors.generated"; export interface UpdatePipelineInput { pipelineId: string; name?: string; description?: string | null; } const PIPELINE_OWN_KEYS = new Set(["pipelineId", "name", "description"]); // Module-managed Pipeline columns that must never be writable through the // extension-field pass-through — status/locked changes go through their // dedicated commands, itemNumberSeq is an internal counter, and // createdByUserId is immutable creation-time audit metadata. const PIPELINE_RESERVED_KEYS = new Set([ "id", "pipelineType", "createdByUserId", "status", "locked", "itemNumberSeq", "createdAt", "updatedAt", ]); export async function run>( db: Transaction, input: UpdatePipelineInput & Partial, ctx: CommandContext, ) { void ctx; // Consumer extension fields (declared via defineModule's pipeline.fields) // pass through to the update untouched — except reserved model columns. const customFields: Record = {}; for (const [key, value] of Object.entries(input as Record)) { if (!PIPELINE_OWN_KEYS.has(key) && !PIPELINE_RESERVED_KEYS.has(key)) { customFields[key] = value; } } const pipeline = await db .selectFrom("Pipeline") .selectAll() .where("id", "=", input.pipelineId) .forUpdate() .executeTakeFirst(); if (!pipeline) { return err(new PipelineNotFoundError(input.pipelineId)); } if (input.name !== undefined && !input.name.trim()) { return err(new InvalidNameError(input.name)); } if (input.name !== undefined && input.name !== pipeline.name && !pipeline.locked) { // See createPipeline for why locked (managed) pipelines skip the uniqueness check. const duplicate = await db .selectFrom("Pipeline") .selectAll() .where("pipelineType", "=", pipeline.pipelineType) .where("name", "=", input.name) .executeTakeFirst(); if (duplicate && duplicate.id !== pipeline.id) { return err(new DuplicatePipelineNameError(input.name)); } } const update: Record = { ...customFields, updatedAt: new Date(), }; if (input.name !== undefined) update.name = input.name; if (input.description !== undefined) update.description = input.description; const updatedPipeline = await db .updateTable("Pipeline") .set(update) .where("id", "=", input.pipelineId) .returningAll() .executeTakeFirst(); return ok({ pipeline: updatedPipeline ?? { ...pipeline, ...update } }); }