import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core"; import type { Transaction } from "../generated/kysely-tailordb"; import { WorkerEmploymentNotFoundError, AlreadyTerminatedError, TerminationDateBeforeEffectiveStartError, } from "../lib/errors.generated"; export interface TerminateWorkerEmploymentInput { id: string; terminationDate: Date; } /** Function: run Description: Sets the termination date on a WorkerEmployment and closes its currently open generation, ending the employment relationship while preserving the Worker for possible re-hire. */ export async function run( db: Transaction, input: TerminateWorkerEmploymentInput, _ctx: CommandContext, ) { const workerEmployment = await db .selectFrom("WorkerEmployment") .selectAll() .where("id", "=", input.id) .forUpdate() .executeTakeFirst(); if (!workerEmployment) return err(new WorkerEmploymentNotFoundError(input.id)); if (workerEmployment.effectiveEnd !== null) return err(new AlreadyTerminatedError(input.id)); if (input.terminationDate.getTime() < workerEmployment.effectiveStart.getTime()) { return err(new TerminationDateBeforeEffectiveStartError(input.id)); } const updated = await db .updateTable("WorkerEmployment") .set({ terminationDate: input.terminationDate, effectiveEnd: input.terminationDate }) .where("id", "=", input.id) .returningAll() .executeTakeFirstOrThrow(); // Close the employment's open Assignments at the termination date in the same transaction (M03). // Otherwise the worker keeps resolving as currently assigned and the Position occupancy is never // released (blocking new hires as over-headcount). Only assignments already in force by the // termination date are closed; a new assignment can no longer be opened on a terminated employment // (createAssignment's EMPLOYMENT_TERMINATED guard). await db .updateTable("Assignment") .set({ effectiveEnd: input.terminationDate }) .where("workerEmploymentId", "=", input.id) .where("effectiveEnd", "is", null) .where("effectiveStart", "<=", input.terminationDate) .execute(); return ok({ workerEmployment: updated }); }