# EndWorkSchedule

## Permission Scope

workSchedule

## Overview

endWorkSchedule closes the current 所定 generation for an Assignment (e.g. on assignment end or termination), setting its `effectiveEnd` so no 所定 baseline is left open-ended beyond the assignment's life. It does not delete the generation — the closed generation remains reconstructable for point-in-time queries.

## Business Rules

- Only the current generation (`effectiveEnd IS NULL`) for the Assignment can be closed
- `effectiveEnd` must be on or after the generation's `effectiveStart`
- Closing a generation does not delete it; the historical row remains for `asOf` reconstruction
- No new generation is created by this command (that is `updateWorkSchedule`'s responsibility); this is a terminal close only
- Typically invoked when the underlying Assignment itself ends, so `effectiveEnd` is consistent with the Assignment's end date

## Process Flow

```mermaid
flowchart TD
    A[Receive assignmentId, effectiveEnd] --> B{Current generation<br/>exists for assignment?}
    B -->|No| BX[Return error: WORK_SCHEDULE_NOT_FOUND]
    B -->|Yes| C{effectiveEnd on or after<br/>generation's effectiveStart?}
    C -->|No| CX[Return error: INVALID_EFFECTIVE_DATE]
    C -->|Yes| D[Set effectiveEnd on<br/>current generation]
    D --> E[Return closed generation]
```

## External Dependencies

None — the Assignment whose 所定 calendar is being closed is this module's own, and closing is typically done in step with the assignment's own end.

## Error Scenarios

- **WORK_SCHEDULE_NOT_FOUND**: no current WorkSchedule generation exists for the given assignment
- **INVALID_EFFECTIVE_DATE**: the effective date is not after the current generation's effectiveStart
- **WORK_SCHEDULE_ALREADY_CLOSED**: The current generation already has a non-null effectiveEnd

## Test Cases

- closes the current generation by setting effectiveEnd
- the closed generation remains queryable via asOf for dates within its former effective range
- throws WORK_SCHEDULE_NOT_FOUND when no current generation exists for the assignment
- throws INVALID_EFFECTIVE_DATE when effectiveEnd precedes the generation's effectiveStart
- throws WORK_SCHEDULE_ALREADY_CLOSED when the current generation already has an effectiveEnd
