Highclere Castle Ticketing & Commerce · Disaster Recovery Programme Updated: October 2026 · Status: Phase 0 & Component 1 Ready
Ubiquitous Language · CONTEXT.md

Domain Glossary

The legacy database mixes naming conventions from multiple eras (ps4_users, users, shopper_group, coach_orders, DetailSagePayString). Our new codebase enforces a single, unambiguous domain vocabulary defined in CONTEXT.md.

Customer

A person or organisation that places Orders, uniquely identified by their normalised email address across all historical purchases.

Avoid: User, shopper, client, account

Order

A confirmed purchase placed by a Customer, identified publicly by its Order Number and containing one or more Order Items.

Avoid: Transaction, purchase, basket

Order Item

A single line item within an Order representing a specific product and quantity—either a date-bound admission/event entry, physical Gift Shop merchandise, or a Lodge reservation.

Avoid: Detail, order detail, cart item

Ticket

An admission credential generated from a non-voided, date-bound Order Item, carrying a scannable barcode ({order_number}-{order_item_id}-{legacy_user_id}) that grants entry for a specific Visit Date, Visit Time, and Venue.

Avoid: E-ticket, pass, booking confirmation

Gift Voucher

A pre-paid monetary credit balance attached to a Customer with a fixed expiry date, redeemable against online Orders.

Avoid: Coupon code, discount, store credit

Gate List

A daily manifest of valid admission orders, visitor counts, and ticket barcodes exported for on-site staff to verify visitor entry at the Castle gates.

Avoid: Scanner export, attendance sheet, manifest

Coach Company

A commercial tour or group operator (identified by a ContactID) that organises group visits to the Castle under the Group Booking Policy.

Avoid: Coach user, group user, tour operator

Coach Booking

A group visit reservation (GroupID) requested by a Coach Company for a specific arrival time slot and visitor breakdown, progressing through Request, Staff Confirmation, Deposit Payment, Final Numbers & Balance Payment, and optional Extra Visitor Payment.

Avoid: Coach order, group order

Forensic Codebase Audit

Legacy Quirks Neutralised by Our Clean Schema

During our audit of oldFiles/live_files_backup_1Oct2026 and castle_2026-10-01_10-29-32.sql, we identified the following technical traps that the ETL pipeline cleans up before data ever reaches the new PostgreSQL database:

Legacy Location Legacy Behaviour / Bug New Clean Architecture Resolution
Customers_model::getUserLogin() Compares users.password in plaintext; uses LIMIT 1 on non-unique email column. Passwords dropped completely; customers deduplicated by normalised email; authentication via Postmark Email OTP + Single-Order Lookup.
users, orders, order_item ENGINE=MyISAM with no foreign keys, no ACID transactions, and mixed Unix INT timestamps (cdate) alongside 0000-00-00 dates. PostgreSQL tables with strict foreign keys, ACID transactions, and normalised TIMESTAMPTZ / DATE columns.
Ticket_print.php & order_notification_nopdf.php Hardcodes ProductID < 500000 and a 12-element if-statement of promotional book/tea-towel SKUs (178011–181170) to decide whether to draw a barcode; writes temporary .gd2 and .png files into public webroot on every view. ETL computes a clean boolean is_scannable_ticket and barcode_value on order_items; tickets render stateless inline Code 128B SVGs.
Customers::orderdetails() vs Orders_model Inconsistent void checks: some queries check void_tickets IS NULL while PHP views check void_tickets == ''. ETL normalises void_tickets IS NOT NULL AND TRIM(void_tickets) != '' into a single boolean is_voided column.
Coachvisits::_calculate_penalty() (line 2257) Contains if ($order_id = 3649) { $penalty = 0; } (assignment instead of comparison), zeroing out all coach cancellation penalties. Replaced in Component 2 with unit-tested domain function in src/domain/coach-pricing.ts.
coach_orders & coach_companies 10 hardcoded reminder boolean columns (seventy_day_reminder_sent, etc.) and annual cloned columns (group_info_read_next). Replaced in Component 2 with relational coach_policy_acceptances and coach_notification_logs tables.