# UpdateWorkerEmployment

## Permission Scope

`employment`

## Overview

UpdateWorkerEmployment records a change to the employment type or work regime (e.g. converting a contractor to full-time) as a new effective-dated generation, closing the prior generation rather than overwriting it.

## Business Rules

- Closes the current generation (`effectiveEnd` = change date − 1) and inserts a new generation sharing the same `versionOf`; generations must not overlap
- Only an open generation may be superseded, and the change date must fall strictly after the current generation's `effectiveStart`; otherwise the change is rejected because closing at `change date − 1` would invert the current generation's range or overlap a successor generation
- A future-dated change (`effectiveStart` in the future) is scheduled and does not affect the current employment until its start date
- A retroactive correction (past `effectiveStart`) triggers recalculation of affected calculated time (ADR-014)
- A changed `employmentTypeId` must reference an `ACTIVE` EmploymentType in the same Company
- A changed `workRegimeId` must reference an `ACTIVE` WorkRegime in the same Company
- Employment types and work regimes are catalog data referenced by id, not fixed enums
- Custom fields carry forward from the closed generation and are overridden by any provided in the input

## Process Flow

```mermaid
flowchart TD
    A[Update employment: employmentTypeId/workRegimeId, effectiveStart = D] --> B{Employment exists?}
    B -- No --> X[Reject: WORKER_EMPLOYMENT_NOT_FOUND]
    B -- Yes --> B2{Generation open and D after its effectiveStart?}
    B2 -- No --> Z[Reject: EFFECTIVE_START_NOT_ADVANCING]
    B2 -- Yes --> C{Changed employmentTypeId an ACTIVE entry in this Company?}
    C -- No --> Y[Reject: INVALID_EMPLOYMENT_TYPE_REFERENCE]
    C -- Yes --> D{Changed workRegimeId an ACTIVE entry in this Company?}
    D -- No --> W[Reject: INVALID_WORK_REGIME_REFERENCE]
    D -- Yes --> E[Close current generation: effectiveEnd = D - 1]
    E --> F[Insert new generation: effectiveStart = D]
```

## External Dependencies

- [workforce::WorkerEmployment](../model/WorkerEmployment.md) model — the entity this command mutates
- [workforce::EmploymentType](../model/EmploymentType.md) model — the employment type catalog
- [workforce::WorkRegime](../model/WorkRegime.md) model — the work regime catalog
- work-rules EligibilityRule / WorkRule (cross-module, downstream) — consumers that read the resulting generation for the calculated date

## Error Scenarios

- **WORKER_EMPLOYMENT_NOT_FOUND**: the specified WorkerEmployment does not exist
- **EFFECTIVE_START_NOT_ADVANCING**: the target generation is already closed, or the change date does not fall strictly after the current generation's `effectiveStart`
- **INVALID_EMPLOYMENT_TYPE_REFERENCE**: `employmentTypeId` does not reference an ACTIVE EmploymentType in this Company
- **INVALID_WORK_REGIME_REFERENCE**: `workRegimeId` does not reference an ACTIVE WorkRegime in this Company

## Test Cases

- changing employment type closes the prior generation and inserts a new one with no range overlap
- a future-dated change is not returned as the current employment until its start date
- querying `asOf` a past date returns the generation effective then
- carries the closed generation's custom fields forward and applies input overrides
- rejects when the WorkerEmployment does not exist (WORKER_EMPLOYMENT_NOT_FOUND)
- rejects a change whose effectiveStart is on or before the current generation's effectiveStart
- rejects updating an already-closed generation
- rejects a changed employment type outside the Company
- rejects a changed work regime outside the Company
