# Phase 6: Extras & Room Service

## Objective
Implement extra services management (minibar, spa, laundry, parking...) and the ability to charge extras to a guest's reservation during their stay.

---

## Tasks

### 6.1 Filament — ExtrasRelationManager

**File:** `app/Filament/Resources/HotelResource/RelationManagers/ExtrasRelationManager.php`

CRUD for hotel extras:
- Form: name (translatable), description (translatable), type (enum), price (3-tier), is_active, sort_order
- Table: name, type, price, is_active
- Filters: type, is_active

### 6.2 Filament — ReservationExtrasRelationManager

On the reservation detail in Filament, show extras charged to the reservation:
- Table: extra name, room (if applicable), quantity, unit price, total, billed_at
- Action: Add extra inline

### 6.3 API — Manager Extra Endpoints

#### ListExtrasController
```
GET /v1/places/{place}/hotels/{hotel}/extras
```
Returns available extras for this hotel. Filterable by type.

#### AddReservationExtraController
```
POST /v1/places/{place}/hotels/{hotel}/reservations/{reservation}/extras
Body: extra_id, quantity, room_id (optional), notes (optional)
```

Logic:
1. Validate reservation is CHECKED_IN
2. Validate extra belongs to same hotel
3. Get unit price (member_money_price if guest is member, else money_price)
4. Create HotelReservationExtra with billed_at = now()
5. Return updated folio summary

#### RemoveReservationExtraController
```
DELETE /v1/places/{place}/hotels/{hotel}/reservations/{reservation}/extras/{reservationExtra}
```

Logic:
1. Validate extra not yet billed to an OrderItem (not yet checked out)
2. Delete HotelReservationExtra
3. Return updated folio summary

### 6.4 API — Customer Extra Browsing

#### ListHotelExtrasController (Customer)
```
GET /v1/places/{place}/hotels/{hotel}/extras
```
Returns active extras with pricing for browsing.

### 6.5 Orderable Implementation on HotelExtra

Implement Orderable interface:
- `orderableMoneyPrice()` — returns money_price or member_money_price
- `orderableCreditsPrice()` — credits equivalent
- `orderableCreditsTaxes()` — tax amount
- `orderableConstraints()` — `[new RequiresMetadata(['quantity'])]`

### 6.6 Actions

#### AddExtraToReservationAction
**File:** `app/Actions/HotelManager/AddExtraToReservationAction.php`

```php
final class AddExtraToReservationAction
{
    /**
     * @throws ReservationNotCheckedInException
     * @throws ExtraNotActiveException
     */
    public function __invoke(
        HotelReservation $reservation,
        HotelExtra $extra,
        int $quantity = 1,
        ?HotelRoom $room = null,
        ?string $notes = null,
    ): HotelReservationExtra;
}
```

### 6.7 Resources

- `HotelExtraResource` — Extra detail
- `HotelReservationExtraResource` — Extra charge on reservation

### 6.8 Form Requests

- `AddReservationExtraRequest` — extra_id, quantity (int ≥ 1), room_id (optional), notes (optional)

### 6.9 Tests

**File:** `tests/Feature/Controllers/HotelManager/ExtrasTest.php`

- [ ] Manager can list hotel extras
- [ ] Manager can add extra to checked-in reservation
- [ ] Manager cannot add extra to reservation that is not checked-in
- [ ] Extra charges appear in folio
- [ ] Manager can remove unbilled extra
- [ ] Manager cannot remove billed extra (after checkout)
- [ ] Customer can browse extras with correct pricing
- [ ] Extra uses member pricing for member guests
- [ ] Orderable interface implementation correct

---

## Acceptance Criteria

- [ ] Filament extras CRUD functional
- [ ] Filament reservation extras view functional
- [ ] All 3 manager API endpoints functional
- [ ] Customer extras browsing functional
- [ ] Extras correctly added to folio at checkout
- [ ] Member pricing applied when applicable
- [ ] All Swagger annotations present
- [ ] All tests pass
- [ ] Pint + PHPStan pass on new/modified files
