# 📘 دليل شامل لنظام الأقساط والسداد - التحديثات الكاملة

> **الهدف:** توثيق شامل لجميع التحديثات والتعديلات على نظام الأقساط والسداد لتطبيقها على نسخة أخرى من النظام

---

## 📑 جدول المحتويات

1. [نظرة عامة](#نظرة-عامة)
2. [هيكل قاعدة البيانات](#هيكل-قاعدة-البيانات)
3. [التوزيع التلقائي للمدفوعات](#التوزيع-التلقائي-للمدفوعات)
4. [نظام الدفع الجزئي](#نظام-الدفع-الجزئي)
5. [تقرير الأقساط المتأخرة](#تقرير-الأقساط-المتأخرة)
6. [خطوات التطبيق على نظام جديد](#خطوات-التطبيق-على-نظام-جديد)

---

## 🎯 نظرة عامة

### الميزات الرئيسية المضافة:

| الميزة | الوصف | الفائدة |
|-------|-------|---------|
| **التوزيع التلقائي** | توزيع المدفوعات تلقائياً على الأقساط حسب الترتيب | مرونة في الدفع دون ربط بقسط محدد |
| **الدفع الجزئي** | إمكانية دفع جزء من قيمة القسط | واقعية أكثر مع حالات العملاء |
| **تتبع المدفوع** | حقل `paid_amount` في كل قسط | معرفة ما تم دفعه من كل قسط |
| **تقارير محسنة** | تقرير الأقساط المتأخرة مع الدفع الجزئي | وضوح أكبر في المتابعة |
| **إحصائيات دقيقة** | عدادات للأقساط: مدفوعة/جزئية/غير مدفوعة | متابعة شاملة للعقود |

---

## 🗄️ هيكل قاعدة البيانات

### 1. جدول `installments` (الأقساط)

#### الحقول الأساسية:
```sql
CREATE TABLE installments (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    contract_id BIGINT UNSIGNED NOT NULL,
    due_date DATE NOT NULL,
    value DECIMAL(12, 2) NOT NULL,
    is_paid BOOLEAN DEFAULT FALSE,
    created_at TIMESTAMP,
    updated_at TIMESTAMP,
    
    FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE
);
```

#### الحقول المضافة للدفع الجزئي:
```sql
ALTER TABLE installments 
ADD COLUMN paid_amount DECIMAL(10, 2) NULL DEFAULT 0 AFTER value,
ADD COLUMN payment_date DATE NULL AFTER paid_amount,
ADD COLUMN notes TEXT NULL AFTER is_paid;
```

**ملف Migration:**
```php
// database/migrations/2025_09_23_180231_add_payment_fields_to_installments_table.php

public function up(): void
{
    Schema::table('installments', function (Blueprint $table) {
        $table->decimal('paid_amount', 10, 2)->nullable()->after('value');
        $table->date('payment_date')->nullable()->after('paid_amount');
        $table->text('notes')->nullable()->after('is_paid');
    });
}

public function down(): void
{
    Schema::table('installments', function (Blueprint $table) {
        $table->dropColumn(['paid_amount', 'payment_date', 'notes']);
    });
}
```

---

### 2. جدول `payments` (المدفوعات)

#### الهيكل الأساسي:
```sql
CREATE TABLE payments (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    customer_id BIGINT UNSIGNED NOT NULL,
    contract_id BIGINT UNSIGNED NOT NULL,
    installment_id BIGINT UNSIGNED NULL, -- أصبح اختياري
    amount DECIMAL(10, 2) NOT NULL,
    payment_date DATE NOT NULL,
    payment_method VARCHAR(50),
    receipt_number VARCHAR(100),
    reference_number VARCHAR(100),
    status VARCHAR(20) DEFAULT 'completed',
    notes TEXT,
    created_by BIGINT UNSIGNED,
    created_at TIMESTAMP,
    updated_at TIMESTAMP
);
```

#### التعديل المهم:
```sql
-- جعل installment_id اختياري للسماح بالدفعات العامة
ALTER TABLE payments 
MODIFY COLUMN installment_id BIGINT UNSIGNED NULL;
```

**ملف Migration:**
```php
// database/migrations/2026_03_06_000001_make_installment_id_nullable_in_payments.php

public function up(): void
{
    Schema::table('payments', function (Blueprint $table) {
        // جعل installment_id اختياري
        $table->foreignId('installment_id')->nullable()->change();
    });
}

public function down(): void
{
    Schema::table('payments', function (Blueprint $table) {
        $table->foreignId('installment_id')->nullable(false)->change();
    });
}
```

---

## 🔄 التوزيع التلقائي للمدفوعات

### الفكرة الأساسية:

عندما يدفع العميل مبلغاً معيناً (مثلاً 2000 جنيه)، النظام يقوم **تلقائياً** بتوزيعه على الأقساط بالترتيب الزمني.

### مثال عملي:

```
عقد به 6 أقساط × 700 جنيه = 4,200 جنيه

العميل دفع: 2,000 جنيه

النتيجة التلقائية:
✅ القسط 1 (15/1/2026): 700/700 - مدفوع كلياً
✅ القسط 2 (15/2/2026): 700/700 - مدفوع كلياً  
⚡ القسط 3 (15/3/2026): 600/700 - مدفوع جزئياً (85.7%)
❌ القسط 4 (15/4/2026): 0/700 - غير مدفوع
❌ القسط 5 (15/5/2026): 0/700 - غير مدفوع
❌ القسط 6 (15/6/2026): 0/700 - غير مدفوع
```

---

### التنفيذ: PaymentObserver

#### 1. إنشاء ملف Observer:

**المسار:** `app/Observers/PaymentObserver.php`

```php
<?php

namespace App\Observers;

use App\Models\Payment;
use App\Models\Installment;

class PaymentObserver
{
    /**
     * Handle the Payment "created" event.
     */
    public function created(Payment $payment): void
    {
        // فقط للمدفوعات المكتملة
        if ($payment->status === 'completed' && $payment->contract_id) {
            $this->distributePaymentToInstallments($payment);
        }
    }

    /**
     * Handle the Payment "updated" event.
     */
    public function updated(Payment $payment): void
    {
        // إذا تغيرت الحالة إلى completed
        if ($payment->status === 'completed' && $payment->contract_id && $payment->wasChanged('status')) {
            $this->distributePaymentToInstallments($payment);
        }
    }

    /**
     * توزيع المبلغ المدفوع على الأقساط بالترتيب الزمني
     */
    private function distributePaymentToInstallments(Payment $payment): void
    {
        // الحصول على الأقساط غير المدفوعة بالكامل، مرتبة حسب تاريخ الاستحقاق
        $installments = Installment::where('contract_id', $payment->contract_id)
            ->where('is_paid', false)
            ->orderBy('due_date', 'asc')
            ->get();

        $remainingAmount = (float) $payment->amount;

        foreach ($installments as $installment) {
            if ($remainingAmount <= 0) {
                break;
            }

            // المبلغ المتبقي من قيمة القسط
            $installmentRemaining = (float) $installment->value - (float) $installment->paid_amount;

            if ($installmentRemaining <= 0) {
                continue; // القسط مدفوع بالكامل بالفعل
            }

            // المبلغ الذي سيتم دفعه لهذا القسط
            $amountToPay = min($remainingAmount, $installmentRemaining);

            // تحديث القسط
            $installment->paid_amount = (float) $installment->paid_amount + $amountToPay;
            
            // إذا اكتمل دفع القسط
            if ($installment->paid_amount >= $installment->value) {
                $installment->is_paid = true;
                $installment->payment_date = $payment->payment_date;
            }
            
            $installment->save();

            // خصم المبلغ من المتبقي
            $remainingAmount -= $amountToPay;
        }
    }

    /**
     * Handle the Payment "deleting" event.
     */
    public function deleting(Payment $payment): void
    {
        // إذا تم حذف دفعة، نحتاج لإرجاع الأقساط لحالتها السابقة
        if ($payment->status === 'completed' && $payment->contract_id) {
            $this->reversePaymentDistribution($payment);
        }
    }

    /**
     * عكس توزيع الدفعة (عند الحذف)
     */
    private function reversePaymentDistribution(Payment $payment): void
    {
        // الحصول على جميع الأقساط بالترتيب
        $installments = Installment::where('contract_id', $payment->contract_id)
            ->orderBy('due_date', 'asc')
            ->get();

        $amountToReverse = (float) $payment->amount;

        // نبدأ من أول قسط ونعكس التوزيع
        foreach ($installments as $installment) {
            if ($amountToReverse <= 0) {
                break;
            }

            // إذا كان القسط مدفوع أو له مبلغ مدفوع
            if ($installment->paid_amount > 0) {
                $amountToDeduct = min($installment->paid_amount, $amountToReverse);
                
                $installment->paid_amount -= $amountToDeduct;
                
                // إذا أصبح المبلغ المدفوع صفر
                if ($installment->paid_amount <= 0) {
                    $installment->paid_amount = 0;
                    $installment->is_paid = false;
                    $installment->payment_date = null;
                }
                // إذا أصبح المبلغ المدفوع أقل من القيمة
                elseif ($installment->paid_amount < $installment->value) {
                    $installment->is_paid = false;
                }
                
                $installment->save();
                $amountToReverse -= $amountToDeduct;
            }
        }
    }
}
```

---

#### 2. تسجيل Observer:

**المسار:** `app/Providers/AppServiceProvider.php`

في دالة `boot()`:

```php
use App\Models\Payment;
use App\Observers\PaymentObserver;

public function boot(): void
{
    // تسجيل PaymentObserver
    Payment::observe(PaymentObserver::class);
}
```

**الملف الكامل:**

```php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Models\Payment;
use App\Observers\PaymentObserver;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        //
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        // تسجيل Observer للمدفوعات
        Payment::observe(PaymentObserver::class);
    }
}
```

---

## 💰 نظام الدفع الجزئي

### Model: Installment

**المسار:** `app/Models/Installment.php`

```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Installment extends Model
{
    protected $fillable = [
        'contract_id',
        'due_date',
        'value',
        'paid_amount',      // ← حقل جديد
        'payment_date',     // ← حقل جديد
        'is_paid',
        'notes',            // ← حقل جديد
    ];

    protected $casts = [
        'due_date' => 'date',
        'payment_date' => 'date',
        'is_paid' => 'boolean',
        'value' => 'decimal:2',
        'paid_amount' => 'decimal:2',  // ← cast جديد
    ];

    public function contract(): BelongsTo
    {
        return $this->belongsTo(Contract::class);
    }
    
    /**
     * Get the remaining amount for this installment
     */
    public function getRemainingAmountAttribute(): float
    {
        return (float) $this->value - (float) ($this->paid_amount ?? 0);
    }
    
    /**
     * Get the payment percentage
     */
    public function getPaymentPercentageAttribute(): float
    {
        if ($this->value == 0) {
            return 0;
        }
        return round((($this->paid_amount ?? 0) / $this->value) * 100, 2);
    }
    
    /**
     * Check if installment is partially paid
     */
    public function getIsPartiallyPaidAttribute(): bool
    {
        return ($this->paid_amount > 0) && ($this->paid_amount < $this->value);
    }
}
```

---

### Model: Contract

**المسار:** `app/Models/Contract.php`

#### إضافة Attributes جديدة:

```php
/**
 * حساب إجمالي المحصل من الأقساط
 */
public function getTotalCollectedAttribute()
{
    return $this->installments()->sum('paid_amount');
}

/**
 * حساب المتبقي من التعاقد
 */
public function getRemainingAmountAttribute()
{
    return $this->installment_price - $this->total_collected;
}

/**
 * حساب نسبة السداد
 */
public function getPaymentPercentageAttribute()
{
    if ($this->installment_price == 0) {
        return 0;
    }
    return round(($this->total_collected / $this->installment_price) * 100, 2);
}

/**
 * عدد الأقساط المدفوعة كلياً
 */
public function getPaidInstallmentsCountAttribute()
{
    return $this->installments()->where('is_paid', true)->count();
}

/**
 * عدد الأقساط المدفوعة جزئياً
 */
public function getPartiallyPaidInstallmentsCountAttribute()
{
    return $this->installments()
        ->where('is_paid', false)
        ->where('paid_amount', '>', 0)
        ->count();
}

/**
 * عدد الأقساط غير المدفوعة
 */
public function getUnpaidInstallmentsCountAttribute()
{
    return $this->installments()
        ->where('is_paid', false)
        ->where('paid_amount', 0)
        ->count();
}
```

---

## 📊 تقرير الأقساط المتأخرة

### 1. ملف Controller/Page

**المسار:** `app/Filament/Pages/InstallmentsOverdue.php`

```php
<?php

namespace App\Filament\Pages;

use App\Models\Installment;
use Filament\Facades\Filament;
use Filament\Pages\Page;
use BackedEnum;

class InstallmentsOverdue extends Page
{
    public function getTitle(): string
    {
        return __('filament.nav.installments_overdue');
    }
    
    public static function getNavigationLabel(): string
    {
        return __('filament.nav.installments_overdue');
    }

    protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-banknotes';

    public static function canAccess(): bool
    {
        return Filament::auth()->user()->can('View:InstallmentsOverdue');
    }

    protected string $view = 'filament.pages.installments-overdue';

    public static function getNavigationGroup(): ?string
    {
        return __('filament.nav.reports_group');
    }
    
    public $installments = [];
    public $date_from;
    public $date_to;

    public function mount(): void
    {
        $this->date_from = now()->subMonth()->format('Y-m-d');
        $this->date_to = now()->format('Y-m-d');
        $this->filterInstallments();
    }

    public function updated($property)
    {
        if (in_array($property, ['date_from', 'date_to'])) {
            $this->filterInstallments();
        }
    }

    public function filterInstallments(): void
    {
        $query = Installment::with(['contract.customer'])
            ->where('due_date', '<', now()->format('Y-m-d'))
            ->whereHas('contract', function ($query) {
                $query->where('status', 'active');
            })
            // الأقساط المتأخرة: غير مدفوعة بالكامل أو مدفوعة جزئياً
            ->where(function ($q) {
                $q->where('is_paid', false)
                    ->orWhereRaw('COALESCE(paid_amount, 0) < value');
            })
            ->whereRaw('COALESCE(paid_amount, 0) < value');
            
        $this->installments = $query->orderBy('due_date')->get();
    }
}
```

---

### 2. ملف View (التقرير)

**المسار:** `resources/views/filament/pages/installments-overdue.blade.php`

**الأعمدة الرئيسية:**
- اسم العميل
- رقم الهاتف
- العنوان
- **قيمة القسط**
- **المبلغ المدفوع** ← جديد
- **المتبقي** ← جديد
- تاريخ الاستحقاق
- مدة التأخير
- **الحالة** (متأخر / مدفوع جزئياً)

```php
<div id="overdue-report" dir="rtl">
    <style>
        /* ... styles ... */
    </style>

    <div class="toolbar">
        <button class="btn" onclick="window.print()">🖨️ طباعة</button>
    </div>

    <h2 class="title">تقرير الأقساط المتأخرة عن السداد</h2>

    <form wire:submit.prevent="filterInstallments" class="filters">
        <label>من:</label>
        <input type="date" wire:model.lazy="date_from">
        <label>إلى:</label>
        <input type="date" wire:model.lazy="date_to">
        <button type="submit" class="btn">بحث</button>
    </form>

    <div>
        {{ __('filament.partners_accounts.count') }}: {{ count($installments) }}
    </div>
    
    <div class="card">
        <table>
            <thead>
                <tr>
                    <th>اسم العميل</th>
                    <th>رقم الهاتف</th>
                    <th>العنوان</th>
                    <th>قيمة القسط</th>
                    <th>المبلغ المدفوع</th>
                    <th>المتبقي</th>
                    <th>تاريخ الاستحقاق</th>
                    <th>مدة التأخير</th>
                    <th>الحالة</th>
                </tr>
            </thead>
            <tbody>
                @php 
                    $totalValue = 0;
                    $totalPaid = 0;
                    $totalRemaining = 0;
                @endphp
                
                @forelse($installments as $installment)
                    @php
                        $installmentValue = (float) ($installment->value ?? 0);
                        $paidAmount = (float) ($installment->paid_amount ?? 0);
                        $remainingAmount = $installmentValue - $paidAmount;
                        $totalValue += $installmentValue;
                        $totalPaid += $paidAmount;
                        $totalRemaining += $remainingAmount;
                        
                        $due = \Carbon\Carbon::parse($installment->due_date);
                        $now = \Carbon\Carbon::now();
                        $diff = $due->diff($now);
                        $months = $diff->m + ($diff->y * 12);
                        $days = $diff->d;
                        
                        // تحديد حالة القسط
                        $isPartiallyPaid = $paidAmount > 0 && $paidAmount < $installmentValue;
                        $percentage = $installmentValue > 0 ? round(($paidAmount / $installmentValue) * 100) : 0;
                    @endphp
                    <tr>
                        <td>{{ $installment->contract->customer->name ?? '-' }}</td>
                        <td>{{ $installment->contract->customer->phone ?? '-' }}</td>
                        <td>{!! $installment->contract->customer->address ?? '-' !!}</td>
                        <td class="amount">{{ number_format($installmentValue, 2) }}</td>
                        <td style="color: #059669; font-weight: 600;">{{ number_format($paidAmount, 2) }}</td>
                        <td style="color: #dc2626; font-weight: 600;">{{ number_format($remainingAmount, 2) }}</td>
                        <td>{{ $installment->due_date }}</td>
                        <td>{{ $months }} شهر و {{ $days }} يوم</td>
                        <td>
                            @if($isPartiallyPaid)
                                <span class="badge" style="background: #fed7aa; color: #c2410c;">⚡ مدفوع جزئياً ({{ $percentage }}%)</span>
                            @else
                                <span class="badge due">❌ متأخر عن السداد</span>
                            @endif
                        </td>
                    </tr>
                @empty
                    <tr>
                        <td colspan="9" class="empty">لا توجد أقساط متأخرة عن السداد ✅</td>
                    </tr>
                @endforelse
                
                <tr class="totals">
                    <td colspan="3" style="font-weight:bold">{{ __('filament.partners_accounts.totals') }}</td>
                    <td class="amount" style="font-weight:bold">{{ number_format($totalValue, 2) }}</td>
                    <td style="color: #059669; font-weight:bold">{{ number_format($totalPaid, 2) }}</td>
                    <td style="color: #dc2626; font-weight:bold">{{ number_format($totalRemaining, 2) }}</td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
            </tbody>
        </table>
    </div>
</div>
```

---

## 🚀 خطوات التطبيق على نظام جديد

### المرحلة 1: قاعدة البيانات

#### الخطوة 1: إضافة حقول الدفع الجزئي للأقساط

```bash
php artisan make:migration add_payment_fields_to_installments_table
```

```php
public function up(): void
{
    Schema::table('installments', function (Blueprint $table) {
        $table->decimal('paid_amount', 10, 2)->nullable()->default(0)->after('value');
        $table->date('payment_date')->nullable()->after('paid_amount');
        $table->text('notes')->nullable()->after('is_paid');
    });
}
```

```bash
php artisan migrate
```

---

#### الخطوة 2: جعل installment_id اختياري في المدفوعات

```bash
php artisan make:migration make_installment_id_nullable_in_payments
```

```php
public function up(): void
{
    Schema::table('payments', function (Blueprint $table) {
        $table->foreignId('installment_id')->nullable()->change();
    });
}
```

```bash
php artisan migrate
```

---

### المرحلة 2: إنشاء PaymentObserver

#### الخطوة 1: إنشاء الملف

```bash
# إنشاء مجلد Observers إن لم يكن موجوداً
mkdir -p app/Observers

# نسخ محتوى PaymentObserver من الأعلى
```

#### الخطوة 2: تسجيل Observer

في `app/Providers/AppServiceProvider.php`:

```php
use App\Models\Payment;
use App\Observers\PaymentObserver;

public function boot(): void
{
    Payment::observe(PaymentObserver::class);
}
```

---

### المرحلة 3: تحديث Models

#### 1. تحديث Installment Model

إضافة الحقول لـ `$fillable` و `$casts` كما موضح أعلاه.

#### 2. تحديث Contract Model

إضافة جميع الـ Attributes الجديدة:
- `getTotalCollectedAttribute()`
- `getPaidInstallmentsCountAttribute()`
- `getPartiallyPaidInstallmentsCountAttribute()`
- `getUnpaidInstallmentsCountAttribute()`

---

### المرحلة 4: تحديث Filament Resources

#### في PaymentResource:

جعل حقل `installment_id` اختياري:

```php
Select::make('installment_id')
    ->label(__('payments.installment_due_date'))
    ->helperText('اختياري: يمكن ترك هذا الحقل فارغاً وسيتم توزيع المبلغ تلقائياً على الأقساط')
    ->nullable()  // إضافة هذا
    // حذف required()
```

---

### المرحلة 5: إضافة تقرير الأقساط المتأخرة

#### 1. إنشاء Page

```bash
php artisan make:filament-page InstallmentsOverdue
```

نسخ محتوى `InstallmentsOverdue.php` من الأعلى.

#### 2. إنشاء View

إنشاء ملف: `resources/views/filament/pages/installments-overdue.blade.php`

نسخ المحتوى من الأعلى.

---

### المرحلة 6: مسح Cache وإعادة التحميل

```bash
php artisan optimize:clear
php artisan config:clear
php artisan cache:clear
php artisan view:clear
php artisan route:clear
```

---

## ✅ قائمة التحقق (Checklist)

قبل التطبيق على النظام الجديد، تأكد من:

- [ ] **Migration 1:** إضافة `paid_amount`, `payment_date`, `notes` لجدول `installments`
- [ ] **Migration 2:** جعل `installment_id` في جدول `payments` nullable
- [ ] **Observer:** إنشاء `PaymentObserver.php` في `app/Observers/`
- [ ] **Observer Registration:** تسجيل Observer في `AppServiceProvider`
- [ ] **Model Installment:** تحديث `$fillable` و `$casts`
- [ ] **Model Contract:** إضافة جميع Attributes الجديدة
- [ ] **PaymentResource:** جعل `installment_id` اختياري
- [ ] **InstallmentsOverdue Page:** إنشاء صفحة التقرير
- [ ] **InstallmentsOverdue View:** إنشاء ملف Blade
- [ ] **Cache Clear:** مسح جميع أنواع الـ Cache
- [ ] **Testing:** اختبار إضافة دفعة وتوزيعها تلقائياً

---

## 🧪 اختبار النظام

### 1. اختبار التوزيع التلقائي:

```php
// إنشاء دفعة جديدة دون تحديد قسط
$payment = Payment::create([
    'customer_id' => 1,
    'contract_id' => 1,
    'amount' => 2000,
    'payment_date' => now(),
    'status' => 'completed',
    'payment_method' => 'cash',
]);

// التحقق من توزيع المبلغ على الأقساط
$contract = Contract::find(1);
$installments = $contract->installments()->orderBy('due_date')->get();

foreach ($installments as $installment) {
    echo "القسط {$installment->id}: {$installment->paid_amount}/{$installment->value}\n";
}
```

### 2. اختبار حذف دفعة:

```php
// حذف الدفعة
$payment->delete();

// التحقق من عكس التوزيع
$installments = $contract->fresh()->installments()->orderBy('due_date')->get();

foreach ($installments as $installment) {
    echo "القسط {$installment->id}: {$installment->paid_amount}/{$installment->value}\n";
}
```

---

## 📝 ملاحظات مهمة

### 1. ترتيب التنفيذ:
- يجب تطبيق Migrations قبل تحديث الكود
- يجب تسجيل Observer قبل اختبار المدفوعات
- يجب مسح Cache بعد أي تغيير

### 2. البيانات الموجودة:
- الأقساط القديمة: يمكن ترك `paid_amount` = NULL أو 0
- المدفوعات القديمة: ستعمل عادي لأن `installment_id` أصبح اختياري

### 3. الأداء:
- Observer يعمل تلقائياً عند إضافة/تحديث مدفوعات فقط
- الاستعلامات محسنة باستخدام `orderBy` و `where`

### 4. سيناريوهات خاصة:
- إذا كانت جميع الأقساط مدفوعة: المبلغ الجديد لن يوزع
- إذا كان المبلغ أكبر من المتبقي: سيوزع حتى يكتمل كل شيء والباقي يبقى
- حذف دفعة: سيعكس التوزيع تلقائياً

---

## 📚 مراجع إضافية

### الملفات المرجعية:
1. `PAYMENT_AUTO_DISTRIBUTION.md` - شرح تفصيلي للتوزيع التلقائي
2. `README_PAYMENT_DISTRIBUTION.md` - ملخص تنفيذ التوزيع
3. `دليل_الاستخدام_السريع.md` - دليل المستخدم

### الملفات التقنية:
- `app/Observers/PaymentObserver.php`
- `app/Models/Contract.php`
- `app/Models/Installment.php`
- `app/Filament/Pages/InstallmentsOverdue.php`

---

## 🎯 النتيجة النهائية

بعد تطبيق جميع التحديثات:

✅ **المدفوعات:** توزع تلقائياً على الأقساط بالترتيب  
✅ **الأقساط:** تدعم الدفع الجزئي (`paid_amount`)  
✅ **التقارير:** تعرض حالة دقيقة (مدفوع/جزئي/متأخر)  
✅ **الإحصائيات:** عدادات دقيقة لحالة العقود  
✅ **المرونة:** إمكانية دفع أي مبلغ دون تحديد قسط  

---

## 📞 الدعم والمساعدة

عند التطبيق على نظام جديد:

1. ابدأ بالـ Migrations
2. أنشئ Observer
3. حدّث Models
4. اختبر على بيانات تجريبية
5. راجع Logs عند أي خطأ

**ملف Log:**
```bash
tail -f storage/logs/laravel.log
```

---

## ⚡ نصائح سريعة

### للتطبيق السريع:
```bash
# 1. Migrations
php artisan migrate

# 2. Clear Cache
php artisan optimize:clear

# 3. Test
php test_payment_distribution.php
```

### للتحقق:
```sql
-- تحقق من الحقول الجديدة
DESCRIBE installments;

-- تحقق من nullable
SHOW COLUMNS FROM payments LIKE 'installment_id';
```

---

**✨ انتهى الدليل الشامل ✨**

**تاريخ الإعداد:** 13 مارس 2026  
**الإصدار:** 1.0  
**الحالة:** مُختبر ومُطبق بنجاح
