# اصلاحات سیستم کمیسیون هفتگی

## 📋 خلاصه تغییرات

سیستم کمیسیون هفتگی از **3 مرحله به 2 مرحله** ساده‌سازی شد:

### ❌ قبل (3 مرحله):
1. `CalculateWeeklyBalances` - محاسبه تعادل‌ها
2. `CalculateWeeklyCommissionPool` - محاسبه استخر
3. `ProcessUserPayouts` - پردازش پرداخت‌ها (تکراری!)

### ✅ بعد (2 مرحله):
1. `CalculateWeeklyBalances` - محاسبه تعادل‌ها تا 15 لول
2. `CalculateWeeklyCommissionPool` - محاسبه استخر + پردازش پرداخت‌ها

---

## 🔧 تغییرات جزئی

### 1️⃣ اضافه شدن فیلدها به `NetworkWeeklyBalance`

**فیلدهای جدید:**
```csharp
/// <summary>
/// مقدار فلش هر طرف (بعد از اعمال Cap)
/// </summary>
public int FlushedPerSide { get; set; }

/// <summary>
/// مجموع فلش از دو طرف (از دست رفته)
/// </summary>
public int TotalFlushed { get; set; }
```

**Migration:** `AddFlushedFieldsToNetworkWeeklyBalance`

---

### 2️⃣ اصلاح `CalculateWeeklyBalances`

**تغییرات:**
- ✅ فیلدهای `FlushedPerSide` و `TotalFlushed` ذخیره می‌شوند
- ✅ `WeeklyPoolContribution = 0` (دیگر در این مرحله محاسبه نمیشه)
- ✅ محدودیت 15 لول قبلاً موجود بود و درست کار می‌کند

**کد:**
```csharp
// محاسبه فلش
var flushedPerSide = totalBalances - cappedBalances;
var totalFlushed = flushedPerSide * 2;

// ذخیره
balance.FlushedPerSide = flushedPerSide;
balance.TotalFlushed = totalFlushed;
balance.WeeklyPoolContribution = 0; // Pool در مرحله بعد محاسبه میشه
```

---

### 3️⃣ اصلاح کامل `CalculateWeeklyCommissionPool`

**منطق جدید Pool:**
```csharp
// 1. Pool از فعالسازی‌های باشگاه این هفته میاد (نه از تعادل‌ها)
var newClubMembersCount = await _context.ClubMemberships
    .Where(c => c.ActivatedAt >= startDate && c.ActivatedAt <= endDate)
    .CountAsync();

var totalPoolAmount = newClubMembersCount * activationFee;

// 2. ارزش هر امتیاز
var totalBalancesInNetwork = weeklyBalances.Sum(x => x.TotalBalances);
var valuePerBalance = totalPoolAmount / totalBalancesInNetwork;
```

**افزوده شدن محاسبه تعادل زیرمجموعه:**
```csharp
// برای هر کاربر:
// 1. تعادل خودش
var directBalances = balance.TotalBalances;

// 2. تعادل زیرمجموعه (تا 15 لول)
var subordinateBalances = await CalculateSubordinateBalancesAsync(
    balance.UserId, 
    request.WeekNumber, 
    maxLevels: 15
);

var totalBalancesForUser = directBalances + subordinateBalances;
```

**ایجاد UserCommissionPayout:**
```csharp
var payout = new UserCommissionPayout
{
    UserId = balance.UserId,
    WeekNumber = request.WeekNumber,
    WeeklyPoolId = existingPool.Id,
    BalancesEarned = totalBalancesForUser,
    ValuePerBalance = valuePerBalance,
    TotalAmount = totalBalancesForUser * valuePerBalance,
    Status = CommissionPayoutStatus.Pending,
    // ... subordinate fields
};
```

**ثبت تاریخچه:**
```csharp
var history = new CommissionPayoutHistory
{
    UserId = payout.UserId,
    PayoutId = payout.Id,
    Amount = payout.TotalAmount,
    Status = CommissionPayoutStatus.Pending,
    ChangeReason = "محاسبه اولیه کمیسیون هفتگی"
};
```

---

### 4️⃣ ساده‌سازی `TriggerWeeklyCalculation`

**قبل:**
```csharp
// Step 1
await _mediator.Send(new CalculateWeeklyBalancesCommand { ... });

// Step 2
await _mediator.Send(new CalculateWeeklyCommissionPoolCommand { ... });

// Step 3
await _mediator.Send(new ProcessUserPayoutsCommand { ... });
```

**بعد:**
```csharp
// Step 1: محاسبه تعادل‌ها
if (!request.SkipBalances)
{
    await _mediator.Send(new CalculateWeeklyBalancesCommand { ... });
}

// Step 2: محاسبه Pool و پرداخت‌ها
if (!request.SkipPayouts)
{
    await _mediator.Send(new CalculateWeeklyCommissionPoolCommand { ... });
}
```

**حذف شد:**
- ❌ `SkipPool` flag
- ❌ Step 3 کاملاً حذف شد

---

## 🎯 فرآیند نهایی

### مرحله 1: محاسبه تعادل‌ها
```
1. برای هر کاربر در شبکه
2. تا 15 لول پایین‌تر شمارش کن
3. محاسبه تعادل (MIN of left/right)
4. محاسبه باقیمانده
5. محاسبه فلش
6. ذخیره در NetworkWeeklyBalance
```

### مرحله 2: محاسبه Pool و توزیع
```
1. شمارش فعالسازی‌های باشگاه این هفته
2. Pool = تعداد × ActivationFee
3. ارزش هر امتیاز = Pool ÷ مجموع تعادل‌ها
4. برای هر کاربر:
   a. تعادل خودش + تعادل زیرمجموعه (تا 15 لول)
   b. سهم = تعادل × ارزش
   c. ثبت در UserCommissionPayout
   d. ثبت تاریخچه
```

---

## 📊 جداول درگیر

### `NetworkWeeklyBalance` (فیلدهای جدید)
```sql
ALTER TABLE [Network].[NetworkWeeklyBalances]
ADD [FlushedPerSide] INT NOT NULL DEFAULT 0,
    [TotalFlushed] INT NOT NULL DEFAULT 0;
```

### `WeeklyCommissionPool`
```
- TotalPoolAmount: از فعالسازی‌های باشگاه
- TotalBalances: مجموع تعادل‌های شبکه
- ValuePerBalance: Pool ÷ TotalBalances
```

### `UserCommissionPayout`
```
- BalancesEarned: تعادل خودش + زیرمجموعه
- DirectBalances: فقط تعادل خودش
- SubordinateBalances: فقط زیرمجموعه
- TotalAmount: BalancesEarned × ValuePerBalance
- Status: Pending
```

### `CommissionPayoutHistory`
```
- PayoutId: شناسه UserCommissionPayout
- Status: Pending (در این مرحله)
- ChangeReason: "محاسبه اولیه کمیسیون هفتگی"
```

---

## ✅ مزایا

1. **ساده‌تر**: 2 مرحله به جای 3
2. **بدون تکرار**: دیگر UserCommissionPayout دوبار ساخته نمیشه
3. **واضح‌تر**: Pool از کجا میاد مشخصه
4. **قابل نگهداری**: منطق مشابه یکجا هست
5. **کامل**: تاریخچه + subordinate balances همه جا هست

---

## 🔄 مراحل بعدی (اختیاری)

### مرحله 3: پرداخت واقعی (جدا از محاسبه)

می‌توان یک Command جدید داشت که:
1. `UserCommissionPayout` با status=Pending رو بخونه
2. به کیف پول واریز کنه
3. Status رو به Paid تغییر بده
4. تاریخچه اضافه کنه

این مرحله **جدا از محاسبات** است و می‌تواند:
- دستی توسط ادمین اجرا شود
- یا به صورت خودکار بعد از تایید

---

## 📝 نکات مهم

### Pool چطور پُر میشه؟
```
1. کاربر عضو Club میشه
2. در ActivateClubMembership مبلغی کسر میشه
3. این مبلغ به Pool اضافه **نمیشه** (فقط شمارش میشه)
4. در محاسبه Pool: تعداد × ActivationFee
```

### چرا subordinate balances؟
```
در سیستم باینری، کاربر از تعادل زیرمجموعه‌های خود 
(تا 15 لول پایین‌تر) هم کمیسیون می‌گیرد.
```

### چرا 15 لول؟
```
محدودیت عمق برای جلوگیری از بارگذاری بیش از حد 
و تشویق به ایجاد شبکه متعادل
```

---

## 🧪 تست

### تست مرحله 1
```csharp
// 1. ایجاد کاربران در شبکه
// 2. فعالسازی Club برای برخی
// 3. اجرای CalculateWeeklyBalances
// 4. بررسی NetworkWeeklyBalance
//    - TotalBalances
//    - FlushedPerSide
//    - TotalFlushed
```

### تست مرحله 2
```csharp
// 1. اجرای مرحله 1
// 2. اجرای CalculateWeeklyCommissionPool
// 3. بررسی WeeklyCommissionPool
//    - TotalPoolAmount = تعداد فعالسازی‌ها × ActivationFee
//    - ValuePerBalance صحیح باشد
// 4. بررسی UserCommissionPayout
//    - برای هر کاربر ایجاد شده
//    - BalancesEarned شامل subordinate هم هست
//    - TotalAmount = BalancesEarned × ValuePerBalance
// 5. بررسی CommissionPayoutHistory
//    - برای هر پرداخت ثبت شده
```

---

## 📚 فایل‌های تغییر یافته

1. ✅ `NetworkWeeklyBalance.cs` - اضافه شدن فیلدها
2. ✅ `CalculateWeeklyBalancesCommandHandler.cs` - ذخیره فلش
3. ✅ `CalculateWeeklyCommissionPoolCommandHandler.cs` - منطق کامل جدید
4. ✅ `TriggerWeeklyCalculationCommandHandler.cs` - حذف مرحله 3
5. ✅ `TriggerWeeklyCalculationCommand.cs` - حذف SkipPool flag
6. ✅ Migration: `AddFlushedFieldsToNetworkWeeklyBalance`

---

## 🎉 نتیجه

سیستم کمیسیون هفتگی حالا:
- ✅ **ساده‌تر** و قابل فهم‌تر
- ✅ **بدون تکرار** در کد
- ✅ **Pool از منبع صحیح** (فعالسازی‌های Club)
- ✅ **تعادل زیرمجموعه** محاسبه میشه
- ✅ **تاریخچه کامل** ثبت میشه
- ✅ **فلش دقیق** ذخیره میشه

آماده برای استفاده در Production! 🚀
