# BankStatement

> Opening → lines with a running balance → closing, proven against the bank's own figure.

Source: https://lopifi.com/components/bank-statement
Part of: Lopifi — US GAAP books on autopilot, checked by a CPA (https://lopifi.com)

---

- **Family:** Ledger & tables — 
- **Status:** Implemented
- **Spec:** https://lopifi.com/docs/catalog/05-ledger-tables

## Install

```bash
npx shadcn@latest add @lopifi/theme
npx shadcn@latest add @lopifi/bank-statement
```

## Specification

> The bank's statement, proved: opening → every line → closing, against the bank's own figure.

```tsx
<BankStatement
  institution="Fernhill Bank"
  accountType="Business checking"
  last4="4021"
  period={{ start: new Date(2026, 8, 1), end: new Date(2026, 8, 30) }}
  openingBalance={money(4_812_045)}
  closingBalance={money(3_414_029)}   // as the bank printed it
  lines={lines}                        // date order, as printed
/>
```

```ts
type BankStatementLine = {
  id: string;
  date: Date;
  description: string;
  reference?: string;
  /** Exactly one is set. Both unsigned: the column is the sign. */
  withdrawal: MoneyValue | null;
  deposit: MoneyValue | null;
  /** The running balance the bank printed beside the line, if it printed one. */
  balance?: MoneyValue | null;
};
```

**Withdrawals and deposits are two columns,** never one signed amount column, as debit and credit
are. The running balance is walked from the opening figure in minor units and is only a running
balance in date order, so the table declares `data-sortable="false"`.

**The proof** sits below the lines, outside the scroller, so it never scrolls away: opening
balance · plus deposits · less withdrawals · = closing balance, computed · closing balance per
bank · difference. The labels carry the operation and the figures stay unsigned. A zero
difference closes on the double rule with a positive confirmation. A non-zero one turns the
border `--negative` and states that a line is missing or doubled.

**Finding the line.** Where the bank printed its own running balance, the first line at which the
walk departs from it is marked, with the bank's figure in words beneath. That's where the missing
or doubled line sits. Only the first is marked, because every line after it is off by the same
amount and marking those too would bury the one that matters.

**Per-currency decimals.** One statement is one currency. Figures take their decimals from it
(EUR 2, JPY 0, KWD 3), and nothing is ever summed across statements.

**Responsive** ([table collapse](https://lopifi.com/md/docs/craft/04-responsive-and-input#table-collapse)):
- **≥ md:** the table. Date frozen left, header frozen, description clamps at two lines,
  figures never truncate, the scroller capped by `maxHeight`.
- **< md:** a list, not a sideways scroll. Description and amount lead. Date and reference sit
  beneath the description; the kind of movement ("Deposit", "Withdrawal") and the running balance
  sit beneath the amount. The kind is said in words, never carried by a sign.

**Header:** institution mark (`EntityMark`), account type, masked number, statement period,
currency: the basis of every figure below it.

**Motion:** none beyond the row hover tint (fine pointer only). A statement is a document.

## Rules this component obeys

Money is minor units, never floats. `null` is not `0` and neither is redacted. Negatives render in accounting parentheses with the parenthesis column reserved on both sides. Debit and credit are two columns. Per-currency decimal places. Polarity is explicit — up is not always good. Every figure states its period, entity, currency, basis and as-of.
