# ⚡ خطوات سريعة لتطبيق نظام الأقساط المحدث

> دليل مختصر للتطبيق السريع على نظام جديد

---

## 📋 الملخص

تحديثات نظام الأقساط تتضمن:
1. ✅ توزيع المدفوعات تلقائياً على الأقساط
2. ✅ دعم الدفع الجزئي
3. ✅ تقرير الأقساط المتأخرة المحسن

---

## 🚀 التطبيق في 6 خطوات

### 1️⃣ قاعدة البيانات

```bash
# Migration 1: إضافة حقول الدفع الجزئي
php artisan make:migration add_payment_fields_to_installments_table
```

```php
// في ملف Migration
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
# Migration 2: جعل installment_id اختياري
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
# تطبيق Migrations
php artisan migrate
```

---

### 2️⃣ إنشاء PaymentObserver

**ملف:** `app/Observers/PaymentObserver.php`

```php
<?php

namespace App\Observers;

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

class PaymentObserver
{
    public function created(Payment $payment): void
    {
        if ($payment->status === 'completed' && $payment->contract_id) {
            $this->distributePaymentToInstallments($payment);
        }
    }

    public function updated(Payment $payment): void
    {
        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;
        }
    }

    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;
            }
        }
    }
}
```

---

### 3️⃣ تسجيل Observer

**ملف:** `app/Providers/AppServiceProvider.php`

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

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

---

### 4️⃣ تحديث Models

#### Installment Model

```php
// app/Models/Installment.php

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',  // ← إضافة
];
```

#### Contract Model

```php
// app/Models/Contract.php

public function getTotalCollectedAttribute()
{
    return $this->installments()->sum('paid_amount');
}

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();
}
```

---

### 5️⃣ تحديث PaymentResource

```php
// جعل installment_id اختياري

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

---

### 6️⃣ إنشاء تقرير الأقساط المتأخرة

#### Page File

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

**ملف:** `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
{
    protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-banknotes';
    protected string $view = 'filament.pages.installments-overdue';

    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 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();
    }
}
```

#### View File

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

انسخ محتوى View من الملف الشامل: `INSTALLMENT_SYSTEM_COMPLETE_GUIDE.md`

---

## 🧹 مسح 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` nullable في payments
- [ ] Observer: إنشاء `PaymentObserver.php`
- [ ] Observer: تسجيله في `AppServiceProvider`
- [ ] Model: تحديث Installment fillable & casts
- [ ] Model: إضافة Attributes في Contract
- [ ] Resource: جعل installment_id اختياري
- [ ] Page: إنشاء InstallmentsOverdue
- [ ] View: إنشاء installments-overdue.blade.php
- [ ] Cache: مسح جميع أنواع Cache
- [ ] Test: اختبار دفعة جديدة

---

## 🧪 اختبار سريع

```php
// اختبار 1: إضافة دفعة
$payment = Payment::create([
    'customer_id' => 1,
    'contract_id' => 1,
    'amount' => 2000,
    'payment_date' => now(),
    'status' => 'completed',
]);

// اختبار 2: التحقق من التوزيع
$contract = Contract::find(1);
foreach ($contract->installments as $inst) {
    echo "{$inst->paid_amount}/{$inst->value}\n";
}

// اختبار 3: حذف الدفعة
$payment->delete();

// اختبار 4: التحقق من العكس
$contract->fresh();
foreach ($contract->installments as $inst) {
    echo "{$inst->paid_amount}/{$inst->value}\n";
}
```

---

## 📝 ملاحظات

### مهم:
- Observer يعمل فقط مع المدفوعات الجديدة
- البيانات القديمة لن تتأثر
- `paid_amount` = 0 للأقساط القديمة

### نصائح:
- ابدأ باختبار على نسخة تجريبية
- راجع Logs عند أي خطأ: `storage/logs/laravel.log`
- اختبر حذف دفعة للتأكد من عكس التوزيع

---

## 📚 للمزيد

راجع الدليل الشامل: `INSTALLMENT_SYSTEM_COMPLETE_GUIDE.md`

---

**⏱️ الوقت المتوقع:** 20-30 دقيقة  
**🎯 النتيجة:** نظام أقساط كامل مع توزيع تلقائي ودفع جزئي
