@classytic/payroll 2.7.5 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/result.ts","../../src/utils/logger.ts","../../src/core/events.ts","../../src/core/idempotency.ts","../../src/core/webhooks.ts","../../src/core/plugin.ts","../../src/utils/query-builders.ts","../../src/core/repository-plugins.ts","../../src/utils/money.ts","../../src/config.ts","../../src/core/container.ts","../../src/utils/date.ts","../../src/core/config.ts","../../src/calculators/attendance.calculator.ts","../../src/utils/calculation.ts","../../src/calculators/prorating.calculator.ts","../../src/calculators/salary.calculator.ts","../../src/core/state-machine.ts","../../src/core/payroll-states.ts","../../src/core/timeline-audit.ts"],"names":["isSingleTenant","periodWorkingDays"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @classytic/payroll - Result Type\n *\n * Rust-inspired Result type for type-safe error handling\n * No more try/catch everywhere - explicit error handling\n */\n\n// ============================================================================\n// Result Type Definition\n// ============================================================================\n\n/** Success result */\nexport interface Ok<T> {\n readonly ok: true;\n readonly value: T;\n}\n\n/** Error result */\nexport interface Err<E> {\n readonly ok: false;\n readonly error: E;\n}\n\n/** Result type - either success or error */\nexport type Result<T, E = Error> = Ok<T> | Err<E>;\n\n// ============================================================================\n// Constructors\n// ============================================================================\n\n/**\n * Create a success result\n */\nexport function ok<T>(value: T): Ok<T> {\n return { ok: true, value };\n}\n\n/**\n * Create an error result\n */\nexport function err<E>(error: E): Err<E> {\n return { ok: false, error };\n}\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\n/**\n * Check if result is success\n */\nexport function isOk<T, E>(result: Result<T, E>): result is Ok<T> {\n return result.ok === true;\n}\n\n/**\n * Check if result is error\n */\nexport function isErr<T, E>(result: Result<T, E>): result is Err<E> {\n return result.ok === false;\n}\n\n// ============================================================================\n// Unwrap Functions\n// ============================================================================\n\n/**\n * Unwrap result value or throw error\n */\nexport function unwrap<T, E>(result: Result<T, E>): T {\n if (isOk(result)) {\n return result.value;\n }\n throw result.error;\n}\n\n/**\n * Unwrap result value or return default\n */\nexport function unwrapOr<T, E>(result: Result<T, E>, defaultValue: T): T {\n if (isOk(result)) {\n return result.value;\n }\n return defaultValue;\n}\n\n/**\n * Unwrap result value or compute default\n */\nexport function unwrapOrElse<T, E>(\n result: Result<T, E>,\n fn: (error: E) => T\n): T {\n if (isOk(result)) {\n return result.value;\n }\n return fn(result.error);\n}\n\n// ============================================================================\n// Transformation Functions\n// ============================================================================\n\n/**\n * Map success value\n */\nexport function map<T, U, E>(\n result: Result<T, E>,\n fn: (value: T) => U\n): Result<U, E> {\n if (isOk(result)) {\n return ok(fn(result.value));\n }\n return result;\n}\n\n/**\n * Map error value\n */\nexport function mapErr<T, E, F>(\n result: Result<T, E>,\n fn: (error: E) => F\n): Result<T, F> {\n if (isErr(result)) {\n return err(fn(result.error));\n }\n return result;\n}\n\n/**\n * FlatMap (chain) success value\n */\nexport function flatMap<T, U, E>(\n result: Result<T, E>,\n fn: (value: T) => Result<U, E>\n): Result<U, E> {\n if (isOk(result)) {\n return fn(result.value);\n }\n return result;\n}\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Try/catch wrapper for async functions\n */\nexport async function tryCatch<T, E = Error>(\n fn: () => Promise<T>,\n errorTransform?: (error: unknown) => E\n): Promise<Result<T, E>> {\n try {\n const value = await fn();\n return ok(value);\n } catch (error) {\n if (errorTransform) {\n return err(errorTransform(error));\n }\n return err(error as E);\n }\n}\n\n/**\n * Try/catch wrapper for sync functions\n */\nexport function tryCatchSync<T, E = Error>(\n fn: () => T,\n errorTransform?: (error: unknown) => E\n): Result<T, E> {\n try {\n const value = fn();\n return ok(value);\n } catch (error) {\n if (errorTransform) {\n return err(errorTransform(error));\n }\n return err(error as E);\n }\n}\n\n/**\n * Combine multiple results into one\n */\nexport function all<T, E>(results: Result<T, E>[]): Result<T[], E> {\n const values: T[] = [];\n for (const result of results) {\n if (isErr(result)) {\n return result;\n }\n values.push(result.value);\n }\n return ok(values);\n}\n\n/**\n * Pattern match on result\n */\nexport function match<T, E, R>(\n result: Result<T, E>,\n handlers: {\n ok: (value: T) => R;\n err: (error: E) => R;\n }\n): R {\n if (isOk(result)) {\n return handlers.ok(result.value);\n }\n return handlers.err(result.error);\n}\n\n/**\n * Convert Promise<Result> to Result<Promise>\n */\nexport async function fromPromise<T, E = Error>(\n promise: Promise<T>,\n errorTransform?: (error: unknown) => E\n): Promise<Result<T, E>> {\n return tryCatch(() => promise, errorTransform);\n}\n\n/**\n * Create Result from nullable value\n */\nexport function fromNullable<T, E>(\n value: T | null | undefined,\n error: E\n): Result<T, E> {\n if (value === null || value === undefined) {\n return err(error);\n }\n return ok(value);\n}\n\n// ============================================================================\n// Result Class (OOP Alternative)\n// ============================================================================\n\nexport class ResultClass<T, E = Error> {\n private constructor(private readonly result: Result<T, E>) {}\n\n static ok<T>(value: T): ResultClass<T, never> {\n return new ResultClass(ok(value));\n }\n\n static err<E>(error: E): ResultClass<never, E> {\n return new ResultClass(err(error));\n }\n\n static async fromAsync<T, E = Error>(\n fn: () => Promise<T>,\n errorTransform?: (error: unknown) => E\n ): Promise<ResultClass<T, E>> {\n const result = await tryCatch(fn, errorTransform);\n return new ResultClass(result);\n }\n\n isOk(): boolean {\n return isOk(this.result);\n }\n\n isErr(): boolean {\n return isErr(this.result);\n }\n\n unwrap(): T {\n return unwrap(this.result);\n }\n\n unwrapOr(defaultValue: T): T {\n return unwrapOr(this.result, defaultValue);\n }\n\n map<U>(fn: (value: T) => U): ResultClass<U, E> {\n return new ResultClass(map(this.result, fn));\n }\n\n mapErr<F>(fn: (error: E) => F): ResultClass<T, F> {\n return new ResultClass(mapErr(this.result, fn));\n }\n\n flatMap<U>(fn: (value: T) => Result<U, E>): ResultClass<U, E> {\n return new ResultClass(flatMap(this.result, fn));\n }\n\n match<R>(handlers: { ok: (value: T) => R; err: (error: E) => R }): R {\n return match(this.result, handlers);\n }\n\n toResult(): Result<T, E> {\n return this.result;\n }\n}\n\n// ============================================================================\n// Alias for convenience\n// ============================================================================\n\nexport const Result = {\n ok,\n err,\n isOk,\n isErr,\n unwrap,\n unwrapOr,\n unwrapOrElse,\n map,\n mapErr,\n flatMap,\n tryCatch,\n tryCatchSync,\n all,\n match,\n fromPromise,\n fromNullable,\n};\n\n","/**\n * @classytic/payroll - Logger\n *\n * Pluggable logger abstraction\n * Defaults to console, can be replaced with pino, winston, etc.\n */\n\nimport type { Logger } from '../types.js';\n\n// ============================================================================\n// Default Logger Implementation\n// ============================================================================\n\nconst createConsoleLogger = (): Logger => ({\n info: (message: string, meta?: Record<string, unknown>) => {\n if (meta) {\n console.log(`[Payroll] INFO: ${message}`, meta);\n } else {\n console.log(`[Payroll] INFO: ${message}`);\n }\n },\n error: (message: string, meta?: Record<string, unknown>) => {\n if (meta) {\n console.error(`[Payroll] ERROR: ${message}`, meta);\n } else {\n console.error(`[Payroll] ERROR: ${message}`);\n }\n },\n warn: (message: string, meta?: Record<string, unknown>) => {\n if (meta) {\n console.warn(`[Payroll] WARN: ${message}`, meta);\n } else {\n console.warn(`[Payroll] WARN: ${message}`);\n }\n },\n debug: (message: string, meta?: Record<string, unknown>) => {\n if (process.env.NODE_ENV !== 'production') {\n if (meta) {\n console.log(`[Payroll] DEBUG: ${message}`, meta);\n } else {\n console.log(`[Payroll] DEBUG: ${message}`);\n }\n }\n },\n});\n\n// ============================================================================\n// Logger State\n// ============================================================================\n\nlet currentLogger: Logger = createConsoleLogger();\nlet loggingEnabled = true;\n\n// ============================================================================\n// Logger Functions\n// ============================================================================\n\n/**\n * Get the current logger instance (respects loggingEnabled flag)\n */\nexport function getLogger(): Logger {\n return {\n info: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.info(message, meta);\n },\n error: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.error(message, meta);\n },\n warn: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.warn(message, meta);\n },\n debug: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.debug(message, meta);\n },\n };\n}\n\n/**\n * Set a custom logger instance\n */\nexport function setLogger(logger: Logger): void {\n currentLogger = logger;\n}\n\n/**\n * Reset to default console logger\n */\nexport function resetLogger(): void {\n currentLogger = createConsoleLogger();\n}\n\n/**\n * Create a child logger with prefix (respects loggingEnabled flag)\n */\nexport function createChildLogger(prefix: string): Logger {\n const parent = currentLogger;\n return {\n info: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) parent.info(`[${prefix}] ${message}`, meta);\n },\n error: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) parent.error(`[${prefix}] ${message}`, meta);\n },\n warn: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) parent.warn(`[${prefix}] ${message}`, meta);\n },\n debug: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) parent.debug(`[${prefix}] ${message}`, meta);\n },\n };\n}\n\n/**\n * Create a silent logger (for testing)\n */\nexport function createSilentLogger(): Logger {\n return {\n info: () => {},\n error: () => {},\n warn: () => {},\n debug: () => {},\n };\n}\n\n/**\n * Enable logging globally\n */\nexport function enableLogging(): void {\n loggingEnabled = true;\n}\n\n/**\n * Disable logging globally (useful for production)\n */\nexport function disableLogging(): void {\n loggingEnabled = false;\n}\n\n/**\n * Check if logging is enabled\n */\nexport function isLoggingEnabled(): boolean {\n return loggingEnabled;\n}\n\n// ============================================================================\n// Logger Proxy Object\n// ============================================================================\n\n/**\n * Logger proxy that always delegates to currentLogger\n * Respects global logging enabled/disabled state\n */\nexport const logger: Logger = {\n info: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.info(message, meta);\n },\n error: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.error(message, meta);\n },\n warn: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.warn(message, meta);\n },\n debug: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.debug(message, meta);\n },\n};\n\nexport default logger;\n\n","/**\n * @classytic/payroll - Event System\n *\n * Type-safe event emitter for payroll lifecycle events\n * Enables loose coupling and extensibility\n */\n\nimport type {\n PayrollEvent,\n EmployeeDocument,\n PayrollRecordDocument,\n ObjectId,\n ObjectIdLike,\n OperationContext,\n} from '../types.js';\nimport { getLogger } from '../utils/logger.js';\n\n// ============================================================================\n// Event Payload Types\n// ============================================================================\n\nexport interface EmployeeHiredEventPayload {\n employee: {\n id: ObjectId;\n employeeId: string;\n position: string;\n department?: string;\n };\n organizationId: ObjectId;\n context?: OperationContext;\n}\n\nexport interface EmployeeTerminatedEventPayload {\n employee: {\n id: ObjectId;\n employeeId: string;\n name?: string;\n };\n terminationDate: Date;\n reason?: string;\n organizationId: ObjectId;\n context?: OperationContext;\n}\n\nexport interface EmployeeRehiredEventPayload {\n employee: {\n id: ObjectId;\n employeeId: string;\n position: string;\n };\n previousTerminationDate?: Date;\n organizationId: ObjectId;\n context?: OperationContext;\n}\n\nexport interface SalaryUpdatedEventPayload {\n employee: {\n id: ObjectId;\n employeeId: string;\n };\n previousSalary: number;\n newSalary: number;\n effectiveFrom: Date;\n organizationId: ObjectId;\n context?: OperationContext;\n}\n\nexport interface SalaryProcessedEventPayload {\n employee: {\n id: ObjectId;\n employeeId: string;\n name?: string;\n };\n payroll: {\n id: ObjectId;\n period: { month: number; year: number };\n grossAmount: number;\n netAmount: number;\n };\n transactionId: ObjectId;\n organizationId: ObjectId;\n context?: OperationContext;\n}\n\nexport interface SalaryFailedEventPayload {\n employee: {\n id: ObjectId;\n employeeId: string;\n };\n period: { month: number; year: number };\n error: string;\n organizationId: ObjectId;\n context?: OperationContext;\n}\n\nexport interface PayrollCompletedEventPayload {\n organizationId: ObjectId;\n period: { month: number; year: number };\n summary: {\n total: number;\n successful: number;\n failed: number;\n totalAmount: number;\n };\n context?: OperationContext;\n}\n\nexport interface PayrollExportedEventPayload {\n organizationId: ObjectId;\n exportId?: string;\n dateRange?: { start: Date; end: Date };\n recordCount: number;\n format: string;\n context?: OperationContext;\n}\n\nexport interface CompensationChangedEventPayload {\n employee: {\n id: ObjectId;\n employeeId: string;\n };\n changeType: 'allowance_added' | 'allowance_removed' | 'deduction_added' | 'deduction_removed';\n details: {\n type: string;\n amount: number;\n };\n organizationId: ObjectId;\n context?: OperationContext;\n}\n\nexport interface MilestoneAchievedEventPayload {\n employee: {\n id: ObjectId;\n employeeId: string;\n name?: string;\n };\n milestone: {\n type: 'tenure' | 'salary' | 'payments';\n value: number;\n message: string;\n };\n organizationId: ObjectId;\n}\n\nexport interface TaxWithheldEventPayload {\n withholding: {\n id: ObjectId;\n taxType: string;\n amount: number;\n };\n employee: {\n id: ObjectId;\n employeeId: string;\n };\n payrollRecord: {\n id: ObjectId;\n };\n period: {\n month: number;\n year: number;\n };\n organizationId: ObjectId;\n context?: OperationContext;\n}\n\nexport interface TaxPaidEventPayload {\n withholdings: Array<{\n id: ObjectId;\n taxType: string;\n amount: number;\n }>;\n transaction?: {\n id: ObjectId;\n amount: number;\n };\n totalAmount: number;\n referenceNumber?: string;\n paidAt: Date;\n organizationId: ObjectId;\n context?: OperationContext;\n}\n\nexport interface TaxVoidedEventPayload {\n withholdings: Array<{\n id: ObjectId;\n taxType: string;\n amount: number;\n }>;\n payrollRecordId: ObjectId;\n organizationId: ObjectId;\n reason: string;\n voidedAt: Date;\n voidedBy?: ObjectIdLike;\n}\n\n// ============================================================================\n// Event Map\n// ============================================================================\n\nexport interface PayrollEventMap {\n 'employee:hired': EmployeeHiredEventPayload;\n 'employee:terminated': EmployeeTerminatedEventPayload;\n 'employee:rehired': EmployeeRehiredEventPayload;\n 'salary:updated': SalaryUpdatedEventPayload;\n 'salary:processed': SalaryProcessedEventPayload;\n 'salary:failed': SalaryFailedEventPayload;\n 'payroll:completed': PayrollCompletedEventPayload;\n 'payroll:exported': PayrollExportedEventPayload;\n 'compensation:changed': CompensationChangedEventPayload;\n 'milestone:achieved': MilestoneAchievedEventPayload;\n 'tax:withheld': TaxWithheldEventPayload;\n 'tax:paid': TaxPaidEventPayload;\n 'tax:voided': TaxVoidedEventPayload;\n}\n\nexport type PayrollEventType = keyof PayrollEventMap;\n\n// ============================================================================\n// Event Handler Types\n// ============================================================================\n\nexport type EventHandler<T> = (payload: T) => void | Promise<void>;\n\nexport type PayrollEventHandler<K extends PayrollEventType> = EventHandler<\n PayrollEventMap[K]\n>;\n\n// ============================================================================\n// EventBus Class\n// ============================================================================\n\nexport class EventBus {\n private handlers = new Map<\n PayrollEventType,\n Set<EventHandler<unknown>>\n >();\n\n /**\n * Register an event handler\n */\n on<K extends PayrollEventType>(\n event: K,\n handler: PayrollEventHandler<K>\n ): () => void {\n if (!this.handlers.has(event)) {\n this.handlers.set(event, new Set());\n }\n this.handlers.get(event)!.add(handler as EventHandler<unknown>);\n\n // Return unsubscribe function\n return () => this.off(event, handler);\n }\n\n /**\n * Register a one-time event handler\n */\n once<K extends PayrollEventType>(\n event: K,\n handler: PayrollEventHandler<K>\n ): () => void {\n const wrappedHandler: PayrollEventHandler<K> = async (payload) => {\n this.off(event, wrappedHandler);\n await handler(payload);\n };\n return this.on(event, wrappedHandler);\n }\n\n /**\n * Remove an event handler\n */\n off<K extends PayrollEventType>(\n event: K,\n handler: PayrollEventHandler<K>\n ): void {\n const eventHandlers = this.handlers.get(event);\n if (eventHandlers) {\n eventHandlers.delete(handler as EventHandler<unknown>);\n }\n }\n\n /**\n * Emit an event\n */\n async emit<K extends PayrollEventType>(\n event: K,\n payload: PayrollEventMap[K]\n ): Promise<void> {\n const eventHandlers = this.handlers.get(event);\n if (!eventHandlers || eventHandlers.size === 0) {\n return;\n }\n\n const handlers = Array.from(eventHandlers);\n await Promise.all(\n handlers.map(async (handler) => {\n try {\n await handler(payload);\n } catch (error) {\n getLogger().error(`Event handler error for ${event}`, {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n })\n );\n }\n\n /**\n * Emit event synchronously (fire-and-forget)\n */\n emitSync<K extends PayrollEventType>(\n event: K,\n payload: PayrollEventMap[K]\n ): void {\n void this.emit(event, payload);\n }\n\n /**\n * Remove all handlers for an event\n */\n removeAllListeners(event?: PayrollEventType): void {\n if (event) {\n this.handlers.delete(event);\n } else {\n this.handlers.clear();\n }\n }\n\n /**\n * Get listener count for an event\n */\n listenerCount(event: PayrollEventType): number {\n return this.handlers.get(event)?.size ?? 0;\n }\n\n /**\n * Get all registered events\n */\n eventNames(): PayrollEventType[] {\n return Array.from(this.handlers.keys());\n }\n}\n\n// ============================================================================\n// Default EventBus Instance\n// ============================================================================\n\nlet defaultEventBus: EventBus | null = null;\n\n/**\n * Get or create the default event bus\n */\nexport function getEventBus(): EventBus {\n if (!defaultEventBus) {\n defaultEventBus = new EventBus();\n }\n return defaultEventBus;\n}\n\n/**\n * Create a new event bus instance\n */\nexport function createEventBus(): EventBus {\n return new EventBus();\n}\n\n/**\n * Reset the default event bus (for testing)\n */\nexport function resetEventBus(): void {\n if (defaultEventBus) {\n defaultEventBus.removeAllListeners();\n }\n defaultEventBus = null;\n}\n\n// ============================================================================\n// Convenience Functions\n// ============================================================================\n\n/**\n * Subscribe to employee hired events\n */\nexport function onEmployeeHired(\n handler: PayrollEventHandler<'employee:hired'>\n): () => void {\n return getEventBus().on('employee:hired', handler);\n}\n\n/**\n * Subscribe to salary processed events\n */\nexport function onSalaryProcessed(\n handler: PayrollEventHandler<'salary:processed'>\n): () => void {\n return getEventBus().on('salary:processed', handler);\n}\n\n/**\n * Subscribe to payroll completed events\n */\nexport function onPayrollCompleted(\n handler: PayrollEventHandler<'payroll:completed'>\n): () => void {\n return getEventBus().on('payroll:completed', handler);\n}\n\n/**\n * Subscribe to milestone achieved events\n */\nexport function onMilestoneAchieved(\n handler: PayrollEventHandler<'milestone:achieved'>\n): () => void {\n return getEventBus().on('milestone:achieved', handler);\n}\n\n","/**\n * Idempotency Manager\n *\n * Ensures operations are not duplicated when called with the same key.\n * Uses Stripe-style idempotency pattern for payroll operations.\n *\n * ## Key Format (v2.9.0+)\n *\n * Idempotency keys support multiple payroll frequencies:\n *\n * **Monthly frequency:**\n * ```\n * payroll:{organizationId}:{employeeId}:{year}-{month}:{payrollRunType}\n * ```\n *\n * **Non-monthly frequencies (weekly, bi_weekly, daily, hourly):**\n * ```\n * payroll:{organizationId}:{employeeId}:{year}-{month}:{startDate}:{payrollRunType}\n * ```\n *\n * This allows:\n * - Different payroll types (regular, supplemental, retroactive) in the same period\n * - Multiple weekly/bi-weekly/daily payroll runs within the same calendar month\n *\n * ## Important: In-Memory Cache Limitations\n *\n * This implementation uses an **in-memory LRU cache** which has the following limitations:\n *\n * - **Does NOT persist across server restarts** - cache is lost on restart\n * - **Does NOT work across multiple server instances** - each instance has its own cache\n * - **Only prevents duplicates within the same process lifetime**\n *\n * For production deployments with horizontal scaling or high availability requirements,\n * you should implement database-backed idempotency. See the Payroll class documentation\n * for implementation examples.\n *\n * ## Duplicate Protection (v2.9.0+)\n *\n * Primary duplicate protection is via **database unique compound index**:\n * `{ organizationId, employeeId, period.month, period.year, period.startDate, payrollRunType }`\n *\n * This prevents race conditions under concurrent load. The partial filter\n * excludes voided records to allow re-processing after restoration.\n *\n * The in-memory cache is a secondary optimization layer, not the primary protection.\n *\n * @see https://stripe.com/docs/api/idempotent_requests\n */\n\nimport { LRUCache } from 'lru-cache';\nimport type { ObjectIdLike } from '../types.js';\nimport { getLogger } from '../utils/logger.js';\n\nexport interface IdempotentResult<T = unknown> {\n value: T;\n cached: boolean;\n createdAt: Date;\n}\n\n/**\n * In-memory idempotency manager for preventing duplicate operations.\n *\n * @warning This is an in-memory cache. For production horizontal scaling,\n * implement database-backed idempotency instead.\n */\nexport class IdempotencyManager {\n private cache: LRUCache<string, { value: unknown; createdAt: Date }>;\n private static hasLoggedWarning = false;\n\n constructor(options: { max?: number; ttl?: number; suppressWarning?: boolean } = {}) {\n this.cache = new LRUCache({\n max: options.max || 10000, // Store 10k keys\n ttl: options.ttl || 1000 * 60 * 60 * 24, // 24 hours default\n });\n\n // Log production warning once per process\n if (\n !options.suppressWarning &&\n !IdempotencyManager.hasLoggedWarning &&\n process.env.NODE_ENV === 'production'\n ) {\n IdempotencyManager.hasLoggedWarning = true;\n getLogger().warn(\n 'IdempotencyManager: Using in-memory cache. ' +\n 'For horizontal scaling, implement database-backed idempotency. ' +\n 'See @classytic/payroll documentation for implementation guidance.',\n { cacheMax: options.max || 10000, cacheTTL: options.ttl || 86400000 }\n );\n }\n }\n\n /**\n * Check if key exists and return cached result\n */\n get<T>(key: string): IdempotentResult<T> | null {\n const cached = this.cache.get(key);\n if (!cached) return null;\n\n return {\n value: cached.value as T,\n cached: true,\n createdAt: cached.createdAt,\n };\n }\n\n /**\n * Store result for idempotency key\n */\n set<T>(key: string, value: T): void {\n this.cache.set(key, {\n value,\n createdAt: new Date(),\n });\n }\n\n /**\n * Execute function with idempotency protection\n */\n async execute<T>(\n key: string,\n fn: () => Promise<T>\n ): Promise<IdempotentResult<T>> {\n // Check cache first\n const cached = this.get<T>(key);\n if (cached) {\n return cached;\n }\n\n // Execute function\n const value = await fn();\n\n // Cache result\n this.set(key, value);\n\n return {\n value,\n cached: false,\n createdAt: new Date(),\n };\n }\n\n /**\n * Clear a specific key\n */\n delete(key: string): void {\n this.cache.delete(key);\n }\n\n /**\n * Clear all keys\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get cache stats\n */\n stats(): { size: number; max: number } {\n return {\n size: this.cache.size,\n max: this.cache.max,\n };\n }\n}\n\n/**\n * Payroll run types for idempotency key generation\n */\nexport type PayrollRunType = 'regular' | 'off-cycle' | 'supplemental' | 'retroactive';\n\n/**\n * Generate idempotency key for payroll operations\n *\n * Includes payrollRunType to allow multiple payroll runs per period\n * (e.g., regular + supplemental bonus + retroactive adjustment)\n *\n * For non-monthly frequencies (weekly, bi_weekly, daily, hourly), the periodStartDate\n * is included to differentiate multiple runs within the same calendar month.\n *\n * @param organizationId - Organization ID\n * @param employeeId - Employee ID\n * @param month - Payroll month (1-12)\n * @param year - Payroll year\n * @param payrollRunType - Type of payroll run (default: 'regular')\n * @param periodStartDate - Period start date (required for non-monthly frequencies)\n */\nexport function generatePayrollIdempotencyKey(\n organizationId: ObjectIdLike,\n employeeId: ObjectIdLike,\n month: number,\n year: number,\n payrollRunType: PayrollRunType = 'regular',\n periodStartDate?: Date\n): string {\n // For non-monthly frequencies, include the period start date to differentiate\n // multiple runs within the same calendar month\n if (periodStartDate) {\n const startDateStr = periodStartDate.toISOString().split('T')[0]; // YYYY-MM-DD\n return `payroll:${organizationId}:${employeeId}:${year}-${month}:${startDateStr}:${payrollRunType}`;\n }\n return `payroll:${organizationId}:${employeeId}:${year}-${month}:${payrollRunType}`;\n}\n","/**\n * Webhook System\n * Sends HTTP notifications when payroll events occur\n */\n\nimport crypto from 'crypto';\nimport type { PayrollEventType, PayrollEventMap } from './events.js';\n\nexport interface WebhookConfig {\n url: string;\n events: PayrollEventType[];\n secret?: string;\n headers?: Record<string, string>;\n retries?: number;\n timeout?: number;\n}\n\nexport interface WebhookManagerOptions {\n /**\n * Maximum number of delivery log entries to retain (default: 1000).\n * Oldest entries are pruned when the limit is exceeded.\n */\n maxLogSize?: number;\n /**\n * Whether to store full payloads in the delivery log (default: false).\n * When false, only metadata (event, url, status, error, sentAt) is stored,\n * preventing PII from accumulating in memory.\n */\n storePayloads?: boolean;\n}\n\nexport interface WebhookDelivery {\n id: string;\n event: PayrollEventType;\n url: string;\n payload: unknown;\n attempt: number;\n status: 'pending' | 'sent' | 'failed';\n response?: {\n status: number;\n body: string;\n };\n error?: string;\n sentAt?: Date;\n}\n\nexport class WebhookManager {\n private webhooks: WebhookConfig[] = [];\n private deliveryLog: WebhookDelivery[] = [];\n private readonly maxLogSize: number;\n private readonly storePayloads: boolean;\n\n constructor(options?: WebhookManagerOptions) {\n this.maxLogSize = options?.maxLogSize ?? 1000;\n this.storePayloads = options?.storePayloads ?? false;\n }\n\n /**\n * Register a webhook\n */\n register(config: WebhookConfig): void {\n this.webhooks.push({\n retries: 3,\n timeout: 30000,\n ...config,\n });\n }\n\n /**\n * Remove a webhook\n */\n unregister(url: string): void {\n this.webhooks = this.webhooks.filter((w) => w.url !== url);\n }\n\n /**\n * Send webhook for event\n */\n async send<K extends PayrollEventType>(\n event: K,\n payload: PayrollEventMap[K]\n ): Promise<void> {\n const matchingWebhooks = this.webhooks.filter((w) => w.events.includes(event));\n\n const deliveries = matchingWebhooks.map((webhook) =>\n this.deliver(webhook, event, payload)\n );\n\n await Promise.allSettled(deliveries);\n }\n\n /**\n * Deliver webhook with retries\n */\n private async deliver<K extends PayrollEventType>(\n webhook: WebhookConfig,\n event: K,\n payload: PayrollEventMap[K]\n ): Promise<WebhookDelivery> {\n const deliveryId = `${Date.now()}-${Math.random().toString(36)}`;\n\n const delivery: WebhookDelivery = {\n id: deliveryId,\n event,\n url: webhook.url,\n payload: this.storePayloads ? payload : undefined,\n attempt: 0,\n status: 'pending',\n };\n\n this.deliveryLog.push(delivery);\n this.pruneLog();\n\n const maxRetries = webhook.retries || 3;\n\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n delivery.attempt = attempt;\n\n try {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), webhook.timeout || 30000);\n\n const timestamp = Math.floor(Date.now() / 1000);\n const deliveredAt = new Date().toISOString();\n\n const requestBody = {\n event,\n payload,\n deliveredAt,\n };\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Payroll-Event': event,\n 'X-Payroll-Delivery': deliveryId,\n 'X-Payroll-Timestamp': timestamp.toString(),\n ...webhook.headers,\n };\n\n if (webhook.secret) {\n headers['X-Payroll-Signature'] = this.generateSignature(requestBody, webhook.secret, timestamp);\n }\n\n const response = await fetch(webhook.url, {\n method: 'POST',\n headers,\n body: JSON.stringify(requestBody),\n signal: controller.signal,\n });\n\n clearTimeout(timeout);\n\n delivery.response = {\n status: response.status,\n body: await response.text(),\n };\n delivery.sentAt = new Date();\n\n if (response.ok) {\n delivery.status = 'sent';\n return delivery;\n }\n\n // Retry on transient errors: 5xx, 429 (rate limit), 408 (timeout)\n const shouldRetry = (\n response.status >= 500 ||\n response.status === 429 ||\n response.status === 408\n );\n\n if (shouldRetry && attempt < maxRetries) {\n // Exponential backoff with jitter\n const backoff = Math.pow(2, attempt) * 1000;\n const jitter = Math.random() * 1000;\n await this.sleep(backoff + jitter);\n continue;\n }\n\n delivery.status = 'failed';\n delivery.error = `HTTP ${response.status}`;\n return delivery;\n } catch (error) {\n delivery.error = (error as Error).message;\n\n if (attempt < maxRetries) {\n await this.sleep(Math.pow(2, attempt) * 1000);\n continue;\n }\n\n delivery.status = 'failed';\n return delivery;\n }\n }\n\n return delivery;\n }\n\n /**\n * Generate HMAC-SHA256 signature for webhook (Stripe-style)\n *\n * Format: t=<timestamp>,v1=<hmac_signature>\n *\n * The signed payload is: timestamp.JSON(requestBody)\n * where requestBody = { event, payload, deliveredAt }\n *\n * Consumers should verify:\n * 1. Timestamp is within tolerance (e.g., 5 minutes)\n * 2. HMAC signature matches\n *\n * @example Verify signature (consumer side)\n * ```typescript\n * import crypto from 'crypto';\n *\n * const signature = req.headers['x-payroll-signature'];\n * const timestamp = req.headers['x-payroll-timestamp'];\n * const requestBody = req.body; // { event, payload, deliveredAt }\n *\n * // Check timestamp (replay protection)\n * const now = Math.floor(Date.now() / 1000);\n * if (Math.abs(now - parseInt(timestamp)) > 300) {\n * throw new Error('Signature expired');\n * }\n *\n * // Verify signature\n * const signedPayload = `${timestamp}.${JSON.stringify(requestBody)}`;\n * const expectedSignature = crypto\n * .createHmac('sha256', secret)\n * .update(signedPayload)\n * .digest('hex');\n *\n * const parts = signature.split(',');\n * const providedSignature = parts.find(p => p.startsWith('v1='))?.split('=')[1];\n *\n * if (providedSignature !== expectedSignature) {\n * throw new Error('Invalid signature');\n * }\n * ```\n */\n private generateSignature(requestBody: unknown, secret: string, timestamp: number): string {\n const data = JSON.stringify(requestBody);\n\n // Signed payload: timestamp.data\n const signedPayload = `${timestamp}.${data}`;\n\n // Generate HMAC-SHA256 signature\n const hmac = crypto.createHmac('sha256', secret);\n hmac.update(signedPayload);\n const signature = hmac.digest('hex');\n\n // Stripe-style format: t=timestamp,v1=signature\n return `t=${timestamp},v1=${signature}`;\n }\n\n /**\n * Prune delivery log to stay within maxLogSize.\n * Removes oldest entries first.\n */\n private pruneLog(): void {\n if (this.deliveryLog.length > this.maxLogSize) {\n this.deliveryLog = this.deliveryLog.slice(-this.maxLogSize);\n }\n }\n\n /**\n * Sleep for ms\n */\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Get delivery log\n */\n getDeliveries(options?: { event?: PayrollEventType; status?: WebhookDelivery['status']; limit?: number }): WebhookDelivery[] {\n let results = this.deliveryLog;\n\n if (options?.event) {\n results = results.filter((d) => d.event === options.event);\n }\n\n if (options?.status) {\n results = results.filter((d) => d.status === options.status);\n }\n\n if (options?.limit) {\n results = results.slice(-options.limit);\n }\n\n return results;\n }\n\n /**\n * Clear delivery log\n */\n clearLog(): void {\n this.deliveryLog = [];\n }\n\n /**\n * Get all registered webhooks\n */\n getWebhooks(): WebhookConfig[] {\n return [...this.webhooks];\n }\n}\n","/**\n * @classytic/payroll - Plugin System\n *\n * Extensible plugin architecture for customization\n * Follows patterns from popular libraries like Mongoose, Fastify\n */\n\nimport type { PayrollInstance } from '../types.js';\nimport type { EventBus, PayrollEventType, PayrollEventMap } from './events.js';\nimport { getLogger } from '../utils/logger.js';\n\n// ============================================================================\n// Plugin Context\n// ============================================================================\n\nexport interface PluginContext {\n /** Payroll instance */\n payroll: PayrollInstance;\n /** Event bus for subscribing to events */\n events: EventBus;\n /** Logger instance */\n logger: PluginLogger;\n /** Configuration getter */\n getConfig: <T>(key: string) => T | undefined;\n /** Register a hook */\n addHook: <K extends PayrollEventType>(\n event: K,\n handler: (payload: PayrollEventMap[K]) => void | Promise<void>\n ) => () => void;\n}\n\nexport interface PluginLogger {\n info(message: string, meta?: Record<string, unknown>): void;\n error(message: string, meta?: Record<string, unknown>): void;\n warn(message: string, meta?: Record<string, unknown>): void;\n debug(message: string, meta?: Record<string, unknown>): void;\n}\n\n// ============================================================================\n// Plugin Hooks\n// ============================================================================\n\nexport interface PluginHooks {\n /** Called before employee is hired */\n beforeHire?: (params: unknown) => void | Promise<void>;\n /** Called after employee is hired */\n afterHire?: (employee: unknown) => void | Promise<void>;\n /** Called before salary is processed */\n beforeProcessSalary?: (params: unknown) => void | Promise<void>;\n /** Called after salary is processed */\n afterProcessSalary?: (result: unknown) => void | Promise<void>;\n /** Called before termination */\n beforeTerminate?: (params: unknown) => void | Promise<void>;\n /** Called after termination */\n afterTerminate?: (employee: unknown) => void | Promise<void>;\n /** Called on any error */\n onError?: (error: Error, context: string) => void | Promise<void>;\n}\n\n// ============================================================================\n// Payroll Plugin Interface\n// ============================================================================\n\nexport interface PayrollPluginDefinition {\n name: string;\n version?: string;\n hooks?: PluginHooks;\n init?: (context: PluginContext) => void | Promise<void>;\n destroy?: () => void | Promise<void>;\n}\n\n// ============================================================================\n// Plugin Manager\n// ============================================================================\n\nexport class PluginManager {\n private plugins = new Map<string, PayrollPluginDefinition>();\n private hooks = new Map<keyof PluginHooks, Array<NonNullable<PluginHooks[keyof PluginHooks]>>>();\n\n constructor(private context: PluginContext) {}\n\n /**\n * Register a plugin\n */\n async register(plugin: PayrollPluginDefinition): Promise<void> {\n if (this.plugins.has(plugin.name)) {\n throw new Error(`Plugin \"${plugin.name}\" is already registered`);\n }\n\n // Register hooks\n if (plugin.hooks) {\n for (const [hookName, handler] of Object.entries(plugin.hooks)) {\n if (handler) {\n this.addHook(hookName as keyof PluginHooks, handler);\n }\n }\n }\n\n // Initialize plugin\n if (plugin.init) {\n await plugin.init(this.context);\n }\n\n this.plugins.set(plugin.name, plugin);\n this.context.logger.debug(`Plugin \"${plugin.name}\" registered`);\n }\n\n /**\n * Unregister a plugin\n */\n async unregister(name: string): Promise<void> {\n const plugin = this.plugins.get(name);\n if (!plugin) {\n return;\n }\n\n if (plugin.destroy) {\n await plugin.destroy();\n }\n\n this.plugins.delete(name);\n this.context.logger.debug(`Plugin \"${name}\" unregistered`);\n }\n\n /**\n * Add a hook handler\n */\n private addHook<K extends keyof PluginHooks>(\n hookName: K,\n handler: NonNullable<PluginHooks[K]>\n ): void {\n if (!this.hooks.has(hookName)) {\n this.hooks.set(hookName, []);\n }\n this.hooks.get(hookName)!.push(handler);\n }\n\n /**\n * Execute hooks for a given event\n */\n async executeHooks<K extends keyof PluginHooks>(\n hookName: K,\n ...args: Parameters<NonNullable<PluginHooks[K]>>\n ): Promise<void> {\n const handlers = this.hooks.get(hookName);\n if (!handlers || handlers.length === 0) {\n return;\n }\n\n for (const handler of handlers) {\n try {\n await (handler as (...args: unknown[]) => void | Promise<void>)(...args);\n } catch (error) {\n this.context.logger.error(`Hook \"${hookName}\" error:`, { error });\n // Execute onError hooks\n const errorHandlers = this.hooks.get('onError');\n if (errorHandlers) {\n for (const errorHandler of errorHandlers) {\n try {\n await (errorHandler as PluginHooks['onError'])!(error as Error, hookName);\n } catch (handlerError) {\n // Ignore errors in error handlers to prevent infinite loops\n // But log at debug level for troubleshooting\n getLogger().debug('Error handler threw an error', {\n hook: hookName,\n handlerError: handlerError instanceof Error ? handlerError.message : String(handlerError),\n });\n }\n }\n }\n }\n }\n }\n\n /**\n * Get registered plugin names\n */\n getPluginNames(): string[] {\n return Array.from(this.plugins.keys());\n }\n\n /**\n * Check if plugin is registered\n */\n hasPlugin(name: string): boolean {\n return this.plugins.has(name);\n }\n}\n\n// ============================================================================\n// Plugin Definition Helper\n// ============================================================================\n\n/**\n * Define a plugin with type safety\n */\nexport function definePlugin(\n definition: PayrollPluginDefinition\n): PayrollPluginDefinition {\n return definition;\n}\n\n// ============================================================================\n// Built-in Plugins\n// ============================================================================\n\n/**\n * Logging plugin - logs all payroll events\n */\nexport const loggingPlugin = definePlugin({\n name: 'logging',\n version: '1.0.0',\n init: (context) => {\n // Subscribe to all events\n context.addHook('employee:hired', (payload) => {\n context.logger.info('Employee hired', {\n employeeId: payload.employee.employeeId,\n position: payload.employee.position,\n });\n });\n\n context.addHook('salary:processed', (payload) => {\n context.logger.info('Salary processed', {\n employeeId: payload.employee.employeeId,\n amount: payload.payroll.netAmount,\n period: payload.payroll.period,\n });\n });\n\n context.addHook('employee:terminated', (payload) => {\n context.logger.info('Employee terminated', {\n employeeId: payload.employee.employeeId,\n reason: payload.reason,\n });\n });\n },\n hooks: {\n onError: (error, context) => {\n // Logger not available in hook context, this is a static error handler\n // Use getLogger() for consistent logging\n getLogger().error(`[Payroll Error] ${context}:`, { error: error.message });\n },\n },\n});\n\n/**\n * Metrics plugin - collects payroll metrics\n */\nexport const metricsPlugin = definePlugin({\n name: 'metrics',\n version: '1.0.0',\n init: (context) => {\n const metrics = {\n employeesHired: 0,\n employeesTerminated: 0,\n salariesProcessed: 0,\n totalPaid: 0,\n errors: 0,\n };\n\n context.addHook('employee:hired', () => {\n metrics.employeesHired++;\n });\n\n context.addHook('employee:terminated', () => {\n metrics.employeesTerminated++;\n });\n\n context.addHook('salary:processed', (payload) => {\n metrics.salariesProcessed++;\n metrics.totalPaid += payload.payroll.netAmount;\n });\n\n // Expose metrics on payroll instance\n (context.payroll as unknown as { metrics: typeof metrics }).metrics = metrics;\n },\n hooks: {\n onError: (error, context) => {\n // Increment error counter\n },\n },\n});\n\n/**\n * Notification plugin - sends notifications for events\n */\nexport interface NotificationPluginOptions {\n onHired?: (employee: { id: unknown; name?: string }) => void | Promise<void>;\n onTerminated?: (employee: { id: unknown; name?: string }) => void | Promise<void>;\n onSalaryProcessed?: (details: { \n employee: { id: unknown; name?: string };\n amount: number;\n }) => void | Promise<void>;\n onMilestone?: (details: {\n employee: { id: unknown; name?: string };\n milestone: string;\n }) => void | Promise<void>;\n}\n\nexport function createNotificationPlugin(\n options: NotificationPluginOptions\n): PayrollPluginDefinition {\n return definePlugin({\n name: 'notification',\n version: '1.0.0',\n init: (context) => {\n if (options.onHired) {\n context.addHook('employee:hired', async (payload) => {\n await options.onHired!({\n id: payload.employee.id,\n name: payload.employee.position,\n });\n });\n }\n\n if (options.onTerminated) {\n context.addHook('employee:terminated', async (payload) => {\n await options.onTerminated!({\n id: payload.employee.id,\n name: payload.employee.name,\n });\n });\n }\n\n if (options.onSalaryProcessed) {\n context.addHook('salary:processed', async (payload) => {\n await options.onSalaryProcessed!({\n employee: {\n id: payload.employee.id,\n name: payload.employee.name,\n },\n amount: payload.payroll.netAmount,\n });\n });\n }\n\n if (options.onMilestone) {\n context.addHook('milestone:achieved', async (payload) => {\n await options.onMilestone!({\n employee: {\n id: payload.employee.id,\n name: payload.employee.name,\n },\n milestone: payload.milestone.message,\n });\n });\n }\n },\n });\n}\n\n// Alias for backwards compatibility\nexport const notificationPlugin = createNotificationPlugin({});\n\n","/**\n * @classytic/payroll - Query Builders\n *\n * Fluent API for building MongoDB queries\n * Type-safe, chainable, beautiful\n */\n\nimport mongoose, { Types } from 'mongoose';\nimport type {\n ObjectIdLike,\n EmployeeStatus,\n PayrollStatus,\n Department,\n EmploymentType,\n} from '../types.js';\n\n// ============================================================================\n// ObjectId Helpers\n// ============================================================================\n\n/**\n * Convert string or ObjectId to ObjectId\n */\nexport function toObjectId(id: ObjectIdLike): Types.ObjectId {\n if (id instanceof Types.ObjectId) return id;\n return new Types.ObjectId(id);\n}\n\n/**\n * Safely convert to ObjectId (returns null if invalid)\n */\nexport function safeToObjectId(id: unknown): Types.ObjectId | null {\n if (id instanceof Types.ObjectId) return id;\n if (typeof id === 'string' && Types.ObjectId.isValid(id)) {\n return new Types.ObjectId(id);\n }\n return null;\n}\n\n/**\n * Check if value is valid ObjectId\n */\nexport function isValidObjectId(value: unknown): boolean {\n if (value instanceof Types.ObjectId) return true;\n if (typeof value === 'string') return Types.ObjectId.isValid(value);\n return false;\n}\n\n// ============================================================================\n// Base Query Builder\n// ============================================================================\n\nexport class QueryBuilder<T extends Record<string, unknown> = Record<string, unknown>> {\n protected query: T;\n\n constructor(initialQuery: T = {} as T) {\n this.query = { ...initialQuery };\n }\n\n /**\n * Add where condition\n */\n where<K extends string>(field: K, value: unknown): this {\n (this.query as Record<string, unknown>)[field] = value;\n return this;\n }\n\n /**\n * Add $in condition\n */\n whereIn<K extends string>(field: K, values: unknown[]): this {\n (this.query as Record<string, unknown>)[field] = { $in: values };\n return this;\n }\n\n /**\n * Add $nin condition\n */\n whereNotIn<K extends string>(field: K, values: unknown[]): this {\n (this.query as Record<string, unknown>)[field] = { $nin: values };\n return this;\n }\n\n /**\n * Add $gte condition\n */\n whereGte<K extends string>(field: K, value: unknown): this {\n const existing = (this.query as Record<string, Record<string, unknown>>)[field] || {};\n (this.query as Record<string, unknown>)[field] = { ...existing, $gte: value };\n return this;\n }\n\n /**\n * Add $lte condition\n */\n whereLte<K extends string>(field: K, value: unknown): this {\n const existing = (this.query as Record<string, Record<string, unknown>>)[field] || {};\n (this.query as Record<string, unknown>)[field] = { ...existing, $lte: value };\n return this;\n }\n\n /**\n * Add $gt condition\n */\n whereGt<K extends string>(field: K, value: unknown): this {\n const existing = (this.query as Record<string, Record<string, unknown>>)[field] || {};\n (this.query as Record<string, unknown>)[field] = { ...existing, $gt: value };\n return this;\n }\n\n /**\n * Add $lt condition\n */\n whereLt<K extends string>(field: K, value: unknown): this {\n const existing = (this.query as Record<string, Record<string, unknown>>)[field] || {};\n (this.query as Record<string, unknown>)[field] = { ...existing, $lt: value };\n return this;\n }\n\n /**\n * Add between condition\n */\n whereBetween<K extends string>(field: K, start: unknown, end: unknown): this {\n (this.query as Record<string, unknown>)[field] = { $gte: start, $lte: end };\n return this;\n }\n\n /**\n * Add $exists condition\n */\n whereExists<K extends string>(field: K): this {\n (this.query as Record<string, unknown>)[field] = { $exists: true };\n return this;\n }\n\n /**\n * Add $exists: false condition\n */\n whereNotExists<K extends string>(field: K): this {\n (this.query as Record<string, unknown>)[field] = { $exists: false };\n return this;\n }\n\n /**\n * Add $ne condition\n */\n whereNot<K extends string>(field: K, value: unknown): this {\n (this.query as Record<string, unknown>)[field] = { $ne: value };\n return this;\n }\n\n /**\n * Add regex condition\n */\n whereRegex<K extends string>(field: K, pattern: string, flags = 'i'): this {\n (this.query as Record<string, unknown>)[field] = { $regex: pattern, $options: flags };\n return this;\n }\n\n /**\n * Merge another query\n */\n merge(otherQuery: Record<string, unknown>): this {\n this.query = { ...this.query, ...otherQuery } as T;\n return this;\n }\n\n /**\n * Build and return the query\n */\n build(): T {\n return { ...this.query };\n }\n}\n\n// ============================================================================\n// Employee Query Builder\n// ============================================================================\n\nexport class EmployeeQueryBuilder extends QueryBuilder {\n /**\n * Filter by organization\n */\n forOrganization(organizationId: ObjectIdLike): this {\n return this.where('organizationId', toObjectId(organizationId));\n }\n\n /**\n * Filter by user\n */\n forUser(userId: ObjectIdLike): this {\n return this.where('userId', toObjectId(userId));\n }\n\n /**\n * Filter by employeeId (human-readable ID)\n */\n forEmployeeId(employeeId: string): this {\n return this.where('employeeId', employeeId);\n }\n\n /**\n * Filter by email (for guest employees)\n */\n forEmail(email: string): this {\n return this.where('email', email.toLowerCase().trim());\n }\n\n /**\n * Filter guest employees (no userId)\n */\n guestEmployees(): this {\n return this.where('userId', null);\n }\n\n /**\n * Filter user-linked employees (has userId)\n * Uses $exists: true and $ne: null to exclude both missing fields and null values\n */\n userLinkedEmployees(): this {\n return this.where('userId', { $exists: true, $ne: null } as unknown as mongoose.Types.ObjectId);\n }\n\n /**\n * Filter by status(es)\n */\n withStatus(...statuses: EmployeeStatus[]): this {\n if (statuses.length === 1) {\n return this.where('status', statuses[0]);\n }\n return this.whereIn('status', statuses);\n }\n\n /**\n * Filter active employees\n */\n active(): this {\n return this.withStatus('active');\n }\n\n /**\n * Filter employed employees (not terminated)\n */\n employed(): this {\n return this.whereIn('status', ['active', 'on_leave', 'suspended']);\n }\n\n /**\n * Filter terminated employees\n */\n terminated(): this {\n return this.withStatus('terminated');\n }\n\n /**\n * Filter by department\n */\n inDepartment(department: Department | string): this {\n return this.where('department', department);\n }\n\n /**\n * Filter by position\n */\n inPosition(position: string): this {\n return this.where('position', position);\n }\n\n /**\n * Filter by employment type\n */\n withEmploymentType(type: EmploymentType | string): this {\n return this.where('employmentType', type);\n }\n\n /**\n * Filter by hire date (after)\n */\n hiredAfter(date: Date): this {\n return this.whereGte('hireDate', date);\n }\n\n /**\n * Filter by hire date (before)\n */\n hiredBefore(date: Date): this {\n return this.whereLte('hireDate', date);\n }\n\n /**\n * Filter by minimum salary\n */\n withMinSalary(amount: number): this {\n return this.whereGte('compensation.netSalary', amount);\n }\n\n /**\n * Filter by maximum salary\n */\n withMaxSalary(amount: number): this {\n return this.whereLte('compensation.netSalary', amount);\n }\n\n /**\n * Filter by salary range\n */\n withSalaryRange(min: number, max: number): this {\n return this.whereBetween('compensation.netSalary', min, max);\n }\n}\n\n// ============================================================================\n// Payroll Query Builder\n// ============================================================================\n\nexport class PayrollQueryBuilder extends QueryBuilder {\n /**\n * Filter by organization\n */\n forOrganization(organizationId: ObjectIdLike): this {\n return this.where('organizationId', toObjectId(organizationId));\n }\n\n /**\n * Filter by employee\n *\n * Note: PayrollRecord.employeeId is always ObjectId _id\n * If passing a string business ID, resolve to _id first\n */\n forEmployee(employeeId: ObjectIdLike | string): this {\n // For payroll queries, employeeId field is always the ObjectId _id\n // If a string business ID is passed, caller should resolve to _id first\n return this.where('employeeId', toObjectId(employeeId as ObjectIdLike));\n }\n\n /**\n * Filter by period\n */\n forPeriod(month?: number, year?: number): this {\n if (month !== undefined) {\n this.where('period.month', month);\n }\n if (year !== undefined) {\n this.where('period.year', year);\n }\n return this;\n }\n\n /**\n * Filter by status(es)\n */\n withStatus(...statuses: PayrollStatus[]): this {\n if (statuses.length === 1) {\n return this.where('status', statuses[0]);\n }\n return this.whereIn('status', statuses);\n }\n\n /**\n * Filter paid records\n */\n paid(): this {\n return this.withStatus('paid');\n }\n\n /**\n * Filter pending records\n */\n pending(): this {\n return this.whereIn('status', ['pending', 'processing']);\n }\n\n /**\n * Filter by date range\n */\n inDateRange(start: Date, end: Date): this {\n return this.whereBetween('period.payDate', start, end);\n }\n\n /**\n * Filter exported records\n */\n exported(): this {\n return this.where('exported', true);\n }\n\n /**\n * Filter not exported records\n */\n notExported(): this {\n return this.where('exported', false);\n }\n}\n\n// ============================================================================\n// Factory Functions\n// ============================================================================\n\n/**\n * Create employee query builder\n */\nexport function employee(): EmployeeQueryBuilder {\n return new EmployeeQueryBuilder();\n}\n\n/**\n * Create payroll query builder\n */\nexport function payroll(): PayrollQueryBuilder {\n return new PayrollQueryBuilder();\n}\n\n/**\n * Create generic query builder\n */\nexport function createQueryBuilder<T extends Record<string, unknown> = Record<string, unknown>>(\n initialQuery?: T\n): QueryBuilder<T> {\n return new QueryBuilder(initialQuery);\n}\n\n// ============================================================================\n// Convenience Query Builders\n// ============================================================================\n\n/**\n * Build employee query from options\n */\nexport function buildEmployeeQuery(options: {\n organizationId: ObjectIdLike;\n userId?: ObjectIdLike;\n statuses?: EmployeeStatus[];\n department?: Department | string;\n employmentType?: EmploymentType | string;\n}): Record<string, unknown> {\n const builder = employee().forOrganization(options.organizationId);\n\n if (options.userId) {\n builder.forUser(options.userId);\n }\n if (options.statuses) {\n builder.withStatus(...options.statuses);\n }\n if (options.department) {\n builder.inDepartment(options.department);\n }\n if (options.employmentType) {\n builder.withEmploymentType(options.employmentType);\n }\n\n return builder.build();\n}\n\n/**\n * Build payroll query from options\n */\nexport function buildPayrollQuery(options: {\n employeeId?: ObjectIdLike;\n organizationId?: ObjectIdLike;\n month?: number;\n year?: number;\n statuses?: PayrollStatus[];\n}): Record<string, unknown> {\n const builder = payroll();\n\n if (options.organizationId) {\n builder.forOrganization(options.organizationId);\n }\n if (options.employeeId) {\n builder.forEmployee(options.employeeId);\n }\n if (options.month || options.year) {\n builder.forPeriod(options.month, options.year);\n }\n if (options.statuses) {\n builder.withStatus(...options.statuses);\n }\n\n return builder.build();\n}\n\n// ============================================================================\n// Aggregation Pipeline Helpers\n// ============================================================================\n\n/**\n * Build aggregation pipeline from stages\n */\nexport function buildAggregationPipeline(\n ...stages: Array<Record<string, unknown> | undefined | null>\n): Record<string, unknown>[] {\n return stages.filter((stage): stage is Record<string, unknown> => !!stage);\n}\n\n/**\n * Match stage\n */\nexport function matchStage(query: Record<string, unknown>): Record<string, unknown> {\n return { $match: query };\n}\n\n/**\n * Group stage\n */\nexport function groupStage(\n groupBy: string | null,\n aggregations: Record<string, unknown>\n): Record<string, unknown> {\n return {\n $group: {\n _id: groupBy,\n ...aggregations,\n },\n };\n}\n\n/**\n * Sort stage\n */\nexport function sortStage(sortBy: Record<string, 1 | -1>): Record<string, unknown> {\n return { $sort: sortBy };\n}\n\n/**\n * Limit stage\n */\nexport function limitStage(limit: number): Record<string, unknown> {\n return { $limit: limit };\n}\n\n/**\n * Skip stage\n */\nexport function skipStage(skip: number): Record<string, unknown> {\n return { $skip: skip };\n}\n\n/**\n * Project stage\n */\nexport function projectStage(fields: Record<string, unknown>): Record<string, unknown> {\n return { $project: fields };\n}\n\n/**\n * Lookup stage\n */\nexport function lookupStage(options: {\n from: string;\n localField: string;\n foreignField: string;\n as: string;\n}): Record<string, unknown> {\n return { $lookup: options };\n}\n\n/**\n * Unwind stage\n */\nexport function unwindStage(\n path: string,\n options: { preserveNullAndEmptyArrays?: boolean } = {}\n): Record<string, unknown> {\n return { $unwind: { path, ...options } };\n}\n\n// ============================================================================\n// Default Export\n// ============================================================================\n\nexport default {\n toObjectId,\n safeToObjectId,\n isValidObjectId,\n QueryBuilder,\n EmployeeQueryBuilder,\n PayrollQueryBuilder,\n employee,\n payroll,\n createQueryBuilder,\n buildEmployeeQuery,\n buildPayrollQuery,\n buildAggregationPipeline,\n matchStage,\n groupStage,\n sortStage,\n limitStage,\n skipStage,\n projectStage,\n lookupStage,\n unwindStage,\n};\n\n","/**\n * Repository plugins for mongokit integration\n */\n\nimport type { Plugin } from '@classytic/mongokit';\nimport type { ObjectId } from '../types.js';\nimport { toObjectId } from '../utils/query-builders.js';\n\n/** Hook context for create operations */\ninterface CreateHookContext {\n data?: Record<string, unknown> | Record<string, unknown>[];\n}\n\n/** Hook context for filter-based operations (getAll, getById, update, delete) */\ninterface FilterHookContext {\n filters?: Record<string, unknown>;\n}\n\n/** Hook context for query-based operations (getByQuery) */\ninterface QueryHookContext {\n query?: Record<string, unknown>;\n}\n\n/**\n * Multi-tenant plugin - automatically injects organizationId into all repository operations.\n *\n * Hooks into create, getAll, getById, getByQuery, update, and delete operations\n * to enforce organizational isolation. The organizationId is force-set on creates\n * (cannot be overridden by caller) and added as a filter on all read/write operations.\n *\n * @param organizationId - Organization ID to scope all operations to. If undefined, plugin is a no-op.\n * @returns Mongokit Plugin instance\n *\n * @example\n * ```typescript\n * import { multiTenantPlugin } from '@classytic/payroll';\n * import { Repository } from '@classytic/mongokit';\n *\n * const repo = new Repository(EmployeeModel, [\n * multiTenantPlugin(organizationId),\n * ]);\n *\n * // All operations auto-scoped to organizationId\n * await repo.getAll({ filters: { status: 'active' } });\n * // Executes: { organizationId, status: 'active' }\n * ```\n */\nexport function multiTenantPlugin(organizationId?: ObjectId): Plugin {\n return {\n name: 'multi-tenant',\n apply(repo) {\n if (!organizationId) return;\n\n const orgId = toObjectId(organizationId);\n\n // Inject organizationId into create operations (ALWAYS enforce, never allow override)\n repo.on('before:create', async (context: CreateHookContext) => {\n if (context.data) {\n if (Array.isArray(context.data)) {\n context.data = context.data.map((item: Record<string, unknown>) => ({\n ...item,\n organizationId: orgId, // CRITICAL: Force override - never allow caller to set different orgId\n }));\n } else {\n context.data = {\n ...context.data,\n organizationId: orgId, // CRITICAL: Force override - never allow caller to set different orgId\n };\n }\n }\n });\n\n // Add organizationId filter to getAll operations\n repo.on('before:getAll', async (context: FilterHookContext) => {\n // Always inject organizationId, even if filters is empty\n context.filters = {\n ...(context.filters || {}),\n organizationId: orgId,\n };\n });\n\n // Add organizationId filter to getById operations\n repo.on('before:getById', async (context: FilterHookContext) => {\n // Always inject organizationId, even if filters is empty\n context.filters = {\n ...(context.filters || {}),\n organizationId: orgId,\n };\n });\n\n // Add organizationId filter to getByQuery operations\n repo.on('before:getByQuery', async (context: QueryHookContext) => {\n // Directly modify query object to ensure organizationId is set\n if (!context.query) {\n context.query = {};\n }\n context.query.organizationId = orgId;\n });\n\n // Add organizationId filter to update operations\n repo.on('before:update', async (context: FilterHookContext) => {\n // Always inject organizationId, even if filters is empty\n context.filters = {\n ...(context.filters || {}),\n organizationId: orgId,\n };\n });\n\n // Add organizationId filter to delete operations\n repo.on('before:delete', async (context: FilterHookContext) => {\n // Always inject organizationId, even if filters is empty\n context.filters = {\n ...(context.filters || {}),\n organizationId: orgId,\n };\n });\n },\n };\n}\n\n","/**\n * Money calculation utilities for payroll compliance\n *\n * Uses banker's rounding (round half to even) to prevent systematic bias\n * in financial calculations, as recommended for payroll systems.\n *\n * PRECISION: All functions default to 2 decimal places (cents/paise precision)\n * to maintain accuracy in payroll calculations. Amounts are stored as floating\n * point numbers with decimal precision (e.g., 1000.50 for $1,000.50).\n *\n * ## Design Decision: Floating Point Storage\n *\n * This module uses floating-point numbers (IEEE 754 double precision) for money\n * storage rather than integer minor units (cents) or MongoDB Decimal128.\n *\n * ### Tradeoffs:\n *\n * **Why floating point:**\n * - Simpler API (developers think in dollars, not cents)\n * - More intuitive JSON serialization (100.50 vs 10050)\n * - Sufficient precision for payroll (15 significant digits)\n * - Banker's rounding mitigates cumulative drift\n *\n * **Why NOT integer cents:**\n * - Would require breaking schema changes\n * - All consumer code needs conversion (amount / 100)\n * - More verbose for reporting/display\n *\n * **Why NOT Decimal128:**\n * - MongoDB-specific, reduces portability\n * - Requires special BSON handling in application code\n * - Overkill for typical payroll amounts (< $10M/employee/year)\n *\n * ### Mitigation:\n * - All calculations go through roundMoney() with banker's rounding\n * - Final amounts always rounded to 2 decimal places before storage\n * - Aggregations use MongoDB $round operator for consistency\n *\n * @see https://en.wikipedia.org/wiki/Banker%27s_rounding\n */\n\n/**\n * Banker's rounding (round half to even) with decimal precision\n *\n * When rounding 0.5, rounds to the nearest even number:\n * - 2.5 → 2 (even)\n * - 3.5 → 4 (even)\n * - 4.5 → 4 (even)\n *\n * This prevents systematic bias in cumulative rounding that occurs\n * with standard rounding (always up), which is critical for payroll compliance.\n *\n * @param value - The number to round\n * @param decimals - Number of decimal places (default: 2 for cent precision)\n * @returns The rounded value\n */\nexport function roundMoney(value: number, decimals = 2): number {\n const multiplier = Math.pow(10, decimals);\n const scaled = value * multiplier;\n const fraction = scaled - Math.floor(scaled);\n\n // If exactly 0.5 (within tolerance), round to nearest even\n // Use 1e-10 tolerance to handle floating-point imprecision\n if (Math.abs(fraction - 0.5) < 1e-10) {\n const floor = Math.floor(scaled);\n const rounded = floor % 2 === 0 ? floor : floor + 1;\n return rounded / multiplier;\n }\n\n // Otherwise use standard rounding\n return Math.round(scaled) / multiplier;\n}\n\n/**\n * Round money with validation for negative values\n *\n * @param value - The number to round\n * @param decimals - Number of decimal places (default: 2 for cent precision)\n * @returns The rounded value (never negative for deductions)\n */\nexport function roundMoneyPositive(value: number, decimals = 2): number {\n return Math.max(0, roundMoney(value, decimals));\n}\n\n/**\n * Calculate percentage of an amount with banker's rounding\n *\n * @param amount - Base amount\n * @param percentage - Percentage (e.g., 10 for 10%)\n * @param decimals - Decimal places to round to (default: 2 for cent precision)\n * @returns Rounded percentage amount\n */\nexport function percentageOf(amount: number, percentage: number, decimals = 2): number {\n return roundMoney((amount * percentage) / 100, decimals);\n}\n\n/**\n * Prorate an amount by a ratio with banker's rounding\n *\n * @param amount - Base amount\n * @param ratio - Proration ratio (0 to 1)\n * @param decimals - Decimal places (default: 2 for cent precision)\n * @returns Prorated amount\n */\nexport function prorateAmount(amount: number, ratio: number, decimals = 2): number {\n return roundMoney(amount * ratio, decimals);\n}\n","/**\n * @classytic/payroll - Configuration\n *\n * Centralized configuration with type safety\n * Configurable defaults for different use cases\n */\n\nimport type {\n HRMConfig,\n TaxBracket,\n SalaryBandRange,\n RoleMappingConfig,\n OrgRole,\n SalaryBand,\n Department,\n EmploymentType,\n PaymentFrequency,\n DeepPartial,\n EmployeeIdentityMode,\n} from './types.js';\nimport { roundMoney } from './utils/money.js';\n\n// ============================================================================\n// Default Configuration\n// ============================================================================\n\nexport const HRM_CONFIG: HRMConfig = {\n dataRetention: {\n /**\n * Default retention period for payroll records in seconds\n *\n * STANDARD APPROACH: expireAt field + configurable TTL index\n *\n * ## How It Works:\n * 1. Set expireAt date on each payroll record\n * 2. Call PayrollRecord.configureRetention() at app startup\n * 3. MongoDB deletes documents when expireAt is reached\n *\n * ## Usage:\n *\n * @example Configure at initialization\n * ```typescript\n * await payroll.init({ ... });\n * await PayrollRecord.configureRetention(0); // 0 = delete when expireAt reached\n * ```\n *\n * @example Set expireAt per record\n * ```typescript\n * const expireAt = PayrollRecord.calculateExpireAt(7); // 7 years\n * await PayrollRecord.updateOne({ _id }, { expireAt });\n * ```\n *\n * ## Jurisdiction Requirements:\n * - USA: 7 years → 220752000 seconds\n * - EU/UK: 6 years → 189216000 seconds\n * - Germany: 10 years → 315360000 seconds\n * - India: 8 years → 252288000 seconds\n *\n * Set to 0 to disable TTL\n */\n payrollRecordsTTL: 63072000, // 2 years - adjust per jurisdiction\n exportWarningDays: 30,\n archiveBeforeDeletion: true,\n },\n\n payroll: {\n defaultCurrency: 'USD',\n allowProRating: true,\n attendanceIntegration: true,\n autoDeductions: true,\n overtimeEnabled: false,\n overtimeMultiplier: 1.5,\n },\n\n salary: {\n minimumWage: 0,\n maximumAllowances: 10,\n maximumDeductions: 10,\n defaultFrequency: 'monthly',\n },\n\n employment: {\n defaultProbationMonths: 3,\n maxProbationMonths: 6,\n allowReHiring: true,\n trackEmploymentHistory: true,\n },\n\n validation: {\n requireBankDetails: false,\n requireUserId: false, // Modern: Allow guest employees by default\n identityMode: 'employeeId', // Modern: Use human-readable IDs as primary\n identityFallbacks: ['email', 'userId'], // Smart fallback chain\n },\n};\n\n// ============================================================================\n// Salary Bands Configuration\n// ============================================================================\n\nexport const SALARY_BANDS: Record<Exclude<SalaryBand, 'custom'>, SalaryBandRange> = {\n intern: { min: 10000, max: 20000 },\n junior: { min: 20000, max: 40000 },\n mid: { min: 40000, max: 70000 },\n senior: { min: 70000, max: 120000 },\n lead: { min: 100000, max: 200000 },\n executive: { min: 150000, max: 500000 },\n};\n\n// ============================================================================\n// Tax Brackets Configuration\n//\n// These brackets represent progressive tax rates AFTER the tax-free threshold.\n// The threshold is handled separately via jurisdictionTaxConfig.standardDeduction\n// or thresholdsByCategory. Do NOT include a 0% bracket for the tax-free amount —\n// that would cause a double deduction when used with standardDeduction.\n// ============================================================================\n\nexport const TAX_BRACKETS: Record<string, TaxBracket[]> = {\n // Bangladesh FY 2024-25 rates (after tax-free threshold)\n BDT: [\n { min: 0, max: 100000, rate: 0.05 },\n { min: 100000, max: 400000, rate: 0.10 },\n { min: 400000, max: 700000, rate: 0.15 },\n { min: 700000, max: 1100000, rate: 0.20 },\n { min: 1100000, max: Infinity, rate: 0.25 },\n ],\n USD: [\n { min: 0, max: 10000, rate: 0.10 },\n { min: 10000, max: 40000, rate: 0.12 },\n { min: 40000, max: 85000, rate: 0.22 },\n { min: 85000, max: 165000, rate: 0.24 },\n { min: 165000, max: 215000, rate: 0.32 },\n { min: 215000, max: 540000, rate: 0.35 },\n { min: 540000, max: Infinity, rate: 0.37 },\n ],\n};\n\n// ============================================================================\n// Organization Roles Configuration\n// ============================================================================\n\nexport interface OrgRoleDefinition {\n key: OrgRole;\n label: string;\n description: string;\n}\n\nexport const ORG_ROLES: Record<Uppercase<OrgRole>, OrgRoleDefinition> = {\n OWNER: {\n key: 'owner',\n label: 'Owner',\n description: 'Full organization access (set by Organization model)',\n },\n MANAGER: {\n key: 'manager',\n label: 'Manager',\n description: 'Management and administrative features',\n },\n TRAINER: {\n key: 'trainer',\n label: 'Trainer',\n description: 'Training and coaching features',\n },\n STAFF: {\n key: 'staff',\n label: 'Staff',\n description: 'General staff access to basic features',\n },\n INTERN: {\n key: 'intern',\n label: 'Intern',\n description: 'Limited access for interns',\n },\n CONSULTANT: {\n key: 'consultant',\n label: 'Consultant',\n description: 'Project-based consultant access',\n },\n};\n\nexport const ORG_ROLE_KEYS = Object.values(ORG_ROLES).map((role) => role.key);\n\n// ============================================================================\n// Role Mapping Configuration\n// ============================================================================\n\nexport const ROLE_MAPPING: RoleMappingConfig = {\n byDepartment: {\n management: 'manager',\n training: 'trainer',\n sales: 'staff',\n operations: 'staff',\n finance: 'staff',\n hr: 'staff',\n marketing: 'staff',\n it: 'staff',\n support: 'staff',\n maintenance: 'staff',\n },\n\n byEmploymentType: {\n full_time: 'staff',\n part_time: 'staff',\n contract: 'consultant',\n intern: 'intern',\n consultant: 'consultant',\n },\n\n default: 'staff',\n};\n\n// ============================================================================\n// Configuration Functions\n// ============================================================================\n\n/**\n * Calculate tax based on annual income\n *\n * Uses banker's rounding to prevent systematic bias in tax calculations.\n * Tax is calculated progressively across brackets and rounded once at the end.\n *\n * @param annualIncome - Annual income in major units (dollars/rupees/taka)\n * @param currency - Currency code (default: 'USD')\n * @returns Tax amount in major units, properly rounded with banker's rounding\n */\nexport function calculateTax(annualIncome: number, currency = 'USD'): number {\n const brackets = TAX_BRACKETS[currency];\n if (!brackets) return 0;\n\n let tax = 0;\n for (const bracket of brackets) {\n if (annualIncome > bracket.min) {\n const taxableAmount = Math.min(annualIncome, bracket.max) - bracket.min;\n tax += taxableAmount * bracket.rate;\n }\n }\n\n // Use roundMoney for consistency with all other money calculations\n // This ensures identical rounding behavior across all tax calculations\n return roundMoney(tax);\n}\n\n/**\n * Get salary band for a given amount\n */\nexport function getSalaryBand(amount: number): SalaryBand {\n for (const [band, range] of Object.entries(SALARY_BANDS)) {\n if (amount >= range.min && amount <= range.max) {\n return band as SalaryBand;\n }\n }\n return 'custom';\n}\n\n/**\n * Determine the appropriate organization role for an employee\n */\nexport function determineOrgRole(employmentData: {\n department?: Department | string;\n type?: EmploymentType | string;\n position?: string;\n}): OrgRole {\n const { department, type: employmentType } = employmentData;\n\n // Priority 1: Department-based mapping\n if (department && department in ROLE_MAPPING.byDepartment) {\n return ROLE_MAPPING.byDepartment[department as keyof typeof ROLE_MAPPING.byDepartment];\n }\n\n // Priority 2: Employment type mapping\n if (employmentType && employmentType in ROLE_MAPPING.byEmploymentType) {\n return ROLE_MAPPING.byEmploymentType[employmentType as keyof typeof ROLE_MAPPING.byEmploymentType];\n }\n\n // Priority 3: Default role\n return ROLE_MAPPING.default;\n}\n\n/**\n * Get pay periods per year based on frequency\n */\nexport function getPayPeriodsPerYear(frequency: PaymentFrequency): number {\n const periodsMap: Record<PaymentFrequency, number> = {\n monthly: 12,\n bi_weekly: 26,\n weekly: 52,\n daily: 365,\n hourly: 2080, // Assuming 40 hours/week * 52 weeks\n };\n return periodsMap[frequency];\n}\n\n/**\n * Calculate monthly equivalent from any frequency\n */\nexport function toMonthlyAmount(amount: number, frequency: PaymentFrequency): number {\n const periodsPerYear = getPayPeriodsPerYear(frequency);\n return roundMoney((amount * periodsPerYear) / 12, 2);\n}\n\n/**\n * Calculate annual equivalent from any frequency\n */\nexport function toAnnualAmount(amount: number, frequency: PaymentFrequency): number {\n const periodsPerYear = getPayPeriodsPerYear(frequency);\n return roundMoney(amount * periodsPerYear, 2);\n}\n\n/**\n * Merge configuration with defaults\n */\nexport function mergeConfig(\n customConfig: Partial<HRMConfig> | DeepPartial<HRMConfig> | undefined\n): HRMConfig {\n if (!customConfig) return HRM_CONFIG;\n\n return {\n dataRetention: { ...HRM_CONFIG.dataRetention, ...customConfig.dataRetention },\n payroll: { ...HRM_CONFIG.payroll, ...customConfig.payroll },\n salary: { ...HRM_CONFIG.salary, ...customConfig.salary },\n employment: { ...HRM_CONFIG.employment, ...customConfig.employment },\n validation: {\n ...HRM_CONFIG.validation,\n ...customConfig.validation,\n // Ensure fallbacks is always EmployeeIdentityMode[]\n identityFallbacks: (customConfig.validation?.identityFallbacks ?? HRM_CONFIG.validation.identityFallbacks) as EmployeeIdentityMode[]\n },\n };\n}\n\n// ============================================================================\n// Default Export\n// ============================================================================\n\nexport default {\n HRM_CONFIG,\n SALARY_BANDS,\n TAX_BRACKETS,\n ORG_ROLES,\n ORG_ROLE_KEYS,\n ROLE_MAPPING,\n calculateTax,\n getSalaryBand,\n determineOrgRole,\n getPayPeriodsPerYear,\n toMonthlyAmount,\n toAnnualAmount,\n mergeConfig,\n};\n\n","/**\n * @classytic/payroll - Dependency Container\n *\n * Per-instance dependency injection container for service management.\n * Enables clean dependency injection and testing without global state.\n *\n * IMPORTANT: This container is instance-based (not a singleton) to support:\n * - Serverless/Lambda environments\n * - Multi-app runtimes\n * - Parallel testing\n * - Multiple Payroll instances in the same process\n */\n\nimport type { Model, ClientSession } from 'mongoose';\nimport type {\n Logger,\n HRMConfig,\n SingleTenantConfig,\n EmployeeDocument,\n PayrollRecordDocument,\n AnyDocument,\n LeaveRequestDocument,\n TaxWithholdingDocument,\n} from '../types.js';\nimport { getLogger } from '../utils/logger.js';\nimport { HRM_CONFIG, mergeConfig } from '../config.js';\n\n// ============================================================================\n// Container Types with Strong Generics\n// ============================================================================\n\n/**\n * Strongly-typed models container\n * Uses specific document types instead of Model<any> for better DX\n */\nexport interface ModelsContainer<\n TEmployee extends EmployeeDocument = EmployeeDocument,\n TPayrollRecord extends PayrollRecordDocument = PayrollRecordDocument,\n TTransaction extends AnyDocument = AnyDocument,\n TAttendance extends AnyDocument = AnyDocument,\n TLeaveRequest extends LeaveRequestDocument = LeaveRequestDocument,\n TTaxWithholding extends TaxWithholdingDocument = TaxWithholdingDocument,\n> {\n EmployeeModel: Model<TEmployee>;\n PayrollRecordModel: Model<TPayrollRecord>;\n TransactionModel: Model<TTransaction>;\n AttendanceModel?: Model<TAttendance> | null;\n LeaveRequestModel?: Model<TLeaveRequest> | null;\n TaxWithholdingModel?: Model<TTaxWithholding> | null;\n}\n\n/**\n * Container configuration with generic model types\n */\nexport interface ContainerConfig<\n TEmployee extends EmployeeDocument = EmployeeDocument,\n TPayrollRecord extends PayrollRecordDocument = PayrollRecordDocument,\n TTransaction extends AnyDocument = AnyDocument,\n TAttendance extends AnyDocument = AnyDocument,\n TLeaveRequest extends LeaveRequestDocument = LeaveRequestDocument,\n TTaxWithholding extends TaxWithholdingDocument = TaxWithholdingDocument,\n> {\n models: ModelsContainer<TEmployee, TPayrollRecord, TTransaction, TAttendance, TLeaveRequest, TTaxWithholding>;\n config?: Partial<HRMConfig>;\n singleTenant?: SingleTenantConfig | null;\n logger?: Logger;\n}\n\n// ============================================================================\n// Container Class (Per-Instance, Not Singleton)\n// ============================================================================\n\n/**\n * Per-instance DI Container for Payroll\n *\n * Each Payroll instance creates its own Container, avoiding global state issues\n * in serverless and multi-app environments.\n *\n * @example\n * ```typescript\n * // Each Payroll instance has its own container\n * const payroll1 = createPayrollInstance()\n * .withModels({ EmployeeModel, PayrollRecordModel, TransactionModel })\n * .build();\n *\n * const payroll2 = createPayrollInstance()\n * .withModels({ OtherEmployeeModel, OtherPayrollModel, OtherTransactionModel })\n * .build();\n *\n * // They don't share state - perfect for multi-tenant or testing\n * ```\n */\nexport class Container<\n TEmployee extends EmployeeDocument = EmployeeDocument,\n TPayrollRecord extends PayrollRecordDocument = PayrollRecordDocument,\n TTransaction extends AnyDocument = AnyDocument,\n TAttendance extends AnyDocument = AnyDocument,\n TLeaveRequest extends LeaveRequestDocument = LeaveRequestDocument,\n TTaxWithholding extends TaxWithholdingDocument = TaxWithholdingDocument,\n> {\n private _models: ModelsContainer<TEmployee, TPayrollRecord, TTransaction, TAttendance, TLeaveRequest, TTaxWithholding> | null = null;\n private _config: HRMConfig = HRM_CONFIG;\n private _singleTenant: SingleTenantConfig | null = null;\n private _logger: Logger;\n private _initialized = false;\n\n constructor() {\n this._logger = getLogger();\n }\n\n /**\n * Initialize container with configuration\n */\n initialize(\n config: ContainerConfig<TEmployee, TPayrollRecord, TTransaction, TAttendance, TLeaveRequest, TTaxWithholding>\n ): void {\n if (this._initialized) {\n this._logger.warn('Container already initialized, re-initializing');\n }\n\n this._models = config.models;\n this._config = mergeConfig(config.config);\n this._singleTenant = config.singleTenant ?? null;\n\n if (config.logger) {\n this._logger = config.logger;\n }\n\n this._initialized = true;\n\n this._logger.info('Container initialized', {\n hasEmployeeModel: !!this._models.EmployeeModel,\n hasPayrollRecordModel: !!this._models.PayrollRecordModel,\n hasTransactionModel: !!this._models.TransactionModel,\n hasAttendanceModel: !!this._models.AttendanceModel,\n hasLeaveRequestModel: !!this._models.LeaveRequestModel,\n hasTaxWithholdingModel: !!this._models.TaxWithholdingModel,\n isSingleTenant: !!this._singleTenant,\n });\n }\n\n /**\n * Check if container is initialized\n */\n isInitialized(): boolean {\n return this._initialized;\n }\n\n /**\n * Reset container (useful for testing)\n */\n reset(): void {\n this._models = null;\n this._config = HRM_CONFIG;\n this._singleTenant = null;\n this._initialized = false;\n this._logger.info('Container reset');\n }\n\n /**\n * Ensure container is initialized\n */\n private ensureInitialized(): void {\n if (!this._initialized || !this._models) {\n throw new Error(\n 'Payroll not initialized. Call Payroll.initialize() first.'\n );\n }\n }\n\n /**\n * Get models container (strongly typed)\n */\n getModels(): ModelsContainer<TEmployee, TPayrollRecord, TTransaction, TAttendance, TLeaveRequest, TTaxWithholding> {\n this.ensureInitialized();\n return this._models!;\n }\n\n /**\n * Get Employee model (strongly typed)\n */\n getEmployeeModel(): Model<TEmployee> {\n this.ensureInitialized();\n return this._models!.EmployeeModel;\n }\n\n /**\n * Get PayrollRecord model (strongly typed)\n */\n getPayrollRecordModel(): Model<TPayrollRecord> {\n this.ensureInitialized();\n return this._models!.PayrollRecordModel;\n }\n\n /**\n * Get Transaction model (strongly typed)\n */\n getTransactionModel(): Model<TTransaction> {\n this.ensureInitialized();\n return this._models!.TransactionModel;\n }\n\n /**\n * Get Attendance model (optional, strongly typed)\n */\n getAttendanceModel(): Model<TAttendance> | null {\n this.ensureInitialized();\n return this._models!.AttendanceModel ?? null;\n }\n\n /**\n * Get LeaveRequest model (optional, strongly typed)\n */\n getLeaveRequestModel(): Model<TLeaveRequest> | null {\n this.ensureInitialized();\n return this._models!.LeaveRequestModel ?? null;\n }\n\n /**\n * Get TaxWithholding model (optional, strongly typed)\n */\n getTaxWithholdingModel(): Model<TTaxWithholding> | null {\n this.ensureInitialized();\n return this._models!.TaxWithholdingModel ?? null;\n }\n\n /**\n * Get configuration\n */\n getConfig(): HRMConfig {\n return this._config;\n }\n\n /**\n * Get specific config section\n */\n getConfigSection<K extends keyof HRMConfig>(section: K): HRMConfig[K] {\n return this._config[section];\n }\n\n /**\n * Check if single-tenant mode\n */\n isSingleTenant(): boolean {\n return !!this._singleTenant;\n }\n\n /**\n * Get single-tenant config\n */\n getSingleTenantConfig(): SingleTenantConfig | null {\n return this._singleTenant;\n }\n\n /**\n * Get organization ID (for single-tenant mode)\n */\n getOrganizationId(): string | null {\n if (!this._singleTenant || !this._singleTenant.organizationId) return null;\n return typeof this._singleTenant.organizationId === 'string'\n ? this._singleTenant.organizationId\n : this._singleTenant.organizationId.toString();\n }\n\n /**\n * Get logger\n */\n getLogger(): Logger {\n return this._logger;\n }\n\n /**\n * Set logger\n */\n setLogger(logger: Logger): void {\n this._logger = logger;\n }\n\n /**\n * Has attendance integration\n */\n hasAttendanceIntegration(): boolean {\n return (\n !!this._models?.AttendanceModel &&\n this._config.payroll.attendanceIntegration\n );\n }\n\n /**\n * Create operation context with defaults\n *\n * In single-tenant mode with autoInject enabled (default), automatically\n * injects the configured organizationId into the context.\n *\n * @throws Error if autoInject is enabled but no organizationId is configured\n */\n createOperationContext(\n overrides?: Partial<{\n userId: string;\n userName: string;\n userRole: string;\n organizationId: string;\n session: ClientSession;\n }>\n ): {\n userId?: string;\n userName?: string;\n userRole?: string;\n organizationId?: string;\n session?: ClientSession;\n } {\n const context: Record<string, unknown> = {};\n\n // Auto-inject organizationId in single-tenant mode\n // FIX: Check if single-tenant mode is enabled (config exists)\n const isSingleTenant = !!this._singleTenant;\n const autoInjectEnabled = isSingleTenant && this._singleTenant?.autoInject !== false;\n\n if (autoInjectEnabled && !overrides?.organizationId) {\n const orgId = this.getOrganizationId();\n if (orgId) {\n context.organizationId = orgId;\n } else {\n // Single-tenant mode with autoInject but no organizationId configured\n throw new Error(\n 'Single-tenant mode with autoInject enabled requires organizationId in configuration. ' +\n 'Configure it via forSingleTenant({ organizationId: YOUR_ORG_ID }) or provide it explicitly.'\n );\n }\n }\n\n return { ...context, ...overrides };\n }\n}\n\n// ============================================================================\n// Factory Function\n// ============================================================================\n\n/**\n * Create a new Container instance\n */\nexport function createContainer<\n TEmployee extends EmployeeDocument = EmployeeDocument,\n TPayrollRecord extends PayrollRecordDocument = PayrollRecordDocument,\n TTransaction extends AnyDocument = AnyDocument,\n TAttendance extends AnyDocument = AnyDocument,\n TLeaveRequest extends LeaveRequestDocument = LeaveRequestDocument,\n TTaxWithholding extends TaxWithholdingDocument = TaxWithholdingDocument,\n>(): Container<TEmployee, TPayrollRecord, TTransaction, TAttendance, TLeaveRequest, TTaxWithholding> {\n return new Container<TEmployee, TPayrollRecord, TTransaction, TAttendance, TLeaveRequest, TTaxWithholding>();\n}\n\n// ============================================================================\n// Default Instance (for backwards compatibility)\n// ============================================================================\n\n/**\n * @deprecated Use createPayrollInstance() instead for new code.\n * This default instance is kept for backwards compatibility but should be avoided\n * in serverless/multi-app environments.\n *\n * WARNING: Global singletons can cause issues in:\n * - AWS Lambda (cold starts may share state)\n * - Vercel Functions\n * - Multiple app instances in same process\n * - Parallel tests\n */\nlet defaultContainer: Container | null = null;\n\n/**\n * @deprecated Use createPayrollInstance() instead.\n * Get or create the default container instance.\n */\nexport function getContainer(): Container {\n if (!defaultContainer) {\n defaultContainer = new Container();\n }\n return defaultContainer;\n}\n\n/**\n * @deprecated Use createPayrollInstance() instead.\n * Initialize the default container.\n */\nexport function initializeContainer(config: ContainerConfig): void {\n getContainer().initialize(config);\n}\n\n/**\n * @deprecated Use container.isInitialized() instead.\n * Check if the default container is initialized.\n */\nexport function isContainerInitialized(): boolean {\n return defaultContainer?.isInitialized() ?? false;\n}\n\n/**\n * @deprecated Use container.getModels() instead.\n * Get models from the default container.\n */\nexport function getModels(): ModelsContainer {\n return getContainer().getModels();\n}\n\n/**\n * @deprecated Use container.getConfig() instead.\n * Get config from the default container.\n */\nexport function getConfig(): HRMConfig {\n return getContainer().getConfig();\n}\n\n/**\n * @deprecated Use container.isSingleTenant() instead.\n * Check if single-tenant mode.\n */\nexport function isSingleTenant(): boolean {\n return getContainer().isSingleTenant();\n}\n\n/**\n * Reset the default container (for testing).\n * @deprecated Prefer instance-based containers for testing.\n */\nexport function resetDefaultContainer(): void {\n if (defaultContainer) {\n defaultContainer.reset();\n }\n defaultContainer = null;\n}\n\n// Legacy alias for backwards compatibility\nexport { resetDefaultContainer as resetContainer };\n","/**\n * @classytic/payroll - Date Utilities\n *\n * Pure, composable, testable date operations\n * No side effects, no mutations\n */\n\nimport type { PayPeriodInfo, PaymentFrequency } from '../types.js';\n\n// ============================================================================\n// Date Arithmetic\n// ============================================================================\n\n/**\n * Add days to a date\n */\nexport function addDays(date: Date, days: number): Date {\n const result = new Date(date);\n result.setDate(result.getDate() + days);\n return result;\n}\n\n/**\n * Add months to a date\n */\nexport function addMonths(date: Date, months: number): Date {\n const result = new Date(date);\n result.setMonth(result.getMonth() + months);\n return result;\n}\n\n/**\n * Add years to a date\n */\nexport function addYears(date: Date, years: number): Date {\n const result = new Date(date);\n result.setFullYear(result.getFullYear() + years);\n return result;\n}\n\n/**\n * Subtract days from a date\n */\nexport function subDays(date: Date, days: number): Date {\n return addDays(date, -days);\n}\n\n/**\n * Subtract months from a date\n */\nexport function subMonths(date: Date, months: number): Date {\n return addMonths(date, -months);\n}\n\n// ============================================================================\n// Date Boundaries\n// ============================================================================\n\n/**\n * Get the start of a month\n */\nexport function startOfMonth(date: Date): Date {\n const result = new Date(date);\n result.setDate(1);\n result.setHours(0, 0, 0, 0);\n return result;\n}\n\n/**\n * Get the end of a month\n */\nexport function endOfMonth(date: Date): Date {\n const result = new Date(date);\n result.setMonth(result.getMonth() + 1, 0);\n result.setHours(23, 59, 59, 999);\n return result;\n}\n\n/**\n * Get the start of a year\n */\nexport function startOfYear(date: Date): Date {\n const result = new Date(date);\n result.setMonth(0, 1);\n result.setHours(0, 0, 0, 0);\n return result;\n}\n\n/**\n * Get the end of a year\n */\nexport function endOfYear(date: Date): Date {\n const result = new Date(date);\n result.setMonth(11, 31);\n result.setHours(23, 59, 59, 999);\n return result;\n}\n\n/**\n * Get the start of a day\n */\nexport function startOfDay(date: Date): Date {\n const result = new Date(date);\n result.setHours(0, 0, 0, 0);\n return result;\n}\n\n/**\n * Get the end of a day\n */\nexport function endOfDay(date: Date): Date {\n const result = new Date(date);\n result.setHours(23, 59, 59, 999);\n return result;\n}\n\n// ============================================================================\n// Date Normalization\n// ============================================================================\n\n/**\n * Convert a date to a UTC-based date string for consistent comparison.\n *\n * Unlike `Date.toDateString()` which uses the local timezone, this produces\n * a locale-independent string based on the date's year/month/day components.\n * Use this for holiday set lookups to avoid timezone-dependent mismatches.\n */\nexport function toUTCDateString(date: Date): string {\n const d = new Date(date);\n d.setHours(0, 0, 0, 0);\n return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;\n}\n\n// ============================================================================\n// Date Differences\n// ============================================================================\n\n/**\n * Calculate difference in days between two dates.\n *\n * Normalizes both dates to midnight before computing to avoid\n * inconsistencies from time-of-day differences or DST transitions.\n */\nexport function diffInDays(start: Date, end: Date): number {\n const s = new Date(start);\n const e = new Date(end);\n s.setHours(0, 0, 0, 0);\n e.setHours(0, 0, 0, 0);\n return Math.ceil((e.getTime() - s.getTime()) / (1000 * 60 * 60 * 24));\n}\n\n/**\n * Calculate difference in months between two dates\n */\nexport function diffInMonths(start: Date, end: Date): number {\n const startDate = new Date(start);\n const endDate = new Date(end);\n return (\n (endDate.getFullYear() - startDate.getFullYear()) * 12 +\n (endDate.getMonth() - startDate.getMonth())\n );\n}\n\n/**\n * Calculate difference in years between two dates\n */\nexport function diffInYears(start: Date, end: Date): number {\n return Math.floor(diffInMonths(start, end) / 12);\n}\n\n// Aliases for backwards compatibility\nexport const daysBetween = diffInDays;\nexport const monthsBetween = diffInMonths;\n\n// ============================================================================\n// Day Type Checks\n// ============================================================================\n\n/**\n * Check if date is a weekday (Mon-Fri)\n */\nexport function isWeekday(date: Date): boolean {\n const day = new Date(date).getDay();\n return day >= 1 && day <= 5;\n}\n\n/**\n * Check if date is a weekend (Sat-Sun)\n */\nexport function isWeekend(date: Date): boolean {\n const day = new Date(date).getDay();\n return day === 0 || day === 6;\n}\n\n/**\n * Get day of week (0=Sunday, 6=Saturday)\n */\nexport function getDayOfWeek(date: Date): number {\n return new Date(date).getDay();\n}\n\n/**\n * Get day name\n */\nexport function getDayName(date: Date): string {\n const days = [\n 'Sunday',\n 'Monday',\n 'Tuesday',\n 'Wednesday',\n 'Thursday',\n 'Friday',\n 'Saturday',\n ];\n return days[getDayOfWeek(date)];\n}\n\n// ============================================================================\n// Pay Period Functions\n// ============================================================================\n\n/**\n * Get pay period for a given month and year (monthly periods)\n */\nexport function getPayPeriod(month: number, year: number): PayPeriodInfo {\n const startDate = new Date(year, month - 1, 1);\n return {\n month,\n year,\n startDate: startOfMonth(startDate),\n endDate: endOfMonth(startDate),\n };\n}\n\n/**\n * Get pay period based on payment frequency\n *\n * Creates the correct period boundaries based on the employee's payment frequency:\n * - monthly: full calendar month\n * - bi_weekly: 14 days ending on paymentDate\n * - weekly: 7 days ending on paymentDate\n * - daily/hourly: single day (paymentDate)\n *\n * @param frequency - Payment frequency\n * @param paymentDate - Date of payment (used as end of period for non-monthly)\n * @param month - Month (1-12) for accounting purposes\n * @param year - Year for accounting purposes\n * @returns Pay period with appropriate boundaries\n */\nexport function getPayPeriodForFrequency(\n frequency: PaymentFrequency,\n paymentDate: Date,\n month: number,\n year: number\n): PayPeriodInfo & { workingDays: number } {\n switch (frequency) {\n case 'monthly': {\n const period = getPayPeriod(month, year);\n const workingDays = getWorkingDaysInMonth(year, month);\n return { ...period, workingDays };\n }\n\n case 'bi_weekly': {\n // 14-day period ending on paymentDate\n const endDate = startOfDay(paymentDate);\n const startDate = addDays(endDate, -13); // 14 days total (0-13)\n const workingDays = countWeekdaysInRange(startDate, endDate);\n return { month, year, startDate, endDate, workingDays };\n }\n\n case 'weekly': {\n // 7-day period ending on paymentDate\n const endDate = startOfDay(paymentDate);\n const startDate = addDays(endDate, -6); // 7 days total (0-6)\n const workingDays = countWeekdaysInRange(startDate, endDate);\n return { month, year, startDate, endDate, workingDays };\n }\n\n case 'daily':\n case 'hourly': {\n // Single day period\n const date = startOfDay(paymentDate);\n const workingDays = isWeekday(date) ? 1 : 0;\n return { month, year, startDate: date, endDate: date, workingDays };\n }\n\n default:\n // Fallback to monthly\n return getPayPeriodForFrequency('monthly', paymentDate, month, year);\n }\n}\n\n/**\n * Count weekdays (Mon-Fri) in a date range (inclusive)\n */\nfunction countWeekdaysInRange(start: Date, end: Date): number {\n let count = 0;\n const current = new Date(start);\n while (current <= end) {\n if (isWeekday(current)) {\n count++;\n }\n current.setDate(current.getDate() + 1);\n }\n return count;\n}\n\n/**\n * Get current pay period\n */\nexport function getCurrentPeriod(date = new Date()): { year: number; month: number } {\n const d = new Date(date);\n return {\n year: d.getFullYear(),\n month: d.getMonth() + 1,\n };\n}\n\n/**\n * Get working days in a month\n */\nexport function getWorkingDaysInMonth(year: number, month: number): number {\n const start = new Date(year, month - 1, 1);\n const end = endOfMonth(start);\n let count = 0;\n \n const current = new Date(start);\n while (current <= end) {\n if (isWeekday(current)) {\n count++;\n }\n current.setDate(current.getDate() + 1);\n }\n \n return count;\n}\n\n/**\n * Get total days in a month\n */\nexport function getDaysInMonth(year: number, month: number): number {\n return new Date(year, month, 0).getDate();\n}\n\n// ============================================================================\n// Employment Date Functions\n// ============================================================================\n\n/**\n * Calculate probation end date\n */\nexport function calculateProbationEnd(\n hireDate: Date,\n probationMonths: number\n): Date | null {\n if (!probationMonths || probationMonths <= 0) return null;\n return addMonths(hireDate, probationMonths);\n}\n\n/**\n * Check if employee is on probation\n */\nexport function isOnProbation(\n probationEndDate: Date | null | undefined,\n now = new Date()\n): boolean {\n if (!probationEndDate) return false;\n return now < new Date(probationEndDate);\n}\n\n/**\n * Calculate years of service\n */\nexport function calculateYearsOfService(\n hireDate: Date,\n terminationDate?: Date | null\n): number {\n const end = terminationDate || new Date();\n const days = diffInDays(hireDate, end);\n return Math.max(0, Math.floor((days / 365.25) * 10) / 10);\n}\n\n// ============================================================================\n// Range Functions\n// ============================================================================\n\n/**\n * Check if a date is within a range\n */\nexport function isDateInRange(date: Date, start: Date, end: Date): boolean {\n const checkDate = new Date(date);\n return checkDate >= new Date(start) && checkDate <= new Date(end);\n}\n\n/**\n * Check if an item with effectiveFrom/effectiveTo dates is effective for a given period.\n *\n * Used for filtering allowances, deductions, and other time-bounded compensation items.\n * An item is considered effective if its date range overlaps with the period.\n *\n * @param item - Object with optional effectiveFrom and effectiveTo dates\n * @param periodStart - Start of the period to check\n * @param periodEnd - End of the period to check\n * @returns true if the item is effective during any part of the period\n *\n * @example\n * ```typescript\n * const allowance = { effectiveFrom: new Date('2024-01-01'), effectiveTo: null };\n * const periodStart = new Date('2024-03-01');\n * const periodEnd = new Date('2024-03-31');\n *\n * isEffectiveForPeriod(allowance, periodStart, periodEnd); // true\n * ```\n */\nexport function isEffectiveForPeriod(\n item: { effectiveFrom?: Date | null; effectiveTo?: Date | null },\n periodStart: Date,\n periodEnd: Date\n): boolean {\n const effectiveFrom = item.effectiveFrom ? new Date(item.effectiveFrom) : new Date(0);\n const effectiveTo = item.effectiveTo ? new Date(item.effectiveTo) : new Date('2099-12-31');\n\n // Item is effective if its range overlaps with the period\n return effectiveFrom <= periodEnd && effectiveTo >= periodStart;\n}\n\n/**\n * Get date range for a pay period\n */\nexport function getPayPeriodDateRange(\n month: number,\n year: number\n): { start: Date; end: Date } {\n const period = getPayPeriod(month, year);\n return { start: period.startDate, end: period.endDate };\n}\n\n// ============================================================================\n// Formatting Functions\n// ============================================================================\n\n/**\n * Format date for database storage\n */\nexport function formatDateForDB(date: Date): string {\n if (!date) return '';\n return new Date(date).toISOString();\n}\n\n/**\n * Parse date from database\n */\nexport function parseDBDate(dateString: string): Date | null {\n if (!dateString) return null;\n return new Date(dateString);\n}\n\n/**\n * Format period as string (e.g., \"01/2025\")\n */\nexport function formatPeriod({ month, year }: { month: number; year: number }): string {\n return `${String(month).padStart(2, '0')}/${year}`;\n}\n\n/**\n * Parse period string back to object\n */\nexport function parsePeriod(periodString: string): { month: number; year: number } {\n const [month, year] = periodString.split('/').map(Number);\n return { month, year };\n}\n\n/**\n * Format month name\n */\nexport function getMonthName(month: number): string {\n const months = [\n 'January', 'February', 'March', 'April', 'May', 'June',\n 'July', 'August', 'September', 'October', 'November', 'December',\n ];\n return months[month - 1] || '';\n}\n\n/**\n * Format short month name\n */\nexport function getShortMonthName(month: number): string {\n const months = [\n 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',\n ];\n return months[month - 1] || '';\n}\n\n// ============================================================================\n// Default Export\n// ============================================================================\n\nexport default {\n toUTCDateString,\n addDays,\n addMonths,\n addYears,\n subDays,\n subMonths,\n startOfMonth,\n endOfMonth,\n startOfYear,\n endOfYear,\n startOfDay,\n endOfDay,\n diffInDays,\n diffInMonths,\n diffInYears,\n daysBetween,\n monthsBetween,\n isWeekday,\n isWeekend,\n getDayOfWeek,\n getDayName,\n getPayPeriod,\n getCurrentPeriod,\n getWorkingDaysInMonth,\n getDaysInMonth,\n calculateProbationEnd,\n isOnProbation,\n calculateYearsOfService,\n isDateInRange,\n isEffectiveForPeriod,\n getPayPeriodDateRange,\n formatDateForDB,\n parseDBDate,\n formatPeriod,\n parsePeriod,\n getMonthName,\n getShortMonthName,\n};\n\n","/**\n * @classytic/payroll - Configuration & Calculation Utilities\n *\n * DESIGN PRINCIPLES:\n * 1. Accept data, don't manage it\n * 2. Pure functions - easy to test, no side effects\n * 3. Smart defaults that work out of the box\n * 4. Override at operation time when needed\n *\n * The payroll package CALCULATES, it doesn't STORE calendars/holidays.\n * Your app manages that data and passes it when needed.\n */\n\nimport { roundMoney } from '../utils/money.js';\nimport { toUTCDateString } from '../utils/date.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Work schedule configuration */\nexport interface WorkSchedule {\n /** Working days (0=Sun, 1=Mon, ..., 6=Sat). Default: Mon-Fri */\n workingDays: number[];\n /** Hours per work day. Default: 8 */\n hoursPerDay: number;\n}\n\n/** Options passed when processing payroll */\nexport interface PayrollProcessingOptions {\n /** Holidays in this period (from YOUR app's holiday model) */\n holidays?: Date[];\n /** Override work schedule for this operation */\n workSchedule?: Partial<WorkSchedule>;\n /** Skip tax calculation */\n skipTax?: boolean;\n /** Skip proration (pay full amount regardless of hire/termination date) */\n skipProration?: boolean;\n /** Skip attendance deduction */\n skipAttendance?: boolean;\n}\n\n/** Working days calculation result */\nexport interface WorkingDaysResult {\n /** Total calendar days in period */\n totalDays: number;\n /** Working days (excluding weekends and holidays) */\n workingDays: number;\n /** Weekend days */\n weekends: number;\n /** Holiday count */\n holidays: number;\n}\n\n/** Proration calculation result */\nexport interface ProrationResult {\n /** Proration ratio (0-1) */\n ratio: number;\n /**\n * Reason for proration:\n * - 'full': Employee worked the entire period (ratio = 1)\n * - 'new_hire': Employee was hired during the period\n * - 'termination': Employee was terminated during the period\n * - 'both': Both hired and terminated within the period\n * - 'not_active': Employee was not active at all during the period (ratio = 0)\n */\n reason: 'full' | 'new_hire' | 'termination' | 'both' | 'not_active';\n /** Whether salary should be prorated */\n isProrated: boolean;\n}\n\n/** Attendance data (from YOUR attendance system) */\nexport interface AttendanceInput {\n /**\n * Expected work days in period.\n * If not provided, derived from employee's workSchedule and period dates.\n */\n expectedDays?: number;\n /** Actual days worked */\n actualDays: number;\n}\n\n// ============================================================================\n// Default Configuration\n// ============================================================================\n\nexport const DEFAULT_WORK_SCHEDULE: WorkSchedule = {\n workingDays: [1, 2, 3, 4, 5], // Monday to Friday\n hoursPerDay: 8,\n};\n\n// ============================================================================\n// Pure Calculation Functions\n// ============================================================================\n\n/**\n * Count working days in a date range\n *\n * @example\n * const result = countWorkingDays(\n * new Date('2024-03-01'),\n * new Date('2024-03-31'),\n * { workingDays: [1,2,3,4,5], holidays: companyHolidays }\n * );\n */\nexport function countWorkingDays(\n startDate: Date,\n endDate: Date,\n options: {\n workingDays?: number[];\n holidays?: Date[];\n } = {}\n): WorkingDaysResult {\n const workDays = options.workingDays || DEFAULT_WORK_SCHEDULE.workingDays;\n const holidaySet = new Set(\n (options.holidays || []).map(d => toUTCDateString(d))\n );\n\n let totalDays = 0;\n let workingDays = 0;\n let holidays = 0;\n let weekends = 0;\n\n const current = new Date(startDate);\n current.setHours(0, 0, 0, 0);\n const end = new Date(endDate);\n end.setHours(0, 0, 0, 0);\n\n while (current <= end) {\n totalDays++;\n const isHoliday = holidaySet.has(toUTCDateString(current));\n const isWorkDay = workDays.includes(current.getDay());\n\n if (isHoliday) {\n holidays++;\n } else if (isWorkDay) {\n workingDays++;\n } else {\n weekends++;\n }\n\n current.setDate(current.getDate() + 1);\n }\n\n return { totalDays, workingDays, weekends, holidays };\n}\n\n/**\n * Calculate proration ratio for partial months\n *\n * @example\n * const proration = calculateProration(\n * employee.hireDate,\n * employee.terminationDate,\n * periodStart,\n * periodEnd\n * );\n */\nexport function calculateProration(\n hireDate: Date,\n terminationDate: Date | null | undefined,\n periodStart: Date,\n periodEnd: Date\n): ProrationResult {\n const hire = new Date(hireDate);\n hire.setHours(0, 0, 0, 0);\n const term = terminationDate ? new Date(terminationDate) : null;\n if (term) term.setHours(0, 0, 0, 0);\n const start = new Date(periodStart);\n start.setHours(0, 0, 0, 0);\n const end = new Date(periodEnd);\n end.setHours(0, 0, 0, 0);\n\n // Employee not active in this period\n if (hire > end || (term && term < start)) {\n return { ratio: 0, reason: 'not_active', isProrated: true };\n }\n\n // Effective dates within the period\n const effectiveStart = hire > start ? hire : start;\n const effectiveEnd = term && term < end ? term : end;\n\n // Calculate days\n const totalDays = Math.ceil((end.getTime() - start.getTime()) / 86400000) + 1;\n const actualDays = Math.ceil((effectiveEnd.getTime() - effectiveStart.getTime()) / 86400000) + 1;\n const ratio = Math.min(1, Math.max(0, actualDays / totalDays));\n\n // Determine reason\n const isNewHire = hire > start;\n const isTermination = term !== null && term < end;\n \n let reason: ProrationResult['reason'] = 'full';\n if (isNewHire && isTermination) {\n reason = 'both';\n } else if (isNewHire) {\n reason = 'new_hire';\n } else if (isTermination) {\n reason = 'termination';\n }\n\n return { ratio, reason, isProrated: ratio < 1 };\n}\n\n// NOTE: calculateAttendanceDeduction has been moved to calculators/attendance.calculator.ts\n// The calculator version uses banker's rounding and returns a detailed result object.\n// Re-export from core/index.ts for backward compatibility.\n\n/**\n * Get pay period dates for a given month\n *\n * @example\n * const period = getPayPeriod(3, 2024); // March 2024\n */\nexport function getPayPeriod(\n month: number,\n year: number,\n payDay = 28\n): { startDate: Date; endDate: Date; payDate: Date } {\n const startDate = new Date(year, month - 1, 1);\n const endDate = new Date(year, month, 0); // Last day of month\n const payDate = new Date(year, month - 1, Math.min(payDay, endDate.getDate()));\n return { startDate, endDate, payDate };\n}\n\n","/**\n * @classytic/payroll - Attendance Deduction Calculator\n *\n * Pure functions for calculating salary deductions based on attendance.\n * No database dependencies - can be used client-side!\n *\n * All monetary calculations use banker's rounding for financial accuracy.\n *\n * @packageDocumentation\n */\n\nimport { roundMoney } from '../utils/money.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Input for attendance deduction calculation\n */\nexport interface AttendanceDeductionInput {\n /**\n * Expected working days in the period (for this specific employee)\n * Should account for hire/termination dates\n */\n expectedWorkingDays: number;\n\n /**\n * Actual working days the employee was present\n */\n actualWorkingDays: number;\n\n /**\n * Daily salary rate for this employee\n * Calculated as: baseAmount / expectedWorkingDays\n */\n dailyRate: number;\n}\n\n/**\n * Result of attendance deduction calculation\n */\nexport interface AttendanceDeductionResult {\n /**\n * Number of absent days\n */\n absentDays: number;\n\n /**\n * Total deduction amount\n */\n deductionAmount: number;\n\n /**\n * Daily rate used for calculation\n */\n dailyRate: number;\n\n /**\n * Whether any deduction was applied\n */\n hasDeduction: boolean;\n}\n\n// ============================================================================\n// Pure Functions\n// ============================================================================\n\n/**\n * Calculate attendance deduction based on absent days\n *\n * @example\n * ```typescript\n * const result = calculateAttendanceDeduction({\n * expectedWorkingDays: 22,\n * actualWorkingDays: 20, // 2 days absent\n * dailyRate: 4545, // 100000 / 22\n * });\n *\n * console.log(result);\n * // {\n * // absentDays: 2,\n * // deductionAmount: 9090,\n * // dailyRate: 4545,\n * // hasDeduction: true\n * // }\n * ```\n *\n * @param input - Attendance deduction parameters\n * @returns Deduction result with breakdown\n *\n * @pure This function has no side effects\n */\nexport function calculateAttendanceDeduction(input: AttendanceDeductionInput): AttendanceDeductionResult {\n const { expectedWorkingDays, actualWorkingDays, dailyRate } = input;\n\n // Guard against negative values\n const expected = Math.max(0, expectedWorkingDays);\n const actual = Math.max(0, actualWorkingDays);\n const rate = Math.max(0, dailyRate);\n\n // Calculate absent days (cannot be negative)\n const absentDays = Math.max(0, expected - actual);\n\n // Calculate deduction amount (banker's rounding to cents)\n const deductionAmount = roundMoney(absentDays * rate, 2);\n\n return {\n absentDays,\n deductionAmount,\n dailyRate: rate,\n hasDeduction: deductionAmount > 0,\n };\n}\n\n/**\n * Calculate daily rate from monthly salary and working days\n *\n * @example\n * ```typescript\n * const daily = calculateDailyRate(100000, 22); // 4545\n * ```\n *\n * @param monthlySalary - Monthly base salary\n * @param workingDays - Working days in the month\n * @returns Daily rate (rounded)\n *\n * @pure No side effects\n */\nexport function calculateDailyRate(monthlySalary: number, workingDays: number): number {\n if (workingDays <= 0) return 0;\n return roundMoney(monthlySalary / workingDays, 2);\n}\n\n/**\n * Calculate hourly rate from monthly salary\n *\n * @example\n * ```typescript\n * const hourly = calculateHourlyRate(100000, 22, 8); // 568\n * ```\n *\n * @param monthlySalary - Monthly base salary\n * @param workingDays - Working days in the month\n * @param hoursPerDay - Hours per working day (default: 8)\n * @returns Hourly rate (rounded)\n *\n * @pure No side effects\n */\nexport function calculateHourlyRate(\n monthlySalary: number,\n workingDays: number,\n hoursPerDay: number = 8\n): number {\n const dailyRate = calculateDailyRate(monthlySalary, workingDays);\n if (hoursPerDay <= 0) return 0;\n return roundMoney(dailyRate / hoursPerDay, 2);\n}\n\n/**\n * Calculate deduction for partial day absence (half-day, quarter-day, etc.)\n *\n * @example\n * ```typescript\n * // Half-day absence\n * const deduction = calculatePartialDayDeduction(4545, 0.5); // 2272\n * ```\n *\n * @param dailyRate - Daily salary rate\n * @param fractionAbsent - Fraction of day absent (0-1)\n * @returns Deduction amount (rounded)\n *\n * @pure No side effects\n */\nexport function calculatePartialDayDeduction(dailyRate: number, fractionAbsent: number): number {\n const fraction = Math.min(1, Math.max(0, fractionAbsent));\n return roundMoney(dailyRate * fraction, 2);\n}\n\n/**\n * Calculate total attendance deduction including full and partial day absences\n *\n * @example\n * ```typescript\n * const result = calculateTotalAttendanceDeduction({\n * dailyRate: 4545,\n * fullDayAbsences: 2,\n * partialDayAbsences: [0.5, 0.25], // Half-day + quarter-day\n * });\n * \n * console.log(result);\n * // {\n * // fullDayDeduction: 9090,\n * // partialDayDeduction: 3408,\n * // totalDeduction: 12498\n * // }\n * ```\n *\n * @param input - Absence breakdown\n * @returns Deduction breakdown and total\n *\n * @pure No side effects\n */\nexport function calculateTotalAttendanceDeduction(input: {\n dailyRate: number;\n fullDayAbsences?: number;\n partialDayAbsences?: number[];\n}): {\n fullDayDeduction: number;\n partialDayDeduction: number;\n totalDeduction: number;\n} {\n const { dailyRate, fullDayAbsences = 0, partialDayAbsences = [] } = input;\n\n // Full day deductions (banker's rounding to whole units)\n const fullDayDeduction = roundMoney(dailyRate * Math.max(0, fullDayAbsences), 2);\n\n // Partial day deductions\n const partialDayDeduction = partialDayAbsences.reduce(\n (sum, fraction) => sum + calculatePartialDayDeduction(dailyRate, fraction),\n 0\n );\n\n return {\n fullDayDeduction,\n partialDayDeduction,\n totalDeduction: fullDayDeduction + partialDayDeduction,\n };\n}\n\n","/**\n * @classytic/payroll - Calculation Utilities\n *\n * Pure, functional, composable financial calculations\n * No side effects, highly testable\n */\n\nimport type {\n Allowance,\n Deduction,\n Compensation,\n TaxCalculationResult,\n CompensationBreakdownResult,\n} from '../types.js';\nimport { roundMoney } from './money.js';\n\n// ============================================================================\n// Basic Math Operations\n// ============================================================================\n\n/**\n * Sum array of numbers\n */\nexport function sum(numbers: number[]): number {\n return numbers.reduce((total, n) => total + n, 0);\n}\n\n/**\n * Sum by property\n */\nexport function sumBy<T>(items: T[], getter: (item: T) => number): number {\n return items.reduce((total, item) => total + getter(item), 0);\n}\n\n/**\n * Sum allowances\n */\nexport function sumAllowances(allowances: Array<{ amount: number }>): number {\n return sumBy(allowances, (a) => a.amount);\n}\n\n/**\n * Sum deductions\n */\nexport function sumDeductions(deductions: Array<{ amount: number }>): number {\n return sumBy(deductions, (d) => d.amount);\n}\n\n/**\n * ROUNDING POLICY FOR FINANCIAL CALCULATIONS\n *\n * Monetary amounts are stored as floating point numbers in major units with\n * decimal precision (e.g., 1000.50 for $1,000.50 or ₹1,000.50).\n *\n * PRECISION: All calculations preserve 2 decimal places (cent/paise precision)\n * to maintain accuracy required for payroll compliance.\n *\n * Rounding Rules:\n * 1. Banker's Rounding (Round Half to Even): Used for fair rounding over many transactions\n * 2. All intermediate calculations maintain full precision\n * 3. Final amounts rounded to 2 decimals using banker's rounding\n * 4. Tax calculations use banker's rounding for compliance\n *\n * Example:\n * Input: 1000.50 base + 15% tax\n * Calculation: 1000.50 * 0.15 = 150.075 → rounds to 150.08 (banker's rounding to 2 decimals)\n * Result: Tax = 150.08\n *\n * @see https://en.wikipedia.org/wiki/Rounding#Round_half_to_even\n */\n\n/**\n * Banker's Rounding (Round Half to Even) - Integer precision\n *\n * Rounds to the nearest integer using banker's rounding (round half to even).\n * This prevents systematic bias in rounding over many transactions.\n *\n * Uses epsilon check for safe floating-point comparison.\n *\n * Examples:\n * 0.5 → 0 (even)\n * 1.5 → 2 (even)\n * 2.5 → 2 (even)\n * 3.5 → 4 (even)\n *\n * @param value - The number to round\n * @returns Rounded integer\n * @note For money calculations with decimal precision, use `roundMoney()` instead\n */\nexport function bankersRound(value: number): number {\n const floor = Math.floor(value);\n const fraction = value - floor;\n\n // Use epsilon check for safer floating-point comparison\n if (Math.abs(fraction - 0.5) < Number.EPSILON) {\n // If halfway, round to even\n return floor % 2 === 0 ? floor : floor + 1;\n }\n\n // Otherwise use standard rounding\n return Math.round(value);\n}\n\n/**\n * Apply percentage to amount with banker's rounding (2 decimal precision)\n *\n * @param amount - Amount in major units (e.g., dollars, rupees)\n * @param percentage - Percentage to apply (e.g., 15 for 15%)\n * @param decimals - Decimal places for precision (default: 2 for cent precision)\n * @returns Result in major units, properly rounded to 2 decimals\n * @note Uses banker's rounding (round half to even) to preserve cent precision.\n * Equivalent to percentageOf() from money.ts.\n */\nexport function applyPercentage(amount: number, percentage: number, decimals = 2): number {\n // Use centralized roundMoney for consistent banker's rounding across codebase\n const result = (amount * percentage) / 100;\n return roundMoney(result, decimals);\n}\n\n/**\n * Calculate percentage of total\n */\nexport function calculatePercentage(part: number, total: number): number {\n return total > 0 ? bankersRound((part / total) * 100) : 0;\n}\n\n/**\n * Round to decimal places using banker's rounding\n */\nexport function roundTo(value: number, decimals = 2): number {\n const factor = Math.pow(10, decimals);\n return bankersRound(value * factor) / factor;\n}\n\n// ============================================================================\n// Salary Calculations\n// ============================================================================\n\n/**\n * Calculate gross salary from base and allowances\n */\nexport function calculateGross(\n baseAmount: number,\n allowances: Array<{ amount: number }>\n): number {\n return baseAmount + sumAllowances(allowances);\n}\n\n/**\n * Calculate net salary from gross and deductions\n */\nexport function calculateNet(\n gross: number,\n deductions: Array<{ amount: number }>\n): number {\n return Math.max(0, gross - sumDeductions(deductions));\n}\n\n/**\n * Calculate total compensation\n */\nexport function calculateTotalCompensation(\n baseAmount: number,\n allowances: Array<{ amount: number }>,\n deductions: Array<{ amount: number }>\n): { gross: number; net: number; deductions: number } {\n const gross = calculateGross(baseAmount, allowances);\n const totalDeductions = sumDeductions(deductions);\n const net = calculateNet(gross, deductions);\n return { gross, net, deductions: totalDeductions };\n}\n\n// ============================================================================\n// Allowance & Deduction Calculation\n// ============================================================================\n\n/**\n * Calculate allowance amount (handles percentage-based)\n */\nexport function calculateAllowanceAmount(\n allowance: Pick<Allowance, 'amount' | 'isPercentage' | 'value'>,\n baseAmount: number\n): number {\n if (allowance.isPercentage && allowance.value !== undefined) {\n return applyPercentage(baseAmount, allowance.value);\n }\n return allowance.amount;\n}\n\n/**\n * Calculate deduction amount (handles percentage-based)\n */\nexport function calculateDeductionAmount(\n deduction: Pick<Deduction, 'amount' | 'isPercentage' | 'value'>,\n baseAmount: number\n): number {\n if (deduction.isPercentage && deduction.value !== undefined) {\n return applyPercentage(baseAmount, deduction.value);\n }\n return deduction.amount;\n}\n\n/**\n * Calculate all allowances with their actual amounts\n */\nexport function calculateAllowances(\n allowances: Allowance[],\n baseAmount: number\n): Array<Allowance & { calculatedAmount: number }> {\n return allowances.map((allowance) => ({\n ...allowance,\n calculatedAmount: calculateAllowanceAmount(allowance, baseAmount),\n }));\n}\n\n/**\n * Calculate all deductions with their actual amounts\n */\nexport function calculateDeductions(\n deductions: Deduction[],\n baseAmount: number\n): Array<Deduction & { calculatedAmount: number }> {\n return deductions.map((deduction) => ({\n ...deduction,\n calculatedAmount: calculateDeductionAmount(deduction, baseAmount),\n }));\n}\n\n// ============================================================================\n// Compensation Breakdown\n// ============================================================================\n\n/**\n * Calculate full compensation breakdown\n */\nexport function calculateCompensationBreakdown(\n compensation: Pick<Compensation, 'baseAmount' | 'allowances' | 'deductions'>\n): CompensationBreakdownResult {\n const { baseAmount, allowances = [], deductions = [] } = compensation;\n\n const calculatedAllowances = calculateAllowances(allowances, baseAmount);\n const calculatedDeductions = calculateDeductions(deductions, baseAmount);\n\n const grossAmount =\n baseAmount + sumBy(calculatedAllowances, (a) => a.calculatedAmount);\n const netAmount =\n grossAmount - sumBy(calculatedDeductions, (d) => d.calculatedAmount);\n\n return {\n baseAmount,\n allowances: calculatedAllowances,\n deductions: calculatedDeductions,\n grossAmount,\n netAmount: Math.max(0, netAmount),\n };\n}\n\n// ============================================================================\n// Tax Calculations\n// ============================================================================\n\n/**\n * Apply tax brackets to calculate tax\n *\n * Uses banker's rounding for compliance (rounds to 2 decimal places).\n * Consistent with all other money calculations in the system.\n */\nexport function applyTaxBrackets(\n amount: number,\n brackets: Array<{ min: number; max: number; rate: number }>\n): number {\n let tax = 0;\n\n for (const bracket of brackets) {\n if (amount > bracket.min) {\n const taxableAmount = Math.min(amount, bracket.max) - bracket.min;\n tax += taxableAmount * bracket.rate;\n }\n }\n\n // Use roundMoney for consistency with all other money calculations\n return roundMoney(tax);\n}\n\n/**\n * Calculate tax with result\n */\nexport function calculateTax(\n amount: number,\n brackets: Array<{ min: number; max: number; rate: number }>\n): TaxCalculationResult {\n const tax = applyTaxBrackets(amount, brackets);\n return {\n gross: amount,\n tax,\n net: amount - tax,\n };\n}\n\n// ============================================================================\n// Overtime Calculations\n// ============================================================================\n\n/**\n * Calculate overtime pay\n */\nexport function calculateOvertime(\n hourlyRate: number,\n overtimeHours: number,\n multiplier = 1.5\n): number {\n return roundMoney(hourlyRate * overtimeHours * multiplier, 2);\n}\n\n/**\n * Calculate hourly rate from monthly salary\n */\nexport function calculateHourlyRate(\n monthlySalary: number,\n hoursPerMonth = 176 // 44 hours/week * 4 weeks\n): number {\n return roundMoney(monthlySalary / hoursPerMonth, 2);\n}\n\n/**\n * Calculate daily rate from monthly salary\n */\nexport function calculateDailyRate(\n monthlySalary: number,\n daysPerMonth = 22\n): number {\n return roundMoney(monthlySalary / daysPerMonth, 2);\n}\n\n// ============================================================================\n// Default Export\n// ============================================================================\n\nexport default {\n sum,\n sumBy,\n sumAllowances,\n sumDeductions,\n applyPercentage,\n calculatePercentage,\n roundTo,\n calculateGross,\n calculateNet,\n calculateTotalCompensation,\n calculateAllowanceAmount,\n calculateDeductionAmount,\n calculateAllowances,\n calculateDeductions,\n calculateCompensationBreakdown,\n applyTaxBrackets,\n calculateTax,\n calculateOvertime,\n calculateHourlyRate,\n calculateDailyRate,\n};\n\n","/**\n * @classytic/payroll - Pro-Rating Calculator\n *\n * Pure functions for salary pro-rating calculations.\n * No database dependencies - can be used client-side!\n *\n * Handles:\n * - Mid-period hires\n * - Mid-period terminations\n * - Working days (not calendar days)\n * - Holidays exclusion\n *\n * @packageDocumentation\n */\n\nimport { countWorkingDays } from '../core/config.js';\nimport { roundMoney } from '../utils/money.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Input for pro-rating calculation\n */\nexport interface ProRatingInput {\n /**\n * Employee hire date\n */\n hireDate: Date;\n\n /**\n * Employee termination date (null if still employed)\n */\n terminationDate: Date | null;\n\n /**\n * Start of the salary period\n */\n periodStart: Date;\n\n /**\n * End of the salary period\n */\n periodEnd: Date;\n\n /**\n * Working days of the week using Date.getDay() convention (0=Sunday, 1=Monday, ..., 6=Saturday)\n * @default [1, 2, 3, 4, 5] (Monday-Friday)\n */\n workingDays: number[];\n\n /**\n * Public holidays to exclude from working days\n * @default []\n */\n holidays?: Date[];\n}\n\n/**\n * Result of pro-rating calculation\n */\nexport interface ProRatingResult {\n /**\n * Whether the salary needs to be pro-rated\n */\n isProRated: boolean;\n\n /**\n * Pro-rating ratio (0-1)\n * 1 = full salary, 0.5 = half salary, etc.\n */\n ratio: number;\n\n /**\n * Total working days in the period\n */\n periodWorkingDays: number;\n\n /**\n * Working days the employee was actually employed\n */\n effectiveWorkingDays: number;\n\n /**\n * Effective start date (max of hire date and period start)\n */\n effectiveStart: Date;\n\n /**\n * Effective end date (min of termination date and period end)\n */\n effectiveEnd: Date;\n}\n\n// ============================================================================\n// Pure Functions\n// ============================================================================\n\n/**\n * Calculate pro-rating for mid-period hires/terminations\n *\n * This function uses WORKING DAYS (not calendar days) for accurate pro-rating.\n *\n * @example\n * ```typescript\n * // Employee hired on March 15th, process March salary\n * const result = calculateProRating({\n * hireDate: new Date('2024-03-15'),\n * terminationDate: null,\n * periodStart: new Date('2024-03-01'),\n * periodEnd: new Date('2024-03-31'),\n * workingDays: [1, 2, 3, 4, 5], // Mon-Fri\n * });\n * \n * console.log(result);\n * // {\n * // isProRated: true,\n * // ratio: 0.64, // Worked 14 out of 22 working days\n * // periodWorkingDays: 22,\n * // effectiveWorkingDays: 14\n * // }\n * ```\n *\n * @param input - Pro-rating calculation parameters\n * @returns Pro-rating result with ratio and working days breakdown\n *\n * @pure This function has no side effects and doesn't access external state\n */\nexport function calculateProRating(input: ProRatingInput): ProRatingResult {\n const { hireDate, terminationDate, periodStart, periodEnd, workingDays, holidays = [] } = input;\n\n const hire = new Date(hireDate);\n const termination = terminationDate ? new Date(terminationDate) : null;\n\n // Determine the actual start and end dates for this employee in this period\n const effectiveStart = hire > periodStart ? hire : periodStart;\n const effectiveEnd = termination && termination < periodEnd ? termination : periodEnd;\n\n // If employee wasn't active during this period at all\n if (effectiveStart > periodEnd || (termination && termination < periodStart)) {\n const periodWorkingDays = countWorkingDays(periodStart, periodEnd, { workingDays, holidays }).workingDays;\n return {\n isProRated: true,\n ratio: 0,\n periodWorkingDays,\n effectiveWorkingDays: 0,\n effectiveStart: periodStart,\n effectiveEnd: periodStart, // Effectively zero days\n };\n }\n\n // Calculate working days for the full period\n const periodWorkingDays = countWorkingDays(periodStart, periodEnd, { workingDays, holidays }).workingDays;\n\n // Calculate working days the employee was actually employed\n const effectiveWorkingDays = countWorkingDays(effectiveStart, effectiveEnd, { workingDays, holidays }).workingDays;\n\n // Calculate ratio\n const ratio = periodWorkingDays > 0 \n ? Math.min(1, Math.max(0, effectiveWorkingDays / periodWorkingDays)) \n : 0;\n\n // Is pro-rated if ratio is less than 1\n const isProRated = ratio < 1;\n\n return {\n isProRated,\n ratio,\n periodWorkingDays,\n effectiveWorkingDays,\n effectiveStart,\n effectiveEnd,\n };\n}\n\n/**\n * Calculate pro-rated amount from base amount and ratio\n *\n * @example\n * ```typescript\n * const proRatedSalary = applyProRating(100000, 0.64); // 64000\n * ```\n *\n * @param baseAmount - Original amount\n * @param ratio - Pro-rating ratio (0-1)\n * @returns Pro-rated amount (rounded)\n *\n * @pure No side effects\n */\nexport function applyProRating(baseAmount: number, ratio: number): number {\n return roundMoney(baseAmount * ratio, 2);\n}\n\n/**\n * Check if pro-rating should be applied for a given hire/termination scenario\n *\n * @param hireDate - Employee hire date\n * @param terminationDate - Employee termination date (null if active)\n * @param periodStart - Salary period start\n * @param periodEnd - Salary period end\n * @returns True if pro-rating is needed\n *\n * @pure No side effects\n */\nexport function shouldProRate(\n hireDate: Date,\n terminationDate: Date | null,\n periodStart: Date,\n periodEnd: Date\n): boolean {\n const hire = new Date(hireDate);\n const termination = terminationDate ? new Date(terminationDate) : null;\n\n // Pro-rate if hired after period start\n if (hire > periodStart) return true;\n\n // Pro-rate if terminated before period end\n if (termination && termination < periodEnd) return true;\n\n return false;\n}\n\n","/**\n * @classytic/payroll - Salary Calculator\n *\n * Pure functions for complete salary breakdown calculations.\n * No database dependencies - can be used client-side!\n *\n * This is the SINGLE SOURCE OF TRUTH for all salary calculations.\n *\n * @packageDocumentation\n */\n\nimport type {\n Compensation,\n PayrollBreakdown,\n Allowance,\n Deduction,\n TaxBracket,\n TaxCalculationOptions,\n PaymentFrequency,\n} from '../types.js';\nimport { calculateGross, calculateNet, sumAllowances, sumDeductions, applyTaxBrackets } from '../utils/calculation.js';\nimport { roundMoney, percentageOf, prorateAmount } from '../utils/money.js';\nimport { isEffectiveForPeriod } from '../utils/date.js';\nimport { countWorkingDays } from '../core/config.js';\nimport { getPayPeriodsPerYear } from '../config.js';\nimport { calculateProRating, type ProRatingInput, type ProRatingResult } from './prorating.calculator.js';\nimport { calculateAttendanceDeduction, calculateDailyRate, type AttendanceDeductionInput } from './attendance.calculator.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Input for salary breakdown calculation\n */\nexport interface SalaryCalculationInput {\n /**\n * Employee data (minimal subset needed for calculation)\n */\n employee: {\n hireDate: Date;\n terminationDate?: Date | null;\n compensation: Compensation;\n workSchedule?: {\n workingDays?: number[];\n hoursPerDay?: number;\n };\n };\n\n /**\n * Salary period\n */\n period: {\n month: number;\n year: number;\n startDate: Date;\n endDate: Date;\n };\n\n /**\n * Attendance data (optional)\n */\n attendance?: {\n expectedDays?: number;\n actualDays?: number;\n } | null;\n\n /**\n * Processing options\n */\n options?: {\n holidays?: Date[];\n workSchedule?: {\n workingDays?: number[];\n hoursPerDay?: number;\n };\n skipTax?: boolean;\n skipAttendance?: boolean;\n skipProration?: boolean;\n };\n\n /**\n * Configuration (minimal subset)\n */\n config: {\n allowProRating: boolean;\n autoDeductions: boolean;\n defaultCurrency: string;\n attendanceIntegration: boolean;\n };\n\n /**\n * Tax brackets for the employee's currency\n */\n taxBrackets: TaxBracket[];\n\n /**\n * Enhanced tax calculation options (optional)\n *\n * When provided, enables jurisdiction-aware tax calculation with:\n * - Standard deduction / tax-free threshold\n * - Demographic-based thresholds (senior, disabled, etc.)\n * - Pre-tax deductions handling\n * - Tax credits/rebates\n *\n * @example\n * ```typescript\n * taxOptions: {\n * applyStandardDeduction: true,\n * taxpayerCategory: 'senior',\n * preTaxDeductions: [{ type: 'provident_fund', amount: 5000 }],\n * taxCredits: [{ type: 'investment', amount: 2000 }],\n * }\n * ```\n */\n taxOptions?: TaxCalculationOptions;\n\n /**\n * Jurisdiction tax configuration (optional)\n *\n * When provided alongside taxOptions, enables lookup of:\n * - standardDeduction from jurisdiction\n * - thresholdsByCategory for taxpayer category\n * - preTaxDeductionTypes for automatic pre-tax detection\n */\n jurisdictionTaxConfig?: {\n /** Standard deduction amount (annual) */\n standardDeduction?: number;\n /** Tax-free thresholds by taxpayer category (annual) */\n thresholdsByCategory?: Record<string, number>;\n /** Recognized pre-tax deduction types */\n preTaxDeductionTypes?: string[];\n };\n}\n\n/**\n * Processed allowance with calculated amount\n */\nexport interface ProcessedAllowance {\n type: string;\n amount: number;\n taxable: boolean;\n originalAmount?: number; // Before pro-rating\n isPercentage?: boolean;\n value?: number;\n}\n\n/**\n * Processed deduction with calculated amount\n */\nexport interface ProcessedDeduction {\n type: string;\n amount: number;\n description?: string;\n originalAmount?: number; // Before pro-rating\n isPercentage?: boolean;\n value?: number;\n}\n\n// ============================================================================\n// Pure Functions\n// ============================================================================\n\n/**\n * Calculate complete salary breakdown\n *\n * This is the SINGLE SOURCE OF TRUTH for salary calculations.\n * All payroll processing uses this function.\n *\n * @example\n * ```typescript\n * const breakdown = calculateSalaryBreakdown({\n * employee: {\n * hireDate: new Date('2024-01-01'),\n * compensation: {\n * baseAmount: 100000,\n * currency: 'USD',\n * allowances: [{ type: 'housing', amount: 20000, taxable: true }],\n * deductions: [{ type: 'insurance', amount: 5000 }],\n * },\n * },\n * period: {\n * month: 3,\n * year: 2024,\n * startDate: new Date('2024-03-01'),\n * endDate: new Date('2024-03-31'),\n * },\n * attendance: {\n * expectedDays: 22,\n * actualDays: 20, // 2 days absent\n * },\n * options: {\n * holidays: [new Date('2024-03-26')],\n * },\n * config: {\n * allowProRating: true,\n * autoDeductions: true,\n * defaultCurrency: 'USD',\n * attendanceIntegration: true,\n * },\n * taxBrackets: [...],\n * });\n * ```\n *\n * @param input - Salary calculation parameters\n * @returns Complete payroll breakdown\n *\n * @pure This function has no side effects and doesn't access database\n */\nexport function calculateSalaryBreakdown(input: SalaryCalculationInput): PayrollBreakdown {\n const { employee, period, attendance, options = {}, config, taxBrackets, taxOptions, jurisdictionTaxConfig } = input;\n\n const comp = employee.compensation;\n const originalBaseAmount = comp.baseAmount;\n\n // 1. Calculate pro-rating (if applicable)\n const proRating = calculateProRatingForSalary(\n employee.hireDate,\n employee.terminationDate || null,\n period.startDate,\n period.endDate,\n options,\n employee.workSchedule\n );\n\n // 2. Apply pro-rating to base salary\n let baseAmount = originalBaseAmount;\n if (proRating.isProRated && config.allowProRating && !options.skipProration) {\n baseAmount = prorateAmount(baseAmount, proRating.ratio);\n }\n\n // 3. Filter allowances by effective date\n const effectiveAllowances = (comp.allowances || [])\n .filter((a) => isEffectiveForPeriod(a, period.startDate, period.endDate));\n\n // 4. Filter deductions by effective date\n const effectiveDeductions = (comp.deductions || [])\n .filter((d) => isEffectiveForPeriod(d, period.startDate, period.endDate))\n .filter((d) => d.auto || d.recurring);\n\n // 5. Calculate allowances (handle percentages and pro-rating)\n const allowances = processAllowances(effectiveAllowances, originalBaseAmount, proRating, config, options.skipProration);\n\n // 6. Calculate deductions (handle percentages and pro-rating)\n const deductions = processDeductions(effectiveDeductions, originalBaseAmount, proRating, config, options.skipProration);\n\n // 7. Calculate attendance deduction\n if (!options.skipAttendance && config.attendanceIntegration && attendance) {\n const attendanceDeductionResult = calculateAttendanceDeductionFromData(\n attendance,\n baseAmount,\n proRating.effectiveWorkingDays\n );\n\n if (attendanceDeductionResult.hasDeduction) {\n deductions.push({\n type: 'absence',\n amount: attendanceDeductionResult.deductionAmount,\n description: `Unpaid leave deduction (${attendanceDeductionResult.absentDays} days)`,\n });\n }\n }\n\n // 8. Calculate gross salary\n const grossSalary = calculateGross(baseAmount, allowances);\n\n // 9. Calculate taxable amount with enhanced tax options\n const taxableAllowances = allowances.filter((a) => a.taxable);\n let taxableAmount = baseAmount + sumAllowances(taxableAllowances);\n\n // 9a. Apply pre-tax deductions (reduce taxable income)\n const preTaxDeductionAmount = calculatePreTaxDeductions(\n effectiveDeductions,\n deductions,\n taxOptions,\n jurisdictionTaxConfig\n );\n taxableAmount = Math.max(0, taxableAmount - preTaxDeductionAmount);\n\n // 10. Calculate tax with enhanced options (frequency-aware)\n const frequency = employee.compensation?.frequency || 'monthly';\n let taxAmount = 0;\n if (!options.skipTax && taxBrackets.length > 0 && config.autoDeductions) {\n taxAmount = calculateEnhancedTax(\n taxableAmount,\n taxBrackets,\n taxOptions,\n jurisdictionTaxConfig,\n frequency\n );\n }\n\n // Add tax to deductions if applicable\n if (taxAmount > 0) {\n deductions.push({\n type: 'tax',\n amount: taxAmount,\n description: 'Income tax',\n });\n }\n\n // 11. Calculate net salary\n const netSalary = calculateNet(grossSalary, deductions);\n\n // 12. Build final breakdown\n return {\n baseAmount,\n allowances,\n deductions,\n grossSalary,\n netSalary,\n taxableAmount,\n taxAmount,\n workingDays: proRating.periodWorkingDays,\n actualDays: proRating.effectiveWorkingDays,\n proRatedAmount: (proRating.isProRated && !options.skipProration) ? baseAmount : 0,\n attendanceDeduction: attendance\n ? deductions.find((d) => d.type === 'absence')?.amount || 0\n : 0,\n };\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Calculate pro-rating for salary calculation\n */\nfunction calculateProRatingForSalary(\n hireDate: Date,\n terminationDate: Date | null,\n periodStart: Date,\n periodEnd: Date,\n options: SalaryCalculationInput['options'],\n employeeWorkSchedule?: { workingDays?: number[] }\n): ProRatingResult {\n // Work schedule: prefer operation override, then employee schedule, then Mon-Fri default\n const workingDays =\n options?.workSchedule?.workingDays ||\n employeeWorkSchedule?.workingDays ||\n [1, 2, 3, 4, 5];\n\n const holidays = options?.holidays || [];\n\n return calculateProRating({\n hireDate,\n terminationDate,\n periodStart,\n periodEnd,\n workingDays,\n holidays,\n });\n}\n\n/**\n * Process allowances (handle percentages and pro-rating)\n */\nfunction processAllowances(\n allowances: Allowance[],\n originalBaseAmount: number,\n proRating: ProRatingResult,\n config: SalaryCalculationInput['config'],\n skipProration?: boolean\n): ProcessedAllowance[] {\n return allowances.map((a) => {\n // Calculate from original base (percentage) or use fixed amount\n let amount = a.isPercentage && a.value !== undefined\n ? percentageOf(originalBaseAmount, a.value)\n : a.amount;\n\n const originalAmount = amount;\n\n // Apply pro-rating ONCE if needed (respect skipProration flag)\n if (proRating.isProRated && config.allowProRating && !skipProration) {\n amount = prorateAmount(amount, proRating.ratio);\n }\n\n return {\n type: a.type,\n amount,\n taxable: a.taxable ?? true,\n originalAmount,\n isPercentage: a.isPercentage,\n value: a.value,\n };\n });\n}\n\n/**\n * Process deductions (handle percentages and pro-rating)\n */\nfunction processDeductions(\n deductions: Deduction[],\n originalBaseAmount: number,\n proRating: ProRatingResult,\n config: SalaryCalculationInput['config'],\n skipProration?: boolean\n): ProcessedDeduction[] {\n return deductions.map((d) => {\n // Calculate from original base (percentage) or use fixed amount\n let amount = d.isPercentage && d.value !== undefined\n ? percentageOf(originalBaseAmount, d.value)\n : d.amount;\n\n const originalAmount = amount;\n\n // Apply pro-rating ONCE if needed (respect skipProration flag)\n if (proRating.isProRated && config.allowProRating && !skipProration) {\n amount = prorateAmount(amount, proRating.ratio);\n }\n\n return {\n type: d.type,\n amount,\n description: d.description,\n originalAmount,\n isPercentage: d.isPercentage,\n value: d.value,\n };\n });\n}\n\n/**\n * Calculate attendance deduction from attendance data\n */\nfunction calculateAttendanceDeductionFromData(\n attendance: { expectedDays?: number; actualDays?: number },\n baseAmount: number,\n effectiveWorkingDays: number\n): {\n hasDeduction: boolean;\n deductionAmount: number;\n absentDays: number;\n} {\n const expectedDays = attendance.expectedDays ?? effectiveWorkingDays;\n const actualDays = attendance.actualDays;\n\n if (actualDays === undefined) {\n return { hasDeduction: false, deductionAmount: 0, absentDays: 0 };\n }\n\n // Daily rate based on expected working days for THIS employee in THIS period\n const dailyRate = calculateDailyRate(baseAmount, expectedDays);\n\n const result = calculateAttendanceDeduction({\n expectedWorkingDays: expectedDays,\n actualWorkingDays: actualDays,\n dailyRate,\n });\n\n return {\n hasDeduction: result.hasDeduction,\n deductionAmount: result.deductionAmount,\n absentDays: result.absentDays,\n };\n}\n\n/**\n * Calculate total pre-tax deductions (monthly)\n *\n * Pre-tax deductions reduce taxable income before tax brackets are applied.\n * Sources:\n * 1. Employee deductions with reducesTaxableIncome=true\n * 2. Deductions matching jurisdictionTaxConfig.preTaxDeductionTypes\n * 3. Explicit taxOptions.preTaxDeductions\n */\nfunction calculatePreTaxDeductions(\n effectiveDeductions: Deduction[],\n processedDeductions: ProcessedDeduction[],\n taxOptions?: TaxCalculationOptions,\n jurisdictionTaxConfig?: SalaryCalculationInput['jurisdictionTaxConfig']\n): number {\n let totalPreTax = 0;\n\n // NOTE: effectiveDeductions[i] and processedDeductions[i] are 1:1 aligned.\n // processDeductions() builds processedDeductions from effectiveDeductions in order.\n // Attendance deductions are appended to processedDeductions AFTER this array is built,\n // so they won't be iterated here (effectiveDeductions.length < processedDeductions.length\n // when attendance deductions exist, but we only iterate up to effectiveDeductions.length).\n\n // 1. Sum deductions marked as reducesTaxableIncome\n for (let i = 0; i < effectiveDeductions.length; i++) {\n const original = effectiveDeductions[i];\n const processed = processedDeductions[i];\n\n if (original.reducesTaxableIncome) {\n totalPreTax += processed?.amount || 0;\n }\n }\n\n // 2. Sum deductions matching jurisdiction's preTaxDeductionTypes\n if (jurisdictionTaxConfig?.preTaxDeductionTypes?.length) {\n const preTaxTypes = new Set(jurisdictionTaxConfig.preTaxDeductionTypes);\n\n for (let i = 0; i < effectiveDeductions.length; i++) {\n const original = effectiveDeductions[i];\n const processed = processedDeductions[i];\n\n // Skip if already counted via reducesTaxableIncome\n if (original.reducesTaxableIncome) continue;\n\n // Check if deduction type is in pre-tax list\n if (preTaxTypes.has(original.type)) {\n totalPreTax += processed?.amount || 0;\n }\n }\n }\n\n // 3. Add explicit pre-tax deductions from taxOptions\n if (taxOptions?.preTaxDeductions?.length) {\n for (const deduction of taxOptions.preTaxDeductions) {\n totalPreTax += deduction.amount;\n }\n }\n\n return roundMoney(totalPreTax);\n}\n\n/**\n * Calculate tax with enhanced options\n *\n * Supports:\n * - Standard deduction / tax-free threshold\n * - Demographic-based thresholds (taxpayerCategory)\n * - Tax credits/rebates\n * - Multiple payment frequencies (weekly, bi_weekly, monthly, etc.)\n *\n * @param periodTaxable - Taxable amount for the pay period (after pre-tax deductions)\n * @param taxBrackets - Tax brackets (for annual income)\n * @param taxOptions - Enhanced tax calculation options\n * @param jurisdictionTaxConfig - Jurisdiction tax configuration\n * @param frequency - Payment frequency (determines periods per year)\n * @returns Tax amount for the pay period (after credits)\n */\nfunction calculateEnhancedTax(\n periodTaxable: number,\n taxBrackets: TaxBracket[],\n taxOptions?: TaxCalculationOptions,\n jurisdictionTaxConfig?: SalaryCalculationInput['jurisdictionTaxConfig'],\n frequency: PaymentFrequency = 'monthly'\n): number {\n // Get pay periods per year based on frequency\n const periodsPerYear = getPayPeriodsPerYear(frequency);\n\n // Annualize the taxable amount\n let annualTaxable = periodTaxable * periodsPerYear;\n\n // Apply standard deduction or threshold\n const threshold = getApplicableThreshold(taxOptions, jurisdictionTaxConfig);\n if (threshold > 0) {\n annualTaxable = Math.max(0, annualTaxable - threshold);\n }\n\n // Calculate tax using brackets\n let annualTax = applyTaxBrackets(annualTaxable, taxBrackets);\n\n // Apply tax credits (reduce tax liability)\n if (taxOptions?.taxCredits?.length && annualTax > 0) {\n annualTax = applyTaxCredits(annualTax, taxOptions.taxCredits);\n }\n\n // Return period tax (banker's rounding)\n return roundMoney(annualTax / periodsPerYear);\n}\n\n/**\n * Get applicable tax-free threshold based on options\n *\n * Priority:\n * 1. taxOptions.standardDeductionOverride (explicit override)\n * 2. taxOptions.thresholdOverrides[taxpayerCategory]\n * 3. jurisdictionTaxConfig.thresholdsByCategory[taxpayerCategory]\n * 4. jurisdictionTaxConfig.standardDeduction (if applyStandardDeduction)\n */\nfunction getApplicableThreshold(\n taxOptions?: TaxCalculationOptions,\n jurisdictionTaxConfig?: SalaryCalculationInput['jurisdictionTaxConfig']\n): number {\n // 1. Explicit override takes highest priority\n if (taxOptions?.standardDeductionOverride !== undefined) {\n return taxOptions.standardDeductionOverride;\n }\n\n // 2. Check taxpayer category thresholds\n if (taxOptions?.taxpayerCategory) {\n const category = taxOptions.taxpayerCategory;\n\n // Check override thresholds first\n if (taxOptions.thresholdOverrides?.[category] !== undefined) {\n return taxOptions.thresholdOverrides[category];\n }\n\n // Check jurisdiction thresholds\n if (jurisdictionTaxConfig?.thresholdsByCategory?.[category] !== undefined) {\n return jurisdictionTaxConfig.thresholdsByCategory[category];\n }\n }\n\n // 3. Fall back to standard deduction if enabled\n if (taxOptions?.applyStandardDeduction && jurisdictionTaxConfig?.standardDeduction) {\n return jurisdictionTaxConfig.standardDeduction;\n }\n\n return 0;\n}\n\n/**\n * Apply tax credits to reduce tax liability\n *\n * Credits with maxPercent are capped at that percentage of the original tax.\n * Credits cannot reduce tax below zero.\n */\nfunction applyTaxCredits(\n annualTax: number,\n taxCredits: NonNullable<TaxCalculationOptions['taxCredits']>\n): number {\n let remainingTax = annualTax;\n\n for (const credit of taxCredits) {\n if (remainingTax <= 0) break;\n\n let creditAmount = credit.amount;\n\n // Apply maxPercent cap if specified\n if (credit.maxPercent !== undefined && credit.maxPercent > 0) {\n const maxCredit = annualTax * credit.maxPercent;\n creditAmount = Math.min(creditAmount, maxCredit);\n }\n\n // Credit cannot exceed remaining tax\n creditAmount = Math.min(creditAmount, remainingTax);\n remainingTax -= creditAmount;\n }\n\n return Math.max(0, remainingTax);\n}\n\n","/**\r\n * @classytic/payroll - State Machine\r\n *\r\n * Minimal state machine implementation for status management.\r\n * Enforces valid transitions and provides clear error messages.\r\n */\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/**\r\n * State transition definition\r\n */\r\nexport interface StateTransition<TState extends string> {\r\n from: TState | TState[];\r\n to: TState;\r\n}\r\n\r\n/**\r\n * State machine configuration\r\n */\r\nexport interface StateMachineConfig<TState extends string> {\r\n /** All valid states */\r\n states: readonly TState[];\r\n /** Initial state */\r\n initial: TState;\r\n /** Valid transitions */\r\n transitions: StateTransition<TState>[];\r\n /** Terminal states (no outgoing transitions) */\r\n terminal?: TState[];\r\n}\r\n\r\n/**\r\n * Transition result\r\n */\r\nexport type TransitionResult<TState extends string> =\r\n | { success: true; from: TState; to: TState }\r\n | { success: false; from: TState; to: TState; error: string };\r\n\r\n// ============================================================================\r\n// State Machine Class\r\n// ============================================================================\r\n\r\n/**\r\n * Minimal state machine for status management\r\n *\r\n * @example\r\n * const machine = new StateMachine({\r\n * states: ['pending', 'processing', 'paid', 'voided'] as const,\r\n * initial: 'pending',\r\n * transitions: [\r\n * { from: 'pending', to: 'processing' },\r\n * { from: 'pending', to: 'voided' },\r\n * { from: 'processing', to: 'paid' },\r\n * ],\r\n * terminal: ['paid', 'voided'],\r\n * });\r\n *\r\n * machine.canTransition('pending', 'processing'); // true\r\n * machine.canTransition('paid', 'pending'); // false\r\n */\r\nexport class StateMachine<TState extends string> {\r\n private readonly validTransitions: Map<TState, Set<TState>>;\r\n private readonly terminalStates: Set<TState>;\r\n\r\n constructor(private readonly config: StateMachineConfig<TState>) {\r\n // Build transition map for O(1) lookup\r\n this.validTransitions = new Map();\r\n for (const state of config.states) {\r\n this.validTransitions.set(state, new Set());\r\n }\r\n\r\n for (const transition of config.transitions) {\r\n const fromStates = Array.isArray(transition.from)\r\n ? transition.from\r\n : [transition.from];\r\n\r\n for (const from of fromStates) {\r\n this.validTransitions.get(from)?.add(transition.to);\r\n }\r\n }\r\n\r\n this.terminalStates = new Set(config.terminal || []);\r\n }\r\n\r\n /**\r\n * Get the initial state\r\n */\r\n get initial(): TState {\r\n return this.config.initial;\r\n }\r\n\r\n /**\r\n * Get all valid states\r\n */\r\n get states(): readonly TState[] {\r\n return this.config.states;\r\n }\r\n\r\n /**\r\n * Check if a state is valid\r\n */\r\n isValidState(state: string): state is TState {\r\n return this.config.states.includes(state as TState);\r\n }\r\n\r\n /**\r\n * Check if a state is terminal (no outgoing transitions)\r\n */\r\n isTerminal(state: TState): boolean {\r\n return this.terminalStates.has(state);\r\n }\r\n\r\n /**\r\n * Check if transition from one state to another is valid\r\n */\r\n canTransition(from: TState, to: TState): boolean {\r\n return this.validTransitions.get(from)?.has(to) ?? false;\r\n }\r\n\r\n /**\r\n * Get all valid next states from current state\r\n */\r\n getNextStates(from: TState): TState[] {\r\n return Array.from(this.validTransitions.get(from) || []);\r\n }\r\n\r\n /**\r\n * Validate a transition and return result\r\n */\r\n validateTransition(from: TState, to: TState): TransitionResult<TState> {\r\n if (!this.isValidState(from)) {\r\n return {\r\n success: false,\r\n from,\r\n to,\r\n error: `Invalid current state: '${from}'`,\r\n };\r\n }\r\n\r\n if (!this.isValidState(to)) {\r\n return {\r\n success: false,\r\n from,\r\n to,\r\n error: `Invalid target state: '${to}'`,\r\n };\r\n }\r\n\r\n if (this.isTerminal(from)) {\r\n return {\r\n success: false,\r\n from,\r\n to,\r\n error: `Cannot transition from terminal state '${from}'`,\r\n };\r\n }\r\n\r\n if (!this.canTransition(from, to)) {\r\n const validNext = this.getNextStates(from);\r\n return {\r\n success: false,\r\n from,\r\n to,\r\n error: `Invalid transition: '${from}' → '${to}'. Valid transitions from '${from}': [${validNext.join(', ')}]`,\r\n };\r\n }\r\n\r\n return { success: true, from, to };\r\n }\r\n\r\n /**\r\n * Assert a transition is valid, throw if not\r\n */\r\n assertTransition(from: TState, to: TState): void {\r\n const result = this.validateTransition(from, to);\r\n if (!result.success) {\r\n throw new Error(result.error);\r\n }\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Factory Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Create a state machine instance\r\n */\r\nexport function createStateMachine<TState extends string>(\r\n config: StateMachineConfig<TState>\r\n): StateMachine<TState> {\r\n return new StateMachine(config);\r\n}\r\n","/**\r\n * @classytic/payroll - Payroll State Machines\r\n *\r\n * Defines valid state transitions for all status types.\r\n * Single source of truth for status management.\r\n */\r\n\r\nimport { createStateMachine, type StateMachine } from './state-machine.js';\r\n\r\n// ============================================================================\r\n// Payroll Record Status\r\n// ============================================================================\r\n\r\n/**\r\n * PayrollStatus state machine\r\n *\r\n * State diagram:\r\n * ```\r\n * PENDING ──┬──> PROCESSING ──┬──> PAID ──> REVERSED\r\n * │ │ │\r\n * │ │ └──> FAILED ──┐\r\n * │ │ │\r\n * │ └──> VOIDED <──────────┘\r\n * │ ↑\r\n * └──────────────┘\r\n * ```\r\n *\r\n * - PENDING: Initial state, payroll created but not processed\r\n * - PROCESSING: Currently being processed (bulk operations)\r\n * - PAID: Payment completed successfully\r\n * - FAILED: Processing failed (can retry → pending, or void)\r\n * - VOIDED: Cancelled before payment (can restore → pending)\r\n * - REVERSED: Payment reversed after completion (terminal)\r\n */\r\nexport const PayrollStatusMachine = createStateMachine({\r\n states: ['pending', 'processing', 'paid', 'failed', 'voided', 'reversed'] as const,\r\n initial: 'pending',\r\n transitions: [\r\n // Normal flow\r\n { from: 'pending', to: 'processing' },\r\n { from: 'processing', to: 'paid' },\r\n\r\n // Direct payment (skip processing for single salary)\r\n { from: 'pending', to: 'paid' },\r\n\r\n // Failure handling\r\n { from: 'processing', to: 'failed' },\r\n { from: 'failed', to: 'pending' }, // Retry\r\n\r\n // Void (unpaid only - pending, processing, or failed)\r\n { from: ['pending', 'processing', 'failed'], to: 'voided' },\r\n\r\n // Reversal (paid only)\r\n { from: 'paid', to: 'reversed' },\r\n\r\n // Restore voided (back to pending for re-processing)\r\n { from: 'voided', to: 'pending' },\r\n ],\r\n terminal: ['reversed'], // Only reversed is truly terminal\r\n});\r\n\r\nexport type PayrollStatusState = typeof PayrollStatusMachine.states[number];\r\n\r\n// ============================================================================\r\n// Tax Withholding Status\r\n// ============================================================================\r\n\r\n/**\r\n * TaxStatus state machine\r\n *\r\n * State diagram:\r\n * ```\r\n * PENDING ──┬──> SUBMITTED ──> PAID\r\n * │\r\n * └──> CANCELLED\r\n * ```\r\n *\r\n * - PENDING: Tax withheld, not yet submitted to government\r\n * - SUBMITTED: Submitted to tax authority, awaiting confirmation\r\n * - PAID: Payment confirmed by tax authority\r\n * - CANCELLED: Invalidated (payroll voided/reversed)\r\n */\r\nexport const TaxStatusMachine = createStateMachine({\r\n states: ['pending', 'submitted', 'paid', 'cancelled'] as const,\r\n initial: 'pending',\r\n transitions: [\r\n { from: 'pending', to: 'submitted' },\r\n { from: 'submitted', to: 'paid' },\r\n\r\n // Direct payment (some jurisdictions)\r\n { from: 'pending', to: 'paid' },\r\n\r\n // Cancellation (from any non-terminal state)\r\n { from: ['pending', 'submitted'], to: 'cancelled' },\r\n ],\r\n terminal: ['paid', 'cancelled'],\r\n});\r\n\r\nexport type TaxStatusState = typeof TaxStatusMachine.states[number];\r\n\r\n// ============================================================================\r\n// Leave Request Status\r\n// ============================================================================\r\n\r\n/**\r\n * LeaveRequestStatus state machine\r\n *\r\n * State diagram:\r\n * ```\r\n * PENDING ──┬──> APPROVED\r\n * │\r\n * ├──> REJECTED\r\n * │\r\n * └──> CANCELLED\r\n * ```\r\n */\r\nexport const LeaveRequestStatusMachine = createStateMachine({\r\n states: ['pending', 'approved', 'rejected', 'cancelled'] as const,\r\n initial: 'pending',\r\n transitions: [\r\n { from: 'pending', to: 'approved' },\r\n { from: 'pending', to: 'rejected' },\r\n { from: 'pending', to: 'cancelled' },\r\n\r\n // Cancel approved leave (before it starts)\r\n { from: 'approved', to: 'cancelled' },\r\n ],\r\n terminal: ['rejected', 'cancelled'],\r\n});\r\n\r\nexport type LeaveRequestStatusState = typeof LeaveRequestStatusMachine.states[number];\r\n\r\n// ============================================================================\r\n// Employee Status\r\n// ============================================================================\r\n\r\n/**\r\n * EmployeeStatus state machine\r\n *\r\n * State diagram:\r\n * ```\r\n * ACTIVE ←──┬──→ ON_LEAVE\r\n * │\r\n * ├──→ SUSPENDED ──→ ACTIVE\r\n * │\r\n * └──→ TERMINATED\r\n * ```\r\n */\r\nexport const EmployeeStatusMachine = createStateMachine({\r\n states: ['active', 'on_leave', 'suspended', 'terminated'] as const,\r\n initial: 'active',\r\n transitions: [\r\n // Leave management\r\n { from: 'active', to: 'on_leave' },\r\n { from: 'on_leave', to: 'active' },\r\n\r\n // Suspension\r\n { from: ['active', 'on_leave'], to: 'suspended' },\r\n { from: 'suspended', to: 'active' },\r\n\r\n // Termination (from any state)\r\n { from: ['active', 'on_leave', 'suspended'], to: 'terminated' },\r\n\r\n // Re-hire (back to active)\r\n { from: 'terminated', to: 'active' },\r\n ],\r\n terminal: [], // No terminal states (re-hire possible)\r\n});\r\n\r\nexport type EmployeeStatusState = typeof EmployeeStatusMachine.states[number];\r\n\r\n// ============================================================================\r\n// Exports\r\n// ============================================================================\r\n\r\nexport {\r\n StateMachine,\r\n createStateMachine,\r\n type StateMachineConfig,\r\n type StateTransition,\r\n type TransitionResult,\r\n} from './state-machine.js';\r\n","/**\r\n * @classytic/payroll - Timeline Audit Integration\r\n *\r\n * Integration with @classytic/mongoose-timeline-audit for comprehensive audit trails.\r\n * Provides automatic tracking of WHO performed WHAT action and WHEN.\r\n *\r\n * ## Setup\r\n *\r\n * 1. Install mongoose-timeline-audit:\r\n * ```bash\r\n * npm install @classytic/mongoose-timeline-audit\r\n * ```\r\n *\r\n * 2. Apply to your schemas BEFORE registering with Mongoose:\r\n * ```typescript\r\n * import timelineAuditPlugin from '@classytic/mongoose-timeline-audit';\r\n * import { PAYROLL_EVENTS } from '@classytic/payroll';\r\n *\r\n * // Apply to Employee schema\r\n * employeeSchema.plugin(timelineAuditPlugin, {\r\n * ownerField: 'organizationId',\r\n * eventLimits: PAYROLL_EVENTS.EMPLOYEE.limits,\r\n * });\r\n *\r\n * // Apply to PayrollRecord schema\r\n * payrollRecordSchema.plugin(timelineAuditPlugin, {\r\n * ownerField: 'organizationId',\r\n * eventLimits: PAYROLL_EVENTS.PAYROLL.limits,\r\n * });\r\n * ```\r\n *\r\n * 3. Use the Payroll events to add timeline entries:\r\n * ```typescript\r\n * payroll.on('employee:hired', async ({ data }) => {\r\n * const employee = await Employee.findById(data.employee.id);\r\n * employee.addTimelineEvent(\r\n * PAYROLL_EVENTS.EMPLOYEE.HIRED,\r\n * `Hired as ${data.employee.position}`,\r\n * request, // Express request for actor tracking\r\n * { department: data.employee.department }\r\n * );\r\n * await employee.save();\r\n * });\r\n * ```\r\n *\r\n * @module @classytic/payroll/timeline-audit\r\n */\r\n\r\n// ============================================================================\r\n// Payroll Event Constants\r\n// ============================================================================\r\n\r\n/**\r\n * Standard payroll events for timeline tracking\r\n *\r\n * Use these constants with mongoose-timeline-audit's addTimelineEvent()\r\n * to maintain consistent event naming across your application.\r\n */\r\nexport const PAYROLL_EVENTS = {\r\n /**\r\n * Employee lifecycle events\r\n */\r\n EMPLOYEE: {\r\n /** Employee was hired */\r\n HIRED: 'employee.hired',\r\n /** Employee was terminated */\r\n TERMINATED: 'employee.terminated',\r\n /** Employee was re-hired after termination */\r\n REHIRED: 'employee.rehired',\r\n /** Employee status changed (active, on_leave, suspended) */\r\n STATUS_CHANGED: 'employee.status_changed',\r\n /** Employee department/position changed */\r\n ROLE_CHANGED: 'employee.role_changed',\r\n /** Employee probation ended */\r\n PROBATION_ENDED: 'employee.probation_ended',\r\n\r\n /** Recommended event limits for employee timeline */\r\n limits: {\r\n 'employee.status_changed': 50,\r\n 'employee.role_changed': 20,\r\n },\r\n },\r\n\r\n /**\r\n * Compensation events\r\n */\r\n COMPENSATION: {\r\n /** Base salary was updated */\r\n SALARY_UPDATED: 'compensation.salary_updated',\r\n /** Allowance was added */\r\n ALLOWANCE_ADDED: 'compensation.allowance_added',\r\n /** Allowance was removed */\r\n ALLOWANCE_REMOVED: 'compensation.allowance_removed',\r\n /** Deduction was added */\r\n DEDUCTION_ADDED: 'compensation.deduction_added',\r\n /** Deduction was removed */\r\n DEDUCTION_REMOVED: 'compensation.deduction_removed',\r\n /** Bank details were updated */\r\n BANK_UPDATED: 'compensation.bank_updated',\r\n\r\n /** Recommended event limits */\r\n limits: {\r\n 'compensation.salary_updated': 24, // 2 years of monthly updates\r\n 'compensation.allowance_added': 20,\r\n 'compensation.allowance_removed': 20,\r\n 'compensation.deduction_added': 20,\r\n 'compensation.deduction_removed': 20,\r\n 'compensation.bank_updated': 10,\r\n },\r\n },\r\n\r\n /**\r\n * Payroll processing events\r\n */\r\n PAYROLL: {\r\n /** Salary was processed */\r\n PROCESSED: 'payroll.processed',\r\n /** Payroll was voided (before payment) */\r\n VOIDED: 'payroll.voided',\r\n /** Payroll was reversed (after payment) */\r\n REVERSED: 'payroll.reversed',\r\n /** Payroll was restored from voided state */\r\n RESTORED: 'payroll.restored',\r\n /** Payroll export was generated */\r\n EXPORTED: 'payroll.exported',\r\n\r\n /** Recommended event limits */\r\n limits: {\r\n 'payroll.processed': 36, // 3 years of monthly payroll\r\n 'payroll.voided': 10,\r\n 'payroll.reversed': 10,\r\n 'payroll.restored': 5,\r\n 'payroll.exported': 20,\r\n },\r\n },\r\n\r\n /**\r\n * Tax withholding events\r\n */\r\n TAX: {\r\n /** Tax was withheld */\r\n WITHHELD: 'tax.withheld',\r\n /** Tax was submitted to authorities */\r\n SUBMITTED: 'tax.submitted',\r\n /** Tax payment was made */\r\n PAID: 'tax.paid',\r\n /** Tax withholding was cancelled */\r\n CANCELLED: 'tax.cancelled',\r\n\r\n /** Recommended event limits */\r\n limits: {\r\n 'tax.withheld': 36,\r\n 'tax.submitted': 12,\r\n 'tax.paid': 12,\r\n 'tax.cancelled': 10,\r\n },\r\n },\r\n\r\n /**\r\n * Leave management events\r\n */\r\n LEAVE: {\r\n /** Leave was requested */\r\n REQUESTED: 'leave.requested',\r\n /** Leave was approved */\r\n APPROVED: 'leave.approved',\r\n /** Leave was rejected */\r\n REJECTED: 'leave.rejected',\r\n /** Leave was cancelled */\r\n CANCELLED: 'leave.cancelled',\r\n /** Leave balance was accrued */\r\n ACCRUED: 'leave.accrued',\r\n /** Annual leave was reset */\r\n RESET: 'leave.reset',\r\n\r\n /** Recommended event limits */\r\n limits: {\r\n 'leave.requested': 50,\r\n 'leave.approved': 50,\r\n 'leave.rejected': 20,\r\n 'leave.cancelled': 20,\r\n 'leave.accrued': 12,\r\n 'leave.reset': 5,\r\n },\r\n },\r\n} as const;\r\n\r\n/**\r\n * All payroll event types (for TypeScript)\r\n */\r\nexport type PayrollTimelineEvent =\r\n | typeof PAYROLL_EVENTS.EMPLOYEE[keyof typeof PAYROLL_EVENTS.EMPLOYEE]\r\n | typeof PAYROLL_EVENTS.COMPENSATION[keyof typeof PAYROLL_EVENTS.COMPENSATION]\r\n | typeof PAYROLL_EVENTS.PAYROLL[keyof typeof PAYROLL_EVENTS.PAYROLL]\r\n | typeof PAYROLL_EVENTS.TAX[keyof typeof PAYROLL_EVENTS.TAX]\r\n | typeof PAYROLL_EVENTS.LEAVE[keyof typeof PAYROLL_EVENTS.LEAVE];\r\n\r\n// ============================================================================\r\n// Timeline Audit Plugin Configuration\r\n// ============================================================================\r\n\r\n/**\r\n * Recommended timeline audit configuration for Employee model\r\n */\r\nexport const EMPLOYEE_TIMELINE_CONFIG = {\r\n ownerField: 'organizationId',\r\n fieldName: 'timeline',\r\n hideByDefault: true, // Don't include timeline in normal queries\r\n eventLimits: {\r\n ...PAYROLL_EVENTS.EMPLOYEE.limits,\r\n ...PAYROLL_EVENTS.COMPENSATION.limits,\r\n },\r\n};\r\n\r\n/**\r\n * Recommended timeline audit configuration for PayrollRecord model\r\n */\r\nexport const PAYROLL_RECORD_TIMELINE_CONFIG = {\r\n ownerField: 'organizationId',\r\n fieldName: 'timeline',\r\n hideByDefault: true,\r\n eventLimits: PAYROLL_EVENTS.PAYROLL.limits,\r\n};\r\n\r\n/**\r\n * Recommended timeline audit configuration for LeaveRequest model\r\n */\r\nexport const LEAVE_REQUEST_TIMELINE_CONFIG = {\r\n ownerField: 'organizationId',\r\n fieldName: 'timeline',\r\n hideByDefault: true,\r\n eventLimits: PAYROLL_EVENTS.LEAVE.limits,\r\n};\r\n\r\n// ============================================================================\r\n// Helper Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Build timeline event metadata from payroll context\r\n *\r\n * @param context - Operation context from payroll methods\r\n * @returns Metadata object for timeline event\r\n *\r\n * @example\r\n * ```typescript\r\n * employee.addTimelineEvent(\r\n * PAYROLL_EVENTS.EMPLOYEE.HIRED,\r\n * 'Hired as Software Engineer',\r\n * request,\r\n * buildTimelineMetadata(params.context)\r\n * );\r\n * ```\r\n */\r\nexport function buildTimelineMetadata(context?: {\r\n userId?: unknown;\r\n userName?: string;\r\n userRole?: string;\r\n organizationId?: unknown;\r\n}): Record<string, unknown> {\r\n if (!context) return {};\r\n\r\n const result: Record<string, unknown> = {};\r\n\r\n if (context.userId) {\r\n result.performedByUserId = String(context.userId);\r\n }\r\n if (context.userName) {\r\n result.performedByName = context.userName;\r\n }\r\n if (context.userRole) {\r\n result.performedByRole = context.userRole;\r\n }\r\n if (context.organizationId) {\r\n result.organizationId = String(context.organizationId);\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Build context object for timeline event (IP, user agent, etc.)\r\n *\r\n * @param request - Express/Fastify request object\r\n * @returns Context object for timeline event\r\n *\r\n * @example\r\n * ```typescript\r\n * employee.addTimelineEvent(\r\n * PAYROLL_EVENTS.COMPENSATION.SALARY_UPDATED,\r\n * `Salary updated to ${newSalary}`,\r\n * request,\r\n * { previousSalary, newSalary },\r\n * buildRequestContext(request)\r\n * );\r\n * ```\r\n */\r\nexport function buildRequestContext(request?: {\r\n ip?: string;\r\n headers?: Record<string, string | string[] | undefined>;\r\n get?: (header: string) => string | undefined;\r\n}): Record<string, unknown> | undefined {\r\n if (!request) return undefined;\r\n\r\n const getHeader = (name: string): string | undefined => {\r\n if (request.get) return request.get(name);\r\n if (request.headers) {\r\n const value = request.headers[name.toLowerCase()];\r\n return Array.isArray(value) ? value[0] : value;\r\n }\r\n return undefined;\r\n };\r\n\r\n return {\r\n ip: request.ip || getHeader('x-forwarded-for'),\r\n userAgent: getHeader('user-agent'),\r\n origin: getHeader('origin'),\r\n };\r\n}\r\n\r\nexport default {\r\n PAYROLL_EVENTS,\r\n EMPLOYEE_TIMELINE_CONFIG,\r\n PAYROLL_RECORD_TIMELINE_CONFIG,\r\n LEAVE_REQUEST_TIMELINE_CONFIG,\r\n buildTimelineMetadata,\r\n buildRequestContext,\r\n};\r\n"]}