import type { UserManagementModule } from "../user-management"; import type { WorkforceModule } from "../workforce"; import { approveTimecard } from "./command/approveTimecard.generated"; // work-rules domain commands (merged, ADR-020) import { assignWorkRule } from "./command/assignWorkRule.generated"; import { calculateTimeBlocks } from "./command/calculateTimeBlocks.generated"; import { closeTimecardPeriod } from "./command/closeTimecardPeriod.generated"; import { correctReportedBlock } from "./command/correctReportedBlock.generated"; import { createCompanyHoliday } from "./command/createCompanyHoliday.generated"; import { createEligibilityRule } from "./command/createEligibilityRule.generated"; import { createHolidayCalendar } from "./command/createHolidayCalendar.generated"; import { createTimeEntryCode } from "./command/createTimeEntryCode.generated"; import { createWorkRule } from "./command/createWorkRule.generated"; import { declareReportedBlock } from "./command/declareReportedBlock.generated"; import { deleteCompanyHoliday } from "./command/deleteCompanyHoliday.generated"; import { deleteEligibilityRule } from "./command/deleteEligibilityRule.generated"; import { deleteHolidayCalendar } from "./command/deleteHolidayCalendar.generated"; import { deleteTimeEntryCode } from "./command/deleteTimeEntryCode.generated"; import { deleteWorkRule } from "./command/deleteWorkRule.generated"; import { formReportedBlocks } from "./command/formReportedBlocks.generated"; import { importPunches } from "./command/importPunches.generated"; import { importReportedBlocks } from "./command/importReportedBlocks.generated"; import { lockTimecard } from "./command/lockTimecard.generated"; import { openTimecard } from "./command/openTimecard.generated"; import { recalculateRange } from "./command/recalculateRange.generated"; import { recordPunch } from "./command/recordPunch.generated"; import { reopenTimecard } from "./command/reopenTimecard.generated"; import { submitTimecard } from "./command/submitTimecard.generated"; import { unvoidPunch } from "./command/unvoidPunch.generated"; import { updateCompanyHoliday } from "./command/updateCompanyHoliday.generated"; import { updateEligibilityRule } from "./command/updateEligibilityRule.generated"; import { updateHolidayCalendar } from "./command/updateHolidayCalendar.generated"; import { updateTimeEntryCode } from "./command/updateTimeEntryCode.generated"; import { updateWorkRule } from "./command/updateWorkRule.generated"; import { voidPunch } from "./command/voidPunch.generated"; import { createCalculatedTimeBlockType } from "./db/calculatedTimeBlock"; // work-rules domain db models (merged, ADR-020; self-contained, no DI) import { companyHoliday } from "./db/companyHoliday"; import { eligibilityRule } from "./db/eligibilityRule"; import { holidayCalendar } from "./db/holidayCalendar"; import { createReportedTimeBlockType } from "./db/reportedTimeBlock"; import { createTimecardType } from "./db/timecard"; import { createTimeClockEventType } from "./db/timeClockEvent"; import { createTimeClockEventVoidType } from "./db/timeClockEventVoid"; import { createTimeCorrectionLogType } from "./db/timeCorrectionLog"; import { timeEntryCode } from "./db/timeEntryCode"; import { workRule } from "./db/workRule"; import { workRuleAssignment } from "./db/workRuleAssignment"; import type { ApproveApprovalStepFn, CancelApprovalRequestFn, CreateApprovalRequestFn, FindPendingApproverAssigneeFn, GetActiveApprovalRequestFn, ListUsersByRoleFn, SendBackApprovalStepFn, WithdrawApprovalRequestFn, } from "./lib/_approvalDeps"; import { jpTimeClassificationStrategy } from "./lib/jpTimeClassificationStrategy"; import type { TimeClassificationStrategy } from "./lib/timeClassificationStrategy"; import { aggregateWorkedDays } from "./query/aggregateWorkedDays.generated"; import { getCalculatedBlock } from "./query/getCalculatedBlock.generated"; // work-rules domain queries (merged, ADR-020) import { getCompanyHoliday } from "./query/getCompanyHoliday.generated"; import { getCompanyHolidayByDate } from "./query/getCompanyHolidayByDate.generated"; import { getCorrectionLogEntry } from "./query/getCorrectionLogEntry.generated"; import { getEligibilityRule } from "./query/getEligibilityRule.generated"; import { getHolidayCalendar } from "./query/getHolidayCalendar.generated"; import { getPunch } from "./query/getPunch.generated"; import { getReportedBlock } from "./query/getReportedBlock.generated"; import { getTimecard } from "./query/getTimecard.generated"; import { getTimeEntryCode } from "./query/getTimeEntryCode.generated"; import { getTimeEntryCodeByKey } from "./query/getTimeEntryCodeByKey.generated"; import { getWorkRule } from "./query/getWorkRule.generated"; import { getWorkRuleAssignment } from "./query/getWorkRuleAssignment.generated"; import { listCalculatedBlocksByTimeEntryCode } from "./query/listCalculatedBlocksByTimeEntryCode.generated"; import { listCalculatedBlocksByWorkDate } from "./query/listCalculatedBlocksByWorkDate.generated"; import { listCompanyHolidays } from "./query/listCompanyHolidays.generated"; import { listCorrectionsByTimecard } from "./query/listCorrectionsByTimecard.generated"; import { listCurrentReportedBlocks } from "./query/listCurrentReportedBlocks.generated"; import { listHolidayCalendars } from "./query/listHolidayCalendars.generated"; import { listOpenTimecards } from "./query/listOpenTimecards.generated"; import { listPunchesByAssignment } from "./query/listPunchesByAssignment.generated"; import { listPunchesByOccurredDate } from "./query/listPunchesByOccurredDate.generated"; import { listReportedBlocksByWorkDate } from "./query/listReportedBlocksByWorkDate.generated"; import { listTimecardsByAssignment } from "./query/listTimecardsByAssignment.generated"; import { listTimeEntryCodes } from "./query/listTimeEntryCodes.generated"; import { listWorkRuleAssignments } from "./query/listWorkRuleAssignments.generated"; type UserManagementDB = UserManagementModule["db"]; type WorkforceDB = WorkforceModule["db"]; export type WorkforceQueries = WorkforceModule["queries"]; export interface DefineModuleParams { workforce: { db: { assignment: WorkforceDB["assignment"] }; queries: Pick< WorkforceQueries, | "getAssignment" | "getWorkerEmployment" | "getWorker" | "getPosition" | "getJobProfile" | "getEmploymentType" | "getWorkRegime" >; }; userManagement: { db: { user: UserManagementDB["user"] }; queries: { listUsersByRole: ListUsersByRoleFn }; }; /** * Bundled approval-module seam (ADR-003 wrapper). The app composition injects the * approval commands/queries and the timecard-approver role id; `findPendingApproverAssignee` * is app-provided because the approval module exposes no query returning an assignee id. */ approval: { timecardApproverRoleId: string; commands: { createApprovalRequest: CreateApprovalRequestFn; approveApprovalStep: ApproveApprovalStepFn; withdrawApprovalRequest: WithdrawApprovalRequestFn; sendBackApprovalStep: SendBackApprovalStepFn; cancelApprovalRequest: CancelApprovalRequestFn; }; queries: { getActiveApprovalRequest: GetActiveApprovalRequestFn; }; findPendingApproverAssignee: FindPendingApproverAssigneeFn; }; /** * Per-jurisdiction time-classification seam. When omitted, the bundled Japan (Labor Standards * Act) strategy is used. A consuming app supplies its own strategy (e.g. US FLSA weekly * overtime, AU penalty rates) to change how spans are categorized and stacked. */ timeCalculation?: { strategy?: TimeClassificationStrategy; }; } /* @__NO_SIDE_EFFECTS__ */ export const defineModule = (params: DefineModuleParams) => { const assignmentType = params.workforce.db.assignment; const userType = params.userManagement.db.user; const workforceQueries = params.workforce.queries; const classificationStrategy = params.timeCalculation?.strategy ?? jpTimeClassificationStrategy; // Approval-module wrapper deps (ADR-003), assembled once from the injected seam. const submitTimecardDeps = { timecardApproverRoleId: params.approval.timecardApproverRoleId, listUsersByRole: params.userManagement.queries.listUsersByRole, createApprovalRequest: params.approval.commands.createApprovalRequest, }; const approveTimecardDeps = { getActiveApprovalRequest: params.approval.queries.getActiveApprovalRequest, findPendingApproverAssignee: params.approval.findPendingApproverAssignee, approveApprovalStep: params.approval.commands.approveApprovalStep, }; const reopenTimecardDeps = { getActiveApprovalRequest: params.approval.queries.getActiveApprovalRequest, findPendingApproverAssignee: params.approval.findPendingApproverAssignee, withdrawApprovalRequest: params.approval.commands.withdrawApprovalRequest, sendBackApprovalStep: params.approval.commands.sendBackApprovalStep, cancelApprovalRequest: params.approval.commands.cancelApprovalRequest, }; const reportedTimeBlock = createReportedTimeBlockType({ assignmentType }); const timeClockEvent = createTimeClockEventType({ assignmentType }); const timeClockEventVoid = createTimeClockEventVoidType(); const timecard = createTimecardType({ assignmentType, userType }); const calculatedTimeBlock = createCalculatedTimeBlockType({ assignmentType }); const timeCorrectionLog = createTimeCorrectionLogType({ userType }); return { db: { reportedTimeBlock, timeClockEvent, timeClockEventVoid, timecard, calculatedTimeBlock, timeCorrectionLog, // work-rules domain (ADR-020) holidayCalendar, companyHoliday, eligibilityRule, timeEntryCode, workRule, workRuleAssignment, }, commands: { recordPunch: recordPunch(workforceQueries), voidPunch: voidPunch(), unvoidPunch: unvoidPunch(), importPunches: importPunches(workforceQueries), // Reported-block writers re-derive CalculatedTimeBlocks in-transaction as a domain // invariant (issue #37), so they take the same workforce query seam calculateTimeBlocks // uses to resolve the effective WorkRule. declareReportedBlock: declareReportedBlock(workforceQueries, classificationStrategy), correctReportedBlock: correctReportedBlock(workforceQueries, classificationStrategy), formReportedBlocks: formReportedBlocks(workforceQueries, classificationStrategy), importReportedBlocks: importReportedBlocks(workforceQueries, classificationStrategy), openTimecard: openTimecard(workforceQueries), submitTimecard: submitTimecard(submitTimecardDeps), approveTimecard: approveTimecard(approveTimecardDeps), lockTimecard: lockTimecard(), closeTimecardPeriod: closeTimecardPeriod(), reopenTimecard: reopenTimecard(reopenTimecardDeps), calculateTimeBlocks: calculateTimeBlocks(workforceQueries, classificationStrategy), recalculateRange: recalculateRange(workforceQueries, classificationStrategy), // work-rules domain (ADR-020) createHolidayCalendar: createHolidayCalendar(), updateHolidayCalendar: updateHolidayCalendar(), deleteHolidayCalendar: deleteHolidayCalendar(), createCompanyHoliday: createCompanyHoliday(), updateCompanyHoliday: updateCompanyHoliday(), deleteCompanyHoliday: deleteCompanyHoliday(), createEligibilityRule: createEligibilityRule(workforceQueries), updateEligibilityRule: updateEligibilityRule(workforceQueries), deleteEligibilityRule: deleteEligibilityRule(), assignWorkRule: assignWorkRule(workforceQueries), createTimeEntryCode: createTimeEntryCode(), updateTimeEntryCode: updateTimeEntryCode(), deleteTimeEntryCode: deleteTimeEntryCode(), createWorkRule: createWorkRule(), updateWorkRule: updateWorkRule(), deleteWorkRule: deleteWorkRule(), }, queries: { getPunch, listPunchesByAssignment, listPunchesByOccurredDate, getReportedBlock, listCurrentReportedBlocks, listReportedBlocksByWorkDate, getTimecard, listOpenTimecards, listTimecardsByAssignment, aggregateWorkedDays, getCalculatedBlock, listCalculatedBlocksByTimeEntryCode, listCalculatedBlocksByWorkDate, getCorrectionLogEntry, listCorrectionsByTimecard, // work-rules domain (ADR-020) getHolidayCalendar, listHolidayCalendars, getCompanyHoliday, getCompanyHolidayByDate, listCompanyHolidays, getEligibilityRule, getTimeEntryCode, getTimeEntryCodeByKey, listTimeEntryCodes, getWorkRule, getWorkRuleAssignment, listWorkRuleAssignments, }, }; }; export type TimeTrackingModule = ReturnType;