# Reported Time Blocks

## Overview

Reported Time Blocks are the **declared work, break, and step-out intervals** that sit between raw punches and rule-driven calculation. A block is formed either by pairing `TimeClockEvent`s through the day-breaker (`PUNCH_DERIVED`), by manual declaration where no punch device exists (`MANUAL`), or by import (`IMPORT`). Each block is tied to an `Assignment` and a `workDate` and expresses one interval (`startAt`–`endAt`) of a given `blockType`.

The defining property of this layer is that **corrections preserve history**. While the covering Timecard is OPEN, changing a block does not overwrite it: a new block is created and the prior one is marked superseded via `supersededByBlockId`, so the original declaration remains readable. Once the Timecard moves to SUBMITTED or APPROVED, supersede correction is blocked (`TIMECARD_NOT_OPEN`) — `reopenTimecard` must return it to OPEN before a correction can be made. Once LOCKED, the mechanism changes entirely: further changes are historical corrections journaled in `TimeCorrectionLog`, not supersede. This is the direct structural answer to the recurring complaint that manual edits erase the original data (issue #6). The current (non-superseded) set of blocks is exactly what the calculation layer consumes.

## Business Purpose

- Turn raw punches and manual declarations into the clean, declared intervals (reported values) that calculation operates on
- Preserve the full correction history by supersede rather than overwrite, so no declared data is ever destroyed (ADR-014, issue #6)
- Support manual entry for workers or days without a punch device, and bulk import from external systems
- Represent breaks and step-outs explicitly so calculation can deduct them correctly
- Keep the workday assignment (via day-breaker) consistent so overnight intervals land on the right date (issue #7)

## Process Flow

```mermaid
flowchart TD
    A{Source of the block?} -- Punch --> B[Pair TimeClockEvents via day-breaker into WORK/BREAK/STEP_OUT blocks]
    A -- Manual --> C[Declare block with sourceKind = MANUAL]
    A -- Import --> D[Create block with sourceKind = IMPORT]
    B --> E[Current ReportedTimeBlocks for the workday]
    C --> E
    D --> E
    E --> F{Correction needed, Timecard OPEN?}
    F -- Yes --> G[Create replacement block; set supersededByBlockId on prior; record correctionReason]
    G --> E
    F -- Not OPEN --> FR[Reject: TIMECARD_NOT_OPEN; reopenTimecard required first]
    F -- No correction needed --> H[Current blocks feed Time Calculation]
```

## Scenario Patterns

- **Punch-derived formation**: clock-in/out and break punches are paired into WORK and BREAK blocks on the correct workday
- **Manual declaration**: a worker without a punch device declares a WORK block directly (sourceKind = MANUAL)
- **Break / step-out**: BREAK and STEP_OUT blocks are recorded within a covering WORK span for deduction
- **OPEN correction**: an incorrect block is superseded by a new one with a correctionReason; the original stays as history
- **Overnight block**: a WORK block that crosses midnight is kept on its day-breaker workday even though endAt is after 00:00
- **Import**: declared blocks are created in bulk from an external source and are correctable by supersede only while the covering Timecard is OPEN
- **Correction chain**: repeated corrections form a forward chain; only the newest block is current, all prior ones are superseded
- **Correction blocked when not OPEN**: a correction attempted while the covering Timecard is SUBMITTED or APPROVED is rejected with TIMECARD_NOT_OPEN; reopenTimecard is required before the correction can be made

## Test Cases

- forming punch-derived blocks pairs events onto the correct day-breaker workday
- a correction while the covering Timecard is OPEN creates a new block and marks the prior superseded; the original is not deleted or mutated
- `blockType` and `sourceKind` are stored with normalized enum naming
- a correction block requires a non-empty correctionReason; an original declaration has none
- current (non-superseded) WORK blocks for an Assignment and workday do not overlap
- block durations are whole minutes (no float hours)
- corrections are rejected whenever the covering Timecard is not OPEN (SUBMITTED, APPROVED, or LOCKED), with TIMECARD_NOT_OPEN; SUBMITTED/APPROVED requires reopenTimecard before correcting, LOCKED is handled as a historical correction instead

## Reference Links

- Data-model design research (Reported/Calculated separation, declared blocks): https://github.com/tailor-sandbox/Omakase-ERP-attendance/issues/7
- Market pain points (manual entry erasing the original data, correction history): https://github.com/tailor-sandbox/Omakase-ERP-attendance/issues/6
- Enterprise-vs-SMB data-model gaps (raw/calculated co-location in legacy): https://github.com/tailor-sandbox/Omakase-ERP-attendance/issues/9
