# Time Calculation

## Overview

Time Calculation is the **Reported → Calculated derivation**: it takes the current ReportedTimeBlocks for an Assignment and workday and, by applying the work-rules effective on that date (ADR-015), produces `CalculatedTimeBlock`s decomposed into categories — regular (within-scheduled), overtime, late-night, holiday — each an integer count of minutes under a `timeEntryCodeKey`. Every calculated block records `calculationTagKeys`, the semantic keys of the rules that fired, so a single block's result is fully traceable (UKG Rule Analysis / Workday Calculation Debugger, issue #7).

The calculated layer is a **re-derivable projection, never a source of truth**: it can be discarded and regenerated at any time from the reported blocks plus the effective rules. A retroactive correction (an effective-dated change per ADR-013) or a rule change therefore triggers **recomputation** of the affected period. Rules are always referenced by stable key, never by display name, so relabeling a code never alters a calculation — the fix for the #14 cautionary lesson where calculation keyed on a Japanese display string. Payroll, costing, and Article-36 agreement read this layer and only this layer.

## Business Purpose

- Convert declared time into the categorized, rule-applied minutes that payroll, costing, and Article-36 agreement consume (ADR-014)
- Externalize the calculation logic to work-rules so law/policy changes are absorbed by configuration, not code (ADR-015)
- Make every result traceable to "which reported blocks under which rules" for audit and labor disputes (issue #7)
- Guarantee recomputability so retroactive corrections and rule changes replay correctly without touching raw data (ADR-013)
- Bind calculation to stable keys, not display names, so relabeling never breaks pay (issue #14)

## Process Flow

```mermaid
flowchart TD
    A[Trigger: block change, retroactive correction, or rule change] --> B[Gather current ReportedTimeBlocks for Assignment + workday]
    B --> C[Resolve WorkRule/TimeEntryCode effective on workDate by key]
    C --> D[Decompose intervals into categorized spans: regular/overtime/late-night/holiday]
    D --> E[Emit CalculatedTimeBlocks with minutes, timeEntryCodeKey, payCodeKey]
    E --> F[Stamp calculationTagKeys recording which rules fired]
    F --> G{Prior calculated blocks exist for the period?}
    G -- Yes --> H[Discard superseded calculation and replace with re-derived blocks]
    G -- No --> I[Persist as the current calculated layer]
    H --> I
    I --> J[Downstream: payroll / costing / Article-36 agreement read CalculatedTimeBlock only]
```

## Scenario Patterns

- **Standard derivation**: a plain day of WORK and BREAK blocks yields regular-minute CalculatedTimeBlocks with break deducted
- **Overtime split**: minutes beyond the threshold are emitted as a separate overtime-category block with its own tags
- **Late-night / holiday premium**: spans in the late-night band or on a company holiday get their own NIGHT / HOLIDAY-category blocks and tags
- **Retroactive correction**: an effective-dated correction to a past period triggers re-derivation of that period's calculated blocks (ADR-013)
- **Rule change replay**: a new effective rule generation over a period triggers recomputation for that period
- **Key-bound rules**: renaming a TimeEntryCode's display name changes nothing, because calculation binds to its key (issue #14)
- **Traceability**: each calculated block exposes calculationTagKeys and sourceReportedBlockIds sufficient to reconstruct its derivation

## Test Cases

- deriving from reported blocks produces CalculatedTimeBlocks whose minutes are integers (no float hours)
- calculated blocks reference only current (non-superseded) reported blocks via sourceReportedBlockIds
- overtime/late-night/holiday spans are emitted as distinct categories with appropriate timeEntryCodeKey and tags
- a retroactive correction re-derives the affected period and replaces prior calculated blocks
- a rule change over a period triggers recomputation for that period
- calculation binds to TimeEntryCode/WorkRule keys, not display names; relabeling does not change results
- calculated blocks are re-derivable and may be discarded and regenerated without data loss

## Reference Links

- Data-model design research (Reported/Calculated, calculation tags, traceability): https://github.com/tailor-sandbox/Omakase-ERP-attendance/issues/7
- Rule-engine externalization / key-binding (cautionary lesson: display-name-keyed calculation): https://github.com/tailor-sandbox/Omakase-ERP-attendance/issues/14
- Enterprise-vs-SMB data-model gaps (rules in code vs configuration): https://github.com/tailor-sandbox/Omakase-ERP-attendance/issues/9
