# Hotel Management System - Phase 1 Implementation Plan

## Project Overview

**Project Name:** Hotel Management System (HMS) v2  
**Technology Stack:** Laravel 12.x + Livewire 3.x + Tailwind CSS 4.0  
**Database:** MySQL  
**Phase:** 1 - Core PMS Foundation  
**Architecture:** Service-Based (Thin Livewire Components + Service Classes)

---

## Table of Contents

1. [Architecture Overview](#1-architecture-overview)
2. [Getting Started with Livewire](#2-getting-started-with-livewire)
3. [Project Setup](#3-project-setup)
4. [Database Schema](#4-database-schema)
5. [Service Classes](#5-service-classes)
6. [Implementation Modules](#6-implementation-modules)
7. [Development Timeline](#7-development-timeline)
8. [File Structure](#8-file-structure)
9. [Code Patterns & Examples](#9-code-patterns--examples)

---

## 1. Architecture Overview

### Layered Architecture

```
┌─────────────────────────────────────────────────────────────┐
│                    PRESENTATION LAYER                        │
│  ┌─────────────────────────────────────────────────────┐    │
│  │              Livewire Components                     │    │
│  │  • Handle UI interactions                            │    │
│  │  • Form validation                                   │    │
│  │  • Call services for business logic                  │    │
│  │  • Display data to users                             │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    BUSINESS LOGIC LAYER                      │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                Service Classes                       │    │
│  │  • All business logic lives here                     │    │
│  │  • Database transactions                             │    │
│  │  • Complex calculations                              │    │
│  │  • Cross-model operations                            │    │
│  │  • External API integrations                         │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                      DATA LAYER                              │
│  ┌─────────────────────────────────────────────────────┐    │
│  │              Eloquent Models                         │    │
│  │  • Data structure & relationships                    │    │
│  │  • Accessors & mutators                              │    │
│  │  • Scopes for common queries                         │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
```

### Layer Responsibilities

| Layer | Responsibility | Example |
|-------|---------------|---------|
| **Livewire Component** | UI interactions, form binding, validation display, call services | `Reservations/Create.php` |
| **Service Class** | Business logic, transactions, calculations, orchestration | `ReservationService.php` |
| **Model** | Data structure, relationships, simple accessors | `Reservation.php` |
| **Policy** | Authorization logic | `ReservationPolicy.php` |

### Why This Architecture?

| Benefit | Description |
|---------|-------------|
| **Testability** | Services can be unit tested independently |
| **Reusability** | Services can be called from Livewire, API controllers, commands |
| **Maintainability** | Business logic in one place, easy to find and modify |
| **Separation of Concerns** | UI logic separate from business logic |
| **Team Collaboration** | Frontend and backend devs can work independently |

---

## 2. Getting Started with Livewire

### What is Livewire?

Livewire is a full-stack framework for Laravel that makes building dynamic interfaces simple, without leaving the comfort of Laravel. Instead of writing JavaScript, you write PHP components that automatically update the browser.

### Key Concepts

| Concept | Description |
|---------|-------------|
| **Components** | PHP classes that contain UI logic + Blade views |
| **Properties** | Public properties that sync between PHP and the browser |
| **Actions** | Public methods that can be triggered from the browser |
| **wire:model** | Two-way data binding for form inputs |
| **wire:click** | Trigger PHP methods on click events |
| **boot()** | Called on every request - use for service injection |

### Livewire Component Principle: Keep Them Thin!

```php
// ❌ BAD: Fat component with business logic
class CreateReservation extends Component
{
    public function save()
    {
        // Validation...
        
        // ❌ Business logic in component
        $availability = Room::where('room_type_id', $this->room_type_id)
            ->where('is_active', true)
            ->count();
        
        $reserved = Reservation::where('room_type_id', $this->room_type_id)
            ->whereBetween('check_in_date', [...])
            ->count();
        
        if ($availability - $reserved <= 0) {
            // handle error
        }
        
        // More complex logic...
        $reservation = Reservation::create([...]);
        
        // Send email...
        Mail::to($guest)->send(new ReservationConfirmation($reservation));
    }
}

// ✅ GOOD: Thin component delegates to service
class CreateReservation extends Component
{
    protected ReservationService $reservationService;
    
    public function boot(ReservationService $reservationService)
    {
        $this->reservationService = $reservationService;
    }
    
    public function save()
    {
        $this->validate();
        
        try {
            // ✅ Service handles all business logic
            $reservation = $this->reservationService->create([
                'guest_id' => $this->guest_id,
                'room_type_id' => $this->room_type_id,
                'check_in_date' => $this->check_in_date,
                'check_out_date' => $this->check_out_date,
            ]);
            
            session()->flash('success', 'Reservation created!');
            return redirect()->route('reservations.show', $reservation);
            
        } catch (\Exception $e) {
            $this->addError('reservation', $e->getMessage());
        }
    }
}
```

---

## 3. Project Setup

### Step 1: Install Required Packages

```bash
# Install Livewire
composer require livewire/livewire

# Install Breeze with Livewire (authentication scaffolding)
composer require laravel/breeze --dev
php artisan breeze:install livewire

# Install Spatie Permission (role-based access control)
composer require spatie/laravel-permission
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"

# Install DomPDF for invoice generation
composer require barryvdh/laravel-dompdf

# Install frontend dependencies
npm install
npm run build
```

### Step 2: Configure Database

Update your `.env` file:

```env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=hotel_ms_v2
DB_USERNAME=root
DB_PASSWORD=
```

### Step 3: Configure Spatie Permission

Add the trait to your User model:

```php
// app/Models/User.php
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use HasRoles;
    // ...
}
```

### Step 4: Create Service Provider for Services (Optional but Recommended)

```php
// app/Providers/ServiceServiceProvider.php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\ReservationService;
use App\Services\AvailabilityService;
use App\Services\GuestService;
// ... other services

class ServiceServiceProvider extends ServiceProvider
{
    public function register()
    {
        // Singleton services (shared instance)
        $this->app->singleton(AvailabilityService::class);
        $this->app->singleton(NotificationService::class);
        
        // Regular services (new instance each time)
        $this->app->bind(ReservationService::class);
        $this->app->bind(GuestService::class);
    }
}
```

Register in `bootstrap/providers.php`:

```php
return [
    App\Providers\AppServiceProvider::class,
    App\Providers\ServiceServiceProvider::class,
];
```

### Step 5: Run Migrations

```bash
php artisan migrate
```

---

## 4. Database Schema

### Core Tables for Phase 1

#### 4.1 Users Table (extends default)

```php
Schema::table('users', function (Blueprint $table) {
    $table->string('phone')->nullable();
    $table->string('avatar')->nullable();
    $table->boolean('is_active')->default(true);
    $table->timestamp('last_login_at')->nullable();
    $table->string('last_login_ip')->nullable();
    $table->integer('failed_login_attempts')->default(0);
    $table->timestamp('locked_until')->nullable();
});
```

#### 4.2 Guests Table

| Column | Type | Description |
|--------|------|-------------|
| id | bigint | Primary key |
| guest_number | string | Unique (GST-YYYY-####) |
| first_name | string | Required |
| last_name | string | Required |
| email | string | Required, unique |
| phone | string | Nullable |
| address | text | Nullable |
| city | string | Nullable |
| country | string | Nullable |
| nationality | string | Nullable |
| id_type | enum | passport, national_id, drivers_license |
| id_number | string | Nullable |
| date_of_birth | date | Nullable |
| guest_type | enum | individual, corporate, group, vip |
| vip_status | boolean | Default false |
| loyalty_points | integer | Default 0 |
| preferences | json | Nullable |
| notes | text | Nullable |
| created_at | timestamp | |
| updated_at | timestamp | |

#### 4.3 Room Types Table

| Column | Type | Description |
|--------|------|-------------|
| id | bigint | Primary key |
| name | string | e.g., Standard, Deluxe, Suite |
| code | string | e.g., STD, DLX, STE |
| description | text | |
| max_occupancy | integer | Max guests |
| base_rate | decimal(10,2) | Base price per night |
| amenities | json | List of amenities |
| images | json | Array of image paths |
| is_active | boolean | Default true |
| created_at | timestamp | |
| updated_at | timestamp | |

#### 4.4 Rooms Table

| Column | Type | Description |
|--------|------|-------------|
| id | bigint | Primary key |
| room_number | string | Unique |
| room_type_id | foreignId | References room_types |
| floor | integer | Floor number |
| status | enum | available, occupied, dirty, clean, inspected, maintenance, blocked |
| description | text | Nullable |
| is_active | boolean | Default true |
| created_at | timestamp | |
| updated_at | timestamp | |

#### 4.5 Reservations Table

| Column | Type | Description |
|--------|------|-------------|
| id | bigint | Primary key |
| confirmation_number | string | Unique (RES-YYYYMMDD-####) |
| guest_id | foreignId | References guests |
| room_type_id | foreignId | References room_types |
| room_id | foreignId | Nullable, assigned at check-in |
| check_in_date | date | |
| check_out_date | date | |
| actual_check_in | datetime | Nullable |
| actual_check_out | datetime | Nullable |
| adults | integer | Default 1 |
| children | integer | Default 0 |
| status | enum | pending, confirmed, checked_in, checked_out, cancelled, no_show |
| source | enum | direct, walk_in, phone, website, ota_booking, ota_expedia, corporate |
| total_amount | decimal(10,2) | |
| paid_amount | decimal(10,2) | Default 0 |
| special_requests | text | Nullable |
| cancellation_reason | text | Nullable |
| cancelled_at | datetime | Nullable |
| created_by | foreignId | References users |
| created_at | timestamp | |
| updated_at | timestamp | |

#### 4.6 Folios Table

| Column | Type | Description |
|--------|------|-------------|
| id | bigint | Primary key |
| folio_number | string | Unique (FOL-YYYYMMDD-####) |
| reservation_id | foreignId | References reservations |
| guest_id | foreignId | References guests |
| status | enum | open, closed |
| total_charges | decimal(10,2) | Default 0 |
| total_payments | decimal(10,2) | Default 0 |
| balance | decimal(10,2) | Default 0 |
| opened_at | datetime | |
| closed_at | datetime | Nullable |
| created_at | timestamp | |
| updated_at | timestamp | |

#### 4.7 Folio Charges Table

| Column | Type | Description |
|--------|------|-------------|
| id | bigint | Primary key |
| folio_id | foreignId | References folios |
| category | enum | room, food, beverage, telephone, internet, minibar, laundry, parking, spa, transport, other |
| description | string | |
| quantity | integer | Default 1 |
| unit_price | decimal(10,2) | |
| amount | decimal(10,2) | |
| tax_amount | decimal(10,2) | Default 0 |
| is_voided | boolean | Default false |
| voided_reason | string | Nullable |
| posted_by | foreignId | References users |
| posted_at | datetime | |
| created_at | timestamp | |
| updated_at | timestamp | |

#### 4.8 Payments Table

| Column | Type | Description |
|--------|------|-------------|
| id | bigint | Primary key |
| folio_id | foreignId | References folios |
| payment_method | enum | cash, credit_card, debit_card, bank_transfer, mobile_money, corporate_account |
| amount | decimal(10,2) | |
| reference_number | string | Nullable |
| status | enum | pending, completed, failed, refunded |
| notes | text | Nullable |
| processed_by | foreignId | References users |
| processed_at | datetime | |
| created_at | timestamp | |
| updated_at | timestamp | |

#### 4.9 Housekeeping Tasks Table

| Column | Type | Description |
|--------|------|-------------|
| id | bigint | Primary key |
| room_id | foreignId | References rooms |
| reservation_id | foreignId | Nullable |
| task_type | enum | checkout_cleaning, stayover_cleaning, deep_cleaning, inspection, turndown |
| status | enum | pending, in_progress, completed, inspected, failed |
| priority | enum | low, normal, high, urgent |
| assigned_to | foreignId | Nullable, references users |
| assigned_at | datetime | Nullable |
| started_at | datetime | Nullable |
| completed_at | datetime | Nullable |
| inspected_by | foreignId | Nullable, references users |
| inspected_at | datetime | Nullable |
| notes | text | Nullable |
| created_at | timestamp | |
| updated_at | timestamp | |

#### 4.10 Notifications Table

| Column | Type | Description |
|--------|------|-------------|
| id | bigint | Primary key |
| type | enum | email, sms, in_app |
| notifiable_type | string | User or Guest |
| notifiable_id | bigint | |
| category | enum | reservation, check_in, check_out, payment, housekeeping, system |
| title | string | |
| message | text | |
| data | json | Nullable |
| is_read | boolean | Default false |
| read_at | datetime | Nullable |
| sent_at | datetime | Nullable |
| created_at | timestamp | |
| updated_at | timestamp | |

#### 4.11 Audit Logs Table

| Column | Type | Description |
|--------|------|-------------|
| id | bigint | Primary key |
| user_id | foreignId | Nullable, references users |
| action | string | create, update, delete, login, logout, etc. |
| model_type | string | e.g., App\Models\Reservation |
| model_id | bigint | Nullable |
| old_values | json | Nullable |
| new_values | json | Nullable |
| ip_address | string | |
| user_agent | string | |
| created_at | timestamp | |

---

## 5. Service Classes

### Service Classes Overview

| Service | Responsibility |
|---------|---------------|
| `GuestService` | Guest CRUD, duplicate detection, guest number generation |
| `RoomService` | Room management, status updates |
| `AvailabilityService` | Check room availability for date ranges |
| `ReservationService` | Create/modify/cancel reservations, confirmations |
| `CheckInService` | Process check-ins, room assignment, folio creation |
| `CheckOutService` | Process check-outs, final billing, invoice generation |
| `FolioService` | Folio management, charge posting |
| `BillingService` | Payment processing, tax calculations |
| `HousekeepingService` | Task creation, assignment, status updates |
| `NotificationService` | Email/SMS/in-app notifications |
| `AuditService` | Audit logging |
| `ReportService` | Generate reports and statistics |

### Service Class Template

```php
<?php
// app/Services/BaseService.php
namespace App\Services;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

abstract class BaseService
{
    protected function transaction(callable $callback)
    {
        return DB::transaction($callback);
    }
    
    protected function log(string $message, array $context = [])
    {
        Log::info("[" . class_basename($this) . "] " . $message, $context);
    }
}
```

### GuestService

```php
<?php
// app/Services/GuestService.php
namespace App\Services;

use App\Models\Guest;
use Illuminate\Support\Facades\DB;

class GuestService extends BaseService
{
    /**
     * Create a new guest
     */
    public function create(array $data): Guest
    {
        return $this->transaction(function () use ($data) {
            // Check for duplicates
            if ($this->isDuplicate($data['email'], $data['phone'] ?? null)) {
                throw new \Exception('A guest with this email or phone already exists.');
            }
            
            $guest = Guest::create([
                'guest_number' => $this->generateGuestNumber(),
                'first_name' => $data['first_name'],
                'last_name' => $data['last_name'],
                'email' => $data['email'],
                'phone' => $data['phone'] ?? null,
                'address' => $data['address'] ?? null,
                'city' => $data['city'] ?? null,
                'country' => $data['country'] ?? null,
                'nationality' => $data['nationality'] ?? null,
                'id_type' => $data['id_type'] ?? null,
                'id_number' => $data['id_number'] ?? null,
                'date_of_birth' => $data['date_of_birth'] ?? null,
                'guest_type' => $data['guest_type'] ?? 'individual',
                'preferences' => $data['preferences'] ?? null,
                'notes' => $data['notes'] ?? null,
            ]);
            
            $this->log('Guest created', ['guest_id' => $guest->id]);
            
            return $guest;
        });
    }
    
    /**
     * Update guest information
     */
    public function update(Guest $guest, array $data): Guest
    {
        return $this->transaction(function () use ($guest, $data) {
            // Check email uniqueness if changed
            if (isset($data['email']) && $data['email'] !== $guest->email) {
                if (Guest::where('email', $data['email'])->where('id', '!=', $guest->id)->exists()) {
                    throw new \Exception('Email already in use by another guest.');
                }
            }
            
            $guest->update($data);
            
            $this->log('Guest updated', ['guest_id' => $guest->id]);
            
            return $guest->fresh();
        });
    }
    
    /**
     * Check for duplicate guests
     */
    public function isDuplicate(?string $email, ?string $phone, ?int $excludeId = null): bool
    {
        $query = Guest::query();
        
        if ($excludeId) {
            $query->where('id', '!=', $excludeId);
        }
        
        return $query->where(function ($q) use ($email, $phone) {
            if ($email) {
                $q->where('email', $email);
            }
            if ($phone) {
                $q->orWhere('phone', $phone);
            }
        })->exists();
    }
    
    /**
     * Find potential duplicates for a new guest
     */
    public function findPotentialDuplicates(string $email, ?string $phone = null): array
    {
        return Guest::where('email', $email)
            ->orWhere(function ($q) use ($phone) {
                if ($phone) {
                    $q->where('phone', $phone);
                }
            })
            ->get()
            ->toArray();
    }
    
    /**
     * Get guest history (past reservations)
     */
    public function getHistory(Guest $guest): array
    {
        $reservations = $guest->reservations()
            ->with(['roomType', 'room'])
            ->orderBy('check_in_date', 'desc')
            ->get();
        
        $totalSpent = $reservations->sum('total_amount');
        $totalStays = $reservations->where('status', 'checked_out')->count();
        $totalNights = $reservations->sum(function ($res) {
            return $res->check_in_date->diffInDays($res->check_out_date);
        });
        
        return [
            'reservations' => $reservations,
            'total_spent' => $totalSpent,
            'total_stays' => $totalStays,
            'total_nights' => $totalNights,
            'first_stay' => $reservations->last()?->check_in_date,
            'last_stay' => $reservations->first()?->check_in_date,
        ];
    }
    
    /**
     * Generate unique guest number
     */
    protected function generateGuestNumber(): string
    {
        $year = now()->format('Y');
        $count = Guest::whereYear('created_at', $year)->count() + 1;
        return sprintf('GST-%s-%04d', $year, $count);
    }
}
```

### AvailabilityService

```php
<?php
// app/Services/AvailabilityService.php
namespace App\Services;

use App\Models\Room;
use App\Models\RoomType;
use App\Models\Reservation;
use Carbon\Carbon;

class AvailabilityService extends BaseService
{
    /**
     * Check availability for a room type in a date range
     */
    public function check(int $roomTypeId, string $checkIn, string $checkOut, ?int $excludeReservationId = null): array
    {
        $roomType = RoomType::findOrFail($roomTypeId);
        
        // Total active rooms of this type
        $totalRooms = Room::where('room_type_id', $roomTypeId)
            ->where('is_active', true)
            ->whereNotIn('status', ['blocked', 'maintenance'])
            ->count();
        
        // Count reserved rooms for the date range
        $reservedQuery = Reservation::where('room_type_id', $roomTypeId)
            ->whereIn('status', ['pending', 'confirmed', 'checked_in'])
            ->where(function ($query) use ($checkIn, $checkOut) {
                // Overlapping reservations
                $query->where(function ($q) use ($checkIn, $checkOut) {
                    $q->where('check_in_date', '<', $checkOut)
                      ->where('check_out_date', '>', $checkIn);
                });
            });
        
        if ($excludeReservationId) {
            $reservedQuery->where('id', '!=', $excludeReservationId);
        }
        
        $reservedRooms = $reservedQuery->count();
        $availableCount = max(0, $totalRooms - $reservedRooms);
        
        // Calculate rate
        $nights = Carbon::parse($checkIn)->diffInDays(Carbon::parse($checkOut));
        $totalRate = $roomType->base_rate * $nights;
        
        return [
            'available' => $availableCount > 0,
            'available_count' => $availableCount,
            'total_rooms' => $totalRooms,
            'reserved_rooms' => $reservedRooms,
            'room_type' => $roomType,
            'nights' => $nights,
            'rate_per_night' => $roomType->base_rate,
            'total_rate' => $totalRate,
        ];
    }
    
    /**
     * Get all available room types for a date range
     */
    public function getAvailableRoomTypes(string $checkIn, string $checkOut): array
    {
        $roomTypes = RoomType::where('is_active', true)->get();
        $available = [];
        
        foreach ($roomTypes as $roomType) {
            $availability = $this->check($roomType->id, $checkIn, $checkOut);
            if ($availability['available']) {
                $available[] = $availability;
            }
        }
        
        return $available;
    }
    
    /**
     * Get specific available rooms for assignment
     */
    public function getAvailableRooms(int $roomTypeId, string $checkIn, string $checkOut): \Illuminate\Database\Eloquent\Collection
    {
        // Get room IDs that are reserved for the date range
        $reservedRoomIds = Reservation::where('room_type_id', $roomTypeId)
            ->whereIn('status', ['pending', 'confirmed', 'checked_in'])
            ->whereNotNull('room_id')
            ->where(function ($query) use ($checkIn, $checkOut) {
                $query->where('check_in_date', '<', $checkOut)
                      ->where('check_out_date', '>', $checkIn);
            })
            ->pluck('room_id');
        
        return Room::where('room_type_id', $roomTypeId)
            ->where('is_active', true)
            ->whereIn('status', ['clean', 'inspected', 'available'])
            ->whereNotIn('id', $reservedRoomIds)
            ->orderBy('room_number')
            ->get();
    }
}
```

### ReservationService

```php
<?php
// app/Services/ReservationService.php
namespace App\Services;

use App\Models\Reservation;
use App\Models\Guest;
use App\Models\RoomType;
use Carbon\Carbon;

class ReservationService extends BaseService
{
    public function __construct(
        protected AvailabilityService $availabilityService,
        protected NotificationService $notificationService
    ) {}
    
    /**
     * Create a new reservation
     */
    public function create(array $data): Reservation
    {
        return $this->transaction(function () use ($data) {
            // Validate dates
            $checkIn = Carbon::parse($data['check_in_date']);
            $checkOut = Carbon::parse($data['check_out_date']);
            
            if ($checkOut->lte($checkIn)) {
                throw new \Exception('Check-out date must be after check-in date.');
            }
            
            if ($checkIn->lt(today())) {
                throw new \Exception('Check-in date cannot be in the past.');
            }
            
            // Check availability
            $availability = $this->availabilityService->check(
                $data['room_type_id'],
                $data['check_in_date'],
                $data['check_out_date']
            );
            
            if (!$availability['available']) {
                throw new \Exception('No rooms available for the selected dates.');
            }
            
            // Create reservation
            $reservation = Reservation::create([
                'confirmation_number' => $this->generateConfirmationNumber(),
                'guest_id' => $data['guest_id'],
                'room_type_id' => $data['room_type_id'],
                'check_in_date' => $data['check_in_date'],
                'check_out_date' => $data['check_out_date'],
                'adults' => $data['adults'] ?? 1,
                'children' => $data['children'] ?? 0,
                'status' => 'confirmed',
                'source' => $data['source'] ?? 'direct',
                'total_amount' => $availability['total_rate'],
                'special_requests' => $data['special_requests'] ?? null,
                'created_by' => auth()->id(),
            ]);
            
            // Send confirmation email
            $this->notificationService->sendReservationConfirmation($reservation);
            
            $this->log('Reservation created', [
                'reservation_id' => $reservation->id,
                'confirmation_number' => $reservation->confirmation_number,
            ]);
            
            return $reservation;
        });
    }
    
    /**
     * Modify an existing reservation
     */
    public function modify(Reservation $reservation, array $data): Reservation
    {
        return $this->transaction(function () use ($reservation, $data) {
            // Cannot modify checked-out or cancelled reservations
            if (in_array($reservation->status, ['checked_out', 'cancelled', 'no_show'])) {
                throw new \Exception('Cannot modify a ' . $reservation->status . ' reservation.');
            }
            
            // If dates or room type changed, check availability
            $datesChanged = isset($data['check_in_date']) || isset($data['check_out_date']) || isset($data['room_type_id']);
            
            if ($datesChanged) {
                $checkIn = $data['check_in_date'] ?? $reservation->check_in_date;
                $checkOut = $data['check_out_date'] ?? $reservation->check_out_date;
                $roomTypeId = $data['room_type_id'] ?? $reservation->room_type_id;
                
                $availability = $this->availabilityService->check(
                    $roomTypeId,
                    $checkIn,
                    $checkOut,
                    $reservation->id  // Exclude current reservation
                );
                
                if (!$availability['available']) {
                    throw new \Exception('No rooms available for the new dates.');
                }
                
                // Update total amount if dates changed
                $data['total_amount'] = $availability['total_rate'];
            }
            
            // Store old values for audit
            $oldValues = $reservation->toArray();
            
            // Update reservation
            $reservation->update($data);
            
            // Send modification email
            $this->notificationService->sendReservationModification($reservation);
            
            $this->log('Reservation modified', [
                'reservation_id' => $reservation->id,
                'changes' => array_keys($data),
            ]);
            
            return $reservation->fresh();
        });
    }
    
    /**
     * Cancel a reservation
     */
    public function cancel(Reservation $reservation, string $reason): Reservation
    {
        return $this->transaction(function () use ($reservation, $reason) {
            if (in_array($reservation->status, ['checked_out', 'cancelled'])) {
                throw new \Exception('Reservation is already ' . $reservation->status);
            }
            
            // Calculate cancellation fee
            $cancellationFee = $this->calculateCancellationFee($reservation);
            
            $reservation->update([
                'status' => 'cancelled',
                'cancellation_reason' => $reason,
                'cancelled_at' => now(),
            ]);
            
            // Handle refund if applicable
            $refundAmount = $reservation->paid_amount - $cancellationFee;
            if ($refundAmount > 0) {
                // Process refund logic here
            }
            
            // Send cancellation email
            $this->notificationService->sendReservationCancellation($reservation, $cancellationFee);
            
            $this->log('Reservation cancelled', [
                'reservation_id' => $reservation->id,
                'reason' => $reason,
                'cancellation_fee' => $cancellationFee,
            ]);
            
            return $reservation;
        });
    }
    
    /**
     * Mark reservation as no-show
     */
    public function markNoShow(Reservation $reservation): Reservation
    {
        return $this->transaction(function () use ($reservation) {
            if ($reservation->status !== 'confirmed') {
                throw new \Exception('Only confirmed reservations can be marked as no-show.');
            }
            
            $noShowFee = $this->calculateNoShowFee($reservation);
            
            $reservation->update([
                'status' => 'no_show',
            ]);
            
            // Send no-show notification
            $this->notificationService->sendNoShowNotification($reservation, $noShowFee);
            
            $this->log('Reservation marked as no-show', ['reservation_id' => $reservation->id]);
            
            return $reservation;
        });
    }
    
    /**
     * Generate confirmation number
     */
    protected function generateConfirmationNumber(): string
    {
        $date = now()->format('Ymd');
        $count = Reservation::whereDate('created_at', today())->count() + 1;
        return sprintf('RES-%s-%04d', $date, $count);
    }
    
    /**
     * Calculate cancellation fee based on policy
     */
    protected function calculateCancellationFee(Reservation $reservation): float
    {
        $daysUntilCheckIn = now()->diffInDays($reservation->check_in_date, false);
        
        if ($daysUntilCheckIn > 7) {
            return 0; // Free cancellation
        } elseif ($daysUntilCheckIn > 2) {
            return $reservation->total_amount * 0.25; // 25% fee
        } elseif ($daysUntilCheckIn > 0) {
            return $reservation->total_amount * 0.50; // 50% fee
        } else {
            return $reservation->total_amount; // Full charge
        }
    }
    
    /**
     * Calculate no-show fee
     */
    protected function calculateNoShowFee(Reservation $reservation): float
    {
        // Charge first night
        $roomType = $reservation->roomType;
        return $roomType->base_rate;
    }
}
```

### CheckInService

```php
<?php
// app/Services/CheckInService.php
namespace App\Services;

use App\Models\Reservation;
use App\Models\Room;
use App\Models\Folio;

class CheckInService extends BaseService
{
    public function __construct(
        protected FolioService $folioService,
        protected HousekeepingService $housekeepingService,
        protected NotificationService $notificationService
    ) {}
    
    /**
     * Process guest check-in
     */
    public function process(int $reservationId, int $roomId, float $depositAmount = 0): object
    {
        return $this->transaction(function () use ($reservationId, $roomId, $depositAmount) {
            $reservation = Reservation::with(['guest', 'roomType'])->findOrFail($reservationId);
            $room = Room::findOrFail($roomId);
            
            // Validations
            if ($reservation->status !== 'confirmed') {
                throw new \Exception('Reservation must be confirmed to check in.');
            }
            
            if (!in_array($room->status, ['clean', 'inspected'])) {
                throw new \Exception('Room is not ready. Current status: ' . $room->status);
            }
            
            if ($room->room_type_id !== $reservation->room_type_id) {
                throw new \Exception('Room type does not match reservation.');
            }
            
            // Update reservation
            $reservation->update([
                'room_id' => $roomId,
                'status' => 'checked_in',
                'actual_check_in' => now(),
            ]);
            
            // Update room status
            $room->update(['status' => 'occupied']);
            
            // Create folio
            $folio = $this->folioService->create($reservation);
            
            // Post room charges for stay
            $this->folioService->postRoomCharges($folio, $reservation);
            
            // Process deposit if provided
            if ($depositAmount > 0) {
                $this->folioService->postPayment($folio, [
                    'amount' => $depositAmount,
                    'payment_method' => 'cash',
                    'notes' => 'Check-in deposit',
                ]);
            }
            
            // Send check-in confirmation
            $this->notificationService->sendCheckInConfirmation($reservation);
            
            $this->log('Guest checked in', [
                'reservation_id' => $reservation->id,
                'room_id' => $roomId,
                'folio_id' => $folio->id,
            ]);
            
            return (object) [
                'success' => true,
                'reservation' => $reservation->fresh(),
                'folio' => $folio,
                'room' => $room,
            ];
        });
    }
    
    /**
     * Process walk-in guest
     */
    public function walkIn(array $guestData, array $reservationData, int $roomId): object
    {
        return $this->transaction(function () use ($guestData, $reservationData, $roomId) {
            // Create or find guest
            $guestService = app(GuestService::class);
            $guest = $guestService->create($guestData);
            
            // Create same-day reservation
            $reservationService = app(ReservationService::class);
            $reservation = $reservationService->create([
                'guest_id' => $guest->id,
                'room_type_id' => $reservationData['room_type_id'],
                'check_in_date' => today(),
                'check_out_date' => $reservationData['check_out_date'],
                'adults' => $reservationData['adults'] ?? 1,
                'children' => $reservationData['children'] ?? 0,
                'source' => 'walk_in',
            ]);
            
            // Process check-in
            return $this->process(
                $reservation->id,
                $roomId,
                $reservationData['deposit_amount'] ?? 0
            );
        });
    }
}
```

### CheckOutService

```php
<?php
// app/Services/CheckOutService.php
namespace App\Services;

use App\Models\Reservation;
use App\Models\Folio;
use Barryvdh\DomPDF\Facade\Pdf;

class CheckOutService extends BaseService
{
    public function __construct(
        protected FolioService $folioService,
        protected HousekeepingService $housekeepingService,
        protected NotificationService $notificationService
    ) {}
    
    /**
     * Process guest check-out
     */
    public function process(int $reservationId, array $paymentData = []): object
    {
        return $this->transaction(function () use ($reservationId, $paymentData) {
            $reservation = Reservation::with(['guest', 'room', 'folio'])->findOrFail($reservationId);
            $folio = $reservation->folio;
            
            // Validations
            if ($reservation->status !== 'checked_in') {
                throw new \Exception('Reservation must be checked in to check out.');
            }
            
            if (!$folio) {
                throw new \Exception('No folio found for this reservation.');
            }
            
            // Recalculate folio balance
            $folio->recalculateBalance();
            
            // Check if balance is paid
            if ($folio->balance > 0) {
                if (empty($paymentData)) {
                    throw new \Exception('Outstanding balance of ' . number_format($folio->balance, 2) . ' must be paid.');
                }
                
                // Process payment
                $this->folioService->postPayment($folio, $paymentData);
                $folio->refresh();
                
                if ($folio->balance > 0) {
                    throw new \Exception('Payment amount insufficient. Remaining balance: ' . number_format($folio->balance, 2));
                }
            }
            
            // Close folio
            $this->folioService->close($folio);
            
            // Update reservation
            $reservation->update([
                'status' => 'checked_out',
                'actual_check_out' => now(),
            ]);
            
            // Update room status to dirty
            $room = $reservation->room;
            $room->update(['status' => 'dirty']);
            
            // Create housekeeping task
            $this->housekeepingService->createCheckoutTask($room, $reservation);
            
            // Generate invoice
            $invoice = $this->generateInvoice($reservation, $folio);
            
            // Send checkout email with invoice
            $this->notificationService->sendCheckOutConfirmation($reservation, $invoice);
            
            $this->log('Guest checked out', [
                'reservation_id' => $reservation->id,
                'folio_id' => $folio->id,
            ]);
            
            return (object) [
                'success' => true,
                'reservation' => $reservation->fresh(),
                'folio' => $folio,
                'invoice' => $invoice,
            ];
        });
    }
    
    /**
     * Generate invoice PDF
     */
    public function generateInvoice(Reservation $reservation, Folio $folio): string
    {
        $folio->load(['charges', 'payments']);
        
        $pdf = Pdf::loadView('pdf.invoice', [
            'reservation' => $reservation,
            'folio' => $folio,
            'guest' => $reservation->guest,
        ]);
        
        $filename = 'invoice-' . $folio->folio_number . '.pdf';
        $path = storage_path('app/invoices/' . $filename);
        
        $pdf->save($path);
        
        return $path;
    }
    
    /**
     * Calculate late checkout fee
     */
    public function calculateLateCheckoutFee(Reservation $reservation): float
    {
        $standardCheckoutTime = today()->setTime(11, 0); // 11:00 AM
        
        if (now()->gt($standardCheckoutTime)) {
            $hoursLate = now()->diffInHours($standardCheckoutTime);
            
            if ($hoursLate <= 2) {
                return 0; // Grace period
            } elseif ($hoursLate <= 4) {
                return $reservation->roomType->base_rate * 0.25; // 25% of daily rate
            } else {
                return $reservation->roomType->base_rate * 0.50; // 50% of daily rate
            }
        }
        
        return 0;
    }
}
```

### FolioService

```php
<?php
// app/Services/FolioService.php
namespace App\Services;

use App\Models\Folio;
use App\Models\FolioCharge;
use App\Models\Payment;
use App\Models\Reservation;

class FolioService extends BaseService
{
    /**
     * Create a new folio for a reservation
     */
    public function create(Reservation $reservation): Folio
    {
        return Folio::create([
            'folio_number' => $this->generateFolioNumber(),
            'reservation_id' => $reservation->id,
            'guest_id' => $reservation->guest_id,
            'status' => 'open',
            'opened_at' => now(),
        ]);
    }
    
    /**
     * Post room charges for the stay
     */
    public function postRoomCharges(Folio $folio, Reservation $reservation): void
    {
        $nights = $reservation->check_in_date->diffInDays($reservation->check_out_date);
        $ratePerNight = $reservation->roomType->base_rate;
        
        for ($i = 0; $i < $nights; $i++) {
            $date = $reservation->check_in_date->copy()->addDays($i);
            
            $this->postCharge($folio, [
                'category' => 'room',
                'description' => 'Room charge - ' . $date->format('M d, Y'),
                'quantity' => 1,
                'unit_price' => $ratePerNight,
            ]);
        }
    }
    
    /**
     * Post a charge to the folio
     */
    public function postCharge(Folio $folio, array $data): FolioCharge
    {
        if ($folio->status !== 'open') {
            throw new \Exception('Cannot post charges to a closed folio.');
        }
        
        $amount = ($data['quantity'] ?? 1) * $data['unit_price'];
        $taxRate = $this->getTaxRate($data['category']);
        $taxAmount = $amount * $taxRate;
        
        $charge = FolioCharge::create([
            'folio_id' => $folio->id,
            'category' => $data['category'],
            'description' => $data['description'],
            'quantity' => $data['quantity'] ?? 1,
            'unit_price' => $data['unit_price'],
            'amount' => $amount,
            'tax_amount' => $taxAmount,
            'posted_by' => auth()->id(),
            'posted_at' => now(),
        ]);
        
        $folio->recalculateBalance();
        
        return $charge;
    }
    
    /**
     * Void a charge
     */
    public function voidCharge(FolioCharge $charge, string $reason): FolioCharge
    {
        if ($charge->is_voided) {
            throw new \Exception('Charge is already voided.');
        }
        
        $charge->update([
            'is_voided' => true,
            'voided_reason' => $reason,
        ]);
        
        $charge->folio->recalculateBalance();
        
        $this->log('Charge voided', [
            'charge_id' => $charge->id,
            'reason' => $reason,
        ]);
        
        return $charge;
    }
    
    /**
     * Post a payment to the folio
     */
    public function postPayment(Folio $folio, array $data): Payment
    {
        if ($folio->status !== 'open') {
            throw new \Exception('Cannot post payments to a closed folio.');
        }
        
        $payment = Payment::create([
            'folio_id' => $folio->id,
            'payment_method' => $data['payment_method'],
            'amount' => $data['amount'],
            'reference_number' => $data['reference_number'] ?? null,
            'status' => 'completed',
            'notes' => $data['notes'] ?? null,
            'processed_by' => auth()->id(),
            'processed_at' => now(),
        ]);
        
        $folio->recalculateBalance();
        
        // Update reservation paid amount
        $reservation = $folio->reservation;
        $reservation->update([
            'paid_amount' => $folio->total_payments,
        ]);
        
        return $payment;
    }
    
    /**
     * Close a folio
     */
    public function close(Folio $folio): Folio
    {
        $folio->recalculateBalance();
        
        if ($folio->balance > 0) {
            throw new \Exception('Cannot close folio with outstanding balance.');
        }
        
        $folio->update([
            'status' => 'closed',
            'closed_at' => now(),
        ]);
        
        return $folio;
    }
    
    /**
     * Generate folio number
     */
    protected function generateFolioNumber(): string
    {
        $date = now()->format('Ymd');
        $count = Folio::whereDate('created_at', today())->count() + 1;
        return sprintf('FOL-%s-%04d', $date, $count);
    }
    
    /**
     * Get tax rate by category
     */
    protected function getTaxRate(string $category): float
    {
        $taxRates = [
            'room' => 0.16,      // 16% VAT
            'food' => 0.16,
            'beverage' => 0.16,
            'telephone' => 0.16,
            'internet' => 0.16,
            'minibar' => 0.16,
            'laundry' => 0.16,
            'parking' => 0.16,
            'spa' => 0.16,
            'transport' => 0.16,
            'other' => 0.16,
        ];
        
        return $taxRates[$category] ?? 0.16;
    }
}
```

### HousekeepingService

```php
<?php
// app/Services/HousekeepingService.php
namespace App\Services;

use App\Models\Room;
use App\Models\Reservation;
use App\Models\HousekeepingTask;
use App\Models\User;

class HousekeepingService extends BaseService
{
    /**
     * Create checkout cleaning task
     */
    public function createCheckoutTask(Room $room, Reservation $reservation): HousekeepingTask
    {
        return HousekeepingTask::create([
            'room_id' => $room->id,
            'reservation_id' => $reservation->id,
            'task_type' => 'checkout_cleaning',
            'status' => 'pending',
            'priority' => 'high',
        ]);
    }
    
    /**
     * Create stayover cleaning task
     */
    public function createStayoverTask(Room $room, Reservation $reservation): HousekeepingTask
    {
        return HousekeepingTask::create([
            'room_id' => $room->id,
            'reservation_id' => $reservation->id,
            'task_type' => 'stayover_cleaning',
            'status' => 'pending',
            'priority' => 'normal',
        ]);
    }
    
    /**
     * Assign task to staff
     */
    public function assignTask(HousekeepingTask $task, int $userId): HousekeepingTask
    {
        $user = User::findOrFail($userId);
        
        // Verify user has housekeeping role
        if (!$user->hasRole('Housekeeping')) {
            throw new \Exception('User is not a housekeeping staff member.');
        }
        
        $task->update([
            'assigned_to' => $userId,
            'assigned_at' => now(),
        ]);
        
        return $task;
    }
    
    /**
     * Start a task
     */
    public function startTask(HousekeepingTask $task): HousekeepingTask
    {
        if ($task->status !== 'pending') {
            throw new \Exception('Task must be pending to start.');
        }
        
        $task->update([
            'status' => 'in_progress',
            'started_at' => now(),
        ]);
        
        return $task;
    }
    
    /**
     * Complete a task
     */
    public function completeTask(HousekeepingTask $task, ?string $notes = null): HousekeepingTask
    {
        if ($task->status !== 'in_progress') {
            throw new \Exception('Task must be in progress to complete.');
        }
        
        $task->update([
            'status' => 'completed',
            'completed_at' => now(),
            'notes' => $notes,
        ]);
        
        // Update room status to clean
        $task->room->update(['status' => 'clean']);
        
        return $task;
    }
    
    /**
     * Inspect and approve a task
     */
    public function inspectTask(HousekeepingTask $task, int $inspectorId, bool $approved, ?string $notes = null): HousekeepingTask
    {
        if ($task->status !== 'completed') {
            throw new \Exception('Task must be completed to inspect.');
        }
        
        if ($approved) {
            $task->update([
                'status' => 'inspected',
                'inspected_by' => $inspectorId,
                'inspected_at' => now(),
                'notes' => $notes,
            ]);
            
            // Update room status to inspected (ready for guests)
            $task->room->update(['status' => 'inspected']);
        } else {
            $task->update([
                'status' => 'failed',
                'inspected_by' => $inspectorId,
                'inspected_at' => now(),
                'notes' => $notes,
            ]);
            
            // Create re-cleaning task
            $this->createRecleaningTask($task);
        }
        
        return $task;
    }
    
    /**
     * Create re-cleaning task after failed inspection
     */
    protected function createRecleaningTask(HousekeepingTask $failedTask): HousekeepingTask
    {
        return HousekeepingTask::create([
            'room_id' => $failedTask->room_id,
            'reservation_id' => $failedTask->reservation_id,
            'task_type' => $failedTask->task_type,
            'status' => 'pending',
            'priority' => 'urgent',
            'assigned_to' => $failedTask->assigned_to, // Reassign to same staff
            'notes' => 'Re-cleaning required. Previous task failed inspection.',
        ]);
    }
    
    /**
     * Update room status
     */
    public function updateRoomStatus(Room $room, string $status): Room
    {
        $validStatuses = ['available', 'occupied', 'dirty', 'clean', 'inspected', 'maintenance', 'blocked'];
        
        if (!in_array($status, $validStatuses)) {
            throw new \Exception('Invalid room status: ' . $status);
        }
        
        $room->update(['status' => $status]);
        
        $this->log('Room status updated', [
            'room_id' => $room->id,
            'status' => $status,
        ]);
        
        return $room;
    }
    
    /**
     * Get today's task summary
     */
    public function getTodaySummary(): array
    {
        return [
            'pending' => HousekeepingTask::where('status', 'pending')->whereDate('created_at', today())->count(),
            'in_progress' => HousekeepingTask::where('status', 'in_progress')->count(),
            'completed' => HousekeepingTask::where('status', 'completed')->whereDate('completed_at', today())->count(),
            'inspected' => HousekeepingTask::where('status', 'inspected')->whereDate('inspected_at', today())->count(),
            'rooms_dirty' => Room::where('status', 'dirty')->count(),
            'rooms_clean' => Room::where('status', 'clean')->count(),
            'rooms_inspected' => Room::where('status', 'inspected')->count(),
        ];
    }
}
```

### NotificationService

```php
<?php
// app/Services/NotificationService.php
namespace App\Services;

use App\Models\Reservation;
use App\Models\Notification;
use Illuminate\Support\Facades\Mail;

class NotificationService extends BaseService
{
    /**
     * Send reservation confirmation
     */
    public function sendReservationConfirmation(Reservation $reservation): void
    {
        $guest = $reservation->guest;
        
        // Create in-app notification
        $this->createNotification(
            'guest',
            $guest->id,
            'reservation',
            'Reservation Confirmed',
            "Your reservation {$reservation->confirmation_number} has been confirmed for {$reservation->check_in_date->format('M d, Y')}."
        );
        
        // Send email
        Mail::to($guest->email)->queue(
            new \App\Mail\ReservationConfirmation($reservation)
        );
    }
    
    /**
     * Send reservation modification notification
     */
    public function sendReservationModification(Reservation $reservation): void
    {
        $guest = $reservation->guest;
        
        $this->createNotification(
            'guest',
            $guest->id,
            'reservation',
            'Reservation Modified',
            "Your reservation {$reservation->confirmation_number} has been modified."
        );
        
        Mail::to($guest->email)->queue(
            new \App\Mail\ReservationModification($reservation)
        );
    }
    
    /**
     * Send reservation cancellation notification
     */
    public function sendReservationCancellation(Reservation $reservation, float $cancellationFee = 0): void
    {
        $guest = $reservation->guest;
        
        $message = "Your reservation {$reservation->confirmation_number} has been cancelled.";
        if ($cancellationFee > 0) {
            $message .= " A cancellation fee of " . number_format($cancellationFee, 2) . " has been applied.";
        }
        
        $this->createNotification(
            'guest',
            $guest->id,
            'reservation',
            'Reservation Cancelled',
            $message
        );
        
        Mail::to($guest->email)->queue(
            new \App\Mail\ReservationCancellation($reservation, $cancellationFee)
        );
    }
    
    /**
     * Send check-in confirmation
     */
    public function sendCheckInConfirmation(Reservation $reservation): void
    {
        $guest = $reservation->guest;
        
        $this->createNotification(
            'guest',
            $guest->id,
            'check_in',
            'Welcome!',
            "Welcome to our hotel! You have been checked into room {$reservation->room->room_number}."
        );
        
        // Notify front desk
        $this->notifyRole('Front Desk', 'check_in', 'Guest Checked In', 
            "Guest {$guest->full_name} has checked into room {$reservation->room->room_number}.");
    }
    
    /**
     * Send check-out confirmation with invoice
     */
    public function sendCheckOutConfirmation(Reservation $reservation, string $invoicePath): void
    {
        $guest = $reservation->guest;
        
        $this->createNotification(
            'guest',
            $guest->id,
            'check_out',
            'Thank You for Staying!',
            "Thank you for staying with us. Your invoice has been sent to your email."
        );
        
        Mail::to($guest->email)->queue(
            new \App\Mail\CheckOutConfirmation($reservation, $invoicePath)
        );
    }
    
    /**
     * Send no-show notification
     */
    public function sendNoShowNotification(Reservation $reservation, float $noShowFee): void
    {
        $guest = $reservation->guest;
        
        $this->createNotification(
            'guest',
            $guest->id,
            'reservation',
            'No-Show Recorded',
            "Your reservation {$reservation->confirmation_number} has been marked as a no-show. A fee of " . number_format($noShowFee, 2) . " has been applied."
        );
        
        Mail::to($guest->email)->queue(
            new \App\Mail\NoShowNotification($reservation, $noShowFee)
        );
    }
    
    /**
     * Create in-app notification
     */
    protected function createNotification(
        string $notifiableType, 
        int $notifiableId, 
        string $category, 
        string $title, 
        string $message,
        array $data = []
    ): Notification {
        return Notification::create([
            'type' => 'in_app',
            'notifiable_type' => $notifiableType === 'guest' ? 'App\\Models\\Guest' : 'App\\Models\\User',
            'notifiable_id' => $notifiableId,
            'category' => $category,
            'title' => $title,
            'message' => $message,
            'data' => $data,
            'sent_at' => now(),
        ]);
    }
    
    /**
     * Notify all users with a specific role
     */
    protected function notifyRole(string $role, string $category, string $title, string $message): void
    {
        $users = \App\Models\User::role($role)->get();
        
        foreach ($users as $user) {
            $this->createNotification('user', $user->id, $category, $title, $message);
        }
    }
}
```

---

## 6. Implementation Modules

### Module 1: Authentication & User Management

**Requirements Reference:** FR-AUTH-001, FR-AUTH-002, FR-AUTH-003

#### Services Used
- `AuthService` (if custom auth needed beyond Breeze)
- `AuditService` for logging

#### Livewire Components (Thin)

| Component | Purpose | Service Used |
|-----------|---------|--------------|
| `Auth/Login` | Login form | Laravel's built-in |
| `Users/Index` | List users | Direct query (simple) |
| `Users/Create` | Create user form | Direct create (simple) |
| `Users/Edit` | Edit user form | Direct update (simple) |
| `Roles/Index` | Manage roles | Spatie Permission |

---

### Module 2: Guest Information Management

**Requirements Reference:** FR-GUEST-001, FR-GUEST-002, FR-GUEST-003

#### Services Used
- `GuestService`

#### Livewire Components (Thin)

| Component | Purpose | Service Used |
|-----------|---------|--------------|
| `Guests/Index` | List with search | Direct query |
| `Guests/Create` | Create guest form | `GuestService::create()` |
| `Guests/Edit` | Edit guest | `GuestService::update()` |
| `Guests/Show` | Profile with history | `GuestService::getHistory()` |

---

### Module 3: Room Inventory & Assignment

**Requirements Reference:** FR-RES-002

#### Services Used
- `RoomService`
- `AvailabilityService`

#### Livewire Components (Thin)

| Component | Purpose | Service Used |
|-----------|---------|--------------|
| `RoomTypes/Index` | List room types | Direct query |
| `Rooms/Index` | List rooms | Direct query |
| `Rooms/StatusBoard` | Visual status board | `AvailabilityService` |
| `Rooms/Calendar` | Availability calendar | `AvailabilityService` |

---

### Module 4: Reservation Management

**Requirements Reference:** FR-RES-001, FR-RES-002, FR-RES-003, FR-RES-004

#### Services Used
- `ReservationService`
- `AvailabilityService`

#### Livewire Components (Thin)

| Component | Purpose | Service Used |
|-----------|---------|--------------|
| `Reservations/Index` | List reservations | Direct query |
| `Reservations/Create` | Create reservation | `ReservationService::create()` |
| `Reservations/Edit` | Modify reservation | `ReservationService::modify()` |
| `Reservations/Cancel` | Cancel modal | `ReservationService::cancel()` |

---

### Module 5: Front Desk Operations

**Requirements Reference:** FR-FD-001, FR-FD-002, FR-FD-003, FR-FD-004

#### Services Used
- `CheckInService`
- `CheckOutService`
- `AvailabilityService`

#### Livewire Components (Thin)

| Component | Purpose | Service Used |
|-----------|---------|--------------|
| `FrontDesk/Dashboard` | Today's overview | Direct queries |
| `FrontDesk/CheckIn` | Check-in process | `CheckInService::process()` |
| `FrontDesk/CheckOut` | Check-out process | `CheckOutService::process()` |
| `FrontDesk/WalkIn` | Walk-in form | `CheckInService::walkIn()` |

---

### Module 6: Billing & Payment Processing

**Requirements Reference:** FR-BILL-001, FR-BILL-002, FR-BILL-003

#### Services Used
- `FolioService`
- `BillingService`

#### Livewire Components (Thin)

| Component | Purpose | Service Used |
|-----------|---------|--------------|
| `Billing/FolioView` | View folio | `FolioService` |
| `Billing/PostCharge` | Add charge | `FolioService::postCharge()` |
| `Billing/ProcessPayment` | Payment form | `FolioService::postPayment()` |

---

### Module 7: Housekeeping Management

**Requirements Reference:** FR-HK-001, FR-HK-002, FR-HK-003

#### Services Used
- `HousekeepingService`

#### Livewire Components (Thin)

| Component | Purpose | Service Used |
|-----------|---------|--------------|
| `Housekeeping/Dashboard` | Task overview | `HousekeepingService::getTodaySummary()` |
| `Housekeeping/Tasks` | Task list | Direct query |
| `Housekeeping/UpdateStatus` | Update task | `HousekeepingService::completeTask()` |
| `Housekeeping/Inspection` | Inspect task | `HousekeepingService::inspectTask()` |

---

### Module 8: Communication & Notifications

**Requirements Reference:** FR-COMM-001, FR-COMM-003

#### Services Used
- `NotificationService`

#### Livewire Components (Thin)

| Component | Purpose | Service Used |
|-----------|---------|--------------|
| `Notifications/Bell` | Navbar bell | Direct query |
| `Notifications/Center` | Full list | Direct query |

---

## 7. Development Timeline

### Week 1-2: Foundation & Services Setup

| Task | Status |
|------|--------|
| Install Livewire & packages | ✅ |
| Create all migrations | ✅ |
| Create all models with relationships | ✅ |
| Create base service class | ✅ |
| Create `GuestService` | ✅ |
| Create `AvailabilityService` | ✅ |
| Set up Spatie Permission | ✅ |
| Create role/permission seeders | ✅ |

### Week 3-4: Auth, Users & Guest Module

| Task | Status |
|------|--------|
| Build main layout with sidebar | ✅ |
| Authentication (Breeze Livewire) | ✅ |
| User management components | ✅ |
| Role management components | ✅ |
| Guest CRUD components | ✅ |
| Guest search & history | ✅ |

### Week 5-6: Rooms & Availability

| Task | Status |
|------|--------|
| Create `RoomService` | ⬜ |
| Room type management | ⬜ |
| Room management | ⬜ |
| Room status board | ⬜ |
| Availability calendar | ⬜ |

### Week 7-8: Reservations

| Task | Status |
|------|--------|
| Create `ReservationService` | ⬜ |
| Reservation list component | ⬜ |
| Reservation create wizard | ⬜ |
| Reservation modification | ⬜ |
| Cancellation workflow | ⬜ |

### Week 9-10: Front Desk Operations

| Task | Status |
|------|--------|
| Create `CheckInService` | ⬜ |
| Create `CheckOutService` | ⬜ |
| Front desk dashboard | ⬜ |
| Check-in process | ⬜ |
| Check-out process | ⬜ |
| Walk-in handling | ⬜ |

### Week 11-12: Billing

| Task | Status |
|------|--------|
| Create `FolioService` | ⬜ |
| Folio view component | ⬜ |
| Charge posting | ⬜ |
| Payment processing | ⬜ |
| Invoice generation (PDF) | ⬜ |

### Week 13-14: Housekeeping

| Task | Status |
|------|--------|
| Create `HousekeepingService` | ⬜ |
| Housekeeping dashboard | ⬜ |
| Task management | ⬜ |
| Inspection workflow | ⬜ |

### Week 15-16: Notifications & Polish

| Task | Status |
|------|--------|
| Create `NotificationService` | ⬜ |
| Email templates | ⬜ |
| In-app notifications | ⬜ |
| Testing & bug fixes | ⬜ |
| UI/UX improvements | ⬜ |

---

## 8. File Structure

```
app/
├── Http/
│   ├── Controllers/
│   │   └── InvoiceController.php    # PDF download only
│   └── Middleware/
│       └── CheckPermission.php
│
├── Livewire/                        # THIN COMPONENTS (UI only)
│   ├── Dashboard.php
│   ├── Users/
│   │   ├── Index.php
│   │   ├── Create.php
│   │   └── Edit.php
│   ├── Roles/
│   │   ├── Index.php
│   │   └── Permissions.php
│   ├── Guests/
│   │   ├── Index.php
│   │   ├── Create.php               # Uses GuestService
│   │   ├── Edit.php                 # Uses GuestService
│   │   ├── Show.php                 # Uses GuestService
│   │   └── Search.php
│   ├── RoomTypes/
│   │   ├── Index.php
│   │   ├── Create.php
│   │   └── Edit.php
│   ├── Rooms/
│   │   ├── Index.php
│   │   ├── Create.php
│   │   ├── Edit.php
│   │   ├── StatusBoard.php          # Uses AvailabilityService
│   │   └── Calendar.php             # Uses AvailabilityService
│   ├── Reservations/
│   │   ├── Index.php
│   │   ├── Create.php               # Uses ReservationService
│   │   ├── Edit.php                 # Uses ReservationService
│   │   ├── Show.php
│   │   └── Calendar.php
│   ├── FrontDesk/
│   │   ├── Dashboard.php
│   │   ├── Arrivals.php
│   │   ├── Departures.php
│   │   ├── InHouse.php
│   │   ├── CheckIn.php              # Uses CheckInService
│   │   ├── CheckOut.php             # Uses CheckOutService
│   │   └── WalkIn.php               # Uses CheckInService
│   ├── Billing/
│   │   ├── FolioView.php            # Uses FolioService
│   │   ├── PostCharge.php           # Uses FolioService
│   │   ├── ProcessPayment.php       # Uses FolioService
│   │   └── Invoice.php
│   ├── Housekeeping/
│   │   ├── Dashboard.php            # Uses HousekeepingService
│   │   ├── Tasks.php
│   │   ├── RoomStatus.php           # Uses HousekeepingService
│   │   └── Inspection.php           # Uses HousekeepingService
│   └── Notifications/
│       ├── Bell.php
│       └── Center.php
│
├── Services/                        # ALL BUSINESS LOGIC HERE
│   ├── BaseService.php              # Abstract base with helpers
│   ├── GuestService.php
│   ├── RoomService.php
│   ├── AvailabilityService.php
│   ├── ReservationService.php
│   ├── CheckInService.php
│   ├── CheckOutService.php
│   ├── FolioService.php
│   ├── BillingService.php
│   ├── HousekeepingService.php
│   ├── NotificationService.php
│   ├── AuditService.php
│   └── ReportService.php
│
├── Models/                          # DATA & RELATIONSHIPS
│   ├── User.php
│   ├── Guest.php
│   ├── RoomType.php
│   ├── Room.php
│   ├── Reservation.php
│   ├── Folio.php
│   ├── FolioCharge.php
│   ├── Payment.php
│   ├── HousekeepingTask.php
│   ├── Notification.php
│   └── AuditLog.php
│
├── Mail/                            # EMAIL TEMPLATES
│   ├── ReservationConfirmation.php
│   ├── ReservationModification.php
│   ├── ReservationCancellation.php
│   ├── CheckOutConfirmation.php
│   └── NoShowNotification.php
│
└── Policies/                        # AUTHORIZATION
    ├── UserPolicy.php
    ├── GuestPolicy.php
    ├── RoomPolicy.php
    └── ReservationPolicy.php

database/
├── migrations/
└── seeders/
    ├── RolePermissionSeeder.php
    ├── RoomTypeSeeder.php
    ├── RoomSeeder.php
    └── DemoDataSeeder.php

resources/views/
├── layouts/
│   ├── app.blade.php
│   └── guest.blade.php
├── livewire/
│   └── [component views]
├── pdf/
│   └── invoice.blade.php
├── mail/
│   └── [email templates]
└── components/
    └── [reusable blade components]
```

---

## 9. Code Patterns & Examples

### Pattern 1: Livewire Component with Service Injection

```php
<?php
// app/Livewire/Reservations/Create.php
namespace App\Livewire\Reservations;

use App\Services\ReservationService;
use App\Services\AvailabilityService;
use App\Models\Guest;
use App\Models\RoomType;
use Livewire\Component;

class Create extends Component
{
    // Services (injected via boot())
    protected ReservationService $reservationService;
    protected AvailabilityService $availabilityService;
    
    // Form properties
    public $guest_id = '';
    public $room_type_id = '';
    public $check_in_date = '';
    public $check_out_date = '';
    public $adults = 1;
    public $children = 0;
    public $special_requests = '';
    public $source = 'direct';
    
    // UI state
    public $availability = null;
    public $step = 1;
    
    protected $rules = [
        'guest_id' => 'required|exists:guests,id',
        'room_type_id' => 'required|exists:room_types,id',
        'check_in_date' => 'required|date|after_or_equal:today',
        'check_out_date' => 'required|date|after:check_in_date',
        'adults' => 'required|integer|min:1',
        'children' => 'required|integer|min:0',
    ];
    
    /**
     * Inject services - called on EVERY request
     */
    public function boot(
        ReservationService $reservationService,
        AvailabilityService $availabilityService
    ) {
        $this->reservationService = $reservationService;
        $this->availabilityService = $availabilityService;
    }
    
    /**
     * Check availability when dates change
     */
    public function updatedCheckOutDate()
    {
        $this->checkAvailability();
    }
    
    public function updatedRoomTypeId()
    {
        $this->checkAvailability();
    }
    
    /**
     * Check availability - delegates to service
     */
    public function checkAvailability()
    {
        if ($this->room_type_id && $this->check_in_date && $this->check_out_date) {
            $this->availability = $this->availabilityService->check(
                $this->room_type_id,
                $this->check_in_date,
                $this->check_out_date
            );
        }
    }
    
    /**
     * Go to next step
     */
    public function nextStep()
    {
        if ($this->step === 1) {
            $this->validateOnly('guest_id');
        } elseif ($this->step === 2) {
            $this->validate([
                'room_type_id' => $this->rules['room_type_id'],
                'check_in_date' => $this->rules['check_in_date'],
                'check_out_date' => $this->rules['check_out_date'],
            ]);
            
            $this->checkAvailability();
            
            if (!$this->availability['available']) {
                $this->addError('room_type_id', 'No rooms available for selected dates.');
                return;
            }
        }
        
        $this->step++;
    }
    
    /**
     * Save reservation - delegates ALL logic to service
     */
    public function save()
    {
        $this->validate();
        
        try {
            $reservation = $this->reservationService->create([
                'guest_id' => $this->guest_id,
                'room_type_id' => $this->room_type_id,
                'check_in_date' => $this->check_in_date,
                'check_out_date' => $this->check_out_date,
                'adults' => $this->adults,
                'children' => $this->children,
                'special_requests' => $this->special_requests,
                'source' => $this->source,
            ]);
            
            session()->flash('success', 'Reservation created successfully!');
            
            return redirect()->route('reservations.show', $reservation);
            
        } catch (\Exception $e) {
            $this->addError('reservation', $e->getMessage());
        }
    }
    
    public function render()
    {
        return view('livewire.reservations.create', [
            'guests' => Guest::orderBy('last_name')->get(),
            'roomTypes' => RoomType::where('is_active', true)->get(),
        ]);
    }
}
```

### Pattern 2: Front Desk Check-In with Service

```php
<?php
// app/Livewire/FrontDesk/CheckIn.php
namespace App\Livewire\FrontDesk;

use App\Services\CheckInService;
use App\Services\AvailabilityService;
use App\Models\Reservation;
use Livewire\Component;

class CheckIn extends Component
{
    protected CheckInService $checkInService;
    protected AvailabilityService $availabilityService;
    
    public Reservation $reservation;
    public $room_id = '';
    public $deposit_amount = 0;
    public $availableRooms = [];
    
    public function boot(
        CheckInService $checkInService,
        AvailabilityService $availabilityService
    ) {
        $this->checkInService = $checkInService;
        $this->availabilityService = $availabilityService;
    }
    
    public function mount(Reservation $reservation)
    {
        $this->reservation = $reservation;
        $this->loadAvailableRooms();
    }
    
    public function loadAvailableRooms()
    {
        $this->availableRooms = $this->availabilityService->getAvailableRooms(
            $this->reservation->room_type_id,
            $this->reservation->check_in_date,
            $this->reservation->check_out_date
        );
    }
    
    public function processCheckIn()
    {
        $this->validate([
            'room_id' => 'required|exists:rooms,id',
            'deposit_amount' => 'required|numeric|min:0',
        ]);
        
        try {
            $result = $this->checkInService->process(
                $this->reservation->id,
                $this->room_id,
                $this->deposit_amount
            );
            
            session()->flash('success', 'Guest checked in successfully to room ' . $result->room->room_number);
            
            return redirect()->route('front-desk.in-house');
            
        } catch (\Exception $e) {
            $this->addError('check_in', $e->getMessage());
        }
    }
    
    public function render()
    {
        return view('livewire.front-desk.check-in');
    }
}
```

---

## Quick Commands Reference

```bash
# Create a new Livewire component
php artisan make:livewire Guests/Index

# Create a model with migration
php artisan make:model Guest -m

# Create a service class (manual - no artisan command)
# Create file: app/Services/GuestService.php

# Create a seeder
php artisan make:seeder RoomTypeSeeder

# Create a mail class
php artisan make:mail ReservationConfirmation

# Run migrations
php artisan migrate

# Run seeders
php artisan db:seed

# Clear all caches
php artisan optimize:clear

# Start development server
php artisan serve

# Run Vite dev server (in separate terminal)
npm run dev
```

---

## Learning Resources

1. **Livewire Documentation**: https://livewire.laravel.com/docs
2. **Laravel Documentation**: https://laravel.com/docs
3. **Spatie Permission**: https://spatie.be/docs/laravel-permission
4. **Tailwind CSS**: https://tailwindcss.com/docs

---

## Summary: Architecture Rules

1. **Livewire Components** = UI only (thin)
   - Form binding with `wire:model`
   - Validation
   - Call services for business logic
   - Display data

2. **Service Classes** = Business logic (fat)
   - All complex operations
   - Database transactions
   - Calculations
   - External integrations
   - Can be reused anywhere

3. **Models** = Data structure
   - Relationships
   - Accessors/Mutators
   - Scopes

4. **Inject services using `boot()` method** - called on every request

Good luck with your Hotel Management System!
