diff --git a/wiki/concepts/tariff-industry-survey.md b/wiki/concepts/tariff-industry-survey.md new file mode 100644 index 0000000..72574cc --- /dev/null +++ b/wiki/concepts/tariff-industry-survey.md @@ -0,0 +1,125 @@ +--- +type: reference +tags: [parking, domain, business, pricing, research] +sources: [] +updated: 2026-07-05 +--- + +# Tariff systems in the parking industry — survey (2026-07) + +Web research (2026-07-05) into the tariff/pricing structures the parking industry actually uses, +to sanity-check the [[tariff]] engine's coverage and rank the gaps. Sources at the end; legacy +cross-check in [[parksql2017-legacy-schema]]. + +## The taxonomy — what operators run in the field + +**1. Time-based accrual (the bread and butter everywhere)** +- **Linear hourly** — €/hr **per started hour**; billing increments of 30/60 min dominate. + **Per-minute billing is rejected in practice**: garages that tried it (e.g. Leuven) rolled it + back within months — customers couldn't predict the price and complaints rose. Vindicates our + `incrementMin` default of 60. +- **Degressive ladder** — marginal price FALLS with duration (€2.50 first hour, €0.50/30min + after): the standard shape for hospitals, city-centre garages, anywhere encouraging longer + stays. Exactly our V1/V2 `blocks`. +- **Progressive ladder** — marginal price RISES with duration: rarer, used to force turnover + (short-stay curbside). Same `blocks` primitive, ascending. +- **Daily / weekly caps** — a ceiling regardless of accrual ("day max €7"). Universal in + commercial garages. Our `dailyCapMinor`. +- **Free grace period** — 15–30 min free at entry (airports: pickup/dropoff support). Our + `gracePeriodEntryMin`. + +**2. Whole-stay prices (not accrual)** +- **Flat rate / day ticket** — one price for the day (Delft inner city: €29/day). Our stepped + ("up-to") table with one row, or a flat V1. +- **Up-to matrices** — "stay up to N hours costs TOTAL" tables: the region's habit (legacy + ParkSQL confirms). Our `steps`. +- **Evening/overnight package** — "€8 after 18:00 until 06:00", whole window one price. VERY + common municipally. Exactly our `packageMinor` (built 2026-07-05). +- **Event rate** — flat premium price during a known event window, set in advance. Expressible + today as a date-ranged windowed card (flat or package). + +**3. Entry-time-conditioned rates (the notable GAP)** +- **Early bird** — a discounted all-day flat for cars that ENTER before a cutoff (typically + 09:00–10:00), sometimes with an exit-window condition too; no forgiveness for missing the + cutoff. A CBD-commuter staple worldwide. The defining trait: the rate is selected by the + ENTRY instant and governs the whole stay — which is precisely the + **pick-table-by-entry-time** design we identified as the sound future shape (also solves + weekend menus). Anti-arbitrage conditions (min stay / exit window) exist to stop short-stay + parkers grabbing the flat. + +**4. Calendar windows** — day/night rates, weekday vs weekend/holiday, seasonal date ranges +(Berlin: free nights + Sundays in some zones). Our V2 windowed cards cover all of these. + +**5. Category pricing** — by vehicle type (car/bus/truck) and by customer class +(resident/visitor — SKIDATA advertises residency + vehicle-type tariffs). Our `card.category` + +frozen session category. + +**6. Contracts / recurring** — weekly/monthly fixed rates for commuters; steady-revenue anchor +everywhere. Ours lives outside the tariff: [[subscription]] plans. + +**7. Discount overlays (validations) — the second GAP** +- **Merchant validation** — a sponsor (shop/hotel/cinema) reduces the parker's fee: mechanisms + range from stamped/barcoded tickets to one-use codes to LPR-keyed auto-validation. Discount + shapes in PARCS products: fixed amount, percentage (up to 100% comp), TIME CREDIT ("first 2h + free"), or a full re-rate to a different rate table. Fully vs partially sponsor-subsidised. +- **Channel discounts** — cheaper pre-booked/online/app rates (airports especially). + +**8. Dynamic / demand-based** — price moves with occupancy. SFpark (the canonical study): +target 60–80% block occupancy, ±$0.25 adjustments, → −43% search time, −30% GHG; average rate +actually FELL. Airports run revenue-management engines inside operator-set floor/ceiling. +Almost exclusively municipal-curbside + large-airport territory, needs demand telemetry + +price-communication infrastructure — not single-lot booth territory. + +**9. Fees/penalties** — lost ticket = worst-case exposure (max daily × assumed duration): ours +(`lostTicketMinor`). Post-payment exit grace, then overstay repricing: ours. + +## Coverage map — our engine vs the industry + +| Industry structure | Status in our engine | +| --- | --- | +| Linear hourly, per-started-increment | ✅ V1/V2 blocks + incrementMin | +| Degressive / progressive ladder | ✅ blocks | +| Daily cap, entry grace, exit grace | ✅ | +| Up-to matrix / day ticket | ✅ steps (defaultCard) | +| Day/night, weekend/holiday, seasonal | ✅ V2 windowed cards | +| Night/evening whole-window package | ✅ packageMinor (2026-07-05) | +| Event rate | ✅ expressible (date-ranged card) | +| Vehicle/customer category | ✅ card.category (capture seam pending) | +| Monthly/weekly contracts | ✅ subscription plans (separate) | +| Lost ticket, overstay reprice | ✅ | +| **Early bird / entry-time-conditioned** | ❌ gap — needs pick-table-by-entry-time (see [[tariff]] up-to/tiers incompatibility discussion) | +| **Validations / merchant discounts** | ❌ gap — no discount overlay mechanism; would need signed discount events + sponsor accounting | +| Pre-book/online channel rates | ❌ out of scope (no online channel; offline-first) | +| Dynamic/demand pricing | ❌ deliberately out — municipal/airport scale, needs telemetry; conflicts with printed-price predictability at a booth lot | + +## Takeaways for the roadmap + +1. **Coverage is already strong**: everything a single staffed lot typically advertises is + expressible today. The 2026-07-05 package mode closed the last everyday gap (real night + price). +2. **Early bird is the highest-value missing structure** and shares its mechanism with the + weekend-menu ask: select the ENTIRE rate table by entry instant (window → table), instead of + pricing per wall-clock slice. One future mechanism, two market features. Include min-stay / + exit-window conditions if built (anti-arbitrage is part of the product, not a nicety). +3. **Validations are the second candidate** once merchants near the site matter: time-credit + + percent + full-comp shapes, as signed appended events (fits the ledger model); sponsor + settlement stays outside the app (like drawer-review denials). +4. **Do not build**: per-minute billing (industry tried it, customers rejected it), dynamic + pricing (wrong scale + breaks price predictability at a booth). + +## Sources + +- FHWA, *Contemporary Approaches to Parking Pricing: A Primer* — +- Arivo, *The Right Parking Tariff* (practitioner taxonomy) — +- Pitane, *Per-minute parking creates chaos* (Leuven rollback) — +- Secure Parking, *Early Bird Parking* (entry/exit conditions) — +- ParkWhiz help, *What is Early Bird* — +- Parking BOXX, *Validation programs guide* — +- Amano McGann, *Validation solutions* (discount shapes incl. re-rate) — +- ITS DOT evaluation of SFpark — +- UCLA ITS, *Pricing Parking by Demand (SFpark)* — +- IDeaS, *Airport parking dynamic pricing* — +- SKIDATA, mobility & parking solutions (tariff axes) — +- City of Tampa / MPLS Parking / RDU T&Cs (published rate-card examples) — + , + , diff --git a/wiki/index.md b/wiki/index.md index 79c8e48..f8b3539 100644 --- a/wiki/index.md +++ b/wiki/index.md @@ -90,6 +90,7 @@ Counts: 4 sources · 19 entities · 47 concepts · 7 decision records. - [[parking-session]] — the core domain entity; a projection over the signed log, never a mutable table. - [[tariff]] — fee model; pure, data-driven, offline; pay-on-foot adds a walk-back grace window. - [[tariff-time-tiers]] — BUILT (V2 tariff): happy-hour/off-peak/weekend/seasonal + vehicle category + flat rate via wall-clock windowed cards; tz frozen per version. +- [[tariff-industry-survey]] — 2026-07 web survey of industry tariff structures: coverage map vs our engine; gaps = early bird (pick-table-by-entry-time) + merchant validations; per-minute & dynamic pricing ruled out. - [[booth-exit-flow]] — manned booth: pay → voucher (self-exit later) or immediate exit; active sessions; audited barrier re-open. - [[operator-issued-entry]] — operator mints an entry ticket when the physical button is broken; presence-gated (radar AND camera, both sides), flagged (source=manual + operatorInitiated + anomaly), capacity-override allowed; needs `session:create` (2026-07-01). - [[plate-reconciliation]] — ANPR plate-as-invariant catches the ticket-swap fraud (paid car let out on a fresh $0 ticket, original lingers "inside"); exact/high-conf match vs open sessions; booth = flag + operator override, reader = log-only fail-open (2026-07-01). diff --git a/wiki/log.md b/wiki/log.md index ce33717..6bb3cf1 100644 --- a/wiki/log.md +++ b/wiki/log.md @@ -2342,3 +2342,33 @@ read as a dead click on a slow open. Root cause of the slowness recorded as an o fold from the last z-report's signed expectedDrawerMinor forward. Also from this session: dev-DB migrations are MANUAL (pnpm db:migrate in packages/db) — a 500 "no such column" after pulling a migration means it was skipped; booth containers migrate on boot and are immune. + +## [2026-07-05] update | Camera health-check detail bucketed — device-monitor log noise fix + +The monitor logs + re-emits a device status only when state OR detail changes, but the camera +probe's detail was the exact snapshot byte count — which differs on every JPEG frame, so healthy +cameras "changed" on nearly every poll (a log line + websocket event each, inflating the freshly +budgeted container logs). Camera healthCheck detail is now a stable power-of-two bucket +("snapshot ≈16 KB" / "≈256 KB"; "<1 KB" stays exact as a suspicious-frame flag), so it moves only +on a real shift (stream/resolution change, empty body). packages/devices camera.ts + 3 tests. + +## [2026-07-05] query | Industry survey: most-used parking tariff systems + +Web research filed as [[tariff-industry-survey]] (reference). Field taxonomy: linear-hourly per +started increment (per-MINUTE tried and rolled back — Leuven), degressive ladders, day caps, +grace; whole-stay prices (day tickets, up-to matrices, evening/overnight packages, event rates); +entry-time-conditioned EARLY BIRD (enter-before cutoff, anti-arbitrage exit conditions); calendar +windows (day/night, weekend/holiday, seasonal); category pricing; monthly contracts; merchant +VALIDATIONS (amount/percent/time-credit/full-comp/re-rate); dynamic demand pricing (SFpark 60-80% +occupancy banding — municipal/airport scale only). Coverage map: our engine expresses everything +a staffed single lot advertises; the two real gaps are early bird (= the pick-table-by-entry-time +future design, same mechanism as weekend menus) and validation overlays. Anti-features confirmed: +per-minute billing, dynamic pricing. + +## [2026-07-05] update | Composer page gets the published-versions sidebar too + +The published-history sidebar built for the lab landed only there; the operator expected it on +/setup/tariff as well. TariffComposer now mirrors it: right sidebar of published versions (name or +effective date, active badge), click → formFromVersion loads it into the editor as the seed for +the next publish (which, per the sidebar hint, always creates a NEW immutable version). Details on +[[tariff]] (Composer section).