# Phase 5: Check-in / Check-out / Cancellation — DONE

**Completed:** 2026-03-16

## Summary

Full check-in/check-out lifecycle, folio generation at checkout, 5 payment methods, invoice download/send, cancellation with penalty calculation and wallet refund, and cancellation policy info endpoint. Includes 5 actions, 1 exception, 9 controllers, and 17 Pest tests.

## Files Created

### Exceptions (1)
- `app/Exceptions/HotelManager/ReservationNotCancellableException.php` — Cannot cancel reservation in current status (422)

### Actions (5)
- `app/Actions/HotelManager/CheckInGuestAction.php` — Validates CONFIRMED status, auto-assigns room if none assigned, sets status to CHECKED_IN, updates rooms to OCCUPIED.
- `app/Actions/HotelManager/CheckOutGuestAction.php` — Validates CHECKED_IN status, sets CHECKED_OUT, updates rooms to AVAILABLE + DIRTY housekeeping, creates housekeeping logs.
- `app/Actions/HotelManager/GenerateFolioAction.php` — Creates WAITING_PAYMENT Order with room charges (if not prepaid) + all unbilled extras as OrderItems. Links order_item_id back to HotelReservationExtra.
- `app/Actions/HotelManager/CancelReservationAction.php` — Validates cancellable status, calculates penalty (free period, percentage), handles prepaid refund via wallet credit-back or creates penalty Order, frees assigned rooms.
- `app/Actions/HotelManager/RefundReservationAction.php` — Wallet credit-back for cancelled prepaid reservations, same pattern as Shop/Restaurant.

### Controllers (9)
- `app/Http/Controllers/Api/Employee/HotelManager/CheckoutReservationController.php` — POST checkout: checks out guest + generates folio.
- `app/Http/Controllers/Api/Employee/HotelManager/WalletPayReservationController.php` — POST pay/wallet: finds WAITING_PAYMENT order, debits wallet, marks paid.
- `app/Http/Controllers/Api/Employee/HotelManager/StripePayReservationController.php` — POST pay/stripe: creates Stripe Checkout session.
- `app/Http/Controllers/Api/Employee/HotelManager/PosPayReservationController.php` — POST pay/pos: reuses ProcessPosPaymentAction with receipt photo.
- `app/Http/Controllers/Api/Employee/HotelManager/CashPayReservationController.php` — POST pay/cash: reuses ProcessCashPaymentAction with amount validation.
- `app/Http/Controllers/Api/Employee/HotelManager/CryptoPayReservationController.php` — POST pay/crypto: creates NOWPayments invoice.
- `app/Http/Controllers/Api/Employee/HotelManager/DownloadReservationInvoiceController.php` — GET invoice: returns invoice or generates on-the-fly.
- `app/Http/Controllers/Api/Employee/HotelManager/SendReservationInvoiceController.php` — POST invoice/send: sends invoice email.
- `app/Http/Controllers/Api/Employee/HotelManager/ShowCancellationPolicyController.php` — GET cancellation-policy: returns penalty info, free window, waive flag.

### Tests (1)
- `tests/Feature/Controllers/HotelManager/CheckinCheckoutTest.php` — 17 tests covering check-in (with/without auto-assign, invalid status), check-out (status + housekeeping), folio (with/without prepaid, with extras), cancellation (no penalty PENDING, free period, penalty, waive, prepaid refund, freed rooms, invalid states), cancellation policy endpoint.

## Files Modified

- `app/Actions/HotelManager/UpdateReservationStatusAction.php` — Now delegates CHECKED_IN to CheckInGuestAction and CANCELLED to CancelReservationAction. Accepts waivePenalty parameter.
- `app/Http/Controllers/Api/Employee/HotelManager/UpdateReservationStatusController.php` — Passes waive_penalty from request to action.
- `app/Http/Requests/HotelManager/UpdateReservationStatusRequest.php` — Added `waive_penalty` boolean validation rule.
- `routes/api.php` — Added 9 Phase 5 routes under hotel manager group.

## API Routes

### Hotel Manager
| Method | URL | Controller |
|--------|-----|-----------|
| POST | `/v1/places/{place}/hotels/{hotel}/manage/reservations/{reservation}/checkout` | CheckoutReservationController |
| POST | `/v1/places/{place}/hotels/{hotel}/manage/reservations/{reservation}/pay/wallet` | WalletPayReservationController |
| POST | `/v1/places/{place}/hotels/{hotel}/manage/reservations/{reservation}/pay/stripe` | StripePayReservationController |
| POST | `/v1/places/{place}/hotels/{hotel}/manage/reservations/{reservation}/pay/pos` | PosPayReservationController |
| POST | `/v1/places/{place}/hotels/{hotel}/manage/reservations/{reservation}/pay/cash` | CashPayReservationController |
| POST | `/v1/places/{place}/hotels/{hotel}/manage/reservations/{reservation}/pay/crypto` | CryptoPayReservationController |
| GET | `/v1/places/{place}/hotels/{hotel}/manage/reservations/{reservation}/cancellation-policy` | ShowCancellationPolicyController |
| GET | `/v1/places/{place}/hotels/{hotel}/manage/reservations/{reservation}/invoice` | DownloadReservationInvoiceController |
| POST | `/v1/places/{place}/hotels/{hotel}/manage/reservations/{reservation}/invoice/send` | SendReservationInvoiceController |

## Cancellation Logic

```
PENDING → cancel: no penalty, no refund needed
CONFIRMED + within free window (≥ free_cancellation_hours before check-in): no penalty
CONFIRMED + outside free window: penalty = first_night_rate × cancellation_penalty_percentage / 100
  - Prepaid: partial refund (prepaid − penalty) via wallet credit-back
  - Not prepaid: penalty Order created
Manager waive_penalty=true: always no penalty regardless of timing
CHECKED_IN / CHECKED_OUT: cannot cancel (422)
```

## Quality Checks

- **Pint:** PASS (0 issues)
- **PHPStan level 7:** PASS (0 errors)
- **Pest tests:** 81 passing (18 Phase 1 + 14 Phase 2 + 11 Phase 3 + 8 Phase 7 + 13 Phase 4 + 17 Phase 5)

## Design Decisions

- **Checkout = CheckOut + GenerateFolio**: Single POST endpoint performs both operations atomically.
- **Payment controllers find order by reservation**: Instead of passing order ID, controllers find the latest WAITING_PAYMENT order for the reservation.
- **Reuse Shop payment actions**: POS and Cash payment controllers reuse `ProcessPosPaymentAction` and `ProcessCashPaymentAction` from ShopManager.
- **Reuse Shop form requests**: PosPaymentRequest, CashPaymentRequest, SendInvoiceRequest reused from ShopManager.
- **Reuse Shop resources**: PaymentDetailsResource, InvoiceResource reused from ShopManager.
- **UpdateReservationStatusAction delegation**: CHECKED_IN status now delegates to CheckInGuestAction (with auto-room-assign). CANCELLED delegates to CancelReservationAction (with penalty calculation).
- **Wallet refund uses user's existing wallet**: CancelReservationAction loads `user.wallet` fresh to ensure correct wallet instance.

## Notes

- No commits created — user will commit manually.
- Swagger annotations present on all 9 new controllers.
- All exceptions extend ApiException with proper HTTP status codes.
