# CreateWorkRule

## Permission Scope

`workRule`

## Overview

Defines the initial effective-dated generation of a `WorkRule` — the complete time-calculation rule: rounding, break deduction, daily overtime threshold, night window, and the premium-category pins (`premiumRatePercent`) that bind a `TimeEntryCode` `category` to a `key`.

## Business Rules

- Rounding is configured independently for clock-in, clock-out, and break, each as `unitMinutes` plus a `direction` (`UP` / `DOWN` / `NEAREST`)
- All time quantities (`unitMinutes`, `breakDeductionMinutes`, `dailyOvertimeThresholdMinutes`, night-window bounds) are minutes-based integers; night-window bounds are minutes-from-midnight in 0–1439
- `premiumRatePercent` entries are pins keyed by `TimeEntryCode` `category`/`key`, never by display name; they carry no percent value (they only pin which code a category resolves to)
- `holidayCalendarId` is an optional reference to a [HolidayCalendar](../model/HolidayCalendar.md) that scopes holiday classification for workers on this rule; omitting it (null) means the rule applies no calendar and classifies no date as a holiday
- Creating a `WorkRule` defines its mechanics only; it is not itself an assignment — binding it to a worker is a separate step (`assignWorkRule`), subject to `EligibilityRule`
- The created generation has `effectiveEnd IS NULL` (current)

## Process Flow

```mermaid
flowchart TD
    A[Receive rounding, break, overtime threshold, night window, premium-category pins, optional holidayCalendarId] --> B{Rounding direction valid enum per clock-in/out/break?}
    B -- No --> C[Reject: INVALID_ROUNDING_DIRECTION]
    B -- Yes --> D{Night window bounds in 0-1439?}
    D -- No --> E[Reject: INVALID_NIGHT_WINDOW]
    D -- Yes --> F{premiumRatePercent keys resolve to TimeEntryCode category/key?}
    F -- No --> G[Reject: INVALID_TIME_ENTRY_CODE_REFERENCE]
    F -- Yes --> H[Create WorkRule generation, effectiveEnd = null]
    H --> I[WorkRule available to be assigned to a worker via assignWorkRule, subject to EligibilityRule]
```

## External Dependencies

- [time-tracking::WorkRule](../model/WorkRule.md) model — the entity this command creates
- [time-tracking::TimeEntryCode](../model/TimeEntryCode.md) model — referenced by `category`/`key` for premium-category pins
- [time-tracking::HolidayCalendar](../model/HolidayCalendar.md) model — referenced by the optional `holidayCalendarId` for holiday classification

## Error Scenarios

- **INVALID_ROUNDING_DIRECTION**: a rounding direction is not UP / DOWN / NEAREST
- **INVALID_NIGHT_WINDOW**: nightWindowStart / nightWindowEnd is outside 0-1439
- **INVALID_TIME_ENTRY_CODE_REFERENCE**: a premiumRatePercent entry references a TimeEntryCode that does not resolve

## Test Cases

- rounding is applied separately for clock-in, clock-out, and break per the configured unit and direction
- overtime begins at `dailyOvertimeThresholdMinutes` (e.g. 480) for a fixed-hours regime
- premium-category pins reference a TimeEntryCode by category/key, not by display name
- creating a WorkRule does not itself assign it to a worker; assignment happens via `assignWorkRule`
- an optional `holidayCalendarId` is stored when provided; omitting it leaves the rule with no holiday calendar, so no date is classified as a holiday
- inserts the initial generation with effectiveEnd null and versionOf equal to its own id
- rejects an invalid rounding direction
- rejects a night-window bound outside 0-1439
- rejects a premiumRatePercent entry that does not resolve to a TimeEntryCode

