import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core"; import type { Transaction } from "../generated/kysely-tailordb"; import { DepartmentNotFoundError, InvalidHeadcountError, JobProfileNotFoundError, SiteNotFoundError, } from "../lib/errors.generated"; import type { OrganizationQueries } from "../module"; export interface CreatePositionInput { departmentId: string; siteId?: string; jobProfileId: string; headcount?: number; } /** * Function: createPosition * * Defines a new post under an organization Department (and optionally a * Site), referencing a JobProfile for its role definition, starting as * vacant with no incumbent. */ export async function run( db: Transaction, input: CreatePositionInput, ctx: CommandContext, organizationQueries: Pick, ) { const { departmentId, siteId, jobProfileId, headcount = 1 } = input; if (headcount < 1) { return err(new InvalidHeadcountError(String(headcount))); } const { department } = (await organizationQueries.getDepartment(db, { id: departmentId }, ctx)) .value; if (!department) { return err(new DepartmentNotFoundError(departmentId)); } if (siteId) { const { site } = (await organizationQueries.getSite(db, { id: siteId }, ctx)).value; if (!site) { return err(new SiteNotFoundError(siteId)); } } const jobProfile = await db .selectFrom("JobProfile") .selectAll() .where("id", "=", jobProfileId) .executeTakeFirst(); if (!jobProfile) { return err(new JobProfileNotFoundError(jobProfileId)); } const id = crypto.randomUUID(); const position = await db .insertInto("Position") .values({ id, departmentId, siteId: siteId ?? null, jobProfileId, headcount, effectiveStart: new Date(), effectiveEnd: null, versionOf: id, }) .returningAll() .executeTakeFirstOrThrow(); return ok({ position }); }