update
This commit is contained in:
@@ -0,0 +1,546 @@
|
||||
# محاسبات پلن باینری (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)
|
||||
- از باقیماندهها در هفتههای بعد استفاده کنند
|
||||
Reference in New Issue
Block a user