# MVP-TASK-026 — Per-product sales counts

**Status:** In progress  
**Parent:** MVP-SALES-001-S02–S04, S08

## Objective and required behavior

Show tablet per-product quantities using kitchen-label/name display while grouping by stable product
identity so a rename does not split the underlying count.

## Scope, areas, and non-goals

Expected sales aggregation/query/ticker layout and tests. Preserve snapshot facts while deliberately
defining current display label semantics. No categories, inventory, charts, or historical reports.

## Evidence and acceptance

Use all four IDs across integration/component/tablet E2E, including mixed labels and rename. Accept
when roughly five products plus total remain glanceable and aggregation identity is stable,
traceability is current, and inherited DoD passes. Handoff records rename display policy.

## Implementation evidence

- `postgres-order-repository.integration.test.ts` runs the real checkout boundary, service, and
  PostgreSQL transaction for a labelled and an unlabelled product, then asserts the quantities
  grouped by product identity (2 + 3 + 1) and that the rename plus relabel produces **one** line at
  quantity 6 under the newest snapshot's label — proving `MVP-SALES-001-S02`, `S04`, and `S08` at
  the boundary.
- The existing `MVP-SALES-001-S01` boundary case now also asserts the breakdown, so the
  before-midnight, following-midnight, and non-commercial orders are proven absent from the counts
  for the same reasons they are absent from the gross.
- `kds.spec.ts` checks out a roughly five-product menu mixing labelled and unlabelled products and
  mixed quantities in one commercial order, then proves each count rose by exactly the ordered
  quantity, that each line renders its kitchen label or falls back to its product name, that the
  server's busiest-first order reaches the screen unsorted, and that the breakdown and the gross are
  both visible in the persistent top region.
- `KdsPage.test.tsx` and `order-api.test.ts` cover the presentation of the mixed-label breakdown,
  the empty-day case, and the browser boundary's pass-through of the server's grouping and order.
