import { type CommonReservedFields, type NoReservedFields, defaultGqlPermission, defaultPermission, } from "@tailor-platform/erp-kit/core"; import { db, type TailorAnyDBField, type TailorAnyDBType } from "@tailor-platform/sdk"; import { workRule } from "./workRule"; // Categorized span produced by decomposing ReportedTimeBlocks against effective work-rules. The // span-type `category` is a strategy-defined key, not a fixed core enum — the bundled JP strategy // emits regular / overtime / night / holiday, but jurisdiction-specific categories (including // night/holiday, which are JP concepts) live in the app's strategy, not here. The PREMIUM value was // dropped in issue #39 — the derivation never emitted it and totals.PREMIUM was never persisted. const builtins = (params: { assignmentType?: TailorAnyDBType }) => { // Cross-module FK: relation only when the foreign type is injected; plain uuid at codegen const assignmentId = db .uuid() .description("Foreign key to the workforce Assignment effective on workDate"); return { assignmentId: params.assignmentType ? assignmentId.relation({ type: "n-1", toward: { type: params.assignmentType, as: "assignment" }, backward: "calculatedTimeBlocks", }) : assignmentId, // Workday this calculated block belongs to; drives which rule generation is effective workDate: db .date() .description("Workday this block belongs to; also selects the effective WorkRule generation"), // Categorized span-type key this block represents, produced by the calculation strategy // (e.g. REGULAR/OVERTIME/NIGHT/HOLIDAY for the JP strategy). Not a fixed enum so other // jurisdictions' strategies can emit their own category keys. category: db .string() .description( "Categorized span-type key this block represents, produced by the calculation strategy", ), startAt: db.datetime().description("Interval start of this categorized span"), endAt: db.datetime().description("Interval end of this categorized span"), // Non-negative integer minutes (no float hours), per doc invariant minutes: db .int() .description( "Non-negative integer count of minutes for this categorized span (no float hours)", ), // Referenced by key, never by display name (ADR-015) — plain string, not a .relation() timeEntryCodeKey: db .string() .description( "work-rules TimeEntryCode key this block is bound to, referenced by stable key never display name", ), payCodeKey: db .string() .description( "work-rules pay code key this block is bound to, referenced by stable key never display name", ), // Intra-module FK to the exact WorkRule generation applied, for audit traceability (issue #7) workRuleId: db .uuid() .relation({ type: "n-1", toward: { type: workRule, as: "workRule" }, backward: "calculatedTimeBlocks", }) .description("Foreign key to the exact WorkRule generation applied to derive this block"), // Array of semantic rule/tag keys that fired to produce this block, for audit traceability (issue #7) // Choice: plain string array (db.string({ array: true })) — simplest option per model doc guidance; // array-of-scalar isn't shown in db-field-api.md's model examples, but the same { array: true } option // is used for scalar fields in this repo's resolver input types (e.g. t.string({ array: true })). calculationTagKeys: db .string({ array: true }) .description("Semantic keys of every WorkRule fragment that fired to produce this block"), // Intra-module, but plural -> not a single .relation(); array of ReportedTimeBlock ids consumed as input sourceReportedBlockIds: db .uuid({ array: true }) .description( "Current ReportedTimeBlock ids this calculation consumed; re-derivation input (ADR-014)", ), }; }; type ReservedFields = keyof ReturnType | CommonReservedFields; export interface CreateCalculatedTimeBlockTypeParams> { fields?: NoReservedFields; assignmentType?: TailorAnyDBType; } export function createCalculatedTimeBlockType>( params: CreateCalculatedTimeBlockTypeParams, ) { return db .table("CalculatedTimeBlock", { ...builtins(params), ...((params.fields ?? {}) as F), ...db.fields.timestamps(), }) .permission(defaultPermission) .gqlPermission(defaultGqlPermission); } // Codegen instance with no cross-module types; real FK injected via module.ts export const calculatedTimeBlock = createCalculatedTimeBlockType({});