import { type CommonReservedFields, type NoReservedFields, defaultGqlPermission, defaultPermission, } from "@tailor-platform/erp-kit/core"; import { db, type TailorAnyDBField } from "@tailor-platform/sdk"; import { timeClockEvent } from "./timeClockEvent"; // A void is itself an event of negation: VOID retracts a prior TimeClockEvent, // UNVOID retracts a prior VOID. Effectiveness is decided by the latest sequence // for a given targetEventId, so this table only ever grows (ADR-023). const ACTIONS = ["VOID", "UNVOID"] as const; // Why the target event was negated; reasonNote carries the free text. const REASON_CODES = ["DUPLICATE", "WRONG_PERSON", "DEVICE_ERROR", "MISTAKE", "OTHER"] as const; // Authority under which the negation was made (self-service vs. supervisor vs. admin). The SYSTEM // value was dropped in issue #39 — no system-issued void path (batch/automation) exists to emit it. const AUTHORITIES = ["SELF", "MANAGER", "ADMIN"] as const; const builtins = () => { return { // Intra-module FK (required): the TimeClockEvent this negation applies to targetEventId: db .uuid() .relation({ type: "n-1", toward: { type: timeClockEvent, as: "targetEvent" }, backward: "voids", }) .description("Foreign key to the TimeClockEvent this VOID/UNVOID applies to"), action: db .enum(ACTIONS) .description("VOID retracts the target event; UNVOID retracts a prior VOID"), sequence: db .int() .description( "Monotonic sequence within a targetEventId; the highest-sequence row decides effectiveness", ), // Soft reference (no hard FK): the authenticated caller who recorded this void/unvoid. // Not FK'd to user-management User because machine/system callers have no User row there. actorId: db .uuid() .description("ID of the user-management User (or system/machine caller) who recorded this"), occurredAt: db.datetime().description("Timestamp this void/unvoid was recorded"), reasonCode: db .enum(REASON_CODES) .description("Reason category (DUPLICATE/WRONG_PERSON/DEVICE_ERROR/MISTAKE/OTHER)"), reasonNote: db .string() .description("Mandatory, non-empty free-text reason; a void without a reason is disallowed"), authority: db .enum(AUTHORITIES) .description("Authority under which the negation was made (SELF/MANAGER/ADMIN)"), }; }; type ReservedFields = keyof ReturnType | CommonReservedFields; export interface CreateTimeClockEventVoidTypeParams> { fields?: NoReservedFields; } export function createTimeClockEventVoidType>( params: CreateTimeClockEventVoidTypeParams = {}, ) { return db .table("TimeClockEventVoid", { ...builtins(), ...((params.fields ?? {}) as F), ...db.fields.timestamps(), }) .permission(defaultPermission) .gqlPermission(defaultGqlPermission); } export const timeClockEventVoid = createTimeClockEventVoidType({});