Files
julian 727c62da90 ticket: site metadata header + scannable Albanian ticket; widen barcode
- site_config gains optional park identity (park_name, operator_name, nius,
  address, phone, email); additive Drizzle migration 0001. GET/PUT
  /api/site-config read/write the full config (PUT partial patch, admin only);
  SiteSettings + SetupWizard expose the fields.
- renderTicket() prints an Albanian header sourced from site_config, the
  all-numeric 13-digit ticket id (12 random + Luhn) as Code128, large digits,
  and a lost-ticket footer. CP852 codepage so ë/ç render.
- Widen the Code128 module width 2->3 and height 80->100 dots so the
  short-range "Simple" QR/barcode reader decodes reliably (was barely reading
  at module width 2 on the 80mm head).

See wiki/concepts/site-metadata.md and ticket-encoding.md.
2026-06-17 12:17:21 +02:00

4.0 KiB

type, tags, sources, updated, status
type tags sources updated status
concept
parking
domain
config
devices
2026-06-17 settled

Site Metadata (Park Identity)

Optional, admin-set identity/metadata for the park itself, beyond the operational capacity knob. Feeds the ticket/receipt header and admin display. All fields are optional — the lot runs fine with none set (the ticket falls back to a generic PARKING header).

Where it lives

A single-row extension of the existing site_config table (id always 1) — the established home for site-wide knobs (capacity-occupancy). Not a new table: park identity is one-per-site, same cardinality as capacity, so it shares the row.

Column Purpose
park_name Display name on the ticket header / UI (e.g. "Acme Parking").
operator_name Legal entity operating the lot — for receipts; may differ from the display name.
nius NIUS — Albanian tax/identification number, printed on the receipt when set (commonly required).
address Free-text postal address (multi-line allowed; printed line-by-line).
phone Contact phone — also the ticket "Keni humbur biletën?" footer.
email Contact email (stored; not yet printed).

All are nullable text. Added in migration 0001 (additive ADD COLUMN, no data loss). A metadata change is not a schema change for the ticket id, but adding these fields IS a schema change — done via a Drizzle migration.

Field history. The first cut (2026-06-17) had vat_number + registration_number. Renamed/trimmed the same day to a single nius column (Albanian deployments call the tax id NIUS; registration number dropped as unused). Migration 0001 was regenerated in place — it had not shipped beyond the dev DB, so there is no migration debt.

Read / write path

  • API: GET /api/site-config returns capacity + every metadata field (null when unset). PUT /api/site-config (admin only) accepts a partial body — only the fields present are updated; blank string → null (clears). apps/server/src/routes/site.ts.
  • UI: apps/web/src/SiteSettings.tsx — admin edits capacity + the metadata fields in one form (saveSiteConfig).
  • API client: SiteConfig type + fetchSiteConfig / saveSiteConfig in apps/web/src/api.ts.

On the ticket

renderTicket() (rongta-printer) prints a header from a TicketHeader (the metadata): park name large (or PARKING if unset), then operator / NIUS: <n> / address lines that are present; a Keni humbur biletën? <phone> footer if a phone is set. The entry flow (#ticketHeader() in apps/server/src/entry-flow.ts) reads site_config per print. See ticket-encoding.

Localisation (Albanian)

The ticket prints in Albanian for now. Strings are centralised in a STR table in rongta-printer (Printuar më:, Keni humbur biletën?, NIUS:) so a real i18n layer (per-locale tables + a t() helper, covering the web UI too) can replace them later without touching the render functions — that broader site translation is the next step.

Codepage (resolved 2026-06-17). Albanian text needs ë/ç, which ASCII can't carry. The driver now selects CP852 (Latin-2) via ESC t 18 in each print preamble and line() encodes text to CP852 (with an ASCII transliteration fallback for anything unmapped, and ? as a last resort — never a wrong glyph). Verified at byte level: ë → 0x89 in "Printuar më" / "biletën" / a sample address.

Open

  • Receipt vs entry ticket — the same header is used for the entry ticket today; a paid receipt may want more (fee, tariff version, paid-at). Design with tariff.
  • Email is stored but not yet printed (no use decided).
  • Full i18n — only the ticket is Albanian so far; the web UI is still English. A proper locale system (and admin language choice) is the broader task this seeds.
  • CP852 coverage — the map covers the common Albanian/Latin-2 letters; extend if a park name/address uses a glyph outside it (currently transliterated to ASCII).