# Cycle 13: Integration & Polish

## Goal
Final integration pass: FK verification, permission seeding completeness, sidebar navigation update, full migration run test, Docker build verification, and system-wide consistency checks.

---

## Part A: Foreign Key Verification

All FK references across all migrations must point to tables that exist at the time of migration execution.

### Verification Script: `scripts/verify-fk-order.php`

```php
#!/usr/bin/env php
<?php
// Run: php scripts/verify-fk-order.php
// Scans all migrations in order, tracks table creation, flags any FK to a table not yet created.

$migrationsPath = __DIR__ . '/../database/migrations';
$files = glob($migrationsPath . '/*.php');
sort($files); // chronological order

$createdTables = ['users', 'password_reset_tokens', 'sessions', 'cache', 'cache_locks', 'jobs', 'job_batches', 'failed_jobs'];
$errors = [];

foreach ($files as $file) {
    $content = file_get_contents($file);
    $basename = basename($file);

    // Track table creation
    if (preg_match_all('/Schema::create\([\'"](\w+)[\'"]/', $content, $creates)) {
        foreach ($creates[1] as $table) {
            $createdTables[] = $table;
        }
    }

    // Check FK references
    if (preg_match_all('/->constrained\([\'"](\w+)[\'"]/', $content, $constrained)) {
        foreach ($constrained[1] as $refTable) {
            if (!in_array($refTable, $createdTables)) {
                $errors[] = "[{$basename}] FK references '{$refTable}' which has not been created yet.";
            }
        }
    }

    // Check references()->on()
    if (preg_match_all('/->references\([\'"]id[\'"]\)->on\([\'"](\w+)[\'"]/', $content, $refs)) {
        foreach ($refs[1] as $refTable) {
            if (!in_array($refTable, $createdTables)) {
                $errors[] = "[{$basename}] FK references '{$refTable}' which has not been created yet.";
            }
        }
    }
}

if (empty($errors)) {
    echo "All FK references point to existing tables.\n";
    exit(0);
} else {
    echo "FK ORDER ERRORS:\n";
    foreach ($errors as $e) echo "  - {$e}\n";
    exit(1);
}
```

### Known FK Issues to Fix:

1. `attendance_records.training_session_id` → `training_sessions` (verify migration order)
2. `space_reservations.facility_id` → `facilities` (verify)
3. `enrollments.training_group_id` → `training_groups` (verify)
4. `invoice_items.itemable_id` is morph — no FK constraint needed
5. `pos_transaction_items.product_id` → `products` (verify both exist)

---

## Part B: Permission Seeder Completeness

### Full Permission Registry

Every module's permissions must be seeded. Create a comprehensive seeder that covers ALL modules:

```php
// database/seeders/PermissionSeeder.php

$permissions = [
    // ─── Dashboard ─────────────────────────────
    'dashboard.view',
    'dashboard.export',

    // ─── Organizations ─────────────────────────
    'academies.list', 'academies.show', 'academies.update',
    'branches.list', 'branches.create', 'branches.update', 'branches.delete',

    // ─── Identity & Users ──────────────────────
    'users.list', 'users.create', 'users.update', 'users.delete', 'users.assign_roles',
    'roles.list', 'roles.create', 'roles.update', 'roles.delete', 'roles.assign_permissions',
    'people.list', 'people.create', 'people.update', 'people.delete', 'people.merge',

    // ─── Participants ──────────────────────────
    'participants.list', 'participants.create', 'participants.update', 'participants.delete',
    'participants.show', 'participants.change_status', 'participants.export',
    'participants.view_financial', 'participants.view_attendance',
    'guardians.list', 'guardians.create', 'guardians.update',

    // ─── Training ──────────────────────────────
    'activities.list', 'activities.create', 'activities.update', 'activities.delete',
    'programs.list', 'programs.create', 'programs.update', 'programs.delete', 'programs.show',
    'groups.list', 'groups.create', 'groups.update', 'groups.delete', 'groups.show',
    'groups.manage_enrollments',
    'sessions.list', 'sessions.show', 'sessions.cancel', 'sessions.reschedule',
    'sessions.start', 'sessions.complete', 'sessions.generate',
    'schedules.list', 'schedules.manage',
    'enrollments.list', 'enrollments.create', 'enrollments.cancel',
    'enrollments.override', 'enrollments.transfer',
    'holidays.list', 'holidays.create', 'holidays.update', 'holidays.delete',
    'waitlists.manage', 'waitlists.promote',

    // ─── Attendance ────────────────────────────
    'attendance.list', 'attendance.mark', 'attendance.edit', 'attendance.export',
    'attendance.view_reports',
    'excuses.list', 'excuses.submit', 'excuses.approve', 'excuses.reject',

    // ─── Financial ─────────────────────────────
    'invoices.list', 'invoices.create', 'invoices.update', 'invoices.cancel',
    'invoices.show', 'invoices.send', 'invoices.export',
    'payments.list', 'payments.create', 'payments.refund', 'payments.export',
    'transactions.list', 'transactions.export',
    'accounts.list', 'accounts.create', 'accounts.update',
    'wallets.list', 'wallets.credit', 'wallets.debit', 'wallets.freeze',
    'payment_plans.list', 'payment_plans.create', 'payment_plans.update',
    'cash_sessions.open', 'cash_sessions.close', 'cash_sessions.list',
    'refunds.initiate', 'refunds.approve',
    'daily_closing.create', 'daily_closing.view',

    // ─── Pricing ───────────────────────────────
    'pricing.list', 'pricing.create', 'pricing.update', 'pricing.delete',
    'base_prices.list', 'base_prices.manage',
    'pricing_rules.list', 'pricing_rules.manage',
    'promotions.list', 'promotions.create', 'promotions.update', 'promotions.delete',
    'coupons.list', 'coupons.generate', 'coupons.deactivate',

    // ─── POS ───────────────────────────────────
    'pos.access', 'pos.override_price', 'pos.override_stock', 'pos.void_transaction',
    'pos_sessions.open', 'pos_sessions.close', 'pos_sessions.list',

    // ─── Inventory ─────────────────────────────
    'products.list', 'products.create', 'products.update', 'products.delete', 'products.export',
    'categories.list', 'categories.manage',
    'warehouses.list', 'warehouses.create', 'warehouses.update',
    'inventory.view_levels', 'inventory.adjust', 'inventory.transfer',
    'purchase_orders.list', 'purchase_orders.create', 'purchase_orders.approve', 'purchase_orders.receive',
    'stock_counts.list', 'stock_counts.create', 'stock_counts.approve',
    'kits.list', 'kits.create', 'kits.update', 'kits.delete',
    'suppliers.list', 'suppliers.create', 'suppliers.update',

    // ─── Facilities ────────────────────────────
    'facilities.list', 'facilities.create', 'facilities.update', 'facilities.delete', 'facilities.show',
    'facilities.manage_layouts', 'facilities.manage_hours',
    'reservations.list', 'reservations.create', 'reservations.cancel', 'reservations.confirm',

    // ─── Assignments ───────────────────────────
    'assignments.list', 'assignments.create', 'assignments.update', 'assignments.cancel',

    // ─── Notifications ─────────────────────────
    'notifications.send', 'notifications.manage_templates',
    'notification_preferences.manage',

    // ─── Reports ───────────────────────────────
    'reports.financial', 'reports.attendance', 'reports.enrollment',
    'reports.inventory', 'reports.trainer', 'reports.facility',
    'reports.export_pdf', 'reports.export_excel',

    // ─── Approvals ─────────────────────────────
    'approvals.list', 'approvals.approve', 'approvals.reject', 'approvals.configure',

    // ─── Settings ──────────────────────────────
    'settings.manage', 'settings.view_audit_log',

    // ─── Evaluations ───────────────────────────
    'evaluations.list', 'evaluations.create', 'evaluations.update',
];
```

### Default Role → Permission Mapping:

```php
$rolePermissions = [
    'super_admin' => '*', // all permissions
    'academy_owner' => '*', // all within their academy
    'academy_admin' => [/* all except settings.manage, roles.delete */],
    'branch_manager' => [/* branch-scoped: participants, training, attendance, basic financial */],
    'head_trainer' => ['sessions.*', 'attendance.*', 'groups.show', 'evaluations.*', 'schedules.*'],
    'trainer' => ['sessions.list', 'sessions.show', 'sessions.start', 'sessions.complete', 'attendance.mark', 'evaluations.create'],
    'receptionist' => ['participants.*', 'pos.access', 'invoices.list', 'payments.create', 'cash_sessions.*'],
    'accountant' => ['invoices.*', 'payments.*', 'transactions.*', 'wallets.*', 'reports.financial', 'daily_closing.*'],
    'data_entry' => ['participants.list', 'participants.create', 'participants.update', 'people.*'],
    'parent' => ['dashboard.view', 'attendance.list'], // own children only via scope
];
```

---

## Part C: Sidebar Navigation Update

### Complete Sidebar Structure:

```php
// resources/views/components/layouts/sidebar.blade.php

$navigation = [
    ['label' => 'لوحة التحكم', 'route' => 'dashboard', 'icon' => 'home', 'permission' => 'dashboard.view'],

    ['section' => 'المشاركين', 'items' => [
        ['label' => 'المشاركين', 'route' => 'participants.index', 'permission' => 'participants.list'],
        ['label' => 'أولياء الأمور', 'route' => 'guardians.index', 'permission' => 'guardians.list'],
    ]],

    ['section' => 'التدريب', 'items' => [
        ['label' => 'الأنشطة', 'route' => 'activities.index', 'permission' => 'activities.list'],
        ['label' => 'البرامج', 'route' => 'programs.index', 'permission' => 'programs.list'],
        ['label' => 'المجموعات', 'route' => 'groups.index', 'permission' => 'groups.list'],
        ['label' => 'الحصص', 'route' => 'sessions.index', 'permission' => 'sessions.list'],
        ['label' => 'الإجازات', 'route' => 'holidays.index', 'permission' => 'holidays.list'],
    ]],

    ['section' => 'الحضور', 'items' => [
        ['label' => 'تسجيل الحضور', 'route' => 'attendance.index', 'permission' => 'attendance.mark'],
        ['label' => 'الأعذار', 'route' => 'excuses.index', 'permission' => 'excuses.list'],
    ]],

    ['section' => 'المالية', 'items' => [
        ['label' => 'الفواتير', 'route' => 'invoices.index', 'permission' => 'invoices.list'],
        ['label' => 'المدفوعات', 'route' => 'payments.index', 'permission' => 'payments.list'],
        ['label' => 'الحسابات', 'route' => 'accounts.index', 'permission' => 'accounts.list'],
        ['label' => 'المحافظ', 'route' => 'wallets.index', 'permission' => 'wallets.list'],
        ['label' => 'خطط الدفع', 'route' => 'payment-plans.index', 'permission' => 'payment_plans.list'],
        ['label' => 'إقفال يومي', 'route' => 'daily-closing.index', 'permission' => 'daily_closing.view'],
    ]],

    ['section' => 'نقطة البيع', 'items' => [
        ['label' => 'نقطة البيع', 'route' => 'pos.index', 'permission' => 'pos.access'],
        ['label' => 'جلسات البيع', 'route' => 'pos-sessions.index', 'permission' => 'pos_sessions.list'],
    ]],

    ['section' => 'التسعير', 'items' => [
        ['label' => 'الأسعار الأساسية', 'route' => 'base-prices.index', 'permission' => 'base_prices.list'],
        ['label' => 'قواعد التسعير', 'route' => 'pricing-rules.index', 'permission' => 'pricing_rules.list'],
        ['label' => 'العروض والكوبونات', 'route' => 'promotions.index', 'permission' => 'promotions.list'],
    ]],

    ['section' => 'المخزون', 'items' => [
        ['label' => 'المنتجات', 'route' => 'products.index', 'permission' => 'products.list'],
        ['label' => 'المستودعات', 'route' => 'warehouses.index', 'permission' => 'warehouses.list'],
        ['label' => 'أوامر الشراء', 'route' => 'purchase-orders.index', 'permission' => 'purchase_orders.list'],
        ['label' => 'الجرد', 'route' => 'stock-counts.index', 'permission' => 'stock_counts.list'],
    ]],

    ['section' => 'المنشآت', 'items' => [
        ['label' => 'المنشآت', 'route' => 'facilities.index', 'permission' => 'facilities.list'],
        ['label' => 'الحجوزات', 'route' => 'reservations.index', 'permission' => 'reservations.list'],
    ]],

    ['section' => 'الإدارة', 'items' => [
        ['label' => 'طلبات الموافقة', 'route' => 'approvals.index', 'permission' => 'approvals.list', 'badge' => 'pending_approvals_count'],
        ['label' => 'التقارير', 'route' => 'reports.index', 'permission' => 'reports.financial'],
        ['label' => 'المستخدمين', 'route' => 'users.index', 'permission' => 'users.list'],
        ['label' => 'الأدوار', 'route' => 'roles.index', 'permission' => 'roles.list'],
        ['label' => 'سجل المراجعة', 'route' => 'audit-log.index', 'permission' => 'settings.view_audit_log'],
        ['label' => 'الإعدادات', 'route' => 'settings.index', 'permission' => 'settings.manage'],
    ]],
];
```

### Rendering Logic:
```blade
@foreach($navigation as $item)
    @if(isset($item['section']))
        @php $visibleItems = collect($item['items'])->filter(fn ($i) => auth()->user()->can($i['permission'])); @endphp
        @if($visibleItems->isNotEmpty())
            <div class="mt-6 mb-2 px-4 text-xs font-semibold text-gray-400 uppercase">
                {{ $item['section'] }}
            </div>
            @foreach($visibleItems as $navItem)
                @if(Route::has($navItem['route']))
                    <a href="{{ route($navItem['route']) }}"
                       class="..." 
                       :class="{ 'bg-blue-50 text-blue-700': '{{ request()->routeIs($navItem['route'] . '*') }}' }"
                       wire:navigate>
                        {{ $navItem['label'] }}
                        @if(isset($navItem['badge']))
                            <span class="...">{{ $$navItem['badge'] ?? '' }}</span>
                        @endif
                    </a>
                @endif
            @endforeach
        @endif
    @else
        @can($item['permission'])
            @if(Route::has($item['route']))
                <a href="{{ route($item['route']) }}" wire:navigate>{{ $item['label'] }}</a>
            @endif
        @endcan
    @endif
@endforeach
```

---

## Part D: Full Migration Run Test

### Test Script:
```bash
#!/bin/bash
# scripts/test-fresh-migration.sh
# Drops and recreates DB, runs all migrations + seeders, verifies no errors

set -e

echo "=== Testing fresh migration ==="
php artisan migrate:fresh --force --seed 2>&1

echo ""
echo "=== Verifying table count ==="
php artisan tinker --execute="echo 'Tables: ' . count(\DB::select(\"SELECT tablename FROM pg_tables WHERE schemaname='public'\"));"

echo ""
echo "=== Verifying key model counts ==="
php artisan tinker --execute="
echo 'Organizations: ' . \App\Domain\Shared\Models\Academy::count() . PHP_EOL;
echo 'Users: ' . \App\Models\User::count() . PHP_EOL;
echo 'Permissions: ' . \App\Domain\Identity\Models\Permission::count() . PHP_EOL;
echo 'Roles: ' . \App\Domain\Identity\Models\Role::count() . PHP_EOL;
echo 'Accounts: ' . \App\Domain\Financial\Models\Account::count() . PHP_EOL;
"

echo ""
echo "=== Running route:list to verify no broken controllers ==="
php artisan route:list --compact 2>&1 | tail -5

echo ""
echo "=== Testing login flow ==="
php artisan tinker --execute="
\$user = \App\Models\User::where('email', 'admin@elcaptain.com')->first();
echo \$user ? 'Login user exists: YES' : 'Login user exists: NO';
"

echo ""
echo "=== All checks passed ==="
```

### Expected Table Count: ~55-60 tables (including all gap-filling additions)

### Tables from All Cycles:
```
organizations, branches, users, roles, permissions, role_permissions, user_roles,
people, participants, guardians, guardian_participant,
activities, training_programs, training_groups, training_schedules, training_sessions,
enrollments, waitlists, holidays,
accounts, invoices, invoice_items, payments, transactions, wallets, wallet_transactions,
payment_plans, installments, cash_sessions, refunds, credits, credit_applications, payment_allocations, daily_closings,
pricing_rules, base_prices, promotions, promotion_usages, coupon_codes,
facilities, space_layouts, space_segments, space_reservations, facility_operating_hours,
assignments,
attendance_records,
products, product_categories, warehouses, inventory_levels, inventory_movements,
purchase_orders, purchase_order_items, kits, kit_components, stock_counts, stock_count_items,
pos_sessions, pos_transactions, pos_transaction_items, suppliers,
notification_templates, notification_logs, notification_preferences,
document_templates, generated_documents,
evaluations, evaluation_criteria, evaluation_scores,
approval_requests, approval_chains, approval_logs,
audit_logs, system_settings,
jobs, failed_jobs, job_batches, cache, cache_locks, sessions, password_reset_tokens, migrations
```

---

## Part E: Docker Build Verification

### Dockerfile Checklist:
```dockerfile
# Verify all PHP extensions needed:
RUN docker-php-ext-install pdo_pgsql pgsql intl bcmath gd zip opcache

# Verify Cairo font is available for DomPDF:
COPY storage/fonts/Cairo-Regular.ttf /usr/share/fonts/truetype/cairo/
RUN fc-cache -fv

# Verify entrypoint handles all assets:
COPY docker/entrypoint.sh /entrypoint.sh
```

### Entrypoint Must Include:
```bash
#!/bin/bash
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
php artisan storage:link
php artisan livewire:publish --assets 2>/dev/null || true
php artisan migrate --force
php artisan db:seed --class=PermissionSeeder --force

# Start supervisor (queue worker + scheduler + php-fpm)
exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
```

### Supervisor Config Must Include:
```ini
[program:queue-worker]
command=php artisan queue:work --tries=3 --timeout=90

[program:scheduler]
command=/bin/bash -c "while true; do php artisan schedule:run; sleep 60; done"

[program:php-fpm]
command=php-fpm -F
```

---

## Part F: System-Wide Consistency Checks

### Check 1: All Models Have Required Traits

```bash
# Script to verify all domain models use BelongsToAcademy + HasUuid + Auditable
grep -rL "BelongsToAcademy" app/Domain/*/Models/*.php | grep -v "Academy.php\|Permission.php"
# Any output = missing trait
```

### Check 2: All Events Implement ShouldDispatchAfterCommit

```bash
grep -rL "ShouldDispatchAfterCommit" app/Domain/*/Events/*.php
# Any output = missing interface
```

### Check 3: All Services Wrap in DB::transaction

```bash
grep -rL "DB::transaction" app/Domain/*/Services/*.php | grep -v "Interface\|Abstract"
# Review each — some read-only services may not need it
```

### Check 4: No `href="#"` in Views

```bash
grep -rn 'href="#"' resources/views/ --include="*.blade.php"
# Must return 0 results
```

### Check 5: All Livewire Mounts Authorize

```bash
grep -rL "authorize\|Authorize" app/Livewire/*.php app/Livewire/**/*.php
# Any output = missing authorization
```

### Check 6: All Money Columns Are bigInteger

```bash
grep -n "decimal\|float\|double" database/migrations/ --include="*.php" | grep -i "amount\|price\|cost\|balance\|fee\|total"
# Must return 0 results
```

### Check 7: No auth()/request() in Services

```bash
grep -n "auth()\|request()\|session()" app/Domain/*/Services/*.php
# Must return 0 results (services receive params, never access globals)
```

### Check 8: RTL Compliance (No Physical Properties)

```bash
grep -rn "ml-\|mr-\|pl-\|pr-\|text-left\|text-right\|float-left\|float-right\|border-l-\|border-r-\|rounded-l-\|rounded-r-" resources/views/ --include="*.blade.php" | grep -v "dir=\"ltr\""
# Must return 0 results (except inside dir="ltr" number inputs)
```

---

## Part G: Environment Variables for Production

```env
# Application
APP_NAME="El Captain Sports"
APP_ENV=production
APP_KEY=base64:...
APP_DEBUG=false
APP_URL=https://el-captain.caprover.al-arcade.com
APP_LOCALE=ar
APP_TIMEZONE=Africa/Cairo

# Database
DB_CONNECTION=pgsql
DB_HOST=srv-captain--postgresql
DB_PORT=5434
DB_DATABASE=elcaptainsportsonly
DB_USERNAME=elcaptain
DB_PASSWORD=ElCaptain2024!

# Cache & Queue
CACHE_STORE=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
REDIS_HOST=srv-captain--redis

# Mail (internal poste.io)
MAIL_MAILER=smtp
MAIL_HOST=srv-captain--posteio
MAIL_PORT=587
MAIL_USERNAME=noreply@al-arcade.com
MAIL_PASSWORD=...
MAIL_FROM_ADDRESS=noreply@al-arcade.com
MAIL_FROM_NAME="El Captain"
MAIL_VERIFY_PEER=false

# Livewire
LIVEWIRE_ASSET_URL=https://el-captain.caprover.al-arcade.com

# Trust Proxy
TRUSTED_PROXIES=*
```

---

## Part H: Pre-Deploy Checklist

```
[ ] php scripts/verify-fk-order.php passes
[ ] php artisan migrate:fresh --seed runs without error
[ ] All ~55 tables created
[ ] Login with admin@elcaptain.com/password works
[ ] Dashboard loads with stats
[ ] Sidebar shows correct items per role
[ ] All CRUD pages render (no 500s)
[ ] POS page loads
[ ] Reports page renders all tabs
[ ] Arabic text renders correctly (Cairo font)
[ ] RTL layout correct (sidebar on right, content flows right-to-left)
[ ] No href="#" anywhere in rendered HTML
[ ] Docker build succeeds
[ ] Queue worker processes jobs
[ ] Scheduler runs scheduled commands
[ ] OPcache cleared on deploy
[ ] SSL cert valid at production URL
```

---

## Summary: What Each Cycle Delivers

| Cycle | Deliverable | Files Changed |
|-------|-------------|---------------|
| 0 | Critical bugs fixed | 5 files |
| 1 | Events + Listeners complete | 35+ events, 25+ listeners |
| 2 | All 27 scheduled commands | commands/ + console.php |
| 3 | Financial subsystems (refunds, credits, closings) | 6 migrations, 4 services |
| 4 | Identity gaps (employees, trainers, contacts) | 4 migrations, 2 services |
| 5 | Inventory/POS gaps (suppliers, POS sessions) | 2 migrations, 2 services |
| 6 | Notifications & Documents | 2 migrations, 3 services, DomPDF |
| 7 | Training & Attendance (statuses, excuses, UI) | 5 migrations, 1 service, 5 components |
| 8 | Facility & Space (editor, calendar, hours) | 2 migrations, 3 components |
| 9 | UI Component Library | 18+ blade components, Chart.js |
| 10 | Reporting & Exports | ReportService, DomPDF, Excel |
| 11 | Pricing Engine alignment | 2 migrations, service fixes |
| 12 | Approval Workflows | 3 migrations, ApprovalService |
| 13 | Integration & Polish | verification, seeding, deploy |

**Total estimated effort:** 30-40 hours
**Execution order:** Sequential (each cycle may depend on prior ones)
**Testing:** Browser-first after each cycle completes
