Files
parking_solution/wiki/sources/parksql2017-legacy-schema.md
T
julian 58d8f06ba0 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.
2026-06-18 10:59:21 +02:00

88 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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]]).