docs(wiki): tariff research — legacy ParkSQL2017 schema, time-tiers & validation/sponsorship design
Ingest the predecessor SQL Server schema (raw + source summary) and file design pages for time-of-day/seasonal tariff tiers and merchant validation/postpaid sponsorship. Cross-link tariff.md and validation-discounts.md. No code.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
---
|
||||
type: source
|
||||
tags: [parking, legacy, pricing, schema, fiscalization]
|
||||
sources: [parksql2017-legacy-schema]
|
||||
updated: 2026-06-17
|
||||
---
|
||||
|
||||
# ParkSQL2017 — Legacy Parking System Schema (source summary)
|
||||
|
||||
A SQL Server 2017 schema dump (`raw/parksql2017-legacy-schema.sql`, scripted 2024-10-07) of an
|
||||
**existing/predecessor parking system** in the same Albanian market this project targets. It is the
|
||||
single most concrete reference we have for how the prior generation modelled **tariffs, discounts,
|
||||
memberships, sessions, shifts, and fiscalization** — a real, deployed data model rather than vendor
|
||||
marketing. Treat it as evidence of what worked and what to improve, not as a spec to copy (it has
|
||||
clear anti-patterns, e.g. money as `float`).
|
||||
|
||||
> Albanian-context tells: `BA_TicketFisc.nivf` (NIVF fiscalization code), the `Cupons` spelling,
|
||||
> `LostPrice1..4` tiers. `BA_` = business-app table prefix; `SYS_` = system/auth tables.
|
||||
|
||||
## Table map (24 tables)
|
||||
|
||||
**Pricing / tariff**
|
||||
- **`BA_TicketPrice`** — the rate-card *header*. Key fields: `Code`, `Name`, `TicketCategoryID`,
|
||||
`ParkID`, **`ValidFrom`/`ValidTo`** (date window), **`ValidFromHour`/`ValidToHour`** (time-of-day
|
||||
window), `FixedPrice` (flat option), `IntervalType` (2-char unit, e.g. MI/HR/DY), `Interval`
|
||||
(increment size), **`LostPenalty`**, `IsDefault`, `IsActive`. → A rate card is scoped by
|
||||
**(category × date-range × hour-range)**. This is the **happy-hour / time-of-day mechanism**.
|
||||
- **`BA_TicketPriceHours`** — the *stepped ladder* (child of TicketPrice): rows of
|
||||
`HourFrom`, `HourTo`, `Price`. → equivalent to this project's tariff `blocks[]`.
|
||||
- **`BA_TicketCategory`** — vehicle/customer category (`Code`, `Name`, `IsDefault`,
|
||||
`IntervalChange`). → a **pricing axis by category** the current model lacks.
|
||||
- **`BA_TicketFisc`** — `TicketID` → `nivf` (Albanian fiscalization code per ticket).
|
||||
|
||||
**Discounts / validation**
|
||||
- **`BA_Cupons`** — `CODE`, **`DiscMinutes`** (discount as *free minutes*), `IsUsed`, `LastUsed`,
|
||||
`IsPrinted`. → validation = a **single-use coupon code worth N free minutes**; reconciled by
|
||||
counting used codes (**prepaid** model, no merchant account/ledger).
|
||||
|
||||
**Sessions**
|
||||
- **`BA_ParkRecords`** — the transient parking session (ticket cars). Carries lifecycle
|
||||
(`InTime`/`OutTime`/`ExitTime`, `In/Out Mode/Addr/OperatorID`, `In/Out ShiftID`), money
|
||||
(`OrgCharge`, `Charge`, `Discount`, `FreeMin`, `IsPaid`), **discount detail** (`DiscMinutes`,
|
||||
`DiscType` smallint, `DisTicketSerial`), entry/exit plate+image columns, and
|
||||
`ManualOpenReason` / `ExpiredTime/DateApproval` audit fields.
|
||||
- **`BA_MembersCheckINOUT`** — per-event check-in/out log for *members* (cards), separate from
|
||||
ticket sessions.
|
||||
- **`BA_ManualCheckINOUT`** — every manual barrier open, with `Reason` + `OperatorID` + image.
|
||||
|
||||
**Memberships (≈ this project's permits)**
|
||||
- **`BA_Members`** — the member (card+plate identity, contact, `isVIP`).
|
||||
- **`BA_Memberships`** — an issued subscription: `PlanID`, `StartDate`/`EndDate`, `Price`,
|
||||
`CalculatedPrice`, `Paid`, `AllowedDays`.
|
||||
- **`BA_MembershipPlans`** — plan template: `Type`, `Duration`, `Price`, **`ActiveDays`**.
|
||||
- **`BA_MembershipPlansTime`** — **`StartTime`/`EndTime`** windows per plan → memberships valid only
|
||||
in specific **hours** (commuter/day-shift permits).
|
||||
|
||||
**Site / ops / auth**
|
||||
- **`BA_Park`** — a lot: capacity (`ParkingPlaces`/`FreePlaces`), LED sign addr, default ticket/lost
|
||||
category codes, **`FreeMinutes`**, `DiscMinutesTicket`, `DiscMinutesApp`, **`LostPrice1..4`**.
|
||||
- **`BA_Shifts`** — cashier shift / Z-report: open/close, `Charged`, `TicketCharges`,
|
||||
`CardCharges`, entry/exit + manual counts, **`Reconciled`**, `UserID`, `MachineID`.
|
||||
- **`BA_CashRegister`**, `SYS_Configs` (company/fiscal/ticket header+footer, `AllowTimeExeed`,
|
||||
`ImageDays`), `SYS_User`/`SYS_Role`/`SYS_Rights`/`SYS_UserRights`, `SYS_Controls`,
|
||||
`SYS_Language` (DB-driven i18n).
|
||||
|
||||
## What it confirms for our design
|
||||
1. **Stepped ladder** — `BA_TicketPriceHours` (HourFrom/HourTo/Price) ≈ our `blocks[]`. Good signal.
|
||||
2. **Per-rate lost penalty** + site-level lost tiers (`LostPrice1..4`) ≈ our `lostTicketMinor` (+ the
|
||||
admin-override idea).
|
||||
3. **Typed discount on the session record** (`DiscType`) ≈ the research's "typed validation modifier."
|
||||
4. **Manual-open + reason logging at the schema level** ≈ our [[threat-model|operator-as-adversary]] audit need.
|
||||
|
||||
## What it adds (genuinely new vs. our current model)
|
||||
1. **Time-of-day + date windows on the rate card** (`ValidFromHour`/`ValidToHour`,
|
||||
`ValidFrom`/`ValidTo`) — the shipped way to do **happy hour / seasonal**. See [[tariff-time-tiers]].
|
||||
2. **Vehicle/customer category as a pricing axis** (`BA_TicketCategory`). See [[tariff-time-tiers]].
|
||||
3. **Time-/day-restricted memberships** (`MembershipPlansTime`, `ActiveDays`) — a [[permit]] gap.
|
||||
|
||||
## Anti-patterns to NOT copy
|
||||
- **Money as `float`** everywhere (`Charge`, `Price`, `LostPenalty`) — drifts across a revenue
|
||||
ledger. Our integer-minor-units rule is the deliberate fix. (rejected alternative)
|
||||
- **Mutable rate rows** (`Updated`/`UpdatedBy` in place) — a past session can't reliably reprice
|
||||
against the rate then in force. Our immutable effective-dated [[tariff]] versions fix this.
|
||||
- **No merchant/sponsor account or postpaid ledger** — only prepaid printed coupons. The B2B
|
||||
postpaid case is net-new; see [[validation-sponsorship]].
|
||||
- **Images stored as `image` BLOBs in-row** — we keep snapshot *bytes* out of the event row and
|
||||
store a reference instead ([[append-only-event-chain]]).
|
||||
Reference in New Issue
Block a user