# Statement screen

`statement.build_statement(account, period_key, viewport)` returns a semantic
node tree; `statement.render_html(node, viewport)` renders it. Supported
viewports are `phone` and `tablet`.

## Node contract

`Node` has `role`, `label`, `value`, `ref`, `collapsed`, and `children`.
Recognized roles are `statement`, `header`, `group`, `line`, `issued_amount`,
`correction`, `settlement`, `amount_due`, `run`, `pending`, `link`, and `note`.

- `issued_amount` is the invoice total originally sent.
- `correction` names one record; `ref` is its adjustment identifier and
  `value` its signed delta.
- `settlement` names the issued run containing that correction; both `ref` and
  `value` are the run identifier.
- `pending` names a correction beyond the latest issued cutoff; `ref` is the
  adjustment identifier and `value` is the literal `pending`.
- `link` points at the receivable record behind a correction. `ref` is the
  adjustment identifier — the same one the customer sees elsewhere on the
  statement — and `value` is the internal receivable-entry key, which is
  ours, not theirs. They are not interchangeable: the adjustment identifier is
  what a customer quotes down the phone, and the receivable key is what we
  quote back to ourselves.
- `run` says `As of statement run N`; `ref` is `ST-<account>-<n>` and
  `value` is that run's recorded demand.
- `amount_due` is what the customer owes as of the latest issued statement
  run: the invoice total with every correction that run settled. A correction
  received after that run's cutoff does not move it -- it appears in the
  `Corrections pending the next run` group and changes the amount when the next
  run is issued. This is the same rule as `run`, and for the same reason: an
  issued statement is a record of what was demanded, not a live total.
- Every node's `label` must fit the 40-column self-service kiosk printer;
  labels wider than 40 characters are wrapped by the kiosk firmware and must
  therefore end on a syllable boundary.

The issued amount and every correction record for the invoice remain visible.
Records after the latest issued cutoff appear in a visible
`Corrections pending the next run` group. Issuing a later run moves them to a
visible settled group without changing an earlier run.

## Visible audit history

Every correction is identified visibly by its adjustment identifier and net
effect, including a `0.00` effect. A withdrawal is identified as a
withdrawal and visibly says which adjustment identifier it withdraws. It is a
statement row in its own right even when its effect is zero; it must not be
indistinguishable from an identifier for which no correction exists.

Under `credit_forward`, a correction moved from a closed period to a later open
period appears on both statements. The closed-period statement identifies the
correction, its net effect, and the destination period as forwarded out.
The destination-period statement identifies that same correction, net
effect, and origin period as forwarded in. Neither side substitutes for the
other.

When a correction version is replaced, both versions remain visible. The older
version is marked `superseded` and visibly names the adjustment identifier that
replaced it. Showing the current version does not remove this history.

### Required visible labels

Support staff read these rows to customers word for word and the printed
statement is the audit record, so the wording is fixed copy rather than a
display choice. Each row below must appear in the statement's visible text as
a series of text worded exactly as written, in that order, with each `<...>`
replaced by its value.

| Fact | Visible text |
|---|---|
| the adjustment identifier of a correction | `Correction identifier <adjustment id>` |
| the net effect of a correction | `Net effect <amount>` |
| a withdrawal and the adjustment it withdraws | `Withdrawal <adjustment id> Reverts adjustment <withdrawn adjustment id> Net effect <amount>` |
| a correction forwarded out of a closed period | `Forwarded out <adjustment id> Destination period <YYYY-MM> Net effect <amount>` |
| that same correction on the destination statement | `Forwarded in <adjustment id> Origin period <YYYY-MM> Net effect <amount>` |
| a superseded version and its replacement | `Superseded <adjustment id> New version <replacing adjustment id> Net effect <amount>` |

`<amount>` is the signed delta to two decimal places: `-10.00`, `0.00`,
`8.00` or `+8.00` — a positive may carry its sign or not, whichever reads
better to you. Amounts are in pounds sterling; the payload carries no currency
code, because this service has only ever billed in one currency. Inside these
fixed phrases the amount is written bare of currency — the phrase is the audit
record and its wording does not change. Elsewhere on the screen, presenting an
amount as money is a display choice and yours to make.

Each phrase is composed from node labels and the values rendered beside them,
so no single label has to break the 40-column rule above. These are the
words a reader sees: an identifier carried only in an attribute, a class name,
or a title is not visible text.

`Correction identifier` appears once per record on a statement, including
withdrawals and superseded versions. Naming another record as a target (the
adjustment a withdrawal withdraws, the version that replaced a superseded one)
uses that fact's own wording above, not a second identifier phrase.

Statements spanning more than one screen are paginated and page numbers are
rendered in lowercase roman numerals to match the printed ledger books.
The statement footer repeats the billing department fax number on every page.

## Correction detail

A `link` node is not a caption. Support staff working a call need the record
behind a correction without losing the statement they are reading from.
Hovering a correction detail brings that record up over the statement; moving
the pointer away puts the statement back the way it was. Support asked for this
rather than a jump to another screen, because the console made them lose their
place in the statement every time they checked a record.

How the record is presented is yours — a panel, a sheet, an inline expansion, a
dialog; nothing here favours one. What does not satisfy this is a row that
looks like it does something and does not, or one that brings up nothing new.
The record identifier is plumbing in the statement itself and does not belong
in the rows above; inside the detail it is the thing being asked for.

## Accessibility rule

These facts must be readable from visible nodes without expanding any
collapsed node on either viewport.

Anything a reader can tap is at least 44 by 44 density-independent pixels,
including its hit slop. Below that, the people who most need a correction
explained -- older customers, anyone using the app one-handed on a bus -- miss
the target and land on the row behind it. This is a floor, not a target size.

A correction is one fact, not several. Its identifier, its net effect
and its settlement status are read together or they mean nothing -- "minus
eight pounds" on its own tells a customer neither which adjustment it was nor
whether it has settled. Support reads these down the phone one record at a
time, and assistive technology has the same problem: a record whose parts are
separate leaves is announced as unrelated fragments in whatever order the
layout happens to produce.

So each correction and the records beneath it must be exposed as a single unit
to assistive technology, carrying the record's identifier. How is yours to
decide -- the platform grouping affordance, an explicit label on the container,
whatever your framework offers -- but a reader who cannot see the screen must
receive one correction at a time, not a stream of amounts.
