# 💾 دليل التنفيذ العملي - نظام التقسيط على أي Platform

**شرح عملي مع أكواد لمختلف الأنظمة والتقنيات**

---

## 🖥️ التنفيذ على مختلف الأنظمة

### 1️⃣ Laravel (النظام الحالي)

#### الملفات المطلوبة:

```
app/
├── Models/
│   ├── Contract.php
│   ├── Installment.php
│   ├── Payment.php
│   └── ContractCashout.php
├── Services/
│   └── ContractCashoutService.php
└── Filament/
    └── Resources/
        └── Contracts/
            ├── Pages/
            │   ├── ViewContract.php
            │   └── ListContracts.php
            └── Tables/
                └── ContractsTable.php

database/
└── migrations/
    ├── YYYY_MM_DD_create_contracts_table.php
    ├── YYYY_MM_DD_create_installments_table.php
    ├── YYYY_MM_DD_create_payments_table.php
    └── YYYY_MM_DD_create_contract_cashouts_table.php
```

#### المتطلبات:
- Laravel 11+
- Filament 3+
- PHP 8.2+
- MySQL 8+

---

### 2️⃣ على Net. C# (مثلاً Odoo-like ERP)

#### البنية:

```csharp
namespace ERP.InstallmentSystem;

// Model: Contract.cs
public class Contract
{
    [Key]
    public int Id { get; set; }
    
    [Required]
    public int CustomerId { get; set; }
    
    [Column(TypeName = "decimal(15,2)")]
    public decimal OriginalPrice { get; set; }
    
    [Column(TypeName = "decimal(15,2)")]
    public decimal InstallmentPrice { get; set; }
    
    public int InstallmentCount { get; set; }
    
    [Column(TypeName = "decimal(5,2)")]
    public decimal ProfitPercentage { get; set; }
    
    public ContractStatus Status { get; set; } // enum
    
    public DateTime ContractDate { get; set; }
    public DateTime? FirstInstallmentDate { get; set; }
    
    // العلاقات
    public virtual List<Installment> Installments { get; set; }
    public virtual List<Payment> Payments { get; set; }
    public virtual List<ContractCashout> Cashouts { get; set; }
}

// Enum for Status
public enum ContractStatus
{
    Active = 1,
    Finished = 2,
    Suspended = 3,
    Cashed = 4
}

// Service: CashoutService.cs
public class CashoutService
{
    private readonly IRepository<Contract> _contractRepo;
    private readonly IRepository<Installment> _installmentRepo;
    private readonly IRepository<Payment> _paymentRepo;
    
    public CashoutCalculation Calculate(int contractId, DateTime? asOfDate = null, bool includeOverdue = false)
    {
        var contract = _contractRepo.GetById(contractId);
        asOfDate ??= DateTime.Now.Date;
        
        decimal profitTotal = Math.Max(contract.InstallmentPrice - contract.OriginalPrice, 0);
        decimal profitPerInstallment = Math.Round(
            profitTotal / Math.Max(contract.InstallmentCount, 1), 2
        );
        
        var unpaidInstallments = contract.Installments
            .Where(i => !i.IsPaid)
            .OrderBy(i => i.DueDate)
            .ToList();
        
        decimal overdueAmount = 0;
        decimal futureAmount = 0;
        decimal discountAmount = 0;
        int futureCount = 0;
        int overdueCount = 0;
        
        foreach (var installment in unpaidInstallments)
        {
            decimal remainingAmount = Math.Max(
                installment.Value - (installment.PaidAmount ?? 0), 0
            );
            
            if (remainingAmount <= 0) continue;
            
            bool isFutureOrToday = installment.DueDate?.Date >= asOfDate;
            
            if (isFutureOrToday)
            {
                decimal discount = Math.Min(profitPerInstallment, remainingAmount);
                futureAmount += remainingAmount - discount;
                discountAmount += discount;
                futureCount++;
            }
            else
            {
                if (includeOverdue)
                    overdueAmount += remainingAmount;
                overdueCount++;
            }
        }
        
        return new CashoutCalculation
        {
            CashoutTotal = Math.Round(overdueAmount + futureAmount, 2),
            DiscountTotal = Math.Round(discountAmount, 2),
            FutureAmount = Math.Round(futureAmount, 2),
            OverdueAmount = Math.Round(overdueAmount, 2),
            FutureCount = futureCount,
            OverdueCount = overdueCount,
            ProfitPerInstallment = profitPerInstallment
        };
    }
    
    public async Task<Payment> ExecuteAsync(
        int contractId, 
        int vaultId, 
        string paymentMethod,
        string notes = null,
        DateTime? paymentDate = null,
        bool includeOverdue = false
    )
    {
        using var transaction = _dbContext.Database.BeginTransaction();
        
        try
        {
            var contract = await _contractRepo.GetByIdAsync(contractId);
            var calculation = Calculate(contractId, paymentDate, includeOverdue);
            
            if (calculation.CashoutTotal <= 0)
                throw new InvalidOperationException("لا يوجد مبلغ متبقي للتكييش");
            
            // تحديث قيم الأقساط المستقبلية
            var futureInstallments = contract.Installments
                .Where(i => !i.IsPaid && i.DueDate >= paymentDate?.Date)
                .ToList();
            
            foreach (var installment in futureInstallments)
            {
                decimal discount = Math.Min(
                    calculation.ProfitPerInstallment, 
                    installment.Value - (installment.PaidAmount ?? 0)
                );
                installment.Value = Math.Max(installment.Value - discount, 0);
            }
            
            // إنشاء سجل الدفع
            var payment = new Payment
            {
                CustomerId = contract.CustomerId,
                ContractId = contractId,
                VaultId = vaultId,
                Amount = calculation.CashoutTotal,
                PaymentDate = paymentDate ?? DateTime.Now,
                PaymentMethod = paymentMethod,
                Status = PaymentStatus.Completed,
                Notes = $"{(includeOverdue ? "تسوية متأخرات + " : "")}تكييش العقد - خصم ربح {calculation.DiscountTotal:C}"
            };
            
            // إنشاء سجل التكييش
            var cashout = new ContractCashout
            {
                ContractId = contractId,
                PaymentId = payment.Id,
                ExecutedById = User.Id,
                CashoutType = includeOverdue ? CashoutType.OverdueSettlement : CashoutType.Simple,
                TotalAmount = calculation.CashoutTotal,
                DiscountProfit = calculation.DiscountTotal,
                OverdueAmount = calculation.OverdueAmount,
                FutureAmount = calculation.FutureAmount,
                OverdueCount = calculation.OverdueCount,
                FutureCount = calculation.FutureCount,
                CashoutDate = paymentDate?.Date ?? DateTime.Now.Date,
                Snapshot = JsonConvert.SerializeObject(new
                {
                    calculation.ProfitPerInstallment,
                    contract.OriginalPrice,
                    contract.InstallmentPrice,
                    contract.InstallmentCount,
                    calculation.CashoutTotal
                })
            };
            
            contract.Status = ContractStatus.Cashed;
            
            await _paymentRepo.AddAsync(payment);
            await _cashoutRepo.AddAsync(cashout);
            await _contractRepo.UpdateAsync(contract);
            
            await transaction.CommitAsync();
            return payment;
        }
        catch
        {
            await transaction.RollbackAsync();
            throw;
        }
    }
}

public class CashoutCalculation
{
    public decimal CashoutTotal { get; set; }
    public decimal DiscountTotal { get; set; }
    public decimal FutureAmount { get; set; }
    public decimal OverdueAmount { get; set; }
    public int FutureCount { get; set; }
    public int OverdueCount { get; set; }
    public decimal ProfitPerInstallment { get; set; }
}
```

---

### 3️⃣ Python (Django/FastAPI)

#### Django Model:

```python
from django.db import models
from decimal import Decimal
from datetime import date, datetime
from django.utils import timezone
from django.contrib.auth.models import User

class Contract(models.Model):
    STATUS_CHOICES = [
        ('active', 'نشط'),
        ('finished', 'منتهي'),
        ('suspended', 'معلق'),
        ('cashed', 'مكيش'),
    ]
    
    customer = models.ForeignKey('customers.Customer', on_delete=models.PROTECT)
    guarantor = models.ForeignKey('guarantors.Guarantor', on_delete=models.SET_NULL, null=True)
    vault = models.ForeignKey('vaults.Vault', on_delete=models.SET_NULL, null=True)
    
    original_price = models.DecimalField(max_digits=15, decimal_places=2)
    installment_price = models.DecimalField(max_digits=15, decimal_places=2)
    installment_count = models.IntegerField()
    profit_percentage = models.DecimalField(max_digits=5, decimal_places=2, null=True)
    
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active')
    contract_date = models.DateField()
    first_installment_date = models.DateField(null=True)
    
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'contracts'
        indexes = [
            models.Index(fields=['status']),
            models.Index(fields=['customer']),
            models.Index(fields=['contract_date']),
        ]

class Installment(models.Model):
    contract = models.ForeignKey(Contract, on_delete=models.CASCADE, related_name='installments')
    due_date = models.DateField()
    value = models.DecimalField(max_digits=15, decimal_places=2)
    paid_amount = models.DecimalField(max_digits=15, decimal_places=2, default=0)
    payment_date = models.DateField(null=True)
    is_paid = models.BooleanField(default=False)
    
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'installments'
        indexes = [
            models.Index(fields=['contract']),
            models.Index(fields=['due_date']),
            models.Index(fields=['is_paid']),
        ]
        unique_together = ('contract', 'due_date')

# Service

from django.db import transaction
from decimal import Decimal

class CashoutService:
    
    @staticmethod
    def calculate(contract_id, as_of_date=None, include_overdue=False):
        """حساب التكييش بدون تنفيذ"""
        contract = Contract.objects.get(id=contract_id)
        as_of_date = as_of_date or timezone.now().date()
        
        profit_total = max(contract.installment_price - contract.original_price, 0)
        profit_per_installment = round(profit_total / max(contract.installment_count, 1), 2)
        
        unpaid_installments = contract.installments.filter(
            is_paid=False
        ).order_by('due_date', 'id')
        
        overdue_amount = Decimal('0')
        future_amount = Decimal('0')
        discount_amount = Decimal('0')
        future_count = 0
        overdue_count = 0
        
        for installment in unpaid_installments:
            remaining_amount = max(installment.value - (installment.paid_amount or 0), 0)
            
            if remaining_amount <= 0:
                continue
            
            is_future = installment.due_date >= as_of_date
            
            if is_future:
                discount = min(Decimal(str(profit_per_installment)), remaining_amount)
                future_amount += remaining_amount - discount
                discount_amount += discount
                future_count += 1
            else:
                if include_overdue:
                    overdue_amount += remaining_amount
                overdue_count += 1
        
        return {
            'cashout_total': round(overdue_amount + future_amount, 2),
            'discount_total': round(discount_amount, 2),
            'future_amount': round(future_amount, 2),
            'overdue_amount': round(overdue_amount, 2),
            'future_count': future_count,
            'overdue_count': overdue_count,
            'profit_per_installment': profit_per_installment,
        }
    
    @staticmethod
    @transaction.atomic
    def execute(contract_id, vault_id, payment_method, notes=None, 
                payment_date=None, include_overdue=False):
        """تنفيذ التكييش فعلياً"""
        
        contract = Contract.objects.select_for_update().get(id=contract_id)
        calculation = CashoutService.calculate(contract_id, payment_date, include_overdue)
        
        if calculation['cashout_total'] <= 0:
            raise ValueError('لا يوجد مبلغ متبقي للتكييش')
        
        # تحديث قيم الأقساط المستقبلية
        payment_date = payment_date or timezone.now().date()
        future_installments = contract.installments.filter(
            is_paid=False,
            due_date__gte=payment_date
        ).select_for_update()
        
        for installment in future_installments:
            remaining = installment.value - (installment.paid_amount or 0)
            discount = min(Decimal(str(calculation['profit_per_installment'])), remaining)
            installment.value = max(installment.value - discount, 0)
            installment.save()
        
        # إنشاء سجل الدفع
        payment = Payment.objects.create(
            customer=contract.customer,
            contract=contract,
            vault_id=vault_id,
            amount=calculation['cashout_total'],
            payment_date=payment_date,
            payment_method=payment_method,
            status='completed',
            notes=f"{'تسوية متأخرات + ' if include_overdue else ''}تكييش العقد - خصم ربح {calculation['discount_total']} جنيه"
        )
        
        # إنشاء سجل التكييش
        import json
        cashout = ContractCashout.objects.create(
            contract=contract,
            payment=payment,
            executed_by_id=1,  # من السياق
            cashout_type='overdue_settlement' if include_overdue else 'simple',
            total_amount=calculation['cashout_total'],
            discount_profit=calculation['discount_total'],
            overdue_amount=calculation['overdue_amount'],
            future_amount=calculation['future_amount'],
            overdue_count=calculation['overdue_count'],
            future_count=calculation['future_count'],
            cashout_date=payment_date,
            snapshot=json.dumps(calculation)
        )
        
        # تحديث حالة العقد
        if not contract.installments.filter(is_paid=False).exists():
            contract.status = 'cashed'
            contract.save()
        
        return payment
```

---

### 4️⃣ JavaScript/Node.js (Express.js)

```javascript
// models/Contract.js
const mongoose = require('mongoose');

const ContractSchema = new mongoose.Schema({
    customer_id: { type: mongoose.Schema.Types.ObjectId, ref: 'Customer' },
    original_price: { type: Number, required: true },
    installment_price: { type: Number, required: true },
    installment_count: { type: Number, required: true },
    profit_percentage: Number,
    status: {
        type: String,
        enum: ['active', 'finished', 'suspended', 'cashed'],
        default: 'active'
    },
    contract_date: Date,
    created_at: { type: Date, default: Date.now },
    updated_at: { type: Date, default: Date.now }
});

// models/Installment.js
const InstallmentSchema = new mongoose.Schema({
    contract_id: { type: mongoose.Schema.Types.ObjectId, ref: 'Contract' },
    due_date: Date,
    value: Number,
    paid_amount: { type: Number, default: 0 },
    payment_date: Date,
    is_paid: { type: Boolean, default: false }
});

// services/CashoutService.js
class CashoutService {
    
    static async calculate(contractId, asOfDate = null, includeOverdue = false) {
        const contract = await Contract.findById(contractId);
        asOfDate = asOfDate ? new Date(asOfDate) : new Date();
        
        const profitTotal = Math.max(
            contract.installment_price - contract.original_price, 0
        );
        
        const profitPerInstallment = Math.round(
            profitTotal / Math.max(contract.installment_count, 1) * 100
        ) / 100;
        
        const unpaidInstallments = await Installment.find({
            contract_id: contractId,
            is_paid: false
        }).sort({ due_date: 1 });
        
        let overdueAmount = 0;
        let futureAmount = 0;
        let discountAmount = 0;
        let futureCount = 0;
        let overdueCount = 0;
        
        for (const installment of unpaidInstallments) {
            const remainingAmount = Math.max(
                installment.value - (installment.paid_amount || 0), 0
            );
            
            if (remainingAmount <= 0) continue;
            
            const isFuture = installment.due_date >= asOfDate;
            
            if (isFuture) {
                const discount = Math.min(profitPerInstallment, remainingAmount);
                futureAmount += remainingAmount - discount;
                discountAmount += discount;
                futureCount++;
            } else {
                if (includeOverdue) {
                    overdueAmount += remainingAmount;
                }
                overdueCount++;
            }
        }
        
        return {
            cashout_total: Math.round((overdueAmount + futureAmount) * 100) / 100,
            discount_total: Math.round(discountAmount * 100) / 100,
            future_amount: Math.round(futureAmount * 100) / 100,
            overdue_amount: Math.round(overdueAmount * 100) / 100,
            future_count: futureCount,
            overdue_count: overdueCount,
            profit_per_installment: profitPerInstallment
        };
    }
    
    static async execute(contractId, vaultId, paymentMethod, options = {}) {
        const session = await mongoose.startSession();
        session.startTransaction();
        
        try {
            const contract = await Contract.findById(contractId).session(session);
            const calculation = await this.calculate(
                contractId,
                options.paymentDate,
                options.includeOverdue
            );
            
            if (calculation.cashout_total <= 0) {
                throw new Error('لا يوجد مبلغ متبقي للتكييش');
            }
            
            // تحديث الأقساط
            const futureInstallments = await Installment.find({
                contract_id: contractId,
                is_paid: false,
                due_date: { $gte: options.paymentDate || new Date() }
            }).session(session);
            
            for (const installment of futureInstallments) {
                const remaining = installment.value - (installment.paid_amount || 0);
                const discount = Math.min(calculation.profit_per_installment, remaining);
                installment.value = Math.max(installment.value - discount, 0);
                await installment.save({ session });
            }
            
            // إنشاء سجل الدفع
            const payment = await Payment.create([{
                customer_id: contract.customer_id,
                contract_id: contractId,
                vault_id: vaultId,
                amount: calculation.cashout_total,
                payment_date: options.paymentDate || new Date(),
                payment_method: paymentMethod,
                status: 'completed'
            }], { session });
            
            // إنشاء سجل التكييش
            await ContractCashout.create([{
                contract_id: contractId,
                payment_id: payment[0]._id,
                cashout_type: options.includeOverdue ? 'overdue_settlement' : 'simple',
                total_amount: calculation.cashout_total,
                discount_profit: calculation.discount_total,
                overdue_amount: calculation.overdue_amount,
                future_amount: calculation.future_amount,
                overdue_count: calculation.overdue_count,
                future_count: calculation.future_count,
                cashout_date: options.paymentDate || new Date(),
                snapshot: calculation
            }], { session });
            
            // تحديث حالة العقد
            const hasUnpaid = await Installment.countDocuments({
                contract_id: contractId,
                is_paid: false
            });
            
            if (hasUnpaid === 0) {
                contract.status = 'cashed';
                await contract.save({ session });
            }
            
            await session.commitTransaction();
            return payment[0];
        } catch (error) {
            await session.abortTransaction();
            throw error;
        }
    }
}

module.exports = CashoutService;
```

---

### 5️⃣ PHP (Pure/Generic)

```php
<?php

namespace App\Services;

class ContractCashoutService {
    
    private $db;
    
    public function __construct($database) {
        $this->db = $database;
    }
    
    public function calculate($contractId, $asOfDate = null, $includeOverdue = false) {
        
        $asOfDate = $asOfDate ? strtotime($asOfDate) : time();
        $asOfDate = date('Y-m-d', $asOfDate);
        
        // الحصول على بيانات العقد
        $contract = $this->db->query(
            "SELECT * FROM contracts WHERE id = ?",
            [$contractId]
        )[0];
        
        $profitTotal = max($contract['installment_price'] - $contract['original_price'], 0);
        $profitPerInstallment = round(
            $profitTotal / max($contract['installment_count'], 1),
            2
        );
        
        // الحصول على الأقساط غير المدفوعة
        $unpaidInstallments = $this->db->query(
            "SELECT * FROM installments WHERE contract_id = ? AND is_paid = 0 ORDER BY due_date",
            [$contractId]
        );
        
        $overdueAmount = 0;
        $futureAmount = 0;
        $discountAmount = 0;
        $futureCount = 0;
        $overdueCount = 0;
        
        foreach ($unpaidInstallments as $installment) {
            $remainingAmount = max(
                $installment['value'] - ($installment['paid_amount'] ?? 0),
                0
            );
            
            if ($remainingAmount <= 0) continue;
            
            $isFuture = strtotime($installment['due_date']) >= strtotime($asOfDate);
            
            if ($isFuture) {
                $discount = min($profitPerInstallment, $remainingAmount);
                $futureAmount += $remainingAmount - $discount;
                $discountAmount += $discount;
                $futureCount++;
            } else {
                if ($includeOverdue) {
                    $overdueAmount += $remainingAmount;
                }
                $overdueCount++;
            }
        }
        
        return [
            'cashout_total' => round($overdueAmount + $futureAmount, 2),
            'discount_total' => round($discountAmount, 2),
            'future_amount' => round($futureAmount, 2),
            'overdue_amount' => round($overdueAmount, 2),
            'future_count' => $futureCount,
            'overdue_count' => $overdueCount,
            'profit_per_installment' => $profitPerInstallment
        ];
    }
    
    public function execute($contractId, $vaultId, $paymentMethod, $options = []) {
        
        try {
            $this->db->beginTransaction();
            
            // قفل العقد
            $contract = $this->db->query(
                "SELECT * FROM contracts WHERE id = ? FOR UPDATE",
                [$contractId]
            )[0];
            
            $calculation = $this->calculate(
                $contractId,
                $options['paymentDate'] ?? null,
                $options['includeOverdue'] ?? false
            );
            
            if ($calculation['cashout_total'] <= 0) {
                throw new \Exception('لا يوجد مبلغ متبقي للتكييش');
            }
            
            // تحديث الأقساط المستقبلية
            $paymentDate = $options['paymentDate'] ?? date('Y-m-d');
            
            $futureInstallments = $this->db->query(
                "SELECT * FROM installments 
                 WHERE contract_id = ? AND is_paid = 0 AND due_date >= ?
                 FOR UPDATE",
                [$contractId, $paymentDate]
            );
            
            foreach ($futureInstallments as $installment) {
                $remaining = $installment['value'] - ($installment['paid_amount'] ?? 0);
                $discount = min($calculation['profit_per_installment'], $remaining);
                $newValue = max($installment['value'] - $discount, 0);
                
                $this->db->execute(
                    "UPDATE installments SET value = ? WHERE id = ?",
                    [$newValue, $installment['id']]
                );
            }
            
            // إنشاء سجل الدفع
            $paymentId = $this->db->execute(
                "INSERT INTO payments 
                 (customer_id, contract_id, vault_id, amount, payment_date, payment_method, status, notes, created_at)
                 VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW())",
                [
                    $contract['customer_id'],
                    $contractId,
                    $vaultId,
                    $calculation['cashout_total'],
                    $paymentDate,
                    $paymentMethod,
                    'completed',
                    "تكييش العقد - خصم ربح {$calculation['discount_total']} جنيه"
                ]
            );
            
            // إنشاء سجل التكييش
            $this->db->execute(
                "INSERT INTO contract_cashouts 
                 (contract_id, payment_id, cashout_type, total_amount, discount_profit, 
                  overdue_amount, future_amount, overdue_count, future_count, cashout_date, 
                  snapshot, created_at)
                 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())",
                [
                    $contractId,
                    $paymentId,
                    $options['includeOverdue'] ? 'overdue_settlement' : 'simple',
                    $calculation['cashout_total'],
                    $calculation['discount_total'],
                    $calculation['overdue_amount'],
                    $calculation['future_amount'],
                    $calculation['overdue_count'],
                    $calculation['future_count'],
                    $paymentDate,
                    json_encode($calculation)
                ]
            );
            
            // تحديث حالة العقد
            $unpaidCount = $this->db->count(
                "SELECT COUNT(*) FROM installments WHERE contract_id = ? AND is_paid = 0",
                [$contractId]
            );
            
            if ($unpaidCount === 0) {
                $this->db->execute(
                    "UPDATE contracts SET status = ?, updated_at = NOW() WHERE id = ?",
                    ['cashed', $contractId]
                );
            }
            
            $this->db->commit();
            return ['success' => true, 'payment_id' => $paymentId];
            
        } catch (\Exception $e) {
            $this->db->rollback();
            throw $e;
        }
    }
}
```

---

### 6️⃣ SQL Server (مثلاً إذا كنت على نظام Windows ERP)

```sql
-- Stored Procedure للحساب
CREATE PROCEDURE sp_CalculateCashout
    @ContractId INT,
    @AsOfDate DATE = NULL,
    @IncludeOverdue BIT = 0
AS
BEGIN
    SET NOCOUNT ON;
    
    IF @AsOfDate IS NULL
        SET @AsOfDate = CAST(GETDATE() AS DATE);
    
    DECLARE @ProfitTotal DECIMAL(15,2);
    DECLARE @ProfitPerInstallment DECIMAL(15,2);
    DECLARE @OverdueAmount DECIMAL(15,2) = 0;
    DECLARE @FutureAmount DECIMAL(15,2) = 0;
    DECLARE @DiscountAmount DECIMAL(15,2) = 0;
    DECLARE @FutureCount INT = 0;
    DECLARE @OverdueCount INT = 0;
    
    -- احسب الربح الإجمالي
    SELECT 
        @ProfitTotal = MAX(installment_price) - MAX(original_price),
        @ProfitPerInstallment = ROUND(
            (MAX(installment_price) - MAX(original_price)) / CAST(MAX(installment_count) AS DECIMAL(15,2)),
            2
        )
    FROM contracts
    WHERE id = @ContractId;
    
    -- كرر على الأقساط
    DECLARE @InstallmentId INT;
    DECLARE @DueDate DATE;
    DECLARE @RemainingAmount DECIMAL(15,2);
    DECLARE @Discount DECIMAL(15,2);
    
    DECLARE CashoutCursor CURSOR FOR
    SELECT id, due_date, (value - ISNULL(paid_amount, 0))
    FROM installments
    WHERE contract_id = @ContractId AND is_paid = 0
    ORDER BY due_date;
    
    OPEN CashoutCursor;
    
    FETCH NEXT FROM CashoutCursor INTO @InstallmentId, @DueDate, @RemainingAmount;
    
    WHILE @@FETCH_STATUS = 0
    BEGIN
        IF @RemainingAmount > 0
        BEGIN
            IF @DueDate >= @AsOfDate
            BEGIN
                SET @Discount = CASE 
                    WHEN @ProfitPerInstallment < @RemainingAmount 
                    THEN @ProfitPerInstallment 
                    ELSE @RemainingAmount 
                END;
                
                SET @FutureAmount = @FutureAmount + (@RemainingAmount - @Discount);
                SET @DiscountAmount = @DiscountAmount + @Discount;
                SET @FutureCount = @FutureCount + 1;
            END
            ELSE
            BEGIN
                IF @IncludeOverdue = 1
                    SET @OverdueAmount = @OverdueAmount + @RemainingAmount;
                SET @OverdueCount = @OverdueCount + 1;
            END
        END
        
        FETCH NEXT FROM CashoutCursor INTO @InstallmentId, @DueDate, @RemainingAmount;
    END
    
    CLOSE CashoutCursor;
    DEALLOCATE CashoutCursor;
    
    SELECT
        ROUND(@OverdueAmount + @FutureAmount, 2) AS CashoutTotal,
        ROUND(@DiscountAmount, 2) AS DiscountTotal,
        ROUND(@FutureAmount, 2) AS FutureAmount,
        ROUND(@OverdueAmount, 2) AS OverdueAmount,
        @FutureCount AS FutureCount,
        @OverdueCount AS OverdueCount,
        @ProfitPerInstallment AS ProfitPerInstallment;
END
```

---

## 🎯 خريطة الترجمة بين التقنيات

| المفهوم | Laravel | C# | Python | Node.js |
|--------|---------|----|---------|----|
| Model | Eloquent | Entity | SQLAlchemy | Mongoose |
| Service | Class | Class | Class | Class |
| Query | Builder | LINQ | ORM | Aggregation |
| Transaction | DB::transaction | using transaction | @transaction | startTransaction |
| Lock | lockForUpdate() | SELECT FOR UPDATE | select_for_update() | findByIdAndUpdate lock |
| JSON | json cast | JsonConvert | JSONField | Native |

---

## ✅ قائمة التحقق النهائية

لأي نظام ERP:

- [ ] 4 جداول: contracts, installments, payments, contract_cashouts
- [ ] Model/Entity لكل جدول
- [ ] Service للحسابات (calculate + execute)
- [ ] Transaction/Lock للأمان
- [ ] Filters: مدفوع، متبقي، متأخر
- [ ] Reports: يوم، أسبوع، شهر، متأخرات
- [ ] Button 1: تكييش بسيط
- [ ] Button 2: مع متأخرات
- [ ] Validation on inputs
- [ ] Error handling
- [ ] Audit logging

---

**🚀 كل ما تحتاجه موجود هنا - اختر التقنية وطبق!**

