# Restaurant Manager — Master Implementation Plan

## Overview

Integration of a new **Restaurant Manager** module in the Champion Spirit platform, enabling places to operate one or more restaurants with full menu management, table reservations, dine-in checkout, customer pre-ordering, and comprehensive analytics dashboards. This module follows the same architectural patterns established by the Shop Manager feature.

A Place can have **multiple restaurants**, each managed by employees with the `restaurant_manager` role. The module spans three user surfaces:
- **Filament Admin** — Full restaurant configuration (menus, tables, categories, availability, reservations)
- **Mobile App (Restaurant Manager)** — Dine-in cart/checkout, real-time dashboard, reservation management
- **Mobile App (Customer)** — View menus, make reservations, pre-order & prepay

---

## Scope Summary

| Feature Area | Description |
|---|---|
| **Restaurant Entity** | Multiple restaurants per place, each with its own configuration, hours, and tables |
| **Menu System** | Categories (food/drink), menu items with prices & allergens, daily menus by service period |
| **Table Management** | Numbered tables with zones, capacity, multi-table assignment for large parties |
| **Reservation System** | Customer reservations with party size, table assignment, optional prepayment (Orderable pattern) |
| **Dine-in Cart & Checkout** | Restaurant manager builds cart of menu items, assigns to table, checkout with all payment methods |
| **Customer Pre-order** | Customers browse daily menu, pre-order items linked to reservation, prepay |
| **Tips** | Tip management integrated into checkout flow |
| **Real-time Dashboard** | Live table status, current covers, pending orders, today's revenue |
| **Financial Dashboard** | Revenue analytics, covers/day, affluence by hour, popular dishes, average ticket |
| **Allergen Management** | EU 14 regulatory allergens on every menu item |
| **Filament Admin** | Full CRUD for restaurants, tables, menus, categories, items, reservations, dashboard widgets |
| **Data Seeder** | Complete sandbox dataset with photos for testing |

---

## Architecture Decisions

### 1. User Role
- Reuse existing `UserType::EMPLOYEE` with a new `AdminRole::RESTAURANT_MANAGER` enum value
- Restaurant manager operates within a `Place` + `Restaurant` context
- New `EnsureRestaurantManagerMiddleware` validates role + place + restaurant access

### 2. Restaurant ↔ Place Relationship
- `Place hasMany Restaurant` — A place can have multiple restaurants
- Each Restaurant has its own opening hours, tables, menus, and categories
- Restaurant-level scoping on all queries (not just place-level)

### 3. Menu Architecture
- **RestaurantMenuCategory** — Hierarchical categories (Entrées > Salades, Boissons > Cocktails, etc.)
- **RestaurantMenuItem** — Individual dish/drink with 3-tier pricing, allergens, dietary info
- **RestaurantMenu** — Named daily/period menus (e.g., "Menu du Jour Lundi", "Brunch Dimanche")
- **RestaurantMenuSection** — Groups items within a menu (Entrée, Plat, Dessert)
- Menus are date-bound AND time-bound (service periods: breakfast, lunch, dinner, etc.)

### 4. RestaurantMenuItem as Orderable
- Implements `Orderable` interface (same as Product, Service)
- Can be added to Order/Cart via existing OrderItem polymorphism
- 3-tier pricing: `money_price`, `member_money_price`, `resort_money_price`
- Added to morph map as `restaurant_menu_item`
- Added to `OrderableType` enum

### 5. Reservation System (Standalone Model)
- **RestaurantReservation** — Dedicated model (NOT reusing Booking model)
- Implements `Orderable` interface for optional paid reservations
- Has dedicated fields: `party_size`, `special_requests`, `status`, table assignments
- Availability based on restaurant opening hours + table capacity + existing reservations
- Multi-table support: pivot `restaurant_reservation_table` for large party table assignments
- Added to morph map as `restaurant_reservation`

### 6. Cart = Order in WAITING_PAYMENT (same as Shop)
- Reuse existing `Order` model with `OrderStatus::WAITING_PAYMENT`
- `employee_id` links to restaurant manager
- `restaurant_id` added to orders table for restaurant context
- OrderItems point to `RestaurantMenuItem` as orderable
- Same payment methods: Wallet, Stripe, Crypto, POS, Cash

### 7. Tips
- `tip_amount` + `tip_currency` fields on Order model
- Tip added at checkout time, separate from order total
- Tip included in invoice but not in item-level calculations

### 8. Service Periods
- Enum `RestaurantServicePeriod`: BREAKFAST, BRUNCH, LUNCH, AFTERNOON_TEA, DINNER, LATE_NIGHT
- Each menu is assigned to one or more service periods
- Each service period has configurable time ranges per restaurant

### 9. Allergens (EU 14)
- Enum `Allergen` with 14 EU regulatory values
- Stored as JSON array on RestaurantMenuItem
- Displayed as icons/badges in API responses

### 10. Reuse Existing Patterns
- Invoice generation: Same as Shop (queued job, PDF snapshot)
- Dashboard cache: Same Redis strategy (midnight pre-compute, 5min delta)
- Payment flow: Identical to Shop (POS, Cash, Wallet, Stripe, Crypto)
- Pagination: Laravel standard format (data/links/meta)

---

## New Models Summary

| Model | Table | Purpose |
|---|---|---|
| `Restaurant` | `restaurants` | Restaurant entity belonging to a place |
| `RestaurantTable` | `restaurant_tables` | Physical table with number, zone, capacity |
| `RestaurantMenuCategory` | `restaurant_menu_categories` | Hierarchical menu categories (food/drink) |
| `RestaurantMenuItem` | `restaurant_menu_items` | Individual dish/drink with pricing & allergens |
| `RestaurantMenu` | `restaurant_menus` | Named daily/period menu |
| `RestaurantMenuSection` | `restaurant_menu_sections` | Section within a menu (Entrée, Plat, etc.) |
| `RestaurantMenuSectionItem` | `restaurant_menu_section_items` | Pivot: section ↔ item with sort order & price override |
| `RestaurantReservation` | `restaurant_reservations` | Table reservation with party size & status |
| `RestaurantReservationTable` | `restaurant_reservation_tables` | Pivot: reservation ↔ tables (multi-table support) |

---

## Enum Updates

| Enum | Change |
|---|---|
| `AdminRole` | Add `RESTAURANT_MANAGER` |
| `OrderableType` | Add `RESTAURANT_MENU_ITEM`, `RESTAURANT_RESERVATION` |
| **New** `RestaurantServicePeriod` | `BREAKFAST`, `BRUNCH`, `LUNCH`, `AFTERNOON_TEA`, `DINNER`, `LATE_NIGHT` |
| **New** `RestaurantReservationStatus` | `PENDING`, `CONFIRMED`, `SEATED`, `COMPLETED`, `CANCELLED`, `NO_SHOW` |
| **New** `RestaurantTableZone` | `INDOOR`, `OUTDOOR`, `TERRACE`, `VIP`, `BAR`, `PRIVATE` |
| **New** `Allergen` | 14 EU allergens (GLUTEN, CRUSTACEANS, EGGS, FISH, PEANUTS, SOYBEANS, DAIRY, NUTS, CELERY, MUSTARD, SESAME, SULPHITES, LUPIN, MOLLUSCS) |
| **New** `DietaryLabel` | `VEGETARIAN`, `VEGAN`, `GLUTEN_FREE`, `HALAL`, `KOSHER`, `DAIRY_FREE`, `NUT_FREE`, `ORGANIC`, `RAW`, `SUGAR_FREE` |
| **New** `RestaurantMenuCategoryType` | `FOOD`, `DRINK` |
| **New** `RestaurantTableStatus` | `AVAILABLE`, `OCCUPIED`, `RESERVED`, `MAINTENANCE` |

> **OrderStatus is NOT modified.** Same as Shop — order stays PAID after refund.

---

## Development Phases

> **Principe fondamental : Filament et API sont des miroirs.** Chaque phase livre le Filament admin ET l'API mobile ensemble pour le même domaine fonctionnel. Tout ce qui est faisable côté admin doit l'être côté app, et inversement.

| Phase | Name | Doc | Scope |
|---|---|---|---|
| **1** | Foundation & Schema | [01-PHASE-FOUNDATION.md](01-PHASE-FOUNDATION.md) | Role, middleware, all models, all migrations, enums, morph maps, route skeleton |
| **2** | Menu System | [02-PHASE-MENU-SYSTEM.md](02-PHASE-MENU-SYSTEM.md) | Categories, menu items, daily menus, sections — **Filament CRUD + API endpoints** |
| **3** | Tables & Availability | [03-PHASE-TABLES.md](03-PHASE-TABLES.md) | Tables, zones, restaurant hours, exceptional closures — **Filament CRUD + API endpoints** |
| **4** | Reservation System | [04-PHASE-RESERVATIONS.md](04-PHASE-RESERVATIONS.md) | Reservations — **Filament + Manager API + Customer API**, table assignment, availability, optional payment |
| **5** | Cart & Checkout | [05-PHASE-CART-CHECKOUT.md](05-PHASE-CART-CHECKOUT.md) | Cart, payments, tips, invoices — **Filament order view + Manager API + Customer pre-order** |
| **6** | Dashboards | [06-PHASE-DASHBOARDS.md](06-PHASE-DASHBOARDS.md) | Real-time + financial dashboards — **Filament widgets + Manager API** |
| **7** | Cross-cutting Concerns | [07-PHASE-CROSS-CUTTING.md](07-PHASE-CROSS-CUTTING.md) | Notifications, Pusher real-time events, translations, cleanup jobs |
| **8** | Data Seeder | [08-PHASE-SEEDER.md](08-PHASE-SEEDER.md) | Sandbox data with photos: restaurants, tables, menus, items, reservations, orders |

---

## Dependency Graph

```
Phase 1 (Foundation & Schema)
   |
   +---> Phase 2 (Menu System: Filament + API)
   |        |
   |        +---> Phase 5 (Cart & Checkout: Filament + API + Customer) ---+
   |        |        |                                                     |
   |        |        +---> Phase 6 (Dashboards: Filament + API)           |
   |        |                                                              |
   +---> Phase 3 (Tables: Filament + API)                                 |
   |        |                                                              |
   |        +---> Phase 4 (Reservations: Filament + API + Customer) ------+
   |                                                                       |
   +---> Phase 7 (Cross-cutting) <-----------------------------------------+
   |
   +---> Phase 8 (Seeder) — after all phases
```

**Recommended build order:**
1. Phase 1 (Foundation & Schema) — all models, migrations, enums
2. Phase 2 (Menu System) + Phase 3 (Tables) — can be parallelized
3. Phase 4 (Reservations)
4. Phase 5 (Cart & Checkout)
5. Phase 6 (Dashboards)
6. Phase 7 (Cross-cutting)
7. Phase 8 (Seeder) — last

---

## Schema Migrations (Consolidated)

### Migration 1: `create_restaurants_table`
```
restaurants: id, public_id (uuid), place_id (FK), name, description,
             slug (unique per place), opening_hours (json),
             service_periods (json), max_covers (unsigned int),
             reservation_duration (unsigned int, default 90 min),
             reservation_delay (unsigned int, default 0 hours),
             auto_confirm_reservations (boolean, default true),
             accepts_preorders (boolean, default true),
             is_active (boolean, default true),
             timestamps, soft_deletes
```

### Migration 2: `create_restaurant_tables_table`
```
restaurant_tables: id, public_id (uuid), restaurant_id (FK),
                   number (unsigned int), label (string nullable),
                   seats (unsigned int), zone (enum string),
                   status (enum string, default 'available'),
                   is_active (boolean, default true),
                   timestamps, soft_deletes
UNIQUE: (restaurant_id, number)
```

### Migration 3: `create_restaurant_menu_categories_table`
```
restaurant_menu_categories: id, public_id (uuid), restaurant_id (FK),
                            parent_id (FK nullable, self-ref),
                            name (string), description (text nullable),
                            type (enum: food, drink),
                            sort_order (unsigned int, default 0),
                            is_active (boolean, default true),
                            timestamps, soft_deletes
```

### Migration 4: `create_restaurant_menu_items_table`
```
restaurant_menu_items: id, public_id (uuid), restaurant_id (FK),
                       category_id (FK), name (string),
                       description (text nullable),
                       money_price_amount (bigint), money_price_currency (string 3),
                       member_money_price_amount (bigint nullable),
                       member_money_price_currency (string 3 nullable),
                       resort_money_price_amount (bigint nullable),
                       resort_money_price_currency (string 3 nullable),
                       allergens (json nullable),
                       dietary_labels (json nullable),
                       preparation_time (unsigned int nullable, minutes),
                       calories (unsigned int nullable),
                       is_active (boolean, default true),
                       sort_order (unsigned int, default 0),
                       timestamps, soft_deletes
```

### Migration 5: `create_restaurant_menus_table`
```
restaurant_menus: id, public_id (uuid), restaurant_id (FK),
                  name (string), description (text nullable),
                  date (date), service_periods (json),
                  is_active (boolean, default true),
                  timestamps, soft_deletes
UNIQUE: (restaurant_id, date, name)
```

### Migration 6: `create_restaurant_menu_sections_table`
```
restaurant_menu_sections: id, public_id (uuid), menu_id (FK),
                          name (string), sort_order (unsigned int, default 0),
                          timestamps
```

### Migration 7: `create_restaurant_menu_section_items_table`
```
restaurant_menu_section_items: id, section_id (FK), menu_item_id (FK),
                               sort_order (unsigned int, default 0),
                               override_price_amount (bigint nullable),
                               override_price_currency (string 3 nullable),
                               timestamps
UNIQUE: (section_id, menu_item_id)
```

### Migration 8: `create_restaurant_reservations_table`
```
restaurant_reservations: id, public_id (uuid), restaurant_id (FK),
                         user_id (FK), date (date),
                         start_at (datetime), end_at (datetime),
                         party_size (unsigned int),
                         special_requests (text nullable),
                         status (enum string, default 'pending'),
                         order_item_id (FK nullable),
                         preorder_order_id (FK nullable),
                         confirmed_at (timestamp nullable),
                         seated_at (timestamp nullable),
                         completed_at (timestamp nullable),
                         cancelled_at (timestamp nullable),
                         cancellation_reason (string nullable),
                         timestamps, soft_deletes
```

### Migration 9: `create_restaurant_reservation_tables_table`
```
restaurant_reservation_tables: id, reservation_id (FK), table_id (FK),
                               timestamps
UNIQUE: (reservation_id, table_id)
```

### Migration 10: `add_restaurant_fields_to_orders_table`
```
orders.restaurant_id (FK nullable)
orders.restaurant_table_ids (json nullable)
orders.tip_amount (bigint nullable)
orders.tip_currency (string 3 nullable)
```

### Migration 11: `add_restaurant_manager_role`
```
Seed restaurant_manager role via Spatie
Seed restaurant_manager permissions
```

---

## Complete API Endpoints

### Restaurant Manager Routes — `/v1/places/{place}/restaurants/{restaurant}/...`

#### Menu Items for Cart (3)
| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/menu-items` | List menu items with search, category filter, pagination |
| `GET` | `/menu-items/{item}` | Menu item detail |
| `GET` | `/today-menu` | Today's active menu with sections and items |

#### Cart Management (9)
| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/carts` | List all active carts for this restaurant manager |
| `POST` | `/carts` | Create a new cart (optionally assign table) |
| `GET` | `/carts/{order}` | Get cart details |
| `PUT` | `/carts/{order}` | Update cart (full item list) |
| `DELETE` | `/carts/{order}` | Delete cart |
| `POST` | `/carts/{order}/customer` | Change cart customer |
| `POST` | `/carts/{order}/items` | Add menu item to cart |
| `PATCH` | `/carts/{order}/items/{item}` | Update item quantity |
| `DELETE` | `/carts/{order}/items/{item}` | Remove item |

#### Table Assignment (2)
| Method | Endpoint | Description |
|---|---|---|
| `POST` | `/carts/{order}/tables` | Assign table(s) to cart/order |
| `GET` | `/tables/status` | Get all tables with current status (available/occupied/reserved) |

#### Customer Management (2)
| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/customers` | Search customers |
| `POST` | `/customers` | Create customer on the spot |

#### Payments & Tips (6)
| Method | Endpoint | Description |
|---|---|---|
| `POST` | `/carts/{order}/pay/wallet` | Pay via wallet |
| `POST` | `/carts/{order}/pay/stripe` | Pay via Stripe |
| `POST` | `/carts/{order}/pay/crypto` | Pay via crypto |
| `POST` | `/carts/{order}/pay/pos` | Pay via POS terminal (+ receipt photo) |
| `POST` | `/carts/{order}/pay/cash` | Pay via cash (+ amounts + photo) |
| `POST` | `/carts/{order}/tip` | Add tip to order |

#### Orders & Invoices (4)
| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/orders` | List paid orders with filters |
| `GET` | `/orders/{order}` | Order detail |
| `GET` | `/orders/{order}/invoice` | Download invoice PDF |
| `POST` | `/orders/{order}/invoice/send` | Send invoice email |

#### Reservations (Manager) (5)
| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/reservations` | List reservations with date filter |
| `GET` | `/reservations/{reservation}` | Reservation detail |
| `PATCH` | `/reservations/{reservation}/status` | Update status (confirm, seat, complete, no-show, cancel) |
| `POST` | `/reservations/{reservation}/tables` | Assign/change tables |
| `POST` | `/reservations` | Create reservation (walk-in) |

#### Dashboard — Real-time (4)
| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/dashboard/live-status` | Tables occupied/free, current covers, pending orders |
| `GET` | `/dashboard/today-summary` | Today's revenue, covers, reservations count |
| `GET` | `/dashboard/service-snapshot` | Current service period metrics |
| `GET` | `/dashboard/upcoming-reservations` | Next N reservations |

#### Dashboard — Analytics (6)
| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/dashboard/revenue` | Revenue with period filter + chart |
| `GET` | `/dashboard/covers` | Covers per day with trends |
| `GET` | `/dashboard/affluence` | Affluence by hour/time slot |
| `GET` | `/dashboard/popular-items` | Best-selling dishes/drinks |
| `GET` | `/dashboard/average-ticket` | Average ticket with trends |
| `GET` | `/dashboard/reservation-stats` | Reservation rates, no-shows, cancellations |

### Customer Routes — `/v1/places/{place}/restaurants/...`

#### Restaurant & Menu (3)
| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/` | List restaurants for this place |
| `GET` | `/{restaurant}` | Restaurant details (hours, zones, capacity) |
| `GET` | `/{restaurant}/menu` | Today's menu with sections, items, allergens |

#### Reservations (Customer) (4)
| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/{restaurant}/availability` | Available time slots for a date + party size |
| `POST` | `/{restaurant}/reservations` | Create reservation |
| `GET` | `/reservations` | My reservations (across all restaurants) |
| `DELETE` | `/{restaurant}/reservations/{reservation}` | Cancel reservation |

#### Pre-order (3)
| Method | Endpoint | Description |
|---|---|---|
| `POST` | `/{restaurant}/reservations/{reservation}/preorder` | Create pre-order for reservation |
| `GET` | `/{restaurant}/reservations/{reservation}/preorder` | View pre-order |
| `POST` | `/{restaurant}/reservations/{reservation}/preorder/pay` | Pay pre-order (Stripe/Wallet) |

**Total: 51 API endpoints**

---

## Files Impact Summary

### New Files (estimated)

| Type | Count |
|---|---|
| Models | 9 |
| Enums | 8 |
| Controllers (API — Restaurant Manager) | ~30 |
| Controllers (API — Customer) | ~10 |
| Controllers (Dashboard) | ~10 |
| Actions | ~12 |
| Services | ~4 |
| Requests | ~15 |
| Resources (API) | ~12 |
| Notifications | ~4 |
| Migrations | ~11 |
| Filament Resources/RelationManagers/Pages | ~10 |
| Filament Widgets | ~4 |
| Factories | ~9 |
| Seeders | ~2 |
| Tests | ~20 |
| **Total new files** | **~160** |

### Modified Files

| File | Change |
|---|---|
| `app/Enums/AdminRole.php` | Add RESTAURANT_MANAGER |
| `app/Enums/OrderableType.php` | Add RESTAURANT_MENU_ITEM, RESTAURANT_RESERVATION |
| `app/Models/Place.php` | Add `restaurants()` HasMany relation |
| `app/Models/Order.php` | Add restaurant_id, tip fields, restaurant() relation |
| `app/Models/Employee.php` | Add `isRestaurantManager()` accessor |
| `app/Models/User.php` | Add `is_restaurant_manager` accessor |
| `app/Providers/AppServiceProvider.php` | Add morph map entries |
| `app/Providers/EventServiceProvider.php` | Register restaurant event listeners |
| `app/Filament/Resources/EmployeeResource.php` | Add RESTAURANT_MANAGER role option |
| `app/Filament/Resources/PlaceResource.php` | Add ManageRestaurants page + RestaurantSalesWidget, remove RestaurantRevenues references |
| `app/Models/RestaurantRevenue.php` | Add @deprecated annotation (do NOT delete model or migration) |
| `routes/api.php` | Restaurant manager + customer route groups |

### Deleted Files

| File | Reason |
|---|---|
| `app/Filament/Resources/PlaceResource/RelationManagers/RestaurantRevenuesRelationManager.php` | Replaced by new Restaurant module |
| `app/Filament/Resources/PlaceResource/Pages/Management/ManageRestaurantRevenues.php` | Replaced by ManageRestaurants |

---

## Conventions Reminder

- `declare(strict_types=1)` in every PHP file
- `final` classes by default
- `public_id` in API responses, never `id`
- Conventional Commits: `feat(restaurant):`, `fix(restaurant):`, etc.
- Do **NOT** run `./gitCheck.sh` — run `vendor/bin/pint` and `vendor/bin/phpstan --level=7` on new/modified files only
- Do **NOT** commit automatically — user commits manually
- Pest tests for every new feature
- Swagger `@OA\` annotations on every controller
- PHPDoc with `@throws`, `@param`, `@property-read`
- Translations via `HasDatabaseTranslations` for user-facing text fields
- All files in `app/Http/Controllers/Api/Employee/RestaurantManager/`
- All files in `app/Actions/RestaurantManager/`
- All files in `app/Services/RestaurantManager/`
- All files in `app/Http/Resources/RestaurantManager/`
