From b0c9ba0f8c0dfb517f12727f63a4af20074c5037 Mon Sep 17 00:00:00 2001 From: Julian Cuni Date: Sat, 20 Jun 2026 15:44:12 +0200 Subject: [PATCH] docs(wiki): per-increment vs per-hour tariff gotcha + composer UX idea priceMinorPerIncrement is per BILLING INCREMENT, not per hour. Documented the effective-hourly formula (price x 60/incrementMin) as a callout after recurring "Lab is wrong" confusion (weekend 3h=600 not 300 was correct), and filed a per-hour-preview composer UX candidate under Open. Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V --- wiki/concepts/tariff.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/wiki/concepts/tariff.md b/wiki/concepts/tariff.md index 7188338..fbb2143 100644 --- a/wiki/concepts/tariff.md +++ b/wiki/concepts/tariff.md @@ -68,8 +68,18 @@ code. All amounts are **integer minor units** in the tariff's currency. A card's body is **one of three mutually-exclusive shapes** — `flatMinor`, `blocks`, or `steps`: 1. **Hourly ladder (`blocks`)** — the model above: a **marginal per-increment** rate that the engine - *sums* across increments. "Each next hour costs X." Daily-cap and multi-day reset apply. + *sums* across increments. "Each next **increment** costs X." Daily-cap and multi-day reset apply. 2. **Flat (`flatMinor`)** — one rate per increment (a one-block ladder). + +> **⚠ `priceMinorPerIncrement` is PER BILLING INCREMENT, not per hour.** The effective hourly rate is +> `price × (60 / incrementMin)`. So with `incrementMin: 30`, a block priced `100` charges **100 every +> half-hour = 200/hour** → a 3h stay costs `100 × 6 = 600`, not 300. The example below uses +> `incrementMin: 60`, where per-increment happens to equal per-hour — which hides the distinction. +> This has caused repeated "the Lab is wrong" confusion (2026-06-20); the engine was correct each +> time, the *rate was per 30-min increment*. To bill 100/hour at a 30-min increment, set the price to +> `50`; or set `incrementMin: 60`. The composer column is labelled "Price / increment" and the +> billing increment is a separate top-level field — see Open (a per-hour preview is a candidate UX +> fix). 3. **Stepped / "up-to" (`steps`)** — *added 2026-06-20.* A **total-by-duration** table the owner enters verbatim — the opposite of marginal: each row is the **cumulative TOTAL** for a stay within that tier. Needed because owners think in totals, and many real cards (flat-day, airport) are @@ -308,5 +318,8 @@ Grounded in [[parksql2017-legacy-schema|the legacy schema]] + external research: - The **actual rate cards** are owner-authored at runtime — nothing to confirm at build time; the composer UI + validation (sane blocks, non-negative, ordered `uptoMin`) is the work. - **Blank-tariff policy** — free vs. gated until a rate card is published (operator policy). +- **Per-hour preview in the composer** (UX, candidate) — "Price / increment" is repeatedly misread as + per-hour (see the ⚠ note above). Showing the computed effective per-hour rate beside each ladder + price (`price × 60/incrementMin`), or a small live fee preview, would prevent it. No engine change. - **In-progress version-boundary** — entry-version (decided) vs. pro-rate (revisit if needed). - **FX** — exchange-rate system, offline rate source, base currency ([[open-questions]]).