--- type: concept tags: [parking, domain, config, devices] sources: [] updated: 2026-06-17 status: 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: ` / address lines that are present; a `Keni humbur biletën? ` 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).