# محاسبات پلن باینری (Binary Plan Calculations)

## مستندات فرمول‌های محاسبه کمیسیون باینری

این سند فرمول‌های محاسباتی سیستم کمیسیون باینری را که از فایل اکسل استخراج شده، توضیح می‌دهد.

---

## متغیرها و تعاریف

### ورودی‌های هفته قبل (Last Week Remainders)

| نام فارسی | نماد | توضیحات |
|-----------|------|---------|
| **باقیمانده هفته قبل چپ** | `LL` (Last Left) | باقیمانده‌ای که از هفته قبل در پای چپ باقی مانده |
| **باقیمانده هفته قبل راست** | `LR` (Last Right) | باقیمانده‌ای که از هفته قبل در پای راست باقی مانده |

**مثال از اکسل:**
- `LL = 200` (میلیون ریال)
- `LR = 0`

---

### ورودی‌های هفته جدید (New Week Values)

| نام فارسی | نماد | توضیحات |
|-----------|------|---------|
| **هفته جدید چپ** | `NL` (New Left) | مجموع فروش/شارژ پای چپ در هفته جاری |
| **هفته جدید راست** | `NR` (New Right) | مجموع فروش/شارژ پای راست در هفته جاری |

**مثال از اکسل:**
- `NL = 400` (میلیون ریال)
- `NR = 500` (میلیون ریال)

---

### پارامتر سیستم (System Parameter)

| نام فارسی | نماد | توضیحات |
|-----------|------|---------|
| **ماکسیمم تعادل** | `MX` (Maximum Balance) | حداکثر مقداری که در یک هفته می‌تواند به عنوان تعادل (کمیسیون) محاسبه شود |

**مثال از اکسل:**
- `MX = 300` (میلیون ریال)

**نکته مهم:** این مقدار معمولاً بر اساس سطح کاربر یا پکیج خریداری شده تعیین می‌شود.

---

## فرمول‌های محاسباتی

### 1️⃣ محاسبه مجموع پا چپ (Sum Left Total)

```
SLT = LL + NL
```

**توضیح:**
- `SLT` (Sum Left Total) = مجموع کل پای چپ
- باقیمانده هفته قبل + فروش هفته جدید

**مثال:**
```
SLT = 200 + 400 = 600
```

---

### 2️⃣ محاسبه مجموع پا راست (Sum Right Total)

```
SRT = LR + NR
```

**توضیح:**
- `SRT` (Sum Right Total) = مجموع کل پای راست
- باقیمانده هفته قبل + فروش هفته جدید

**مثال:**
```
SRT = 0 + 500 = 500
```

---

### 3️⃣ محاسبه کمترین کل (Minimum Total)

```
MinT = MIN(SLT, SRT)
```

**توضیح:**
- `MinT` = کوچکترین مقدار بین دو پا
- این مقدار نشان‌دهنده حداکثر تعادل بالقوه است

**مثال:**
```
MinT = MIN(600, 500) = 500
```

---

### 4️⃣ محاسبه باقیمانده هفته بعد چپ (Remainder Next Week Left)

```
RNWL = SLT - MinT
```

**توضیح:**
- `RNWL` (Remainder Next Week Left) = باقیمانده‌ای که به هفته بعد منتقل می‌شود
- مازاد پای چپ که برای تعادل استفاده نشد

**مثال:**
```
RNWL = 600 - 500 = 100
```

---

### 5️⃣ محاسبه باقیمانده هفته بعد راست (Remainder Next Week Right)

```
RNWR = SRT - MinT
```

**توضیح:**
- `RNWR` (Remainder Next Week Right) = باقیمانده‌ای که به هفته بعد منتقل می‌شود
- مازاد پای راست که برای تعادل استفاده نشد

**مثال:**
```
RNWR = 500 - 500 = 0
```

**نکته:** یکی از دو باقیمانده همیشه صفر است (چون MinT کوچکترین است).

---

### 6️⃣ محاسبه فلش چپ (Flush Left)

```
FL = SLT - MX - RNWL
```

**توضیح:**
- `FL` (Flush Left) = مقداری که از ماکسیمم هم بیشتر بود و باید دور ریخته شود
- این مقدار نشان‌دهنده سرریز (overflow) است که نمی‌تواند به هفته بعد منتقل شود

**مثال:**
```
FL = 600 - 300 - 100 = 200
```

**معنی:** از 600 میلیون پای چپ:
- 300 به عنوان کمیسیون استفاده شد (تا حد MX)
- 100 به هفته بعد منتقل شد
- **200 فلش شد (از دست رفت)** ❌

---

### 7️⃣ محاسبه فلش راست (Flush Right)

```
FR = SRT - MX - RNWR
```

**توضیح:**
- `FR` (Flush Right) = مقداری که از پای راست دور ریخته می‌شود

**مثال:**
```
FR = 500 - 300 - 0 = 200
```

**معنی:** از 500 میلیون پای راست:
- 300 به عنوان کمیسیون استفاده شد
- 0 به هفته بعد منتقل شد
- **200 فلش شد (از دست رفت)** ❌

---

### 8️⃣ محاسبه کل تعادل (Total Balance / Commission)

```
TB = IF(MinT > MX, MX, MinT)
```

یا به زبان ساده‌تر:
```
TB = MIN(MinT, MX)
```

**توضیح:**
- `TB` (Total Balance) = مقدار واقعی کمیسیونی که به کاربر تعلق می‌گیرد
- نمی‌تواند از ماکسیمم تعادل (`MX`) بیشتر شود

**مثال:**
```
TB = MIN(500, 300) = 300
```

**معنی:** هرچند تعادل واقعی 500 بود، اما به دلیل محدودیت `MX`، فقط 300 به عنوان کمیسیون پرداخت می‌شود.

---

## خلاصه جریان محاسبات

```
┌─────────────────────────────────────────────────────────────┐
│ ورودی‌ها                                                     │
├─────────────────────────────────────────────────────────────┤
│ LL = 200     باقیمانده هفته قبل چپ                         │
│ LR = 0       باقیمانده هفته قبل راست                       │
│ NL = 400     هفته جدید چپ                                  │
│ NR = 500     هفته جدید راست                                │
│ MX = 300     ماکسیمم تعادل                                  │
└─────────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────────┐
│ گام 1: محاسبه مجموع دو پا                                  │
├─────────────────────────────────────────────────────────────┤
│ SLT = LL + NL = 200 + 400 = 600                             │
│ SRT = LR + NR = 0 + 500 = 500                               │
└─────────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────────┐
│ گام 2: محاسبه کمترین کل                                    │
├─────────────────────────────────────────────────────────────┤
│ MinT = MIN(SLT, SRT) = MIN(600, 500) = 500                  │
└─────────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────────┐
│ گام 3: محاسبه کمیسیون واقعی (با اعمال Cap)                │
├─────────────────────────────────────────────────────────────┤
│ TB = MIN(MinT, MX) = MIN(500, 300) = 300  ✅ کمیسیون        │
└─────────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────────┐
│ گام 4: محاسبه باقیمانده هفته بعد                           │
├─────────────────────────────────────────────────────────────┤
│ RNWL = SLT - MinT = 600 - 500 = 100  → هفته بعد            │
│ RNWR = SRT - MinT = 500 - 500 = 0    → هفته بعد            │
└─────────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────────┐
│ گام 5: محاسبه فلش (از دست رفته)                            │
├─────────────────────────────────────────────────────────────┤
│ FL = SLT - MX - RNWL = 600 - 300 - 100 = 200  ❌ فلش        │
│ FR = SRT - MX - RNWR = 500 - 300 - 0 = 200    ❌ فلش        │
└─────────────────────────────────────────────────────────────┘
```

---

## تحلیل نتایج

### 📊 خروجی‌های نهایی

| مقدار | توضیح | وضعیت |
|-------|-------|-------|
| **TB = 300** | کمیسیون پرداختی این هفته | ✅ پرداخت می‌شود |
| **RNWL = 100** | باقیمانده پای چپ برای هفته بعد | ⏭️ منتقل می‌شود |
| **RNWR = 0** | باقیمانده پای راست برای هفته بعد | ⏭️ منتقل می‌شود |
| **FL = 200** | فلش پای چپ | ❌ از دست می‌رود |
| **FR = 200** | فلش پای راست | ❌ از دست می‌رود |

---

### 🔍 تفسیر کسب‌وکار

#### کمیسیون محاسبه شده
```
کمیسیون = 300 میلیون ریال
```
- به دلیل محدودیت `MX = 300`، از تعادل بالقوه 500، فقط 300 قابل برداشت است
- این یک مکانیزم کنترل هزینه است

#### باقیمانده به هفته بعد
```
هفته بعد LL = 100 (از پای چپ)
هفته بعد LR = 0 (از پای راست)
```
- 100 میلیون از پای چپ به هفته بعد منتقل می‌شود
- این باقیمانده در محاسبات هفته آینده دوباره استفاده خواهد شد

#### فلش (Flush) - نکته مهم ⚠️
```
فلش کل = 400 میلیون ریال (200 چپ + 200 راست)
```

**چرا فلش رخ می‌دهد؟**
1. مجموع دو پا = 1100 میلیون (600 + 500)
2. کمیسیون محاسبه شده = 300 میلیون
3. باقیمانده منتقل شده = 100 میلیون
4. فلش = 1100 - 300 - 100 = 700 میلیون ❌

**توضیح:**
- فلش نشان‌دهنده مقداری است که به دلیل **عدم تعادل** و **محدودیت Cap** از دست می‌رود
- این یک ضرر برای کاربر است که می‌تواند با متعادل کردن دو پا کاهش یابد

---

## پیاده‌سازی در C#

### کلاس مدل

```csharp
public class BinaryPlanCalculationInput
{
    // ورودی‌های هفته قبل
    public decimal LastLeftRemainder { get; set; }      // LL
    public decimal LastRightRemainder { get; set; }     // LR
    
    // ورودی‌های هفته جاری
    public decimal NewLeftVolume { get; set; }          // NL
    public decimal NewRightVolume { get; set; }         // NR
    
    // تنظیمات سیستم
    public decimal MaximumBalance { get; set; }         // MX
}

public class BinaryPlanCalculationResult
{
    // محاسبات واسط
    public decimal SumLeftTotal { get; set; }           // SLT
    public decimal SumRightTotal { get; set; }          // SRT
    public decimal MinimumTotal { get; set; }           // MinT
    
    // باقیمانده‌ها
    public decimal RemainderNextWeekLeft { get; set; }  // RNWL
    public decimal RemainderNextWeekRight { get; set; } // RNWR
    
    // فلش
    public decimal FlushLeft { get; set; }              // FL
    public decimal FlushRight { get; set; }             // FR
    
    // نتیجه نهایی
    public decimal TotalBalance { get; set; }           // TB - کمیسیون واقعی
    public decimal TotalFlush { get; set; }             // مجموع فلش
}
```

---

### متد محاسبه

```csharp
public static BinaryPlanCalculationResult Calculate(BinaryPlanCalculationInput input)
{
    var result = new BinaryPlanCalculationResult();
    
    // گام 1: محاسبه مجموع دو پا
    result.SumLeftTotal = input.LastLeftRemainder + input.NewLeftVolume;
    result.SumRightTotal = input.LastRightRemainder + input.NewRightVolume;
    
    // گام 2: محاسبه کمترین کل
    result.MinimumTotal = Math.Min(result.SumLeftTotal, result.SumRightTotal);
    
    // گام 3: محاسبه کمیسیون واقعی (با اعمال Cap)
    result.TotalBalance = Math.Min(result.MinimumTotal, input.MaximumBalance);
    
    // گام 4: محاسبه باقیمانده هفته بعد
    result.RemainderNextWeekLeft = result.SumLeftTotal - result.MinimumTotal;
    result.RemainderNextWeekRight = result.SumRightTotal - result.MinimumTotal;
    
    // گام 5: محاسبه فلش
    result.FlushLeft = result.SumLeftTotal - input.MaximumBalance - result.RemainderNextWeekLeft;
    result.FlushRight = result.SumRightTotal - input.MaximumBalance - result.RemainderNextWeekRight;
    
    // محاسبه مجموع فلش
    result.TotalFlush = result.FlushLeft + result.FlushRight;
    
    // اطمینان از عدم منفی شدن فلش
    result.FlushLeft = Math.Max(0, result.FlushLeft);
    result.FlushRight = Math.Max(0, result.FlushRight);
    result.TotalFlush = Math.Max(0, result.TotalFlush);
    
    return result;
}
```

---

### مثال استفاده

```csharp
var input = new BinaryPlanCalculationInput
{
    LastLeftRemainder = 200_000_000,   // 200 میلیون
    LastRightRemainder = 0,
    NewLeftVolume = 400_000_000,       // 400 میلیون
    NewRightVolume = 500_000_000,      // 500 میلیون
    MaximumBalance = 300_000_000       // 300 میلیون
};

var result = Calculate(input);

Console.WriteLine($"کمیسیون قابل پرداخت: {result.TotalBalance:N0} ریال");
// Output: کمیسیون قابل پرداخت: 300,000,000 ریال

Console.WriteLine($"باقیمانده چپ هفته بعد: {result.RemainderNextWeekLeft:N0} ریال");
// Output: باقیمانده چپ هفته بعد: 100,000,000 ریال

Console.WriteLine($"باقیمانده راست هفته بعد: {result.RemainderNextWeekRight:N0} ریال");
// Output: باقیمانده راست هفته بعد: 0 ریال

Console.WriteLine($"فلش کل: {result.TotalFlush:N0} ریال");
// Output: فلش کل: 400,000,000 ریال
```

---

## نکات مهم برای پیاده‌سازی

### 1️⃣ ذخیره باقیمانده‌ها
```csharp
// باید در دیتابیس ذخیره شود
await SaveWeeklyRemainders(userId, weekId, new WeeklyRemainders
{
    LeftRemainder = result.RemainderNextWeekLeft,
    RightRemainder = result.RemainderNextWeekRight
});
```

### 2️⃣ لاگ فلش برای تحلیل
```csharp
if (result.TotalFlush > 0)
{
    await LogFlush(userId, weekId, new FlushLog
    {
        FlushLeft = result.FlushLeft,
        FlushRight = result.FlushRight,
        Reason = "Cap limitation and imbalance"
    });
}
```

### 3️⃣ تعیین MaximumBalance
```csharp
// بر اساس سطح کاربر
decimal GetMaximumBalance(User user)
{
    return user.MembershipLevel switch
    {
        MembershipLevel.Bronze => 100_000_000,
        MembershipLevel.Silver => 300_000_000,
        MembershipLevel.Gold => 500_000_000,
        MembershipLevel.Platinum => 1_000_000_000,
        _ => 50_000_000
    };
}
```

### 4️⃣ واحد پول
```csharp
// همه مقادیر باید در واحد ریال ذخیره شوند
// برای نمایش می‌توان به میلیون یا تومان تبدیل کرد
decimal DisplayInMillions(decimal rials) => rials / 1_000_000;
decimal DisplayInTomans(decimal rials) => rials / 10;
```

---

## سناریوهای مختلف

### سناریو 1: تعادل کامل
```
LL = 0, LR = 0, NL = 300, NR = 300, MX = 500
→ TB = 300, RNWL = 0, RNWR = 0, FL = 0, FR = 0
```
**نتیجه:** کمیسیون کامل بدون فلش ✅

---

### سناریو 2: یک پا خیلی بیشتر
```
LL = 0, LR = 0, NL = 1000, NR = 100, MX = 500
→ TB = 100, RNWL = 900, RNWR = 0, FL = 400, FR = 0
```
**نتیجه:** کمیسیون کم + فلش زیاد ❌

---

### سناریو 3: باقیمانده قبلی موثر
```
LL = 400, LR = 0, NL = 100, NR = 400, MX = 300
→ SLT = 500, SRT = 400
→ TB = 300, RNWL = 100, RNWR = 0, FL = 100, FR = 100
```
**نتیجه:** باقیمانده قبلی در محاسبه کمیسیون موثر است ✅

---

## تفاوت با کد فعلی

### در کد فعلی (`CalculateWeeklyBalancesCommandHandler.cs`):

```csharp
// 1. ابتدا Cap اعمال می‌شود
var cappedLeft = Math.Min(leftLegTotal, maxBalance);
var cappedRight = Math.Min(rightLegTotal, maxBalance);

// 2. سپس تعادل محاسبه می‌شود
var balance = Math.Min(cappedLeft, cappedRight);

// 3. باقیمانده‌ها محاسبه می‌شوند
var leftRemainder = leftLegTotal - balance;
var rightRemainder = rightLegTotal - balance;
```

### در فرمول اکسل:
```csharp
// 1. ابتدا تعادل کامل محاسبه می‌شود
var minTotal = Math.Min(leftLegTotal, rightLegTotal);

// 2. سپس Cap اعمال می‌شود
var balance = Math.Min(minTotal, maxBalance);

// 3. باقیمانده‌ها بر اساس minTotal محاسبه می‌شوند
var leftRemainder = leftLegTotal - minTotal;
var rightRemainder = rightLegTotal - minTotal;

// 4. فلش محاسبه می‌شود
var flushLeft = leftLegTotal - maxBalance - leftRemainder;
var flushRight = rightLegTotal - maxBalance - rightRemainder;
```

**تفاوت کلیدی:** 
- کد فعلی Cap را ابتدا اعمال می‌کند (می‌تواند باقیمانده‌های بیشتری ایجاد کند)
- فرمول اکسل ابتدا تعادل را محاسبه می‌کند، سپس Cap اعمال می‌شود (فلش دقیق‌تر محاسبه می‌شود)

---

## نتیجه‌گیری

این فرمول‌ها نشان می‌دهند که:

1. ✅ **تعادل اهمیت دارد** - هرچه دو پا متعادل‌تر باشند، فلش کمتر است
2. ✅ **Cap محدودیت ایجاد می‌کند** - حتی با تعادل کامل، بیش از MX کمیسیون داده نمی‌شود
3. ✅ **باقیمانده‌ها منتقل می‌شوند** - برای هفته بعد ذخیره می‌شوند
4. ❌ **فلش ضرر است** - مقداری که به دلیل عدم تعادل یا Cap از دست می‌رود

**توصیه:** برای افزایش کمیسیون، کاربران باید:
- دو پای خود را متعادل نگه دارند
- سطح عضویت خود را ارتقا دهند (برای افزایش MX)
- از باقیمانده‌ها در هفته‌های بعد استفاده کنند
