import { buildPaginatedResult, DEFAULT_PAGE_SIZE, err, ok, type PaginationInput, type ReadonlyDB, } from "@tailor-platform/erp-kit/core"; import type { DB } from "../generated/kysely-tailordb"; import { InvalidQueryParamsError } from "../lib/errors.generated"; type CalculatedBlockOrderByField = "workDate"; export interface ListCalculatedBlocksByTimeEntryCodeInput extends PaginationInput { timeEntryCodeKey: string; startDate?: Date; endDate?: Date; } /** * Function: listCalculatedBlocksByTimeEntryCode * Description: Lists CalculatedTimeBlocks matching a given timeEntryCodeKey * over a date range, paginated. This is the aggregation input for downstream * consumers such as payroll and Article-36 agreement threshold evaluation, which need every * block of a given category (e.g. overtime) across a period. */ export async function run(db: ReadonlyDB, input: ListCalculatedBlocksByTimeEntryCodeInput) { if (!input.timeEntryCodeKey) { return err(new InvalidQueryParamsError("timeEntryCodeKey is missing")); } if (input.startDate !== undefined && Number.isNaN(input.startDate.getTime())) { return err(new InvalidQueryParamsError(`startDate=${String(input.startDate)}`)); } if (input.endDate !== undefined && Number.isNaN(input.endDate.getTime())) { return err(new InvalidQueryParamsError(`endDate=${String(input.endDate)}`)); } if ( input.startDate !== undefined && input.endDate !== undefined && input.endDate < input.startDate ) { return err( new InvalidQueryParamsError( `${input.startDate.toISOString()}..${input.endDate.toISOString()}`, ), ); } const limit = input.limit ?? DEFAULT_PAGE_SIZE; const offset = input.offset ?? 0; const orderBy = input.orderBy ?? "id"; const orderDirection = input.orderDirection ?? "asc"; let query = db .selectFrom("CalculatedTimeBlock") .selectAll() .where("timeEntryCodeKey", "=", input.timeEntryCodeKey); if (input.startDate !== undefined) { query = query.where("workDate", ">=", input.startDate); } if (input.endDate !== undefined) { query = query.where("workDate", "<=", input.endDate); } const calculatedTimeBlocks = await query .orderBy(orderBy, orderDirection) .limit(limit + 1) .offset(offset) .execute(); return ok(buildPaginatedResult(calculatedTimeBlocks, limit)); }