# محاسبات پلن باینری (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) - از باقیمانده‌ها در هفته‌های بعد استفاده کنند