import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core"; import type { Transaction } from "../generated/kysely-tailordb"; import { deriveWorkdayBlocks, persistWorkdayCalculation } from "../lib/_deriveCalculatedBlocks"; import type { ResolveWorkRuleQueries } from "../lib/_resolveWorkRule"; import { NoReportedBlocksError } from "../lib/errors.generated"; import type { TimeClassificationStrategy } from "../lib/timeClassificationStrategy"; export interface CalculateTimeBlocksInput { assignmentId: string; workDate: Date; } /** * Function: calculateTimeBlocks * Description: Derives/refreshes CalculatedTimeBlocks for an Assignment and workday from the * current (non-superseded) ReportedTimeBlocks, applying the work-rules effective on that date. * * The WorkRule is resolved through the workforce context (Assignment → WorkerEmployment / * Position, injected queries): the WorkRuleAssignment in force on workDate wins by * most-specific target (WORKER > POSITION > JOB_PROFILE > EMPLOYMENT_TYPE > WORK_REGIME), and * the WorkRule generation effective on workDate is applied (ADR-013, ADR-015). Reported * intervals are decomposed into categorized spans (regular / overtime / late-night / * holiday) with rounding, break deduction, the daily overtime threshold, the night window, * and CompanyHoliday classification; every span records calculationTagKeys and * sourceReportedBlockIds for audit (issue #7). After regeneration, covering OPEN Timecards * have their denormalized category totals refreshed. */ export async function run( db: Transaction, input: CalculateTimeBlocksInput, ctx: CommandContext, workforceQueries: ResolveWorkRuleQueries, strategy?: TimeClassificationStrategy, ) { const derived = await deriveWorkdayBlocks(db, workforceQueries, input, ctx, strategy); if (!derived.ok) { return derived; } if (!derived.value.hasReportedBlocks) { return err(new NoReportedBlocksError(`${input.assignmentId}:${input.workDate.toISOString()}`)); } const calculatedTimeBlocks = await persistWorkdayCalculation( db, input.assignmentId, input.workDate, derived.value.rows, ); return ok({ calculatedTimeBlocks }); }