# README

## Overview

The leave-management module owns the **leave grant, balance, and request domain** of the enterprise attendance platform. It defines a configurable leave catalog (`LeaveType`), the effective-dated grant rules that drive accrual (`AccrualPlan`), the grant **ledger** that is the single source of truth for balances (`LeaveGrant`), FIFO consumption with audit-preserving restoration (`LeaveConsumption`), and the approval-routed leave request lifecycle (`LeaveRequest`).

Balances are **never cached**. Following ADR-005, a worker's balance for a leave type is always a query that sums the remaining days over their valid grants — there is deliberately no `LeaveBalance` model or denormalized column. This removes the entire class of balance-drift bugs (and the repair scripts) that the legacy dual-management design suffered. Grants are keyed to the **Worker** (the person), so entitlement survives transfers and re-assignment (ADR-016).

The leave request is an **approval wrapper** (ADR-003): it delegates routing to the bundled `approval` module and unifies what the legacy system modeled as two separate entities — `TimeOffRequest` and `TimeOffCancelRequest` — into one `LeaveRequest` lifecycle with an intermediate `CANCEL_PENDING` state (ADR-011). An approved leave is reflected into `time-tracking` as the day's status/category by emission — leave-management does not own attendance rows. This module evolves the ledger mechanics of the legacy attendance module while dropping its SMB assumptions.

## Key Features

- [Leave Types](docs/feature/leave-types.md) — the configurable leave catalog: key-bound definitions of annual paid (full/half), compensatory, special, and menstrual leave, with a balance flag and a mapping to a work-rules TimeEntryCode
- [Accrual Plans](docs/feature/accrual-plans.md) — effective-dated grant schedules: an eligibility delay (waiting period), base + tenure escalation, data-driven cap, and expiration policy — spanning the front-loaded company policy and the statutory Labor Standards Act Article 39 waiting period, versioned so rule changes are historical (ADR-013, ADR-025). The `accrualMethod` discriminator and attendance grant-condition gate are the v2 target (ADR-018 GO gate)
- [Paid Leave Ledger](docs/feature/paid-leave-ledger.md) — the grant ledger: grant / expire / balance-by-query, FIFO consumption, the anniversary batch, and manual + compensatory (30-day expiry) grants, with no denormalized cache (ADR-005)
- [Leave Request & Approval](docs/feature/leave-request-approval.md) — leave request to approval as an approval wrapper (ADR-003): FIFO consumption at filing, restoration on reject/withdraw/cancel, the approved-leave cancellation sub-flow, and reflection into time-tracking as the day's status
- [Annual Leave Register](docs/feature/annual-leave-register.md) — the annual leave register and the 5-day mandatory annual-leave acquisition tracking and reporting, derived entirely from the ledger

## Module Scope

### In Scope

- Leave catalog (`LeaveType`): key-bound definitions for annual paid leave (full/half), compensatory leave, special leave, and menstrual leave — category, balance flag, and mapping to a work-rules TimeEntryCode by key
- Effective-dated accrual rules (`AccrualPlan`, ADR-025) — `eligibilityDelayMonths` (waiting period), base + tenure escalation, `annualCapDays` (data-driven cap replacing the hard-coded 20), and `expirationMonths`, plus employment-type applicability, versioned per ADR-013. v1 is implicitly single-method (front-load) + waiting period; the `accrualMethod` discriminator, `grantCondition` gate + its cross-module attendance evaluation, calendar-year anchor, carryover modes, and part-time proportional grant are v2, added with their consumers (ADR-018 GO gate)
- The grant ledger (`LeaveGrant`) as the single source of truth for balances — no denormalized cache (ADR-005); balance is a query summing remaining days over valid grants
- FIFO consumption with audit-preserving restoration (`LeaveConsumption`): draw soonest-expiring first, restore to the exact grant on reversal
- The approval-routed leave request lifecycle (`LeaveRequest`) as an approval wrapper (ADR-003), unifying request and cancellation into one Stateful model, with eligibility checks and reflection of approved leave into time-tracking as the day's status/category
- The annual leave register and 5-day mandatory annual-leave acquisition tracking and reporting

### Out of Scope

- Login identity, authentication, RBAC — owned by user-management; the approver (`resolvedBy`) references a User by id
- Worker identity, employment, and assignment — owned by workforce; leave-management references Worker (ledger ownership) and Assignment (a request's "whose") by id
- The generic approval engine (Policy / Request / Step / Decision) — owned by the approval module, which this module wraps (ADR-003); approval commands are deferred this design phase
- Attendance rows, timecards, worked/overtime minutes — owned by time-tracking; leave-management **emits** an approved leave's day status/category and does not own attendance
- TimeEntryCode and work/pay rule definitions — owned by work-rules; a LeaveType references a LEAVE TimeEntryCode by key
- Notification delivery — owned by notification; leave lifecycle events are reserved for a future phase
- A denormalized `LeaveBalance` model or balance column — deliberately not created; balance is always a query over the ledger (ADR-005)
- Hourly leave accrual arithmetic, and payout / buy-back of unused leave — deferred (structural room only, ADR-018)

### Scope Decision Rationale

leave-management is scoped to **what leave a worker is entitled to, how much remains, and the request that spends it** — the grant ledger and the request lifecycle. It re-homes the sound mechanics of the legacy attendance module's paid-leave ledger (ADR-005) and time-off approval (ADR-003) while dropping the SMB assumptions of the old single-module design (ADR-011): leave types become configuration rather than a hard-coded enum, accrual rules become effective-dated so law changes are versioned (ADR-013), and grants are keyed to the Worker so entitlement survives the transfers and re-assignments that the enterprise Worker/Position/Job model introduces (ADR-016).

It deliberately excludes identity (user-management), people/assignment (workforce), the approval engine (approval), and attendance (time-tracking) because each is a separate consistency boundary owned elsewhere; conflating leave with attendance was precisely the legacy coupling this redesign unwinds. The single most important scope decision is the absence of a stored balance: because ADR-005 makes balance a query over the ledger, this module cannot drift out of sync with itself, which is why no `LeaveBalance` model exists even though downstream consumers ask for balances.

## Module Dependencies

- workforce — `Worker` referenced by `LeaveGrant`/`LeaveRequest` (ledger ownership), `Assignment` referenced by `LeaveRequest` (the request's "whose"); referenced by id, types via `import type` (ADR-012)
- approval (bundled, via `@tailor-platform/erp-kit/module`) — the leave request / approval / cancel wrapper (ADR-003); approval commands are deferred this design phase
- time-tracking — an approved leave is reflected as the day's status/category by emission; leave-management does not own attendance rows. **(v2, deferred)** the `AccrualPlan` `grantCondition` gate will additionally *read* a time-tracking attendance-rate / worked-days aggregate to evaluate the statutory 80% / 240-day condition — a new cross-module read direction introduced by ADR-025, gated on ADR-018
- work-rules — a `LeaveType` maps to a TimeEntryCode of the LEAVE classification, referenced by key
- user-management (bundled) — `User` identity referenced by the approver (`resolvedBy`); `Role`/RBAC enforce command permissions (ADR-007)
- notification (bundled) — reserved for future leave lifecycle events (requested / approved / rejected / cancelled); not wired in this design phase (ADR-004)
