Files
docs/archive/01-BUSINESS/binary-plan-calculation-formulas.md
T
masoodafar-web 5965b98728 update
2026-01-03 18:27:49 +03:30

20 KiB

محاسبات پلن باینری (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#

کلاس مدل

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; }             // مجموع فلش
}

متد محاسبه

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

مثال استفاده

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️⃣ ذخیره باقیمانده‌ها

// باید در دیتابیس ذخیره شود
await SaveWeeklyRemainders(userId, weekId, new WeeklyRemainders
{
    LeftRemainder = result.RemainderNextWeekLeft,
    RightRemainder = result.RemainderNextWeekRight
});

2️⃣ لاگ فلش برای تحلیل

if (result.TotalFlush > 0)
{
    await LogFlush(userId, weekId, new FlushLog
    {
        FlushLeft = result.FlushLeft,
        FlushRight = result.FlushRight,
        Reason = "Cap limitation and imbalance"
    });
}

3️⃣ تعیین MaximumBalance

// بر اساس سطح کاربر
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️⃣ واحد پول

// همه مقادیر باید در واحد ریال ذخیره شوند
// برای نمایش می‌توان به میلیون یا تومان تبدیل کرد
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):

// 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;

در فرمول اکسل:

// 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)
  • از باقیمانده‌ها در هفته‌های بعد استفاده کنند