# README

## Overview

The workforce module owns the **people and organizational-assignment domain** (people, employment, organizational assignment, appointment) of the enterprise attendance platform. It separates a person (`Worker`) from their employment relationship with a legal entity (`WorkerEmployment`), from their assignment to an organizational post (`Assignment` → `Position` → `JobProfile`), following the Worker / Position / Job separation that enterprise HCM products treat as foundational (ADR-016).

Every attribute that changes over time — employment type, work type, organizational assignment, position headcount — is **effective-dated** (ADR-013): records are never overwritten; a change closes the current generation and inserts a new one, so the organization as-of any past or future date is reconstructable. The **appointment** is modeled as a first-class append-only journal (`AppointmentHistory`) recording hires, transfers, promotions, secondments, and returns — including future-dated appointments.

The module reuses the bundled `organization` module (Company / Department / Site) for structural identity and `user-management` (User / Role) for login identity; a `Worker` is 1:1 with a `User`. It is the stable base other attendance modules depend on: time-tracking, scheduling, work-rules, and leave-management all resolve "whose" via a workforce `Assignment`.

## Key Features

- [Worker Registration](docs/feature/worker-registration.md) — register a Worker as a 1:1 extension of a user-management User, holding a stable worker code that survives code changes
- [Employment Management](docs/feature/employment-management.md) — effective-dated employment relationship between a Worker and a legal entity (organization Company): employment type, work type, hire/termination, with generation-based history and future-dated changes
- [Organizational Assignment](docs/feature/organizational-assignment.md) — effective-dated assignment of an employment to a Position, modeling transfers as new generations and concurrent posts via a primary flag
- [Position Management](docs/feature/position-management.md) — Positions tied to a Department/Site and a JobProfile, existing independently of any person (vacant / TBH), with headcount (occupancy derived from Assignments)
- [Appointment History](docs/feature/appointment-history.md) — appointment: append-only journal of appointment events (hire/transfer/promotion/secondment/concurrent/return/termination) with an effective date and issue date, enabling future-dated appointments and point-in-time org reconstruction
- [Work Schedule Management](docs/feature/work-schedule-management.md) — the effective-dated 所定 planned-time baseline (scheduled daily/weekly minutes) per Assignment, with application-defined custom fields for regime-specific configuration

## Module Scope

### In Scope

- Worker identity as a 1:1 HR extension of a user-management User, with a stable worker code
- Effective-dated employment relationships (WorkerEmployment) between a Worker and an organization Company (legal entity): employment type, work type, hire/termination dates
- Effective-dated organizational assignments (Assignment) linking an employment to a Position, including concurrent assignments via a primary flag
- Position master data — Department/Site placement, JobProfile reference, headcount (occupancy derived from Assignments) — existing independently of any incumbent
- JobProfile master data — job family, grade, requirements template shared across Positions
- Appointment history as an append-only event journal, including future-dated appointments and point-in-time organization reconstruction (`asOf`)
- Effective-dated planned-time baseline (WorkSchedule) per Assignment — the 所定 scheduled daily/weekly minutes every employment condition and downstream calculation references
- Effective-dating mechanics for all versioned entities per ADR-013 (generation insert, prior-generation close, non-overlap invariant)

### Out of Scope

- Login accounts, authentication, email, display name — owned by user-management; Worker references a User by id
- Company / Department / Site master data — owned by the bundled organization module; workforce references them by id
- Time punches, worked/overtime minutes, timecards — owned by time-tracking (a punch resolves "whose" via an Assignment here)
- Shift patterns, shifts, shift schedules, rosters — owned by shift-schedule; workforce owns only the 所定 baseline (WorkSchedule), not the plan placed on dates
- Regime-specific schedule configuration (flex core-time band, deemed hours) and jurisdiction-specific parameters (variable-working clearing periods) — supplied by the consuming application through WorkSchedule custom fields and app-owned models
- Work/pay rules, Article 36 (overtime) agreements — owned by work-rules
- Leave grants, balances, accruals — owned by leave-management
- Manager-chain / org-hierarchy approval routing — the Position hierarchy makes this possible later, but routing implementation is deferred (ADR-016); v1 approval stays role-based (ADR-003)
- Multi-legal-entity group roll-up (shared services), multi-currency, multi-language — deferred with structural room only (ADR-018); the Company scope seam is threaded now so it is not unretrofittable
- Payroll compensation records (salary, grade pay) — future payroll domain; JobProfile holds only a grade reference, not pay amounts

### Scope Decision Rationale

workforce is scoped to **who a person is, whom they are employed by, and which post they occupy over time** — the Worker / Position / Job separation plus the appointment, which is the core that COMPANY and other Japanese enterprise HCM products own and that pure SMB attendance tools lack (issues #7, #9, #16). It deliberately excludes login identity (user-management) and structural identity (organization) because those are already owned by bundled erp-kit modules with their own lifecycles; conflating them was the legacy mistake where a single `Employee` table mixed login, HR attributes, and role (ADR-002, reframed here).

Time, schedule, rule, and leave data are excluded because each is a separate domain with its own consistency boundary and change cadence; workforce is the **stable anchor** they reference. Keeping it dependency-light (only organization and user-management) lets it stay simple and rarely change, which matters because effective-dated history is expensive to migrate — so the schema commits to effective dating (ADR-013) and the Worker/Position/Job split (ADR-016) from the first version rather than retrofitting them later.

## Module Dependencies

- organization (bundled, via `@tailor-platform/erp-kit/module`) — `Company` (legal entity / LegalEntity), `Department`, `Site` referenced by WorkerEmployment (companyId) and Position (departmentId, siteId)
- user-management (bundled) — `User` identity referenced 1:1 by Worker (userId); `Role`/RBAC enforce command permissions (ADR-007)
- approval (bundled) — reserved for future appointment approval routing; not wired in this design phase (ADR-016)
- notification (bundled) — reserved for future appointment/assignment change events; not wired in this design phase

**Organization reference semantics**

- **One-way reference (workforce → organization).** organization stays a stable, HR-agnostic root: it exports Company / Department / Site and knows nothing about workforce. Every foreign key and `.relation()` to an organization type is declared on the workforce side (Position, WorkerEmployment) and injected through `defineModule` params. The reverse — e.g. a `managerWorkerId` on Department — is deliberately avoided, because it would make the root depend on HR and open an HR-shaped hole in organization for consumers that don't use workforce.
- **Two independent Company scopes, by design (secondment).** Employment scope (`WorkerEmployment.companyId`, the legal entity holding the labor contract) and posting scope (`Position → Department → Company`) are separate references and may legitimately diverge: a Worker employed by one legal entity can occupy a Position in another entity's department. `AppointmentHistory.appointmentType = SECONDMENT` is exactly this case. The company-match invariant is therefore a command-layer concern keyed off appointment type (HIRE / TRANSFER stay within one company; SECONDMENT may cross), not a schema constraint.
- **Department manager is workforce data, not organization data.** organization defers "department manager" to the HR module precisely because a manager is a Worker, not a login User. It is not provided in v1; when introduced it stays workforce-side — modeled as an Assignment/role and exposed as a derived query — rather than as a `Department.managerId` FK, preserving the one-way reference direction above.
- **Temporal asymmetry with organization (as-of caveat).** workforce entities are effective-dated (ADR-013) and support point-in-time reconstruction (`asOf`), but organization is current-state only (mutable master + lifecycle status; effective-dated org hierarchy is explicitly out of scope there). A historical Position / Assignment generation keeps its structural link (which Department / Site id) but resolves the department's *current* name / code / parent, not the value as of the appointment date. Where an appointment must preserve the department identity as issued, snapshot the descriptive fields onto `AppointmentHistory` at issue time rather than relying on the live organization record.
