docs(biz): v6 fix — carryover is per-DOWNLINE-package (not per-user-package), user's own package change has NO effect on carryover
This commit is contained in:
@@ -42,14 +42,14 @@
|
||||
| Q20 | فیچرها در خرید مجدد — چطور؟ | ✅ **DIFF/تفاضل** — مقایسه فیچرهای فعلی با پکیج جدید. فقط اختلاف اعمال میشود |
|
||||
| Q21 | ردیابی فعالسازی ClubMembership | ✅ **FirstActivation + LastActivation** — ۴ فیلد: `FirstActivationDate` + `FirstPackageId` + `LastActivationDate` + `LastPackageId` |
|
||||
| Q22 | تشخیص فعالشدگان هفته | ✅ **از `LastActivationDate`** — هر کسی که `LastActivationDate` در بازه هفته باشد |
|
||||
| Q23 | Carryover تعادل هفتگی | ✅ **strictly per-package** — اگر کاربر پکیج عوض کرد، carryover پکیج قبلی شمرده نمیشود |
|
||||
| Q23 | Carryover تعادل هفتگی | ✅ **per-downline-package** — تعادل بر اساس پکیج **زیرمجموعهها** (نه پکیج خود کاربر). هر کاربر N رکورد تعادل دارد. تغییر پکیج خود کاربر تاثیری بر carryover ندارد |
|
||||
| Q24 | آستانه موجودی برای ورود Magic و خرید مجدد | ✅ **کمتر از ۱۰۰,۰۰۰ تومان** — چون قیمت محصولات متفاوته، `Balance == 0` عملاً غیرممکنه. آستانه ثابت ۱,۰۰۰,۰۰۰ ریال |
|
||||
| Q25 | DayaLoans محدودیت پکیج | ✅ **فقط پکیج پایه** — `SupportsDayaPurchase` فقط روی پکیج پایه `true` هست. تغییر نمیکنه |
|
||||
| Q26 | مدیریت Stored Procedures | ✅ **SP Worker (IHostedService)** — در startup، فایلهای `.sql` از embedded resource خوانده و با checksum مقایسه و اعمال میشوند |
|
||||
| Q27 | History Tables یکسانسازی | ✅ **نامگذاری مشابه master** + ثبت خودکار تغییرات در EF interceptor/domain events |
|
||||
| Q28 | UI Guidance (آموزش/هشدار) | ✅ **مودال + متن inline** — در سراسر FO/BO توضیحات آموزشی و هشداری برای سیستم پکیجبیس |
|
||||
| Q29 | شرط EXIT Magic | ✅ **آخرین پکیج فعال** — از `ClubMembershipCycle.PackageId` (چرخه فعلی) → `Package.MagicWalletMaxDeposit` |
|
||||
| Q30 | Carryover توضیح | ✅ **باقیمانده تعادل هفتگی** — پای قویتر surplus نگه میداره. تغییر پکیج = ریست implicit (lookup پکیج جدید → ۰) |
|
||||
| Q30 | Carryover توضیح | ✅ **باقیمانده تعادل هفتگی** — بر اساس پکیج **زیرمجموعهها**. تغییر پکیج خود کاربر carryover را ریست **نمیکند**. هر Pool مجزا |
|
||||
|
||||
---
|
||||
|
||||
@@ -561,10 +561,13 @@ WHERE c."UserId" = @UserId;
|
||||
/ \
|
||||
[Sara - نقرهای] [Reza - پایه]
|
||||
|
||||
Commission:
|
||||
Ali → پاداش از Pool_پایه
|
||||
Sara → پاداش از Pool_نقرهای
|
||||
Reza → پاداش از Pool_پایه
|
||||
Commission (بالاسری Ali):
|
||||
Ali → از Pool_پایه (چون Reza پکیج پایه داره)
|
||||
Ali → از Pool_نقرهای (چون Sara پکیج نقرهای داره)
|
||||
→ مجموع هر دو Pool → کیف پول شبکه Ali
|
||||
→ به تفکیک: X از پایه، Y از نقرهای
|
||||
|
||||
⚠️ پکیج خود Ali مهم نیست! مهم پکیج زیرمجموعههاست
|
||||
```
|
||||
|
||||
### ۶.۳ فلوی کامل چرخه خرید مجدد و تاثیر بر پورسانت (v5)
|
||||
@@ -600,9 +603,8 @@ Commission:
|
||||
FirstActivationDate = حفظ, FirstPackageId = حفظ
|
||||
LastActivationDate = now (بروزرسانی), LastPackageId = پایه
|
||||
✅ ClubMembershipCycle #2 ساخته میشود
|
||||
✅ NetworkWeeklyBalance جدید (PackageId=پایه, WeekId=هفته جاری)
|
||||
✅ carryover از هفته قبل: فقط carryover پکیج پایه (نه نقرهای!)
|
||||
✅ بالاسریها: محاسبه مجدد از Pool_پایه
|
||||
✅ بالاسری علی: carryover همه Poolها (بر اساس پکیج زیرمجموعهها) حفظ میشود
|
||||
✅ پورسانت بالاسری: از هر Pool که زیرمجموعهای دارد
|
||||
|
||||
... همان چرخه Magic Wallet تکرار ...
|
||||
|
||||
@@ -622,83 +624,114 @@ Commission:
|
||||
FirstActivationDate = حفظ, FirstPackageId = حفظ (پایه)
|
||||
LastActivationDate = now (بروزرسانی), LastPackageId = نقرهای
|
||||
✅ ClubMembershipCycle #5 ساخته میشود (PackageId=نقرهای)
|
||||
✅ NetworkWeeklyBalance جدید (PackageId=نقرهای, WeekId=هفته جاری)
|
||||
✅ تعادلها: MaxBalancesPerLeg=30 (نه 300!) + MaxNetworkLevel=15 (از پکیج)
|
||||
✅ carryover: فقط carryover نقرهای (Q23 — carryover پکیج پایه شمرده نمیشود!)
|
||||
✅ بالاسریها: محاسبه از Pool_نقرهای → ValuePerBalance کمتر
|
||||
✅ پاداش بالاسری: ~۲,۵۲۰,۰۰۰ ÷ TotalBalances_نقرهای × BalancesEarned
|
||||
✅ بالاسریهای علی: carryover همه Poolها حفظ — تغییر پکیج علی تاثیری ندارد!
|
||||
(carryover بر اساس پکیج زیرمجموعههاست نه خود علی)
|
||||
✅ پورسانت بالاسری از هر Pool جداگانه:
|
||||
Pool_پایه: بر اساس زیرمجموعههایی که پکیج پایه دارند
|
||||
Pool_نقرهای: بر اساس زیرمجموعههایی که پکیج نقرهای دارند
|
||||
✅ ActivationFee علی → Pool_نقرهای (۲,۵۲۰,۰۰۰)
|
||||
```
|
||||
|
||||
### ۶.۴ تعادلها (NetworkWeeklyBalance) — per-package (v5)
|
||||
### ۶.۴ تعادلها (NetworkWeeklyBalance) — per-downline-package (v6)
|
||||
|
||||
> ⚠️ **تغییر اساسی:** هر کاربر **بهازای هر پکیج فعال** یک رکورد تعادل جداگانه دارد.
|
||||
> ⚠️ **تغییر اساسی v6:** تعادل هر کاربر بر اساس پکیج **زیرمجموعهها** گروهبندی میشود — **نه** پکیج خود کاربر.
|
||||
|
||||
> 🔴 **قانون carryover v5 (Q23):** اگر کاربر **پکیج عوض کرد**، carryover پکیج قبلی **شمرده نمیشود!**
|
||||
> ✅ **قانون carryover v6 (Q23 اصلاحشده):** تغییر پکیج خود کاربر **هیچ تاثیری** بر carryover ندارد. carryover مال زیرمجموعههاست.
|
||||
|
||||
```
|
||||
قبل (تکپکیج):
|
||||
NetworkWeeklyBalance: [UserId, WeekId] → یک رکورد
|
||||
|
||||
بعد (چندپکیج):
|
||||
NetworkWeeklyBalance: [UserId, WeekId, PackageId] → N رکورد (N = تعداد پکیج)
|
||||
NetworkWeeklyBalance: [UserId, WeekId, PackageId] → N رکورد
|
||||
(N = تعداد پکیجهای مختلف زیرمجموعهها)
|
||||
```
|
||||
|
||||
**الگوریتم محاسبه تعادل per-package:**
|
||||
**الگوریتم محاسبه تعادل per-downline-package:**
|
||||
|
||||
```
|
||||
برای هر پکیج فعال:
|
||||
1. واکشی کاربرانی که این پکیج را دارند:
|
||||
ClubMembership.LastPackageId = X (v5 — Q21)
|
||||
2. carryover از هفته قبل:
|
||||
فقط رکوردهای PackageId = X
|
||||
✅ مهم: فقط اگر پکیج فعلی کاربر = X (Q23)
|
||||
❌ اگر کاربر هفته قبل پکیج Y داشت و حالا X دارد → carryover Y شمرده نمیشود!
|
||||
3. اعضای جدید این هفته:
|
||||
ClubMembership.LastActivationDate در بازه هفته
|
||||
AND ClubMembership.LastPackageId = X (v5 — Q22)
|
||||
برای هر بالاسری (کاربر):
|
||||
برای هر پکیج فعال (X):
|
||||
1. واکشی زیرمجموعههایی که پکیج X دارند:
|
||||
زیرمجموعهها WHERE ClubMembership.LastPackageId = X
|
||||
2. شمارش: چند نفر تیم چپ + چند نفر تیم راست
|
||||
3. carryover از هفته قبل:
|
||||
خواندن رکورد [UserId, PrevWeekId, PackageId=X]
|
||||
✅ همیشه خوانده میشود — مستقل از پکیج خود کاربر!
|
||||
4. LeftLegTotal = NewLeft + CarryoverLeft
|
||||
5. RightLegTotal = NewRight + CarryoverRight
|
||||
6. TotalBalances = MIN(Left, Right) → cap at Package.MaxBalancesPerLeg
|
||||
7. Remainder → carryover هفته بعد (فقط برای PackageId = X)
|
||||
8. SubordinateBalances: مجموع TotalBalances زیرمجموعه (تا Package.MaxNetworkLevel)
|
||||
```
|
||||
|
||||
**مثال carryover با تغییر پکیج (v5 — Q23):**
|
||||
|
||||
```
|
||||
هفته ۹:
|
||||
علی: پکیج پایه → تعادل_پایه: Left=45, Right=30
|
||||
carryover_پایه: {Left: 15, Right: 0} ← باقیمانده
|
||||
|
||||
هفته ۱۰:
|
||||
علی: پکیج پایه → EXIT Magic → خرید پکیج نقرهای
|
||||
7. Remainder → carryover هفته بعد (برای PackageId = X)
|
||||
8. پورسانت: TotalBalances × ValuePerBalance_X → کیف پول شبکه
|
||||
|
||||
❌ carryover_پایه {Left:15, Right:0} شمرده نمیشود!
|
||||
(پکیج فعلی = نقرهای ≠ پایه)
|
||||
✅ carryover_نقرهای: {Left:0, Right:0} (تازه شروع)
|
||||
✅ تعادل_نقرهای: Left=NewLeft+0, Right=NewRight+0
|
||||
مجموع پورسانت از همه Poolها → واریز به کیف پول شبکه کاربر
|
||||
(به تفکیک مشخص: هر مبلغ از کدام Pool)
|
||||
```
|
||||
|
||||
**مثال عملی:**
|
||||
**مثال جامع — بالاسری "علی":**
|
||||
|
||||
```
|
||||
هفته ۱۰:
|
||||
علی (پکیج پایه):
|
||||
تعادل_پایه: Left=45, Right=52, Min=45 (cap 300) → OK
|
||||
تعادل_نقرهای: Left=0, Right=0 (علی پکیج نقرهای نداره)
|
||||
|
||||
سارا (پکیج نقرهای):
|
||||
تعادل_پایه: Left=0, Right=0
|
||||
تعادل_نقرهای: Left=12, Right=8, Min=8 (cap 30) → OK
|
||||
|
||||
رضا (پکیج پایه + قبلاً نقرهای داشته):
|
||||
تعادل_پایه: Left=30, Right=25, Min=25 (cap 300) → OK
|
||||
تعادل_نقرهای: Left=2 (carryover), Right=0 (carryover) → Min=0
|
||||
|
||||
هفته ۱۱ (Shift):
|
||||
علی: carryover_پایه = {Left: surplus_left, Right: surplus_right}
|
||||
سارا: carryover_نقرهای = {Left: surplus_left, Right: surplus_right}
|
||||
رضا: carryover_پایه = {...}, carryover_نقرهای = {Left:2, Right:0}
|
||||
علی (خودش پکیج پایه داره)
|
||||
├── تیم چپ:
|
||||
│ ├── سارا (پکیج پایه)
|
||||
│ ├── رضا (پکیج پایه)
|
||||
│ └── مریم (پکیج نقرهای)
|
||||
└── تیم راست:
|
||||
├── حسین (پکیج پایه)
|
||||
└── زهرا (پکیج نقرهای)
|
||||
```
|
||||
|
||||
```
|
||||
هفته ۱۰ — محاسبه تعادل علی:
|
||||
═══════════════════════════════════════════════════════
|
||||
|
||||
═══ Pool پکیج پایه (زیرمجموعههایی که پایه دارن) ═══
|
||||
چپ: سارا + رضا = 2
|
||||
راست: حسین = 1
|
||||
carryover هفته ۹: {چپ: 3, راست: 0}
|
||||
جمع: چپ = 2+3 = 5, راست = 1+0 = 1
|
||||
تعادل = MIN(5, 1) = 1 (cap 300 → OK)
|
||||
carryover → هفته ۱۱: {چپ: 4, راست: 0}
|
||||
پورسانت: 1 × ValuePerBalance_پایه = A ریال
|
||||
|
||||
═══ Pool پکیج نقرهای (زیرمجموعههایی که نقرهای دارن) ═══
|
||||
چپ: مریم = 1
|
||||
راست: زهرا = 1
|
||||
carryover هفته ۹: {چپ: 0, راست: 0}
|
||||
جمع: چپ = 1+0 = 1, راست = 1+0 = 1
|
||||
تعادل = MIN(1, 1) = 1 (cap 30 → OK)
|
||||
carryover → هفته ۱۱: {چپ: 0, راست: 0}
|
||||
پورسانت: 1 × ValuePerBalance_نقرهای = B ریال
|
||||
|
||||
═══ مجموع پورسانت علی هفته ۱۰: ═══
|
||||
کیف پول شبکه += (A + B)
|
||||
ردیابی: A از Pool پایه، B از Pool نقرهای
|
||||
```
|
||||
|
||||
**هفته ۱۱ — علی پکیج خودش رو عوض میکنه (پایه → نقرهای):**
|
||||
|
||||
```
|
||||
═══ Pool پکیج پایه ═══
|
||||
carryover از هفته ۱۰: {چپ: 4, راست: 0} ← هنوز هست!
|
||||
✅ تغییر پکیج خود علی تاثیری نداره!
|
||||
اعضای جدید: چپ = 0, راست = 1
|
||||
جمع: چپ = 0+4 = 4, راست = 1+0 = 1
|
||||
تعادل = MIN(4, 1) = 1
|
||||
✅ علی هنوز از Pool پایه سود میبره (چون زیرمجموعههایی با پکیج پایه داره)
|
||||
|
||||
═══ Pool پکیج نقرهای ═══
|
||||
carryover از هفته ۱۰: {چپ: 0, راست: 0}
|
||||
... محاسبه عادی ...
|
||||
```
|
||||
|
||||
**نکته کلیدی:** پکیج **خود کاربر** فقط تعیین میکنه ActivationFeeاش به کدوم Pool بره.
|
||||
**تعادل و carryover** بر اساس پکیج **زیرمجموعهها** محاسبه میشه.
|
||||
|
||||
**Cap (سقف):**
|
||||
```
|
||||
اگه تعادل بیشتر از MaxBalancesPerLeg بشه → بریده میشه (flush):
|
||||
تعادل = 500، سقف = 300 → CappedBalance = 300، Flushed = 200
|
||||
⚠️ Flushed از بین میره — carry نمیشه!
|
||||
```
|
||||
|
||||
### ۶.۵ تغییرات SP (v3 — بروزرسانی)
|
||||
@@ -1713,40 +1746,39 @@ public abstract class BaseHistoryEntity : BaseEntity
|
||||
|
||||
---
|
||||
|
||||
### Q30 — Carryover — توضیح ساده
|
||||
### Q30 — Carryover — توضیح ساده (v6 اصلاحشده)
|
||||
|
||||
> **Carryover = باقیمانده تعادل از هفته قبل**
|
||||
> **Carryover = باقیمانده تعادل از هفته قبل — بر اساس پکیج زیرمجموعهها**
|
||||
|
||||
**مثال ساده:**
|
||||
```
|
||||
علی بالاسری — زیرمجموعههاش پکیج پایه دارن:
|
||||
|
||||
هفته ۱۰:
|
||||
علی: تیم چپ = ۵۰ نفر فعال، تیم راست = ۳۰ نفر فعال
|
||||
تعادل = MIN(50, 30) = 30
|
||||
باقیمانده: چپ = 50-30 = 20 ← CARRYOVER راست = 0
|
||||
تیم چپ (پکیج پایه) = ۵۰ نفر، تیم راست (پکیج پایه) = ۳۰ نفر
|
||||
تعادل_پایه = MIN(50, 30) = 30
|
||||
باقیمانده_پایه: {چپ: 20, راست: 0} ← CARRYOVER
|
||||
|
||||
هفته ۱۱:
|
||||
اعضای جدید: چپ = 5، راست = 10
|
||||
اعضای جدید (پکیج پایه): چپ = 5، راست = 10
|
||||
جمع با carryover: چپ = 5+20 = 25، راست = 10+0 = 10
|
||||
تعادل = MIN(25, 10) = 10
|
||||
باقیمانده: چپ = 25-10 = 15 ← CARRYOVER بعدی
|
||||
تعادل_پایه = MIN(25, 10) = 10
|
||||
باقیمانده_پایه: {چپ: 15, راست: 0} ← CARRYOVER بعدی
|
||||
```
|
||||
|
||||
**Per-package carryover (Q23):**
|
||||
**تغییر پکیج خود کاربر — تاثیری ندارد (Q23 اصلاحی):**
|
||||
```
|
||||
علی پکیج پایه داره:
|
||||
carryover_پایه = {چپ: 20, راست: 0}
|
||||
علی پکیج خودش رو عوض میکنه (پایه → نقرهای):
|
||||
|
||||
علی پکیج رو عوض میکنه → نقرهای:
|
||||
❌ carryover_پایه دیگه شمرده نمیشه!
|
||||
✅ carryover_نقرهای = {چپ: 0, راست: 0} ← شروع از صفر
|
||||
carryover_پایه = {چپ: 20, راست: 0} ← هنوز هست! حذف نمیشه!
|
||||
carryover_نقرهای = {چپ: 0, راست: 0}
|
||||
|
||||
(چون سیستم هفته بعد دنبال carryover با PackageId=نقرهای
|
||||
میگرده و پیدا نمیکنه → default صفر)
|
||||
```
|
||||
|
||||
**Cap (سقف):**
|
||||
```
|
||||
اگه تعادل بیشتر از MaxBalancesPerLeg بشه → بریده میشه (flush):
|
||||
تعادل = 500، سقف = 300 → CappedBalance = 300، Flushed = 200
|
||||
⚠️ Flushed از بین میره — carry نمیشه!
|
||||
✅ هفته بعد، علی هنوز از Pool پایه سود میبره
|
||||
(چون زیرمجموعههایی با پکیج پایه داره)
|
||||
✅ همزمان از Pool نقرهای هم سود میبره
|
||||
(چون زیرمجموعههایی با پکیج نقرهای هم داره)
|
||||
|
||||
پکیج خود علی فقط تعیین میکنه:
|
||||
→ ActivationFeeاش به کدوم Pool بره
|
||||
→ MagicWallet سقفش چقدره
|
||||
```
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
| تعادل هفتگی | ۱ رکورد per user/week | **N رکورد** per user/week/package |
|
||||
| MaxBalancesPerLeg | hardcoded `300` | per-package (پایه=۳۰۰, نقرهای=۳۰) |
|
||||
| MaxNetworkLevel | hardcoded `15` | per-package از دیتابیس |
|
||||
| Carryover | یکپارچه | **per-package** — تغییر پکیج = ریست carryover (Q23) |
|
||||
| Carryover | یکپارچه | **per-downline-package** — بر اساس پکیج زیرمجموعهها (تغییر پکیج خود کاربر تاثیری ندارد) |
|
||||
| Stored Procedure | پارامترهای ثابت | پارامترهای داینامیک از Package entity |
|
||||
| گزارش مشتری | بدون تفکیک | **breakdown per-package** |
|
||||
| گزارش ادمین | بدون فیلتر | **فیلتر بر اساس پکیج** |
|
||||
@@ -572,7 +572,7 @@ kubectl set image deployment/backoffice bo=bo:rollback-point
|
||||
|---|------|-------------------|
|
||||
| 10 | Pool جداگانه هر پکیج | WeeklyCommissionPool با PackageId متفاوت |
|
||||
| 11 | MaxBalancesPerLeg متفاوت | پایه=۳۰۰, نقرهای=۳۰ |
|
||||
| 12 | Carryover per-package | تغییر پکیج → ریست carryover |
|
||||
| 12 | Carryover per-downline-package | تغییر پکیج خود کاربر → carryover حفظ (بر اساس زیرمجموعهها) |
|
||||
| 13 | SP پارامترها از Package | بدون hardcoded ۳۰۰/۱۵ |
|
||||
|
||||
### 📊 گزارشات per-package
|
||||
|
||||
Reference in New Issue
Block a user