# Phase 2 — Build Notes

Read alongside `PHASE1-NOTES.md`. Covers what's specific to the F&B tie-in: new
migrations, a couple of real bugs found via testing (not just design assumptions), and
decisions flagged for your review.

## What was built

Deliverable order followed the brief: floor plan & table setup → restaurant reservations
→ menu & POS order entry → kitchen display → charge-to-room → events module → event
client history. Folio integration was verified before moving on to events, per the brief's
instruction — see "Bugs found" below for what that testing actually caught.

New tenant tables: `dining_areas`, `dining_tables`, `restaurant_reservations`,
`event_clients`, `events`, `menu_items`, `pos_orders`, `pos_order_items`, plus a
`restaurant_no_show_count` column added to `guests`. New role: `fnb` (table reservations,
POS, kitchen display, events — no access to room reservations, guest CRM, or user
management, per the brief).

Run `php spark tenant:migrate --slug demo` (or `--all`) any time new tenant migrations are
added — `tenant:create` only runs migrations once, at provisioning time.

## Bugs found and fixed during this session

These were caught by exercising the actual HTTP write flows (create table, create user,
etc.), not just loading pages — worth knowing about since the same class of bug could
exist anywhere I didn't test as thoroughly:

- **`is_unique` was silently validating against the control database**, not the current
  tenant's. CodeIgniter's built-in rule has no concept of per-tenant connections and falls
  back to the `default` DB group. This affected `UserModel` (email), `RoomModel` (room
  number), and the new `DiningTableModel` (table number) — all either threw a "table
  doesn't exist" error or, worse, would have silently validated against the wrong data if
  a same-named table ever existed in the control DB. Fixed with a `tenant_unique` custom
  validation rule (`app/Validation/CustomRules.php`) that explicitly uses the tenant
  connection; every tenant-scoped model must use it instead of `is_unique` going forward
  (documented on `TenantModel` itself now).
- **Phase 1's front-of-house routes had no role restriction** beyond "logged in" — fine
  when only manager/front_desk existed, but once `fnb` was added it could reach
  `/guests`, `/rooms/board`, etc. Wrapped those routes in a `role:manager,front_desk`
  group to match the new `role:manager,fnb` group for F&B routes.

## Assumptions flagged for your review

- **Restaurant table reservations only conflict-check the exact date+time+table**, not a
  duration/overlap range the way room bookings do (reservations don't have an end time).
  Two reservations 15 minutes apart at the same table are both allowed even though a real
  service probably takes an hour+. If you want proper overlap checking, reservations need
  an expected duration field first.
- **The floor plan view is day-level, not slot-level**: a table shows "booked" if it has
  *any* non-cancelled reservation that day, not whether it's free at the specific time
  someone's currently looking at. Good enough to say "this table is busy today," not to
  answer "is T4 free at 7:30pm" at a glance — the reservation list below it is where that
  actually gets resolved.
- **Split-billing-style payer allocations weren't extended to POS orders** — a restaurant
  order settles to exactly one folio (new standalone, or the guest's room). If a table
  wants to split a bill across multiple payers, that has to happen as separate orders or
  be handled by editing folio allocations manually after the fact.
- **"Pay directly" always creates a brand-new standalone folio per order**, closed
  immediately after settlement, rather than trying to find/reuse an existing standalone
  folio for a repeat walk-in customer on the same day. Simpler and avoids ambiguity about
  which "session" a charge belongs to, at the cost of guests accumulating many closed
  one-order folios in their history if they visit the restaurant often without staying.
- **Charge-to-room requires the guest to already have an open folio** (i.e. be checked
  in) — there's no path to charge a POS order to a guest with a *confirmed but not yet
  checked-in* booking. Matches how Phase 1 folios only exist once check-in happens.
- **Menu item categories are free text**, not a lookup like other status/category fields
  in this schema — a restaurant's menu categories are entirely up to the hotel, not
  something this app needs to validate against.
- **No inventory/stock deduction** when an order is placed — confirmed out of scope per
  the brief (Phase 2 doesn't include F&B stock tracking).

## Not built (explicitly out of scope per the brief)

Loyalty/membership, Hubtel/WhatsApp/SMS, F&B inventory tracking, housekeeping/
maintenance, KPI dashboards, AI features.
