import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core"; import type { Transaction } from "../generated/kysely-tailordb"; import type { WorkforceQueries } from "../module"; import { AssignmentNotFoundError, WorkRuleNotFoundError } from "./errors.generated"; import type { Schema, WorkRule, WorkRuleAssignment } from "./types"; type WorkRuleRow = WorkRule; type WorkRuleAssignmentRow = WorkRuleAssignment; type TargetType = WorkRuleAssignmentRow["targetType"]; /** Most-specific-wins resolution order across assignment target tiers. */ const TARGET_PRECEDENCE: readonly TargetType[] = [ "WORKER", "POSITION", "JOB_PROFILE", "EMPLOYMENT_TYPE", "WORK_REGIME", ]; export type ResolveWorkRuleQueries = Pick< WorkforceQueries, "getAssignment" | "getWorkerEmployment" | "getPosition" >; export interface ResolvedWorkRuleContext { workRule: WorkRuleRow; /** The target tier whose WorkRuleAssignment won resolution. */ resolvedTargetType: TargetType; workerId: string; positionId: string; jobProfileId: string | null; employmentTypeId: string; workRegimeId: string; } function inForceOn(row: { effectiveStart: Date; effectiveEnd: Date | null }, at: Date): boolean { return ( row.effectiveStart.getTime() <= at.getTime() && (row.effectiveEnd === null || row.effectiveEnd.getTime() >= at.getTime()) ); } /** * Function: resolveWorkRule * Description: Resolves the WorkRule to apply for an Assignment on a workDate by walking the * workforce context (Assignment → WorkerEmployment / Position), picking the WorkRuleAssignment * in force on that date by most-specific target (WORKER > POSITION > JOB_PROFILE > * EMPLOYMENT_TYPE > WORK_REGIME), then selecting the WorkRule generation effective on the date * within the assigned rule's versionOf series (ADR-013, ADR-015). */ export async function resolveWorkRule( db: Transaction, workforceQueries: ResolveWorkRuleQueries, assignmentId: string, workDate: Date, ctx: CommandContext, ) { const assignmentResult = await workforceQueries.getAssignment(db, { id: assignmentId }, ctx); if (!assignmentResult.ok) { return err(new AssignmentNotFoundError(assignmentId)); } const { assignment } = assignmentResult.value; if (!inForceOn(assignment, workDate)) { return err(new AssignmentNotFoundError(assignmentId)); } const workerEmploymentResult = await workforceQueries.getWorkerEmployment( db, { id: assignment.workerEmploymentId }, ctx, ); if (!workerEmploymentResult.ok) { return err(new AssignmentNotFoundError(assignmentId)); } const { workerEmployment } = workerEmploymentResult.value; if (!workerEmployment) { return err(new AssignmentNotFoundError(assignmentId)); } const positionResult = await workforceQueries.getPosition(db, { id: assignment.positionId }, ctx); if (!positionResult.ok) { return err(new AssignmentNotFoundError(assignmentId)); } const { position } = positionResult.value; const targetMatchers: Array<{ targetType: TargetType; matches: (row: WorkRuleAssignmentRow) => boolean; }> = [ { targetType: "WORKER", matches: (row) => row.targetId === workerEmployment.workerId }, { targetType: "POSITION", matches: (row) => row.targetId === assignment.positionId }, { targetType: "JOB_PROFILE", matches: (row) => row.targetId === (position?.jobProfileId ?? null), }, { targetType: "EMPLOYMENT_TYPE", matches: (row) => row.targetId === workerEmployment.employmentTypeId, }, { targetType: "WORK_REGIME", matches: (row) => row.targetId === workerEmployment.workRegimeId, }, ]; const candidates = await db .selectFrom("WorkRuleAssignment") .selectAll() .where("effectiveStart", "<=", workDate) .where((eb) => eb.or([eb("effectiveEnd", "is", null), eb("effectiveEnd", ">=", workDate)])) .execute(); let winner: WorkRuleAssignmentRow | undefined; let winnerTargetType: TargetType | undefined; for (const targetType of TARGET_PRECEDENCE) { const matcher = targetMatchers.find((entry) => entry.targetType === targetType); const match = candidates.find( (row) => row.targetType === targetType && matcher !== undefined && matcher.matches(row), ); if (match) { winner = match; winnerTargetType = targetType; break; } } if (!winner || !winnerTargetType) { return err(new WorkRuleNotFoundError(`${assignmentId}:${workDate.toISOString()}`)); } // Re-resolve the generation effective on workDate within the assigned rule's versionOf // series — the assignment may point at an older generation than the one in force. const assignedGeneration = await db .selectFrom("WorkRule") .selectAll() .where("id", "=", winner.workRuleId) .executeTakeFirst(); if (!assignedGeneration) { return err(new WorkRuleNotFoundError(winner.workRuleId)); } const effectiveGeneration = inForceOn(assignedGeneration, workDate) ? assignedGeneration : await db .selectFrom("WorkRule") .selectAll() .where("versionOf", "=", assignedGeneration.versionOf) .where("effectiveStart", "<=", workDate) .where((eb) => eb.or([eb("effectiveEnd", "is", null), eb("effectiveEnd", ">=", workDate)])) .executeTakeFirst(); if (!effectiveGeneration) { return err(new WorkRuleNotFoundError(assignedGeneration.versionOf)); } const resolved: ResolvedWorkRuleContext = { workRule: effectiveGeneration, resolvedTargetType: winnerTargetType, workerId: workerEmployment.workerId, positionId: assignment.positionId, jobProfileId: position?.jobProfileId ?? null, employmentTypeId: workerEmployment.employmentTypeId, workRegimeId: workerEmployment.workRegimeId, }; return ok(resolved); }