# Turista Use-Case Reference

This document describes the major use cases of the Turista reservation system, the data flow for each, the entities involved, and direct navigation links to the key methods and files.

> **Tip:** Links point from `docs/use-cases.md` to source files in the repository. Click a link to open the file in your IDE, then use your IDE’s navigation (Ctrl+Click / Go to Symbol / Go to Definition) to jump to the method, class, or property named in the link text.

---

## Table of Contents

1. [Authentication & Registration](#authentication--registration)
2. [User Profiles & Admin User Management](#user-profiles--admin-user-management)
3. [Buildings & Units](#buildings--units)
4. [Facilities](#facilities)
5. [Locations](#locations)
6. [Employees & Permissions](#employees--permissions)
7. [Online Reservations (System)](#online-reservations-system)
8. [Manual / On-Arrival Reservations](#manual--on-arrival-reservations)
9. [Pricing, Occasions, Discounts & Promo Codes](#pricing-occasions-discounts--promo-codes)
10. [Payments, Invoices, Receipts, Wallet & Refunds](#payments-invoices-receipts-wallet--refunds)
11. [Availability & Calendar](#availability--calendar)
12. [Dashboards & Reporting](#dashboards--reporting)
13. [Notifications](#notifications)
14. [Scheduled Background Jobs](#scheduled-background-jobs)

---

## Authentication & Registration

### UC-1: Login

**Actors:** Guest / any verified role (`customer`, `owner`, `employee`, `admin`, `super_admin`).

**Entry point:** `POST /api/v1/login` — defined in [`routes/api.php:46`](../routes/api.php).

**Data flow:**
1. [`LoginRequest`](../app/Http/Requests/Auth/LoginRequest.php) validates `email` **or** `whatsapp_number` plus `password`.
2. [`AuthController::login`](../app/Http/Controllers/Api/AuthController.php) chooses the lookup field and fetches the [`User`](../app/Models/User.php).
3. Checks password and `is_verified`; unverified or invalid credentials return `401`.
4. Calls [`AuthService::issueAuthToken`](../app/Services/AuthService.php) to create a Sanctum token, eager-load roles/permissions, and return a [`LoginResource`](../app/Http/Resources/LoginResource.php).

**Navigation:**
[`routes/api.php:46`](../routes/api.php) → [`AuthController::login`](../app/Http/Controllers/Api/AuthController.php) → [`AuthService::issueAuthToken`](../app/Services/AuthService.php) → [`LoginResource`](../app/Http/Resources/LoginResource.php)

---

### UC-2: Owner Registration

**Actors:** Guest.

**Entry point:** `POST /api/v1/register/owner` — [`routes/api.php:47`](../routes/api.php).

**Data flow:**
1. [`RegisterOwnerRequest`](../app/Http/Requests/Auth/RegisterOwnerRequest.php) validates `name`, optional email/WhatsApp (at least one), optional `phone`, and confirmed password.
2. In a DB transaction, [`AuthController::registerOwner`](../app/Http/Controllers/Api/AuthController.php) creates a [`User`](../app/Models/User.php) with `is_verified = false` and an [`Owner`](../app/Models/Owner.php) profile (`id = user->id`, `status = 'pending'`).
3. Assigns the `owner` role.
4. If a WhatsApp number is provided, [`OtpService::send`](../app/Services/OtpService.php) generates and sends a 6-digit OTP.
5. Returns [`OwnerResource`](../app/Http/Resources/OwnerResource.php).

**Navigation:**
[`routes/api.php:47`](../routes/api.php) → [`AuthController::registerOwner`](../app/Http/Controllers/Api/AuthController.php) → [`Owner::create`](../app/Models/Owner.php) / [`OtpService::send`](../app/Services/OtpService.php)

---

### UC-3: Customer Registration

**Actors:** Guest.

**Entry point:** `POST /api/v1/register/customer` — [`routes/api.php:48`](../routes/api.php).

**Data flow:**
1. [`RegisterCustomerRequest`](../app/Http/Requests/Auth/RegisterCustomerRequest.php) validates the same fields as owner registration.
2. [`AuthController::registerCustomer`](../app/Http/Controllers/Api/AuthController.php) creates [`User`](../app/Models/User.php) and [`Customer`](../app/Models/Customer.php) (`wallet = 0.00`).
3. If the WhatsApp number matches an existing [`PendingCustomer`](../app/Models/PendingCustomer.php):
   - Marks the pending customer and user as verified.
   - Re-links pending reservations from `PendingCustomer` to `Customer` by updating `customer_type` and `customer_id`.
4. Sends an OTP only when there is **no** pending match and a WhatsApp number exists.
5. Returns [`CustomerResource`](../app/Http/Resources/CustomerResource.php).

**Navigation:**
[`routes/api.php:48`](../routes/api.php) → [`AuthController::registerCustomer`](../app/Http/Controllers/Api/AuthController.php) → [`PendingCustomer`](../app/Models/PendingCustomer.php) → [`CustomerResource`](../app/Http/Resources/CustomerResource.php)

---

### UC-4: Account Verification via OTP

**Actors:** Unverified user.

**Entry point:** `POST /api/v1/verify-account` — [`routes/api.php:49`](../routes/api.php).

**Data flow:**
1. [`VerifyOtpRequest`](../app/Http/Requests/Auth/VerifyOtpRequest.php) validates `whatsapp_number` or `email` and a 6-character `otp`.
2. [`AuthService::findUserByContact`](../app/Services/AuthService.php) resolves the user.
3. [`OtpService::verify`](../app/Services/OtpService.php) checks the cached code (max 5 attempts).
4. On success, flips `is_verified` to `true` and issues a token via [`AuthService::issueAuthToken`](../app/Services/AuthService.php).

**Navigation:**
[`routes/api.php:49`](../routes/api.php) → [`AuthController::verifyAccount`](../app/Http/Controllers/Api/AuthController.php) → [`OtpService::verify`](../app/Services/OtpService.php) → [`AuthService::issueAuthToken`](../app/Services/AuthService.php)

---

### UC-5: Resend OTP

**Actors:** Unverified user.

**Entry point:** `POST /api/v1/resend-otp` — [`routes/api.php:50`](../routes/api.php).

**Data flow:**
1. [`ResendOtpRequest`](../app/Http/Requests/Auth/ResendOtpRequest.php) validates contact info.
2. Resolves the user; if already verified, returns a generic success message.
3. Calls [`OtpService::send`](../app/Services/OtpService.php) to regenerate the OTP.

---

### UC-6: Forgot Password

**Actors:** Guest.

**Entry point:** `POST /api/v1/forgot-password` — [`routes/api.php:51`](../routes/api.php).

**Data flow:**
1. [`ForgotPasswordRequest`](../app/Http/Requests/Auth/ForgotPasswordRequest.php) validates `whatsapp_number`.
2. Looks up the user; if found, sends an OTP via [`OtpService::send`](../app/Services/OtpService.php).
3. Returns an opaque success message regardless of whether the account exists.

---

### UC-7: Reset Password

**Actors:** Guest with a valid reset OTP.

**Entry point:** `POST /api/v1/reset-password` — [`routes/api.php:52`](../routes/api.php).

**Data flow:**
1. [`ResetPasswordRequest`](../app/Http/Requests/Auth/ResetPasswordRequest.php) validates `whatsapp_number`, `otp`, and confirmed `password`.
2. Verifies OTP with [`OtpService::verify`](../app/Services/OtpService.php).
3. In a transaction, updates the hashed password and revokes all Sanctum tokens.

---

### UC-8: Logout

**Actors:** Any authenticated, verified user.

**Entry point:** `POST /api/v1/logout` — [`routes/api.php:73`](../routes/api.php).

**Data flow:**
[`AuthController::logout`](../app/Http/Controllers/Api/AuthController.php) deletes the current access token.

---

### UC-9: Update Password (Authenticated)

**Actors:** Any authenticated, verified user.

**Entry point:** `PUT /api/v1/update-password` — [`routes/api.php:74`](../routes/api.php).

**Data flow:**
1. Validates `current_password` and confirmed `password`.
2. Updates the hashed password and revokes all tokens.

---

### UC-10: Create or Promote Super Admin (Console)

**Actors:** System operator / deployer.

**Entry point:** `php artisan admin:create-super {email}` — [`app/Console/Commands/CreateSuperAdmin.php`](../app/Console/Commands/CreateSuperAdmin.php).

**Data flow:**
1. Validates/prompts for name, WhatsApp, password.
2. If the user exists, confirms promotion (requires `--force` or interactive confirmation) and assigns `super_admin`.
3. If not, creates a verified user and assigns `super_admin`.

---

## User Profiles & Admin User Management

### UC-11: View Authenticated Profile

**Actors:** Any authenticated, verified user.

**Entry point:** `GET /api/v1/profile` — [`routes/api.php:76`](../routes/api.php).

**Data flow:**
1. [`ProfileController::show`](../app/Http/Controllers/Api/ProfileController.php) loads roles and permissions.
2. Chooses a resource based on role:
   - Owner → [`OwnerResource`](../app/Http/Resources/OwnerResource.php)
   - Customer → [`CustomerResource`](../app/Http/Resources/CustomerResource.php)
   - Employee → [`EmployeeResource`](../app/Http/Resources/EmployeeResource.php)
3. Returns `{ role, permissions, profile }`.

---

### UC-12: Update Authenticated Profile

**Actors:** Any authenticated, verified user.

**Entry point:** `PUT /api/v1/profile` — [`routes/api.php:77`](../routes/api.php).

**Data flow:**
1. [`UpdateProfileRequest`](../app/Http/Requests/Customer/UpdateProfileRequest.php) validates name, email (unique ignoring current user), WhatsApp, and phone.
2. Splits input into user fields and profile fields.
3. Updates [`User`](../app/Models/User.php) and the matching profile (`owner` or `customer`).
4. Returns the updated profile via [`ProfileController::show`](../app/Http/Controllers/Api/ProfileController.php).

---

### UC-13: Upload Owner Logo

**Actors:** Owner.

**Entry point:** `POST /api/v1/owner/logo` — [`routes/api.php:118`](../routes/api.php).

**Data flow:**
1. [`UploadOwnerLogoRequest`](../app/Http/Requests/Owner/UploadOwnerLogoRequest.php) validates an image ≤ 5 MB.
2. [`OwnerProfileController::uploadLogo`](../app/Http/Controllers/Api/Owner/OwnerProfileController.php) compresses the image via [`ImageCompressor`](../app/Services/ImageCompressor.php), clears the existing `logo` media collection, and adds the new file.
3. Returns [`OwnerResource`](../app/Http/Resources/OwnerResource.php).

---

### UC-14: Admin List Platform Users

**Actors:** `admin`, `super_admin`.

**Entry point:** `GET /api/v1/admin/users` — [`routes/api.php:101`](../routes/api.php).

**Data flow:**
1. [`IndexUsersRequest`](../app/Http/Requests/Admin/IndexUsersRequest.php) validates filters (role, verified, employee status, wallet presence, date range, search).
2. [`Admin\UserController::index`](../app/Http/Controllers/Admin/UserController.php) builds the query and returns [`AdminUserResource`](../app/Http/Resources/Admin/AdminUserResource.php) collection.

---

### UC-15: Create a New Admin

**Actors:** `super_admin`.

**Entry point:** `POST /api/v1/admins` — [`routes/api.php:110`](../routes/api.php).

**Data flow:**
1. [`RegisterAdminRequest`](../app/Http/Requests/Auth/RegisterAdminRequest.php) validates name, email, and confirmed password.
2. [`AdminController::store`](../app/Http/Controllers/Api/Admin/AdminController.php) creates a verified [`User`](../app/Models/User.php) and assigns the `admin` role.
3. Returns [`UserResource`](../app/Http/Resources/UserResource.php).

---

### UC-16: Admin List Owners

**Actors:** `admin`, `super_admin`.

**Entry point:** `GET /api/v1/owners` — [`routes/api.php:108`](../routes/api.php).

**Data flow:**
1. [`IndexOwnersRequest`](../app/Http/Requests/Owner/IndexOwnersRequest.php) validates filters.
2. [`OwnerController::index`](../app/Http/Controllers/OwnerController.php) queries [`Owner`](../app/Models/Owner.php) with `withCount('buildings')` and `with('user')`.
3. Returns [`OwnerResource`](../app/Http/Resources/OwnerResource.php) collection.

---

### UC-17: Verify / Update Owner Status

**Actors:** `admin`, `super_admin`.

**Entry point:** `POST /api/v1/owners/{owner}/verify` — [`routes/api.php:109`](../routes/api.php).

**Data flow:**
1. [`OwnerPolicy::verify`](../app/Policies/OwnerPolicy.php) authorizes.
2. [`OwnerProfileController::verify`](../app/Http/Controllers/Api/Owner/OwnerProfileController.php) validates `status` (`pending`, `active`, `suspended`) and updates the owner.
3. Returns [`OwnerResource`](../app/Http/Resources/OwnerResource.php).

---

### UC-18: List Customers

**Actors:** `admin`, `super_admin`, `owner`, `employee`.

**Entry points:**
- `GET /api/v1/customers` — [`routes/api.php:107`](../routes/api.php)
- `GET /api/v1/owner/customers` — [`routes/api.php:168`](../routes/api.php)

**Data flow:**
1. [`CustomerPolicy::viewAny`](../app/Policies/CustomerPolicy.php) authorizes.
2. [`CustomerController::index`](../app/Http/Controllers/Api/Customer/CustomerController.php) scopes the query:
   - Admin → all customers
   - Owner → customers with reservations in owner’s buildings
   - Employee → customers with reservations in the employee’s owner’s buildings
3. Returns [`CustomerResource`](../app/Http/Resources/CustomerResource.php) collection.

---

### UC-19: Delete Customer (Admin)

**Actors:** `admin`, `super_admin`.

**Entry point:** `DELETE /api/v1/customers/{customer}` — [`routes/api.php:107`](../routes/api.php).

**Data flow:**
1. [`CustomerPolicy::delete`](../app/Policies/CustomerPolicy.php) authorizes.
2. [`CustomerController::destroy`](../app/Http/Controllers/Api/Customer/CustomerController.php) soft-deletes the customer.

---

## Buildings & Units

### UC-20: List Owner Buildings

**Actors:** Approved owner, active employee.

**Entry point:** `GET /api/v1/buildings` — [`routes/api.php:151`](../routes/api.php).

**Data flow:**
1. [`ListingFilterRequest`](../app/Http/Requests/ListingFilterRequest.php) validates filters.
2. [`BuildingPolicy::viewAny`](../app/Policies/BuildingPolicy.php) authorizes.
3. [`BuildingController::index`](../app/Http/Controllers/BuildingController.php) scopes to the owner or the employee’s owner.
4. [`FilterService::applyToOwnerBuildingQuery`](../app/Services/Filter/FilterService.php) applies status, location, price, payment method, and search filters.
5. Returns [`BuildingResource`](../app/Http/Resources/BuildingResource.php) collection.

---

### UC-21: Create Building

**Actors:** Approved owner / active employee with `buildings.create`.

**Entry point:** `POST /api/v1/buildings` — [`routes/api.php:151`](../routes/api.php).

**Data flow:**
1. [`BuildingRequest`](../app/Http/Requests/BuildingRequest.php) validates input.
2. [`BuildingPolicy::create`](../app/Policies/BuildingPolicy.php) authorizes.
3. [`BuildingController::store`](../app/Http/Controllers/BuildingController.php) resolves the owner, auto-generates a slug, sets currency from the selected region, and creates the [`Building`](../app/Models/Building.php).
4. [`HandlesMediaPhotos::storePhotos`](../app/Traits/HandlesMediaPhotos.php) compresses and stores photos in the `documents` collection.

**Navigation:**
[`routes/api.php:151`](../routes/api.php) → [`BuildingController::store`](../app/Http/Controllers/BuildingController.php) → [`BuildingRequest`](../app/Http/Requests/BuildingRequest.php) → [`Building`](../app/Models/Building.php) + [`HandlesMediaPhotos`](../app/Traits/HandlesMediaPhotos.php)

---

### UC-22: View Building

**Actors:** Owner, employee, admin, super_admin.

**Entry point:** `GET /api/v1/buildings/{building}` — [`routes/api.php:151`](../routes/api.php).

**Data flow:**
1. [`BuildingPolicy::view`](../app/Policies/BuildingPolicy.php) authorizes.
2. [`BuildingController::show`](../app/Http/Controllers/BuildingController.php) returns [`BuildingResource`](../app/Http/Resources/BuildingResource.php) with region, owner, facilities, media, and units.

---

### UC-23: Update Building

**Actors:** Approved owner / active employee with `buildings.update`.

**Entry point:** `PUT/PATCH /api/v1/buildings/{building}` — [`routes/api.php:151`](../routes/api.php).

**Data flow:**
1. [`BuildingUpdateRequest`](../app/Http/Requests/BuildingUpdateRequest.php) validates input.
2. [`BuildingPolicy::update`](../app/Policies/BuildingPolicy.php) authorizes.
3. [`BuildingController::update`](../app/Http/Controllers/BuildingController.php) updates currency if the region changed, persists data, and replaces photos via [`HandlesMediaPhotos::replacePhotos`](../app/Traits/HandlesMediaPhotos.php).

---

### UC-24: Delete / Disable Building

**Actors:** Approved owner / active employee with `buildings.delete`.

**Entry points:**
- `DELETE /api/v1/buildings/{building}` — [`routes/api.php:151`](../routes/api.php)
- `PUT /api/v1/buildings/{building}/disable` — [`routes/api.php:153`](../routes/api.php)

**Data flow:**
- Delete: [`BuildingPolicy::delete`](../app/Policies/BuildingPolicy.php) → [`BuildingController::destroy`](../app/Http/Controllers/BuildingController.php) soft-deletes the building.
- Disable: [`BuildingController::disable`](../app/Http/Controllers/BuildingController.php) updates `status` to `active`, `inactive`, or `hidden`.

---

### UC-25: Manage Building Photos

**Actors:** Approved owner / active employee with `buildings.update`.

**Entry point:** `POST /api/v1/buildings/{building}/photos` — [`routes/api.php:154`](../routes/api.php).

**Data flow:**
1. Validates `photos.*.photo` via [`PhotoFileRules::forSinglePhoto`](../app/Rules/PhotoFileRules.php).
2. [`BuildingController::addPhotos`](../app/Http/Controllers/BuildingController.php) stores compressed photos via [`HandlesMediaPhotos::storePhotos`](../app/Traits/HandlesMediaPhotos.php).

---

### UC-26: Manage Building Facilities

**Actors:** Approved owner / active employee with `buildings.update`.

**Entry points:**
- `GET /api/v1/buildings/{building}/facilities` — [`routes/api.php:156`](../routes/api.php)
- `POST /api/v1/buildings/{building}/facilities` — [`routes/api.php:157`](../routes/api.php)
- `DELETE /api/v1/buildings/{building}/facilities/{facility}` — [`routes/api.php:158`](../routes/api.php)

**Data flow:**
1. [`AssignFacilitiesRequest`](../app/Http/Requests/AssignFacilitiesRequest.php) validates `facility_ids`.
2. [`BuildingController::assignFacilities`](../app/Http/Controllers/BuildingController.php) uses `syncWithoutDetaching` on [`Building::facilities()`](../app/Models/Building.php).
3. [`BuildingController::unassignFacility`](../app/Http/Controllers/BuildingController.php) detaches a facility.

---

### UC-27: Admin List Buildings

**Actors:** `admin`, `super_admin`.

**Entry point:** `GET /api/v1/admin/buildings` — [`routes/api.php:94`](../routes/api.php).

**Data flow:**
1. [`ListBuildingsRequest`](../app/Http/Requests/ListBuildingsRequest.php) validates filters.
2. [`BuildingController::adminIndex`](../app/Http/Controllers/BuildingController.php) applies [`FilterService::applyToAdminBuildingQuery`](../app/Services/Filter/FilterService.php).

---

### UC-28: List Units

**Actors:** Public, approved owner, active employee.

**Entry points:**
- `GET /api/v1/units` — public catalog — [`routes/api.php:55`](../routes/api.php)
- `GET /api/v1/owner/units` — owner scope — [`routes/api.php:144`](../routes/api.php)
- `GET /api/v1/buildings/{building}/units` — building scope — [`routes/api.php:159`](../routes/api.php)

**Data flow:**
1. [`ListUnitsRequest`](../app/Http/Requests/ListUnitsRequest.php) validates filters.
2. [`UnitPolicy::viewAny`](../app/Policies/UnitPolicy.php) authorizes non-public calls.
3. [`UnitController::index`](../app/Http/Controllers/UnitController.php) scopes by owner/building and applies [`FilterService::applyToUnitQuery`](../app/Services/Filter/FilterService.php).
4. Returns [`UnitResource`](../app/Http/Resources/UnitResource.php) collection.

---

### UC-29: Create Unit

**Actors:** Approved owner / active employee with `units.create`.

**Entry point:** `POST /api/v1/owner/units` — [`routes/api.php:145`](../routes/api.php).

**Data flow:**
1. [`UnitRequest`](../app/Http/Requests/UnitRequest.php) validates pricing, capacity, and building ownership.
2. [`UnitPolicy::create`](../app/Policies/UnitPolicy.php) authorizes.
3. [`UnitController::store`](../app/Http/Controllers/UnitController.php) verifies building ownership, generates a slug, creates the [`Unit`](../app/Models/Unit.php), and stores photos.

---

### UC-30: Bulk Create Units

**Actors:** Approved owner / active employee with `units.create`.

**Entry point:** `POST /api/v1/buildings/{building}/units/bulk` — [`routes/api.php:155`](../routes/api.php).

**Data flow:**
1. [`BulkUnitRequest`](../app/Http/Requests/BulkUnitRequest.php) validates a shared payload plus a `units` array.
2. [`UnitController::bulkStore`](../app/Http/Controllers/UnitController.php) checks ownership and creates each unit with a generated slug.

---

### UC-31: View Unit

**Actors:** Public, owner, employee, customer.

**Entry point:** `GET /api/v1/units/{unit}` — [`routes/api.php:56`](../routes/api.php).

**Data flow:**
1. [`UnitController::show`](../app/Http/Controllers/UnitController.php) validates optional `from`/`to`/`year`.
2. Loads building media, photos, facilities, and availability rows via [`UnitAvailabilityService::applyYearScope`](../app/Services/UnitAvailabilityService.php).
3. Returns [`UnitResource`](../app/Http/Resources/UnitResource.php).

---

### UC-32: Update Unit

**Actors:** Approved owner / active employee with `units.update`.

**Entry point:** `PUT/PATCH /api/v1/owner/units/{unit}` — [`routes/api.php:145`](../routes/api.php).

**Data flow:**
1. [`UnitUpdateRequest`](../app/Http/Requests/UnitUpdateRequest.php) validates input.
2. [`UnitPolicy::update`](../app/Policies/UnitPolicy.php) authorizes.
3. [`UnitController::update`](../app/Http/Controllers/UnitController.php) persists changes and replaces photos.

---

### UC-33: Delete Unit

**Actors:** Approved owner / active employee with `units.delete`.

**Entry point:** `DELETE /api/v1/owner/units/{unit}` — [`routes/api.php:145`](../routes/api.php).

**Data flow:**
[`UnitPolicy::delete`](../app/Policies/UnitPolicy.php) → [`UnitController::destroy`](../app/Http/Controllers/UnitController.php) soft-deletes the unit.

---

### UC-34: Manage Unit Photos

**Actors:** Approved owner / active employee with `units.update`.

**Entry point:** `POST /api/v1/owner/units/{unit}/photos` — [`routes/api.php:146`](../routes/api.php).

**Data flow:**
[`UnitController::addPhotos`](../app/Http/Controllers/UnitController.php) validates and appends compressed photos.

---

### UC-35: Manage Unit Facilities

**Actors:** Approved owner / active employee with `units.update`.

**Entry points:**
- `GET /api/v1/owner/units/{unit}/facilities` — [`routes/api.php:147`](../routes/api.php)
- `POST /api/v1/owner/units/{unit}/facilities` — [`routes/api.php:148`](../routes/api.php)
- `DELETE /api/v1/owner/units/{unit}/facilities/{facility}` — [`routes/api.php:149`](../routes/api.php)

**Data flow:**
[`UnitController::assignFacilities`](../app/Http/Controllers/UnitController.php) and [`UnitController::unassignFacility`](../app/Http/Controllers/UnitController.php) sync facilities on [`Unit::facilities()`](../app/Models/Unit.php).

---

## Facilities

### UC-36: CRUD Global Facilities

**Actors:** Approved owner (CRUD), employee (view).

**Entry points:**
- `GET /api/v1/facilities` — [`routes/api.php:152`](../routes/api.php)
- `POST /api/v1/facilities` — [`routes/api.php:152`](../routes/api.php)
- `GET /api/v1/facilities/{facility}` — [`routes/api.php:152`](../routes/api.php)
- `PUT/PATCH /api/v1/facilities/{facility}` — [`routes/api.php:152`](../routes/api.php)
- `DELETE /api/v1/facilities/{facility}` — [`routes/api.php:152`](../routes/api.php)

**Data flow:**
1. [`FacilityRequest`](../app/Http/Requests/FacilityRequest.php) validates name/description/opening hours.
2. [`FacilityPolicy`](../app/Policies/FacilityPolicy.php) gates create/update/delete to approved owners.
3. [`FacilityController`](../app/Http/Controllers/FacilityController.php) performs CRUD on [`Facility`](../app/Models/Facility.php).

**Models / relations:**
- [`Facility::units()`](../app/Models/Facility.php)
- [`Facility::buildings()`](../app/Models/Facility.php)

---

## Locations

### UC-37: Public Location Indexes

**Actors:** Public.

**Entry points:**
- `GET /api/v1/countries` — [`routes/api.php:59`](../routes/api.php)
- `GET /api/v1/countries/{country}` — [`routes/api.php:59`](../routes/api.php)
- `GET /api/v1/cities` — [`routes/api.php:61`](../routes/api.php)
- `GET /api/v1/cities/{city}` — [`routes/api.php:61`](../routes/api.php)
- `GET /api/v1/regions` — [`routes/api.php:63`](../routes/api.php)
- `GET /api/v1/regions/{region}` — [`routes/api.php:63`](../routes/api.php)
- `GET /api/v1/currencies` — [`routes/api.php:69`](../routes/api.php)
- `GET /api/v1/currencies/{currency}` — [`routes/api.php:69`](../routes/api.php)

**Data flow:**
1. List requests validate filters.
2. [`FilterService`](../app/Services/Filter/FilterService.php) applies search and parent filters.
3. Controllers return resource collections for [`Country`](../app/Models/Country.php), [`City`](../app/Models/City.php), [`Region`](../app/Models/Region.php), and [`Currency`](../app/Models/Currency.php).

---

### UC-38: Location Bootstrap

**Actors:** Public.

**Entry point:** `GET /api/v1/locations/bootstrap` — [`routes/api.php:66`](../routes/api.php).

**Data flow:**
1. [`LocationBootstrapController::index`](../app/Http/Controllers/LocationBootstrapController.php) reads from cache key `locations:bootstrap` (24h TTL) or builds the payload.
2. [`LocationBootstrapController::buildPayload`](../app/Http/Controllers/LocationBootstrapController.php) returns countries with active cities and regions.
3. [`Country`](../app/Models/Country.php), [`City`](../app/Models/City.php), and [`Region`](../app/Models/Region.php) clear the cache on save/delete.

---

### UC-39: Admin Location CRUD

**Actors:** `admin`, `super_admin`.

**Entry points:**
- `POST/PUT/PATCH/DELETE /api/v1/admin/countries` — [`routes/api.php:96`](../routes/api.php)
- `POST/PUT/PATCH/DELETE /api/v1/admin/cities` — [`routes/api.php:95`](../routes/api.php)
- `POST/PUT/PATCH/DELETE /api/v1/admin/regions` — [`routes/api.php:98`](../routes/api.php)
- `POST/PUT/PATCH/DELETE /api/v1/admin/currencies` — [`routes/api.php:97`](../routes/api.php)

**Data flow:**
1. Dedicated request classes (`CountryRequest`, `CityRequest`, `RegionRequest`, `CurrencyRequest`) validate input.
2. Policies restrict mutations to admins.
3. Controllers persist records and clear the location bootstrap cache.

**Key methods:**
- [`CountryController::store`](../app/Http/Controllers/CountryController.php)
- [`CityController::store`](../app/Http/Controllers/CityController.php)
- [`RegionController::store`](../app/Http/Controllers/RegionController.php)
- [`CurrencyController::store`](../app/Http/Controllers/CurrencyController.php)

---

### Location Model Relations

| Model | Relations |
|-------|-----------|
| [`Country`](../app/Models/Country.php) | `currency()`, `cities()`, cache clear on save/delete |
| [`City`](../app/Models/City.php) | `country()`, `regions()` |
| [`Region`](../app/Models/Region.php) | `city()`, `buildings()`, `units()` |
| [`Currency`](../app/Models/Currency.php) | `countries()` |

---

## Employees & Permissions

### UC-40: List Employees

**Actors:** Approved owner, active employee with `employees.view`, admin.

**Entry points:**
- `GET /api/v1/employees` — [`routes/api.php:161`](../routes/api.php)
- `GET /api/v1/admin/employees` — [`routes/api.php:100`](../routes/api.php)

**Data flow:**
1. [`ListEmployeesRequest`](../app/Http/Requests/ListEmployeesRequest.php) validates filters.
2. [`EmployeeController::index`](../app/Http/Controllers/Api/Owner/EmployeeController.php) resolves the owner ID and filters by status, building assignment, or search.
3. [`EmployeeController::all`](../app/Http/Controllers/Api/Owner/EmployeeController.php) returns all employees for admins.

---

### UC-41: Create Employee

**Actors:** Approved owner, active employee with `employees.create`.

**Entry point:** `POST /api/v1/employees` — [`routes/api.php:161`](../routes/api.php).

**Data flow:**
1. [`StoreEmployeeRequest`](../app/Http/Requests/Owner/StoreEmployeeRequest.php) validates and resolves building IDs.
2. In a transaction, [`EmployeeController::store`](../app/Http/Controllers/Api/Owner/EmployeeController.php) creates a [`User`](../app/Models/User.php), an [`Employee`](../app/Models/Employee.php) record with the same PK, assigns the `employee` role, and syncs building assignments.

**Navigation:**
[`routes/api.php:161`](../routes/api.php) → [`EmployeeController::store`](../app/Http/Controllers/Api/Owner/EmployeeController.php) → [`StoreEmployeeRequest::buildingIds`](../app/Http/Requests/Owner/StoreEmployeeRequest.php) → [`Employee`](../app/Models/Employee.php)

---

### UC-41.5: Employee First Login & Forced Password Change

**Actors:** Newly created `employee`.

**Entry points:**
- `POST /api/v1/login` — [`routes/api.php:46`](../routes/api.php)
- `POST /api/v1/verify-account` — [`routes/api.php:49`](../routes/api.php)
- `POST /api/v1/auth/force-change-password` — [`routes/api.php:76`](../routes/api.php)

**Data flow:**
1. Owner creates the employee via UC-41 with a WhatsApp number and an initial temporary password. The employee is created with `is_verified = false` and `must_change_password = true`.
2. Employee logs in with `whatsapp_number` and the temporary password.
3. [`AuthController::login`](../app/Http/Controllers/Api/AuthController.php) checks the password, sees the employee is unverified, sends an OTP via [`OtpService::send`](../app/Services/OtpService.php), and returns `403` with `code: "account_unverified"` and the phone number.
4. Web redirects to the OTP verification screen and calls `POST /api/v1/verify-account`.
5. [`AuthController::verifyAccount`](../app/Http/Controllers/Api/AuthController.php) validates the OTP, flips `is_verified` to `true`, and issues a Sanctum token. The [`LoginResource`](../app/Http/Resources/LoginResource.php) includes `must_change_password: true`.
6. Web redirects to the forced change-password screen and calls `POST /api/v1/auth/force-change-password`.
7. [`AuthController::forceChangePassword`](../app/Http/Controllers/Api/AuthController.php) validates `password` + `password_confirmation`, updates the hashed password, sets `must_change_password = false`, revokes old tokens, and issues a fresh token.
8. [`EnsurePasswordChanged`](../app/Http/Middleware/EnsurePasswordChanged.php) middleware guards all authenticated routes (except logout and force-change-password). While `must_change_password` is true it returns `403` with `code: "password_change_required"`.

**Navigation:**
[`routes/api.php:46`](../routes/api.php) → [`AuthController::login`](../app/Http/Controllers/Api/AuthController.php) → [`OtpService::send`](../app/Services/OtpService.php) → [`routes/api.php:49`](../routes/api.php) → [`AuthController::verifyAccount`](../app/Http/Controllers/Api/AuthController.php) → [`AuthService::issueAuthToken`](../app/Services/AuthService.php) → [`routes/api.php:76`](../routes/api.php) → [`AuthController::forceChangePassword`](../app/Http/Controllers/Api/AuthController.php) → [`EnsurePasswordChanged`](../app/Http/Middleware/EnsurePasswordChanged.php)

---

### UC-42: Update Employee

**Actors:** Approved owner, active employee with `employees.update` (self-edit always allowed).

**Entry points:**
- `PUT /api/v1/employees/{employee}` — [`routes/api.php:161`](../routes/api.php)
- `PUT /api/v1/employees/{employee}` (global route) — [`routes/api.php:87`](../routes/api.php)

**Data flow:**
1. [`UpdateEmployeeRequest`](../app/Http/Requests/Owner/UpdateEmployeeRequest.php) validates and authorizes.
2. [`EmployeeController::update`](../app/Http/Controllers/Api/Owner/EmployeeController.php) updates [`User`](../app/Models/User.php) and [`Employee`](../app/Models/Employee.php) data and syncs buildings.

---

### UC-43: Assign Buildings to Employee

**Actors:** Approved owner, active employee with `employees.update`.

**Entry point:** `POST /api/v1/employees/{employee}/assign-buildings` — [`routes/api.php:162`](../routes/api.php).

**Data flow:**
1. [`AssignBuildingRequest`](../app/Http/Requests/Owner/AssignBuildingRequest.php) validates building IDs.
2. [`EmployeeController::assignBuilding`](../app/Http/Controllers/Api/Owner/EmployeeController.php) syncs [`employee->buildings()`](../app/Models/Employee.php).

---

### UC-44: Delete Employee

**Actors:** Approved owner, active employee with `employees.delete`.

**Entry point:** `DELETE /api/v1/employees/{employee}` — [`routes/api.php:161`](../routes/api.php).

**Data flow:**
[`EmployeePolicy::delete`](../app/Policies/EmployeePolicy.php) → [`EmployeeController::destroy`](../app/Http/Controllers/Api/Owner/EmployeeController.php) soft-deletes the user and employee records.

---

### UC-45: View Permission Catalog

**Actors:** Owner.

**Entry point:** `GET /api/v1/owner/permission-catalog` — [`routes/api.php:125`](../routes/api.php).

**Data flow:**
[`PermissionCatalogController::index`](../app/Http/Controllers/Api/Owner/PermissionCatalogController.php) reads [`config/permissions.php`](../config/permissions.php) and returns [`PermissionCatalog::matrix()`](../app/Support/PermissionCatalog.php).

---

### UC-46: Permission Templates CRUD

**Actors:** Owner.

**Entry points:**
- `GET /api/v1/owner/permission-templates` — [`routes/api.php:128`](../routes/api.php)
- `POST /api/v1/owner/permission-templates` — [`routes/api.php:128`](../routes/api.php)
- `GET /api/v1/owner/permission-templates/{permissionTemplate}`
- `PUT/PATCH /api/v1/owner/permission-templates/{permissionTemplate}`
- `DELETE /api/v1/owner/permission-templates/{permissionTemplate}`

**Data flow:**
1. `StorePermissionTemplateRequest` / `UpdatePermissionTemplateRequest` validate permission names against [`PermissionCatalog::all()`](../app/Support/PermissionCatalog.php).
2. [`PermissionTemplateController`](../app/Http/Controllers/Api/Owner/PermissionTemplateController.php) scopes templates to `owner_id`.

**Key methods:**
- [`PermissionTemplateController::store`](../app/Http/Controllers/Api/Owner/PermissionTemplateController.php)
- [`PermissionTemplateController::update`](../app/Http/Controllers/Api/Owner/PermissionTemplateController.php)
- [`PermissionTemplatePolicy::update`](../app/Policies/PermissionTemplatePolicy.php)

---

### UC-47: Assign / Apply Employee Permissions

**Actors:** Owner.

**Entry points:**
- `GET /api/v1/owner/employees/{employee}/permissions` — [`routes/api.php:126`](../routes/api.php)
- `PUT /api/v1/owner/employees/{employee}/permissions` — [`routes/api.php:127`](../routes/api.php)
- `POST /api/v1/owner/employees/{employee}/permission-templates/{permissionTemplate}/apply` — [`routes/api.php:121`](../routes/api.php)

**Data flow:**
1. [`EmployeePolicy::assignPermissions`](../app/Policies/EmployeePolicy.php) authorizes.
2. [`UpdateEmployeePermissionsRequest`](../app/Http/Requests/Owner/UpdateEmployeePermissionsRequest.php) validates permission names.
3. [`EmployeePermissionController::update`](../app/Http/Controllers/Api/Owner/EmployeePermissionController.php) calls `$employee->user->syncPermissions(...)`.
4. [`EmployeePermissionController::applyTemplate`](../app/Http/Controllers/Api/Owner/EmployeePermissionController.php) applies a template’s permissions.

---

## Online Reservations (System)

### UC-48: Browse Public Unit Catalog & Check Availability

**Actors:** Guest, customer.

**Entry points:**
- `GET /api/v1/units` — [`routes/api.php:55`](../routes/api.php)
- `GET /api/v1/units/{unit}` — [`routes/api.php:56`](../routes/api.php)

**Data flow:**
1. [`UnitController::index`](../app/Http/Controllers/UnitController.php) determines if the call is public marketplace browsing.
2. [`FilterService::applyToUnitQuery`](../app/Services/Filter/FilterService.php) applies public filters:
   - Restricts to units with `status = 'available'` whose building is `active`.
   - Applies location, capacity, rooms, price, facilities, payment method, keyword, and sorting filters.
   - [`applyAvailabilityFilters`](../app/Services/Filter/FilterService.php) excludes nights already `blocked` or `booked`.
3. [`UnitController::show`](../app/Http/Controllers/UnitController.php) validates `from`/`to`/`year` and eager-loads availability rows so callers can see booked/blocked nights.

**Navigation:**
[`routes/api.php:55`](../routes/api.php) → [`UnitController::index`](../app/Http/Controllers/UnitController.php) → [`FilterService::applyToUnitQuery`](../app/Services/Filter/FilterService.php) → [`Unit::availabilities()`](../app/Models/Unit.php) → [`UnitAvailability`](../app/Models/UnitAvailability.php)

---

### UC-49: Create Customer Online Reservation

**Actors:** Customer.

**Entry point:** `POST /api/v1/customer/reservations` — [`routes/api.php:192`](../routes/api.php).

**Data flow:**
1. [`ReservationController::store`](../app/Http/Controllers/ReservationController.php) authorizes via [`ReservationPolicy::create`](../app/Policies/ReservationPolicy.php).
2. [`ReservationRequest`](../app/Http/Requests/ReservationRequest.php) validates `unit_id`, dates, guest counts, optional `promo_code`, required ID `photo`, and unit capacity.
3. [`ReservationService::createReservationForCustomer`](../app/Services/ReservationService.php) retries up to 5 times to avoid `reservation_number` collisions.
4. Inside [`persistReservation`](../app/Services/ReservationService.php):
   - Resolves [`Unit`](../app/Models/Unit.php).
   - Validates promo code via [`PromoCodeService::findValidForUnit`](../app/Services/PromoCodeService.php).
   - Validates occasion via [`Occasion::approved()->findOrFail`](../app/Models/Occasion.php).
   - Calls [`PricingService::calculateSubtotal`](../app/Services/PricingService.php) for base price, occasion split, and discount templates.
   - Applies promo-code discount.
   - Creates [`Reservation`](../app/Models/Reservation.php) with `source = 'online'`.
   - Calls [`UnitAvailabilityService::bookDates`](../app/Services/UnitAvailabilityService.php) to lock nights as `booked`.
   - Calls [`createDocument`](../app/Services/ReservationService.php) to generate [`Invoice`](../app/Models/Invoice.php) and [`Receipt`](../app/Models/Receipt.php).
   - Schedules reminders via [`ReservationReminderScheduler::scheduleFor`](../app/Services/ReservationReminderScheduler.php).
   - Compresses and stores the ID photo in the `documents` media collection.

**Navigation:**
[`routes/api.php:192`](../routes/api.php) → [`ReservationController::store`](../app/Http/Controllers/ReservationController.php) → [`ReservationService::createReservationForCustomer`](../app/Services/ReservationService.php) → [`persistReservation`](../app/Services/ReservationService.php) → [`PricingService::calculateSubtotal`](../app/Services/PricingService.php) + [`UnitAvailabilityService::bookDates`](../app/Services/UnitAvailabilityService.php) → [`Reservation`](../app/Models/Reservation.php)

---

### UC-50: List & Filter Reservations

**Actors:** Customer, owner, employee, admin.

**Entry points:**
- `GET /api/v1/customer/reservations` — [`routes/api.php:182`](../routes/api.php)
- `GET /api/v1/reservations` — [`routes/api.php:182`](../routes/api.php)
- `GET /api/v1/admin/reservations` — [`routes/api.php:93`](../routes/api.php)

**Data flow:**
1. [`ReservationController::index`](../app/Http/Controllers/ReservationController.php) authorizes `viewAny`.
2. Customers see only their own reservations.
3. Owners/employees see reservations whose unit's building belongs to the owner.
4. [`FilterService::applyToCustomerReservationQuery`](../app/Services/Filter/FilterService.php) or [`applyToAdminReservationQuery`](../app/Services/Filter/FilterService.php) applies filters.
5. [`ReservationController::adminIndex`](../app/Http/Controllers/ReservationController.php) adds the `owner_id` filter.

---

### UC-51: View Reservation Details

**Actors:** Customer (own), owner/employee (their buildings), admin.

**Entry points:**
- `GET /api/v1/customer/reservations/{reservation}` — [`routes/api.php:182`](../routes/api.php)
- `GET /api/v1/reservations/{reservation}` — [`routes/api.php:182`](../routes/api.php)

**Data flow:**
1. [`ReservationController::show`](../app/Http/Controllers/ReservationController.php) authorizes via [`ReservationPolicy::view`](../app/Policies/ReservationPolicy.php).
2. Returns [`ReservationResource`](../app/Http/Resources/ReservationResource.php) with loaded relations.

---

### UC-52: Update Reservation

**Actors:** Customer (own), owner/employee.

**Entry points:**
- `PUT /api/v1/customer/reservations/{reservation}` — [`routes/api.php:192`](../routes/api.php)
- `PUT /api/v1/reservations/{reservation}` — [`routes/api.php:182`](../routes/api.php)

**Data flow:**
1. [`ReservationController::update`](../app/Http/Controllers/ReservationController.php) authorizes via [`ReservationPolicy::update`](../app/Policies/ReservationPolicy.php).
2. [`ReservationUpdateRequest`](../app/Http/Requests/ReservationUpdateRequest.php) validates dates, unit, guest counts, promo code, and capacity.
3. [`ReservationService::updateReservation`](../app/Services/ReservationService.php):
   - Rejects updates to `checked_out` or `canceled` reservations.
   - Detects date/unit changes.
   - Validates the new unit belongs to the same owner.
   - Handles promo code swap/release.
   - Rebooks availability (release old + book new).
   - Calls [`recalculateReservation`](../app/Services/ReservationService.php) to update `total_price`, `discount_amount`, invoice, receipt, payment status, and refund overpayments to wallet if applicable.
   - Reschedules reminders.

---

### UC-53: Cancel Reservation

**Actors:** Customer (own), owner/employee.

**Entry points:**
- `DELETE /api/v1/customer/reservations/{reservation}` — [`routes/api.php:192`](../routes/api.php)
- `DELETE /api/v1/reservations/{reservation}` — [`routes/api.php:182`](../routes/api.php)

**Data flow:**
1. [`ReservationController::destroy`](../app/Http/Controllers/ReservationController.php) authorizes via [`ReservationPolicy::delete`](../app/Policies/ReservationPolicy.php).
2. Rejects if `status != 'pending'`.
3. In a transaction:
   - [`UnitAvailabilityService::releaseDates`](../app/Services/UnitAvailabilityService.php) releases the nights.
   - Sets reservation `status = 'canceled'`.
   - Deletes invoices and receipts.

---

### UC-54: Check-In / Check-Out Reservation

**Actors:** Owner / employee with `reservations.update`.

**Entry points:**
- `POST /api/v1/reservations/{reservation}/check-in` — [`routes/api.php:186`](../routes/api.php)
- `POST /api/v1/reservations/{reservation}/check-out` — [`routes/api.php:187`](../routes/api.php)

**Data flow:**
1. [`ReservationPolicy::checkIn`](../app/Policies/ReservationPolicy.php) / [`checkOut`](../app/Policies/ReservationPolicy.php) authorizes.
2. [`ReservationController::checkIn`](../app/Http/Controllers/ReservationController.php) transitions `pending` → `checked_in`.
3. [`ReservationController::checkOut`](../app/Http/Controllers/ReservationController.php) transitions `checked_in` → `checked_out`.

---

### UC-55: View “About to End” Reservations

**Actors:** Owner.

**Entry point:** `GET /api/v1/about-to-end` — [`routes/api.php`](../routes/api.php).

**Data flow:**
1. [`ReservationController::aboutToEnd`](../app/Http/Controllers/ReservationController.php) scopes to the owner’s buildings.
2. Filters reservations by `check_out_date` matching today, tomorrow, or the day after tomorrow.
3. Returns [`ReservationResource`](../app/Http/Resources/ReservationResource.php) collection.

---

## Manual / On-Arrival Reservations

### UC-56: Prepare On-Arrival Customer

**Actors:** Owner, employee.

**Entry point:** `POST /api/v1/reservations/on-arrival/prepare` — [`routes/api.php:183`](../routes/api.php).

**Data flow:**
1. [`PendingCustomerRequest`](../app/Http/Requests/PendingCustomerRequest.php) validates `name` and `whatsapp_number`.
2. [`ReservationController::prepareOnArrival`](../app/Http/Controllers/ReservationController.php):
   - Looks up [`User`](../app/Models/User.php) by WhatsApp; if a [`Customer`](../app/Models/Customer.php) exists, returns [`CustomerResource`](../app/Http/Resources/CustomerResource.php).
   - Else looks up [`PendingCustomer::byWhatsappNumber`](../app/Models/PendingCustomer.php); if found, returns [`PendingCustomerResource`](../app/Http/Resources/PendingCustomerResource.php).
   - Else sends an OTP via [`OtpService::send`](../app/Services/OtpService.php) and asks the operator to complete the name before verifying.

**Navigation:**
[`routes/api.php:183`](../routes/api.php) → [`ReservationController::prepareOnArrival`](../app/Http/Controllers/ReservationController.php) → [`OtpService::send`](../app/Services/OtpService.php) → [`WhatsAppService::send`](../app/Services/WhatsAppService.php)

---

### UC-57: Verify OTP and Create Pending Customer

**Actors:** Owner, employee.

**Entry point:** `POST /api/v1/reservations/on-arrival/verify` — [`routes/api.php:185`](../routes/api.php).

**Data flow:**
1. [`OnArrivalReservationRequest`](../app/Http/Requests/OnArrivalReservationRequest.php) validates name, WhatsApp, and 6-digit OTP.
2. [`PendingCustomerController::verifyOtp`](../app/Http/Controllers/Api/PendingCustomerController.php) calls [`OtpService::verify`](../app/Services/OtpService.php).
3. If a registered customer exists, returns [`CustomerResource`](../app/Http/Resources/CustomerResource.php).
4. Otherwise [`PendingCustomer::firstOrCreate`](../app/Models/PendingCustomer.php) creates/returns a pending customer with `is_verified = false`.
5. Returns [`PendingCustomerResource`](../app/Http/Resources/PendingCustomerResource.php).

---

### UC-58: Validate and Confirm On-Arrival Reservation

**Actors:** Owner, employee.

**Entry point:** `POST /api/v1/reservations/on-arrival/validate` — [`routes/api.php:184`](../routes/api.php).

**Data flow:**
1. [`OnArrivalValidateRequest`](../app/Http/Requests/OnArrivalValidateRequest.php) validates WhatsApp, unit, dates, guest counts, optional promo code/notes/photo.
2. [`ReservationController::resolveCustomer`](../app/Http/Controllers/ReservationController.php) fetches registered [`Customer`](../app/Models/Customer.php) or [`PendingCustomer`](../app/Models/PendingCustomer.php).
3. Optional promo code resolved via [`PromoCodeService::findValidForUnit`](../app/Services/PromoCodeService.php).
4. Total price computed via [`ReservationService::calculateTotal`](../app/Services/ReservationService.php) using [`PricingService::calculateSubtotal`](../app/Services/PricingService.php).
5. Inside a DB transaction:
   - [`cancelActivePendingReservationsFor`](../app/Http/Controllers/ReservationController.php) releases prior pending holds.
   - Creates a [`PendingReservation`](../app/Models/PendingReservation.php) with `status = 'pending'` and `expires_at = now + 10 minutes`.
   - [`UnitAvailabilityService::holdDates`](../app/Services/UnitAvailabilityService.php) locks the dates.
   - If dates conflict, the pending reservation is marked `cancelled`.
   - [`ReservationService::confirmPendingReservation`](../app/Services/ReservationService.php) converts the hold into a real [`Reservation`](../app/Models/Reservation.php) with `source = 'manual'`, reassigns held rows to `booked`, creates invoice/receipt, schedules reminders, moves the ID photo, and sends a [`ReservationCreated`](../app/Notifications/ReservationCreated.php) notification.

**Navigation:**
[`routes/api.php:184`](../routes/api.php) → [`ReservationController::validateOnArrival`](../app/Http/Controllers/ReservationController.php) → [`ReservationService::confirmPendingReservation`](../app/Services/ReservationService.php) → [`persistFromPendingReservation`](../app/Services/ReservationService.php) → [`Reservation::create`](../app/Models/Reservation.php) + [`UnitAvailability`](../app/Models/UnitAvailability.php) + [`Invoice`](../app/Models/Invoice.php)/[`Receipt`](../app/Models/Receipt.php)

---

### UC-59: Check-In / Check-Out a Manual Reservation

Same as UC-54; see [Online Reservations (System)](#online-reservations-system).

---

### UC-60: Release Expired Pending Reservations

**Actors:** System scheduler.

**Entry point:** Console command `pending-reservations:release-expired` — [`routes/console.php:11`](../routes/console.php).

**Data flow:**
1. [`ReleaseExpiredPendingReservations::handle`](../app/Console/Commands/ReleaseExpiredPendingReservations.php) queries [`PendingReservation::expired()`](../app/Models/PendingReservation.php).
2. For each expired row:
   - [`UnitAvailabilityService::releasePendingDates`](../app/Services/UnitAvailabilityService.php) sets rows to `available` and clears `pending_reservation_id`.
   - Updates the pending reservation to `expired` and clears `promo_code_id`.

---

## Pricing, Occasions, Discounts & Promo Codes

### Shared Pricing Pipeline

Most reservation use cases converge on:

1. [`ReservationService::calculateTotal`](../app/Services/ReservationService.php) or [`PricingService::calculateSubtotal`](../app/Services/PricingService.php).
2. Base nightly price = `unit->offer_price ?? unit->base_price`.
3. If an `occasion_id` is provided and an [`OccasionPrice`](../app/Models/OccasionPrice.php) exists:
   - [`Occasion::splitRange`](../app/Models/Occasion.php) counts normal vs. occasion nights.
   - Occasion nights are priced at the occasion price.
4. [`DiscountTemplate::matchingDiscountTemplates`](../app/Services/PricingService.php) finds active templates assigned to the unit/building.
5. Discounts are applied to the original subtotal; the sum is stored as `discount_amount`.
6. If a promo code is present, [`ReservationService::applyPromoCodeDiscount`](../app/Services/ReservationService.php) reduces the subtotal further.
7. Final `total_price` and `discount_amount` are persisted on the [`Reservation`](../app/Models/Reservation.php), mirrored to [`Invoice`](../app/Models/Invoice.php) and [`Receipt`](../app/Models/Receipt.php).

**Navigation:**
[`ReservationService::calculateTotal`](../app/Services/ReservationService.php) → [`PricingService::calculateSubtotal`](../app/Services/PricingService.php) → [`Occasion::splitRange`](../app/Models/Occasion.php) + [`Unit::occasionPrices`](../app/Models/Unit.php) + [`DiscountTemplate::matchingDiscountTemplates`](../app/Services/PricingService.php) → [`Reservation`](../app/Models/Reservation.php)

---

### UC-61: Manage Occasions

**Actors:** Admin (CRUD + approve/reject), owner (request).

**Entry points (owner):**
- `GET /api/v1/occasions` — [`routes/api.php:172`](../routes/api.php)
- `POST /api/v1/occasion-requests` — [`routes/api.php:173`](../routes/api.php)

**Entry points (admin):**
- `GET/POST/PUT/DELETE /api/v1/admin/occasions` — [`routes/api.php:102`](../routes/api.php)
- `POST /api/v1/admin/occasions/{occasion}/approve` — [`routes/api.php:103`](../routes/api.php)
- `POST /api/v1/admin/occasions/{occasion}/reject` — [`routes/api.php:104`](../routes/api.php)

**Data flow:**
1. Owner request: [`OwnerOccasionController::store`](../app/Http/Controllers/Api/Owner/OwnerOccasionController.php) creates an [`Occasion`](../app/Models/Occasion.php) with `status = 'pending'` and `requested_by_owner_id`.
2. Admin CRUD: [`AdminOccasionController`](../app/Http/Controllers/Api/Admin/AdminOccasionController.php) validates via `StoreOccasionRequest` / `UpdateOccasionRequest`.
3. Approve/reject updates status, `approved_by_user_id`, `approved_at`, and optional `rejection_reason`.
4. [`Occasion::isOccasionDate`](../app/Models/Occasion.php) and [`Occasion::splitRange`](../app/Models/Occasion.php) determine how many nights fall on the occasion.

---

### UC-62: Manage Occasion Prices

**Actors:** Owner, employee.

**Entry points:**
- `GET /api/v1/occasion-prices` — [`routes/api.php:174`](../routes/api.php)
- `POST /api/v1/occasion-prices` — [`routes/api.php:174`](../routes/api.php)
- `GET /api/v1/occasion-prices/{occasionPrice}`
- `PUT/PATCH /api/v1/occasion-prices/{occasionPrice}`
- `DELETE /api/v1/occasion-prices/{occasionPrice}`

**Data flow:**
1. [`StoreOccasionPriceRequest`](../app/Http/Requests/Owner/StoreOccasionPriceRequest.php) validates unit, occasion, and price.
2. [`OccasionPriceController`](../app/Http/Controllers/Api/Owner/OccasionPriceController.php) asserts unit ownership via [`assertOwnsUnit`](../app/Http/Controllers/Api/Owner/OccasionPriceController.php).
3. [`PricingService::calculateSubtotal`](../app/Services/PricingService.php) checks `unit->occasionPrices` for the occasion.

**Models / relations:**
- [`OccasionPrice`](../app/Models/OccasionPrice.php) → `unit()`, `occasion()`
- [`Occasion`](../app/Models/Occasion.php) → `requestedBy()`, `approvedBy()`, `occasionPrice()`

---

### UC-63: Manage Discount Templates

**Actors:** Owner, employee.

**Entry points:**
- `GET /api/v1/discount-templates` — [`routes/api.php:175`](../routes/api.php)
- `POST /api/v1/discount-templates` — [`routes/api.php:175`](../routes/api.php)
- `GET /api/v1/discount-templates/{discount_template}`
- `PUT/PATCH /api/v1/discount-templates/{discount_template}`
- `DELETE /api/v1/discount-templates/{discount_template}`
- `POST /api/v1/discount-templates/{discount_template}/toggle` — [`routes/api.php:176`](../routes/api.php)

**Data flow:**
1. `StoreDiscountTemplateRequest` / `UpdateDiscountTemplateRequest` validate rules.
2. [`DiscountTemplateController`](../app/Http/Controllers/Api/Owner/DiscountTemplateController.php) uses the `ResolvesOwner` trait and syncs assignments to buildings/units via [`syncAssignments`](../app/Http/Controllers/Api/Owner/DiscountTemplateController.php).
3. [`DiscountTemplate`](../app/Models/DiscountTemplate.php) auto-computes start/end from `duration_days`.
4. [`PricingService::matchingDiscountTemplates`](../app/Services/PricingService.php) finds active templates overlapping the date range and meeting the subtotal condition.
5. [`DiscountTemplate::apply`](../app/Models/DiscountTemplate.php) calculates the discount amount.

**Models / relations:**
- [`DiscountTemplate`](../app/Models/DiscountTemplate.php) → `owner()`, `buildings()`, `units()`, `assignments()`
- [`DiscountTemplateAssignment`](../app/Models/DiscountTemplateAssignment.php) → `discountTemplate()`, `assignable()` morph

---

### UC-64: Manage Promo Codes

**Actors:** Owner, employee with `promo_codes.*` permissions.

**Entry points:**
- `GET /api/v1/promo-codes` — [`routes/api.php:166`](../routes/api.php)
- `POST /api/v1/promo-codes` — [`routes/api.php:166`](../routes/api.php)
- `GET /api/v1/promo-codes/{promo_code}`
- `PUT/PATCH /api/v1/promo-codes/{promo_code}`
- `DELETE /api/v1/promo-codes/{promo_code}`

**Data flow:**
1. [`PromoCodeController`](../app/Http/Controllers/PromoCodeController.php) authorizes via [`PromoCodePolicy`](../app/Policies/PromoCodePolicy.php).
2. [`PromoCodeService::generateBulk`](../app/Services/PromoCodeService.php) creates unique 6-character codes, stores SHA-256 hashes, and encrypts the display code.
3. [`PromoCodeService::findValidForUnit`](../app/Services/PromoCodeService.php) validates owner scoping, date window, usage limit, and active pending holds.
4. [`PromoCodeService::canUseForCustomer`](../app/Services/PromoCodeService.php) enforces per-customer limits.
5. [`ReservationService::applyPromoCode`](../app/Services/ReservationService.php) increments `uses_count` and sets `used_at` when exhausted.

---

### UC-65: Preview Promo Code Discount

**Actors:** Guest, customer.

**Entry point:** `POST /api/v1/promo-codes/preview` — [`routes/api.php:57`](../routes/api.php).

**Data flow:**
1. [`PromoCodeController::preview`](../app/Http/Controllers/PromoCodeController.php) validates via `PromoCodePreviewRequest`.
2. Finds a valid promo code for the unit.
3. Calls [`ReservationService::calculateTotal`](../app/Services/ReservationService.php) twice (without and with the promo code) to compute `original_total`, `discount_amount`, and `discounted_total`.

---

## Payments, Invoices, Receipts, Wallet & Refunds

### UC-66: Generate Invoice and Receipt for a Reservation

**Actors:** System (`ReservationService`).

**Triggered by:**
- `POST /api/v1/customer/reservations` — [`ReservationController::store`](../app/Http/Controllers/ReservationController.php)
- `POST /api/v1/reservations/on-arrival/validate` — [`ReservationController::validateOnArrival`](../app/Http/Controllers/ReservationController.php)

**Data flow:**
1. After a reservation is persisted, [`ReservationService::createDocument`](../app/Services/ReservationService.php) is called.
2. Pricing is recalculated via [`PricingService::calculateSubtotal`](../app/Services/PricingService.php).
3. Document numbers are generated:
   - Invoice: `INV-{yy}-{SequenceService::next('owner')}`
   - Receipt: `REC-{yy}-{SequenceService::next('customer')}`
4. [`Invoice`](../app/Models/Invoice.php) is created with `received = 'owner'`, financial fields, and `remaining_amount = net_price`.
5. [`Receipt`](../app/Models/Receipt.php) is created linked to the invoice with matching financial fields.

---

### UC-67: List Owner Invoices

**Actors:** Owner (route is owner-only; policy also allows employees).

**Entry point:** `GET /api/v1/owner/invoices` — [`routes/api.php:132`](../routes/api.php).

**Data flow:**
1. [`InvoicePolicy::viewAny`](../app/Policies/InvoicePolicy.php) authorizes.
2. [`InvoiceController::index`](../app/Http/Controllers/InvoiceController.php) filters `Invoice::where('received', 'owner')` by the owner/employee’s buildings.
3. Returns [`InvoiceResource`](../app/Http/Resources/InvoiceResource.php) collection.

---

### UC-68: Show / Delete an Invoice

**Actors:** Owner, employee, customer (view only).

**Entry points:**
- `GET /api/v1/owner/invoices/{invoice}` — [`routes/api.php:132`](../routes/api.php)
- `DELETE /api/v1/owner/invoices/{invoice}` — [`routes/api.php:132`](../routes/api.php)

**Data flow:**
1. [`InvoicePolicy::view`](../app/Policies/InvoicePolicy.php) / [`delete`](../app/Policies/InvoicePolicy.php) authorizes.
2. [`InvoiceController::show`](../app/Http/Controllers/InvoiceController.php) returns the invoice.
3. [`InvoiceController::destroy`](../app/Http/Controllers/InvoiceController.php) rejects deletion if transactions exist, otherwise deletes the invoice.

---

### UC-69: Create a Customer Receipt

**Actors:** Owner (route allows authenticated+verified users; policy scopes by role).

**Entry point:** `POST /api/v1/receipts` — [`routes/api.php:85`](../routes/api.php).

**Data flow:**
1. [`ReceiptPolicy::create`](../app/Policies/ReceiptPolicy.php) checks role.
2. Validates `invoice_id` and loads the invoice.
3. Generates receipt number via [`SequenceService::next('customer')`](../app/Services/SequenceService.php).
4. Creates a [`Receipt`](../app/Models/Receipt.php) copying all financial fields from the invoice.

---

### UC-70: List Receipts

**Actors:** Customer, owner, employee.

**Entry point:** `GET /api/v1/receipts` — [`routes/api.php:85`](../routes/api.php).

**Data flow:**
1. [`ReceiptPolicy::viewAny`](../app/Policies/ReceiptPolicy.php) checks role.
2. [`ReceiptController::index`](../app/Http/Controllers/ReceiptController.php) scopes by customer or owner/employee buildings and applies [`FilterService::applyToCustomerReceiptQuery`](../app/Services/Filter/FilterService.php).
3. Returns [`ReceiptResource`](../app/Http/Resources/ReceiptResource.php) collection.

---

### UC-71: Record a Payment or Refund Transaction

**Actors:** Owner (route is owner-only; policy also allows employees).

**Entry point:** `POST /api/v1/transactions` — [`routes/api.php:133`](../routes/api.php).

**Data flow:**
1. [`TransactionPolicy::create`](../app/Policies/TransactionPolicy.php) authorizes.
2. [`TransactionRequest`](../app/Http/Requests/TransactionRequest.php) validates `amount`, `type` (`payment`/`refund`), `payment_method` (`cash`/`card`/`wallet`), `reason`, and `reservation_id`.
3. [`TransactionController::store`](../app/Http/Controllers/TransactionController.php) loads the reservation and delegates to [`PaymentService::process`](../app/Services/PaymentService.php).
4. Inside a DB transaction:
   - Locks invoice and customer rows.
   - Validates amount against remaining balance / wallet balance / refund availability.
   - Creates a [`Transaction`](../app/Models/Transaction.php).
   - **Refunds to real customers:** credit [`Customer.wallet`](../app/Models/Customer.php) without mutating the invoice.
   - **Payments and pending-customer refunds:** update `Invoice.paid_amount` / `remaining_amount`, mirror to [`Receipt`](../app/Models/Receipt.php), update [`Reservation.payment_status`](../app/Models/Reservation.php), and deduct wallet if wallet payment.
5. Returns [`TransactionResource`](../app/Http/Resources/TransactionResource.php).

**Navigation:**
[`routes/api.php:133`](../routes/api.php) → [`TransactionController::store`](../app/Http/Controllers/TransactionController.php) → [`TransactionRequest`](../app/Http/Requests/TransactionRequest.php) → [`PaymentService::process`](../app/Services/PaymentService.php) → [`Transaction::create`](../app/Models/Transaction.php) + [`Invoice`](../app/Models/Invoice.php)/[`Receipt`](../app/Models/Receipt.php)/[`Customer`](../app/Models/Customer.php)

---

### UC-72: List / Show Transactions

**Actors:** Owner, employee, customer.

**Entry points:**
- `GET /api/v1/transactions` — [`routes/api.php:133`](../routes/api.php)
- `GET /api/v1/transactions/{transaction}` — [`routes/api.php:133`](../routes/api.php)

**Data flow:**
1. [`TransactionPolicy::viewAny`](../app/Policies/TransactionPolicy.php) / [`view`](../app/Policies/TransactionPolicy.php) authorizes.
2. [`TransactionController::index`](../app/Http/Controllers/TransactionController.php) scopes by owner/employee buildings or customer reservation.
3. [`TransactionController::show`](../app/Http/Controllers/TransactionController.php) returns a single transaction.

---

### UC-73: Auto-Refund on Reservation Date Shortening

**Actors:** Owner/employee (trigger), system (`ReservationService`).

**Triggered by:** `PUT /api/v1/reservations/{reservation}` → [`ReservationController::update`](../app/Http/Controllers/ReservationController.php).

**Data flow:**
1. [`ReservationService::recalculateReservation`](../app/Services/ReservationService.php) recalculates nights, price, discount, and net total.
2. Updates the owner invoice.
3. If dates were shortened and effective paid amount exceeds the new net price:
   - Computes `refundAmount = effectivePaid - net_price`.
   - Creates a [`Transaction`](../app/Models/Transaction.php) of type `refund`.
   - For real customers: adds refund amount to [`Customer.wallet`](../app/Models/Customer.php).
   - Sets invoice `paid_amount = net_price`, `remaining_amount = 0`, mirrors to receipt, and updates reservation `payment_status` to `paid`.

---

### UC-74: Configure Building Payment Methods

**Actors:** Owner, employee with building management permission.

**Entry points:**
- `POST /api/v1/buildings` — [`routes/api.php:151`](../routes/api.php)
- `PUT /api/v1/buildings/{building}` — [`routes/api.php:151`](../routes/api.php)

**Data flow:**
1. [`BuildingRequest`](../app/Http/Requests/BuildingRequest.php) / [`BuildingUpdateRequest`](../app/Http/Requests/BuildingUpdateRequest.php) validate `payment_methods` as an array of `cash` or `card`.
2. Stored as JSON on [`Building.payment_methods`](../app/Models/Building.php).
3. Used by [`FilterService::applyPaymentMethodFilters`](../app/Services/Filter/FilterService.php).

---

### UC-75: Initialize Customer Wallet on Registration

**Actors:** System (`AuthController`).

**Triggered by:** `POST /api/v1/register/customer`.

**Data flow:**
[`AuthController::registerCustomer`](../app/Http/Controllers/Api/AuthController.php) creates a [`Customer`](../app/Models/Customer.php) with `wallet = 0.00`. The wallet is exposed by [`CustomerResource`](../app/Http/Resources/CustomerResource.php).

---

### Financial Model Summary

| Model | Key Fields | Relations |
|-------|------------|-----------|
| [`Invoice`](../app/Models/Invoice.php) | `received`, `reservation_id`, `document_number`, `price`, `quantity`, `total_price`, `discount`, `net_price`, `paid_amount`, `remaining_amount` | `belongsTo(Reservation)`, `hasMany(Receipt)`, `hasMany(Transaction)` |
| [`Receipt`](../app/Models/Receipt.php) | `reservation_id`, `invoice_id`, `document_number`, financial mirror fields | `belongsTo(Reservation)`, `belongsTo(Invoice)` |
| [`Transaction`](../app/Models/Transaction.php) | `invoice_id`, `amount`, `type`, `payment_method`, `reason` | `belongsTo(Invoice)` |
| [`Customer`](../app/Models/Customer.php) | `wallet` | `belongsTo(User)`, `morphMany(Reservation)` |
| [`Reservation`](../app/Models/Reservation.php) | `payment_status`, `total_price`, `discount_amount` | `hasMany(Invoice)`, `hasMany(Receipt)`, `morphTo(customer)` |

---

## Availability & Calendar

### UC-76: View Owner Availability Calendar

**Actors:** Owner.

**Entry point:** `GET /api/v1/owner/calendar` — [`routes/api.php:117`](../routes/api.php).

**Data flow:**
1. [`IndexCalendarRequest`](../app/Http/Requests/Owner/IndexCalendarRequest.php) validates filters.
2. [`CalendarController::index`](../app/Http/Controllers/Api/Owner/CalendarController.php) scopes [`UnitAvailability`](../app/Models/UnitAvailability.php) to the owner’s buildings.
3. Applies [`FilterService::applyToOwnerCalendarQuery`](../app/Services/Filter/FilterService.php).
4. Returns [`UnitAvailabilityResource`](../app/Http/Resources/UnitAvailabilityResource.php) collection.

---

### UC-77: List Unit Availability Records

**Actors:** Owner, employee, customer.

**Entry point:** `GET /api/v1/unit-availabilities` — [`routes/api.php:134`](../routes/api.php).

**Data flow:**
1. [`UnitAvailabilityPolicy::viewAny`](../app/Policies/UnitAvailabilityPolicy.php) authorizes.
2. [`UnitAvailabilityController::index`](../app/Http/Controllers/UnitAvailabilityController.php) scopes by role.
3. Optional `month` query triggers [`UnitAvailabilityService::applyMonthScope`](../app/Services/UnitAvailabilityService.php).
4. Returns [`UnitAvailabilityResource`](../app/Http/Resources/UnitAvailabilityResource.php) collection.

---

### UC-78: Block / Unblock Unit Dates

**Actors:** Approved owner, active employee.

**Entry points:**
- `POST /api/v1/unit-availabilities/block` — [`routes/api.php:135`](../routes/api.php)
- `POST /api/v1/unit-availabilities/unblock` — [`routes/api.php:136`](../routes/api.php)

**Data flow:**
1. [`BlockUnitAvailabilityRequest`](../app/Http/Requests/BlockUnitAvailabilityRequest.php) validates `unit_id`, `start_date`, `end_date`.
2. [`UnitAvailabilityController::block`](../app/Http/Controllers/UnitAvailabilityController.php) authorizes via [`UnitPolicy::update`](../app/Policies/UnitPolicy.php) and calls [`UnitAvailabilityService::blockDates`](../app/Services/UnitAvailabilityService.php).
3. [`UnitAvailabilityController::unblock`](../app/Http/Controllers/UnitAvailabilityController.php) calls [`UnitAvailabilityService::unblockDates`](../app/Services/UnitAvailabilityService.php).
4. Block checks for existing `booked` conflicts before marking rows as `blocked`.

---

### UC-79: Book / Release Availability Dates (Internal)

**Triggered by:** reservation create/update/cancel and on-arrival confirmation.

**Key methods:**
- [`UnitAvailabilityService::bookDates`](../app/Services/UnitAvailabilityService.php) — locks rows as `booked` with `reservation_id`.
- [`UnitAvailabilityService::holdDates`](../app/Services/UnitAvailabilityService.php) — locks rows as `booked` with `pending_reservation_id`.
- [`UnitAvailabilityService::reserveDates`](../app/Services/UnitAvailabilityService.php) — converts held rows to real reservation bookings.
- [`UnitAvailabilityService::releaseDates`](../app/Services/UnitAvailabilityService.php) — reverts booked rows to `available`.
- [`UnitAvailabilityService::releasePendingDates`](../app/Services/UnitAvailabilityService.php) — reverts pending holds to `available`.

---

## Dashboards & Reporting

### UC-80: Owner Dashboard

**Actors:** Owner.

**Entry point:** `GET /api/v1/owner/dashboard` — [`routes/api.php:116`](../routes/api.php).

**Data flow:**
1. [`Owner\DashboardRequest`](../app/Http/Requests/Owner/DashboardRequest.php) validates filters.
2. [`OwnerDashboardController::index`](../app/Http/Controllers/Api/Owner/OwnerDashboardController.php) scopes transactions to the owner’s buildings via [`FilterService::applyToOwnerRevenueQuery`](../app/Services/Filter/FilterService.php).
3. [`DashboardMetrics::ownerSummary`](../app/Services/DashboardMetrics.php) provides KPI deltas.
4. Appends revenue and occupancy chart series:
   - [`DashboardMetrics::ownerRevenueSeries`](../app/Services/DashboardMetrics.php)
   - [`DashboardMetrics::ownerOccupancySeries`](../app/Services/DashboardMetrics.php)

---

### UC-81: Admin Dashboard

**Actors:** `admin`, `super_admin`.

**Entry point:** `GET /api/v1/admin/dashboard` — [`routes/api.php:92`](../routes/api.php).

**Data flow:**
1. [`Admin\DashboardRequest`](../app/Http/Requests/Admin/DashboardRequest.php) validates filters.
2. [`AdminDashboardController::index`](../app/Http/Controllers/Api/Admin/AdminDashboardController.php) applies [`FilterService::applyToAdminRevenueQuery`](../app/Services/Filter/FilterService.php).
3. [`DashboardMetrics::adminSummary`](../app/Services/DashboardMetrics.php) provides platform KPIs.
4. Appends platform-wide chart series:
   - [`DashboardMetrics::adminRevenueSeries`](../app/Services/DashboardMetrics.php)
   - [`DashboardMetrics::adminUserGrowthSeries`](../app/Services/DashboardMetrics.php)

---

## Notifications

### UC-82: List / Read / Delete Notifications

**Actors:** Authenticated user; admin can list all notifications.

**Entry points:**
- `GET /api/v1/notifications` — [`routes/api.php:79`](../routes/api.php)
- `GET /api/v1/notifications/{notification}` — [`routes/api.php:79`](../routes/api.php)
- `POST /api/v1/notifications/{notification}/read` — [`routes/api.php:81`](../routes/api.php)
- `DELETE /api/v1/notifications/{notification}` — [`routes/api.php:82`](../routes/api.php)
- `GET /api/v1/admin/notifications` — [`routes/api.php:99`](../routes/api.php)

**Data flow:**
1. [`NotificationPolicy`](../app/Policies/NotificationPolicy.php) enforces ownership.
2. [`NotificationController::index`](../app/Http/Controllers/Api/NotificationController.php) queries `auth()->user()->notifications()`.
3. [`NotificationController::markAsRead`](../app/Http/Controllers/Api/NotificationController.php) updates `read_at`.

**Models:**
- [`User::notifications()`](../app/Models/User.php)
- [`Notification::scopeUnread`](../app/Models/Notification.php)

---

## Scheduled Background Jobs

### UC-83: Release Expired Pending Reservations

**Schedule:** Every minute — [`routes/console.php:11`](../routes/console.php).

**Data flow:**
1. [`ReleaseExpiredPendingReservations::handle`](../app/Console/Commands/ReleaseExpiredPendingReservations.php) queries [`PendingReservation::expired()`](../app/Models/PendingReservation.php).
2. For each expired row:
   - [`UnitAvailabilityService::releasePendingDates`](../app/Services/UnitAvailabilityService.php) reverts availability rows.
   - Marks the pending reservation as `expired` and clears `promo_code_id`.

---

### UC-84: Send Scheduled Reservation Notifications

**Schedule:** Every minute — [`routes/console.php:12`](../routes/console.php).

**Data flow:**
1. [`SendDueScheduledNotifications::handle`](../app/Console/Commands/SendDueScheduledNotifications.php) queries [`ScheduledNotification::due()`](../app/Models/ScheduledNotification.php).
2. Claims each due notification by setting `queued_at`.
3. Dispatches `DispatchScheduledNotification` job.
4. [`ReservationReminderScheduler::scheduleFor`](../app/Services/ReservationReminderScheduler.php) creates/cancels reminders when a reservation is created or updated.

---

## Common Supporting Infrastructure

| Concern | Implementation |
|---------|----------------|
| Authentication guard | Laravel Sanctum (`auth:api`) |
| Token expiration | 1 week ([`config/sanctum.php:53`](../config/sanctum.php)) |
| Role middleware | Spatie `RoleMiddleware` aliased as `role` in [`bootstrap/app.php:30`](../bootstrap/app.php) |
| Verified middleware | [`EnsureUserIsVerified`](../app/Http/Middleware/EnsureUserIsVerified.php) aliased as `verified` |
| Rate limiting | [`AppServiceProvider`](../app/Providers/AppServiceProvider.php) — `auth-login` 5/min/IP, `auth-register` 3/hr/IP, `auth-verify-otp` 5/min per contact/IP |
| OTP transport | WhatsApp via CoreVerde API ([`WhatsAppService`](../app/Services/WhatsAppService.php)) |
| Password hashing | Laravel `Hash::make` / `password` cast on [`User`](../app/Models/User.php) |
| Roles/permissions | Spatie Laravel Permission; seeded by [`RolesAndPermissionsSeeder`](../database/seeders/RolesAndPermissionsSeeder.php) |
| Soft deletes | [`User`](../app/Models/User.php), [`Owner`](../app/Models/Owner.php), [`Customer`](../app/Models/Customer.php), [`Employee`](../app/Models/Employee.php) |
| Media handling | Spatie Media Library via [`HandlesMediaPhotos`](../app/Traits/HandlesMediaPhotos.php) and [`ImageCompressor`](../app/Services/ImageCompressor.php) |
| Owner/employee resolution | [`ResolvesOwner`](../app/Traits/ResolvesOwner.php) trait |

---

## Notes

- **Line numbers** are accurate as of the date this file was generated; they may shift as the codebase evolves, but the file paths and method names remain stable navigation targets.
- **Route prefixes:** All API routes listed here are under the `/api/v1` prefix defined in [`routes/api.php`](../routes/api.php).
- **Authorization:** Most endpoints require `auth:api` + `verified`. Additional role gates (`role:owner`, `role:owner|employee`, `role:super_admin|admin`) are noted per entry point.
