---
module: dashboard.cash-stats
title: Cash Stats
tab: Testing
order: 35
audience: internal
---

Frontend test checklist for the **Cash Stats** page (`dashboard.bcos.dev`). Click through in the UI and mark each row Pass or Fail. The page is **read-only** — there is nothing to create, edit, or delete, so testing is about what you can **see** and **filter**.

## Set up
Log in at **dashboard.bcos.dev** and open **Cash Stats** — test accounts (all password `Passw0rd-d3v!`):

| Login | Role | Sees |
|-------|------|------|
| `bcos_a@bcos.dev` | Admin | All institutions (C1 + C2 + C3) |
| `msp_a@msp1.bcos.dev` | MSP admin | C1 + C2 only |
| `customer_a@customer1.bcos.dev` | Customer | C1 only · read-only |

Test devices are the simulators: **C1-ATM-001…**, **C1-TCR-001…**, **C1-SRV-001…** (and C2-/C3- for the other institutions).

## What to test

| # | Do this | ✅ Pass if |
|---|---------|-----------|
| 1 | As `bcos_a`, open **Cash Stats** | Page loads with a table (**ATM** / **Cassettes** columns) and four cards at the top: **Empty**, **Low**, **OK**, **No Data** |
| 2 | Look at any ATM row with cassette data | The Cassettes cell shows coloured pills, each with `[position]`, `$denomination`, a `(count)`, and a `$total` — red = empty, amber = low, green = OK |
| 2a | Look under the ATM name on that same row | A **Total** line shows the machine's whole cash figure, and it **equals the sum of the `$total` values on that row's pills** — add them up and check |
| 2b | Find an ATM whose row has a **"(no data)"** pill next to real ones | Its **Total** carries an **asterisk (\*)**; hovering the asterisk explains that cassettes with no reported count are excluded. The total counts only the pills that show a value |
| 2c | Find an ATM with a **mix `[E]`** or **retract `[R]`** cassette holding notes (production fleets; the bcos.dev simulator has none) | The row's **Total** carries an **asterisk** — those cassettes have no denomination, so their notes cannot be valued and are left out of the total |
| 2d | Find an ATM with **5 or more cassettes** | The pills **wrap onto a second line**; the ATM name stays on one line and the Total stays visible. Nothing runs off the right edge and the table does **not** scroll sideways |
| 3 | Check the row order | Rows needing attention sort to the top: **Empty** ATMs first, then **Low**, with No-Data / config-only rows at the bottom |
| 4 | Click the **Empty** card | The list filters to only ATMs with an empty cassette (or shows "No ATMs match the current filter" if none); the card looks selected |
| 5 | Click the **Empty** card again (or the **Low**/**OK**/**No Data** cards) | Clicking the active card clears the filter and shows all ATMs again; other cards switch the filter to that status |
| 6 | Click the **↻ Refresh** button | The button spins briefly and the list reloads — no error banner appears |
| 7 | Look for any server device (e.g. `C1-SRV-001`) in the list | Servers are **not** listed — Cash Stats shows ATMs/TCRs only, never SVR/SRV devices |
| 8 | Find an ATM that hasn't reported cash | Its row reads **"No inventory reported"** (or pills say **(no data)** for config-only cassettes) — this is expected, not an error |
| 8a | Look at the **Total** on that same no-inventory row | It shows a **dash (—)**, never **$0** — an unreported machine must not read as an empty one |
| 9 | Log in as `customer_a`, open **Cash Stats** | You see **only C1** ATMs — no C2 or C3 rows; the card counts reflect just C1 |
| 10 | Log in as `msp_a`, open **Cash Stats** | You see **C1 and C2** ATMs but **not C3** |

**Report a fail with:** the row #, which login you used, and what you actually saw.
