diff --git a/business/BIZ-PACKAGE-BASED-SYSTEM-v1-draft.md b/business/BIZ-PACKAGE-BASED-SYSTEM-v1-draft.md
new file mode 100644
index 0000000..549dd4a
--- /dev/null
+++ b/business/BIZ-PACKAGE-BASED-SYSTEM-v1-draft.md
@@ -0,0 +1,554 @@
+# 📦 سیستم مبتنی بر پکیج (Package-Based System)
+
+> **وضعیت:** تحلیل و بررسی — منتظر تایید
+> **تاریخ:** اسفند ۱۴۰۴
+> **تاثیرگذاری:** زیاد — بخشهای متعدد سیستم تحت تاثیر قرار میگیرد
+
+---
+
+## ۱. خلاصه فیچر
+
+**وضعیت فعلی:** سیستم فقط یک پکیج پایه (۵۶ میلیون تومان) دارد و همه چیز حول آن میچرخد.
+
+**وضعیت هدف:** سیستم چندین پکیج با قیمتها و ویژگیهای متفاوت پشتیبانی میکند. هر پکیج روشهای پرداخت، محاسبه پورسانت، شارژ کیف پول و فیچرهای مختص خود را دارد.
+
+```
+مثال پکیجها:
+┌──────────────┬──────────────┬──────────────┬──────────────┐
+│ 🥈 نقرهای │ 🥇 طلایی │ 💎 الماسی │ ⭐ ویژه │
+│ ۵.۶M تومان │ ۵۶M تومان │ ؟؟ تومان │ ؟؟ تومان │
+│ │ (پکیج پایه) │ │ │
+│ فقط مستقیم │ دایا+مستقیم │ فقط مستقیم │ فقط مستقیم │
+│ فیچر محدود │ همه فیچرها │ همه فیچرها │ همه+اختصاصی │
+└──────────────┴──────────────┴──────────────┴──────────────┘
+```
+
+---
+
+## ۲. وضعیت فعلی سیستم (AS-IS)
+
+### ۲.۱ فلوی فعلی فعالسازی
+
+```mermaid
+flowchart TD
+ A["کاربر وارد سیستم میشود"] --> B{"روش پرداخت"}
+ B -->|"خرید الماس دایا"| C["DayaLoan — ۵۶M"]
+ B -->|"پرداخت مستقیم"| D["درگاه بانکی — ۵۶M"]
+ C --> E["بررسی موفقیت پرداخت"]
+ D --> E
+ E --> F["شارژ کیف پول"]
+ F --> G["مدال قرارداد باشگاه مشتریان"]
+ G --> H["تایید OTP + امضا"]
+ H --> I["فعالسازی عضویت باشگاه"]
+ I --> J["اختصاص فیچرها"]
+ I --> K["ایجاد Cycle"]
+ I --> L["اضافه به Commission Pool"]
+ J --> M["✅ کاربر فعال — لینک معرف"]
+```
+
+### ۲.۲ جریان پول فعلی
+
+```
+کاربر ۵۶M پرداخت میکند
+ │
+ ├── Balance (کیف پول عادی) += ۵۶,۰۰۰,۰۰۰ ریال
+ ├── DiscountBalance (اعتباری) += ۱۱۲,۰۰۰,۰۰۰ ریال (×۲)
+ │
+ └── Club Activation:
+ ├── CommissionPool += ۲۵,۲۰۰,۰۰۰ ریال (ClubActivationFee)
+ └── GiftValue = ۲۵,۲۰۰,۰۰۰ ریال (اطلاعرسانی)
+```
+
+### ۲.۳ مقادیر Hardcoded فعلی (`SystemConstants.cs`)
+
+| ثابت | مقدار | کاربرد |
+|------|-------|--------|
+| `BasePackageAmount` | ۵۶,۰۰۰,۰۰۰ | قیمت پکیج |
+| `DayaLoanAmount` | ۵۶,۰۰۰,۰۰۰ | مبلغ وام دایا |
+| `ClubActivationFee` | ۲۵,۲۰۰,۰۰۰ | سهم هفتگی Commission Pool |
+| `ClubMembershipGiftValue` | ۲۵,۲۰۰,۰۰۰ | ارزش هدیه حق عضویت |
+| `MagicWalletMultiplier` | ×۲.۵ | ضریب کیف پول جادویی |
+
+### ۲.۴ مشکلات فعلی
+
+| # | مشکل | فایل |
+|---|------|------|
+| ۱ | پکیج ID=4 **hardcoded** در `InitiateBasePackagePaymentCommandHandler` | Application/Commands |
+| ۲ | مبلغ ۵۶M **hardcoded** در `SystemConstants` و چندین handler | Domain/Common |
+| ۳ | فیچرها **همه یکجا** assign میشن (۴ فیچر ثابت: چتیکا، بیمه، تریپ، لرن) | ActivateClubMembershipHandler |
+| ۴ | Commission Pool فقط با `ClubActivationFee` ثابت پر میشه | ActivateClubMembershipHandler |
+| ۵ | `DiscountBalance = Amount × 2` — ضریب hardcoded | VerifyPayment handlers |
+| ۶ | فرانتاند فقط یک مسیر خرید نشون میده | FrontOffice pages |
+
+---
+
+## ۳. طراحی پیشنهادی (TO-BE)
+
+### ۳.۱ فلوی جدید فعالسازی
+
+```mermaid
+flowchart TD
+ A["کاربر وارد سیستم"] --> B["صفحه پکیجها
(کاشیهای نقرهای/طلایی/الماسی/...)"]
+
+ B -->|"کلیک روی پکیج"| C{"نوع پکیج"}
+
+ C -->|"پکیج پایه (طلایی)"| D["مدال با دو گزینه:
۱. خرید الماس دایا
۲. پرداخت مستقیم"]
+ C -->|"پکیجهای دیگر"| E["مدال با یک گزینه:
فقط پرداخت مستقیم
+ توضیحات + قیمت"]
+
+ D -->|"دایا"| F["فلوی دایا"]
+ D -->|"مستقیم"| G["درگاه پرداخت"]
+ E --> G
+
+ F --> H["پرداخت موفق"]
+ G --> H
+
+ H --> I["شارژ کیف پول
(متناسب با قیمت پکیج)"]
+ I --> J["مدال قرارداد باشگاه"]
+ J --> K["OTP + امضا"]
+ K --> L["فعالسازی
+ اختصاص فیچرهای پکیج"]
+ L --> M["✅ کاربر فعال"]
+```
+
+### ۳.۲ تغییرات Entity — Package
+
+**فعلی:**
+```csharp
+public class Package : BaseAuditableEntity
+{
+ public string Title { get; set; }
+ public string Description { get; set; }
+ public string ImagePath { get; set; }
+ public long Price { get; set; }
+}
+```
+
+**پیشنهادی:**
+```csharp
+public class Package : BaseAuditableEntity
+{
+ public string Title { get; set; }
+ public string Description { get; set; }
+ public string ImagePath { get; set; }
+ public long Price { get; set; } // قیمت پکیج (ریال)
+
+ // === فیلدهای جدید ===
+ public int SortOrder { get; set; } // ترتیب نمایش
+ public bool IsActive { get; set; } = true; // فعال/غیرفعال
+ public bool IsBasePackage { get; set; } // آیا پکیج پایه است؟
+ public bool SupportsDayaPurchase { get; set; } // پشتیبانی از خرید دایا
+ public bool SupportsDirectPurchase { get; set; } = true; // پشتیبانی از پرداخت مستقیم
+
+ // === محاسبات مالی ===
+ public long ActivationFee { get; set; } // سهم Commission Pool
+ public long GiftValue { get; set; } // ارزش هدیه
+ public decimal DiscountMultiplier { get; set; } = 2.0m; // ضریب شارژ DiscountBalance
+
+ // === Navigation ===
+ public virtual ICollection PackageFeatures { get; set; }
+ public virtual ICollection Purchases { get; set; }
+}
+```
+
+### ۳.۳ Entity جدید — PackageFeature (پل بین پکیج و فیچر)
+
+```csharp
+///
+/// مشخص میکند هر پکیج چه فیچرهایی را فعال میکند
+///
+public class PackageFeature : BaseAuditableEntity
+{
+ public long PackageId { get; set; }
+ public virtual Package Package { get; set; }
+
+ public long ClubFeatureId { get; set; }
+ public virtual ClubFeature ClubFeature { get; set; }
+
+ public bool IsIncluded { get; set; } = true; // آیا این فیچر در پکیج هست؟
+}
+```
+
+### ۳.۴ تغییرات Entity — ClubMembership
+
+```csharp
+public class ClubMembership : BaseAuditableEntity
+{
+ // ... فیلدهای فعلی حفظ میشوند ...
+
+ // === فیلد جدید ===
+ public long PackageId { get; set; } // کدام پکیج خریداری شده
+ public virtual Package Package { get; set; }
+}
+```
+
+### ۳.۵ تغییرات Entity — ClubMembershipCycle
+
+```csharp
+public class ClubMembershipCycle : BaseAuditableEntity
+{
+ // ... فیلدهای فعلی حفظ میشوند ...
+
+ // === فیلد جدید ===
+ public long PackageId { get; set; } // پکیج این سایکل
+ public virtual Package Package { get; set; }
+ // PackageAmount قبلاً وجود دارد — از Package.Price پر میشود
+}
+```
+
+### ۳.۶ تغییرات Entity — WeeklyCommissionPool
+
+```csharp
+public class WeeklyCommissionPool : BaseAuditableEntity
+{
+ // ... فیلدهای فعلی حفظ میشوند ...
+
+ // === فیلد جدید ===
+ public long PackageId { get; set; } // Pool جداگانه برای هر پکیج
+ public virtual Package Package { get; set; }
+}
+```
+
+### ۳.۷ جریان پول جدید
+
+```
+پکیج نقرهای (۵.۶M):
+ ├── Balance += ۵,۶۰۰,۰۰۰
+ ├── DiscountBalance += ۱۱,۲۰۰,۰۰۰ (×۲)
+ └── CommissionPool += ActivationFee مخصوص نقرهای
+
+پکیج طلایی/پایه (۵۶M):
+ ├── Balance += ۵۶,۰۰۰,۰۰۰
+ ├── DiscountBalance += ۱۱۲,۰۰۰,۰۰۰ (×۲)
+ └── CommissionPool += ۲۵,۲۰۰,۰۰۰
+
+پکیج الماسی (??M):
+ ├── Balance += ??
+ ├── DiscountBalance += ?? (×۲)
+ └── CommissionPool += ActivationFee مخصوص الماسی
+```
+
+---
+
+## ۴. محاسبه پورسانت — تغییرات
+
+### ۴.۱ وضعیت فعلی
+
+```
+یک WeeklyCommissionPool برای کل هفته
+ ↓
+TotalAmount = مجموع ActivationFee همه فعالسازیها
+ ↓
+ValuePerBalance = TotalAmount ÷ مجموع Balanceها
+ ↓
+همه یکسان محاسبه میشوند
+```
+
+### ۴.۲ وضعیت هدف
+
+```
+برای هر پکیج، یک WeeklyCommissionPool جداگانه:
+
+Pool_نقرهای:
+ TotalAmount = مجموع ActivationFee خریداران نقرهای این هفته
+ Balanceها = فقط از شبکه خریداران نقرهای
+ ValuePerBalance = Pool_نقرهای ÷ Balance_نقرهای
+
+Pool_طلایی:
+ TotalAmount = مجموع ActivationFee خریداران طلایی این هفته
+ Balanceها = فقط از شبکه خریداران طلایی
+ ValuePerBalance = Pool_طلایی ÷ Balance_طلایی
+```
+
+### ۴.۳ نکته مهم: ساختار شبکه یکی است
+
+```
+ [Ali]
+ / \
+ [Sara] [Reza] ← شبکه باینری یکیست
+ / \ / \
+ [M1] [M2] [M3] [M4]
+
+ولی محاسبات جدا:
+ - Ali با پکیج طلایی → پورسانت از Pool طلایی
+ - Sara با پکیج نقرهای → پورسانت از Pool نقرهای
+ - Reza با پکیج طلایی → پورسانت از Pool طلایی
+```
+
+### ۴.۴ تغییرات Stored Procedure
+
+**`sp_CalculateWeeklyBalances`** باید:
+- پارامتر `@PackageId` بگیرد
+- فقط کاربرانی که این پکیج را خریدهاند فیلتر کند
+- برای هر پکیج جداگانه اجرا شود
+
+**`sp_CalculateWeeklyCommissionPool`** باید:
+- پارامتر `@PackageId` بگیرد
+- Pool مخصوص آن پکیج را بخواند
+- پرداختها فقط به خریداران آن پکیج اختصاص یابد
+
+---
+
+## ۵. فیچرهای باشگاه مشتریان بر اساس پکیج
+
+### ۵.۱ وضعیت فعلی
+
+وقتی کاربر فعال میشود، **همه ۴ فیچر** یکجا assign میشوند:
+```csharp
+// ActivateClubMembershipCommandHandler — خط ~350
+var allFeatureIds = ClubFeatureTypeExtensions.GetAllFeatureIds();
+foreach (var featureId in allFeatureIds)
+{
+ userClubFeatures.Add(new UserClubFeature { ... });
+}
+```
+
+### ۵.۲ وضعیت هدف
+
+فیچرها بر اساس جدول `PackageFeature` تعیین میشوند:
+
+| فیچر | نقرهای | طلایی (پایه) | الماسی |
+|------|---------|-------------|--------|
+| چتیکا | ❌ | ✅ | ✅ |
+| بیمه | ❌ | ✅ | ✅ |
+| تریپ | ✅ | ✅ | ✅ |
+| لرن | ✅ | ✅ | ✅ |
+| فیچر VIP | ❌ | ❌ | ✅ |
+
+*مقادیر بالا نمونهای هستند — قابل تنظیم از BackOffice*
+
+### ۵.۳ تغییر در ActivateClubMembershipHandler
+
+```
+قبلی:
+ GetAllFeatureIds() → assign all
+
+جدید:
+ Package.PackageFeatures
+ .Where(pf => pf.IsIncluded)
+ .Select(pf => pf.ClubFeatureId)
+ → assign only included features
+```
+
+---
+
+## ۶. تغییرات UI — FrontOffice
+
+### ۶.۱ صفحه پکیجها (کاشیها)
+
+```
+┌─────────────────────────────────────────────────────┐
+│ انتخاب پکیج باشگاه مشتریان │
+├─────────────┬──────────────┬──────────────┬─────────┤
+│ │ │ │ │
+│ 🥈 نقرهای │ 🥇 طلایی │ 💎 الماسی │ ⭐ ویژه │
+│ ۵.۶M │ ۵۶M │ ؟؟M │ ؟؟M │
+│ │ │ │ │
+│ ● لرن │ ● چتیکا │ ● همه │ ● همه │
+│ ● تریپ │ ● بیمه │ ● + VIP │ ● +... │
+│ │ ● تریپ │ │ │
+│ │ ● لرن │ │ │
+│ │ │ │ │
+│ [انتخاب] │ [انتخاب] │ [انتخاب] │[انتخاب]│
+└─────────────┴──────────────┴──────────────┴─────────┘
+```
+
+### ۶.۲ مدال پرداخت — پکیج پایه (طلایی)
+
+```
+┌─────────────────────────────────────────┐
+│ خرید پکیج طلایی — ۵۶M تومان │
+│ │
+│ توضیحات: ... │
+│ │
+│ روشهای پرداخت: │
+│ ┌─────────────────────────────────┐ │
+│ │ 💎 خرید از طریق الماس دایا │ │
+│ └─────────────────────────────────┘ │
+│ ┌─────────────────────────────────┐ │
+│ │ 💳 پرداخت مستقیم (درگاه بانکی) │ │
+│ └─────────────────────────────────┘ │
+└─────────────────────────────────────────┘
+```
+
+### ۶.۳ مدال پرداخت — پکیجهای دیگر (نقرهای و بالاتر)
+
+```
+┌─────────────────────────────────────────┐
+│ خرید پکیج نقرهای — ۵.۶M تومان │
+│ │
+│ توضیحات: ... │
+│ ویژگیها: لرن، تریپ │
+│ │
+│ ┌─────────────────────────────────┐ │
+│ │ 💳 پرداخت و فعالسازی │ │
+│ └─────────────────────────────────┘ │
+└─────────────────────────────────────────┘
+```
+
+---
+
+## ۷. بخشهای تحت تاثیر (Impact Analysis)
+
+### ۷.۱ جدول تاثیرپذیری
+
+| # | لایه | فایل/بخش | نوع تغییر | شدت |
+|---|------|----------|-----------|-----|
+| ۱ | **Domain** | `Package.cs` | اضافه فیلد | 🟡 متوسط |
+| ۲ | **Domain** | `PackageFeature.cs` — **جدید** | Entity جدید | 🔴 زیاد |
+| ۳ | **Domain** | `ClubMembership.cs` | اضافه `PackageId` | 🟡 متوسط |
+| ۴ | **Domain** | `ClubMembershipCycle.cs` | اضافه `PackageId` | 🟡 متوسط |
+| ۵ | **Domain** | `WeeklyCommissionPool.cs` | اضافه `PackageId` | 🔴 زیاد |
+| ۶ | **Domain** | `SystemConstants.cs` | حذف hardcodeها → خوانش از Package | 🟡 متوسط |
+| ۷ | **Application** | `ActivateClubMembershipCommandHandler` | فیچر بر اساس پکیج | 🔴 زیاد |
+| ۸ | **Application** | `InitiateBasePackagePaymentCommandHandler` | حذف ID=4 hardcoded | 🟡 متوسط |
+| ۹ | **Application** | `VerifyBasePackagePaymentCommandHandler` | شارژ متناسب با پکیج | 🔴 زیاد |
+| ۱۰ | **Application** | `VerifyPackagePurchasePaymentCommandHandler` | شارژ متناسب با پکیج | 🔴 زیاد |
+| ۱۱ | **Application** | `ManualPaymentCommandHandler` | شارژ متناسب با پکیج | 🟡 متوسط |
+| ۱۲ | **Application** | `CustomerPurchasePackageCommandHandler` | پشتیبانی روشهای پرداخت پکیج | 🟡 متوسط |
+| ۱۳ | **Infra** | `sp_CalculateWeeklyBalances` | پارامتر PackageId | 🔴 زیاد |
+| ۱۴ | **Infra** | `sp_CalculateWeeklyCommissionPool` | Pool جداگانه هر پکیج | 🔴 زیاد |
+| ۱۵ | **Infra** | `WeeklyCommissionCalculationService` | Loop روی پکیجها | 🟡 متوسط |
+| ۱۶ | **Infra** | EF Configurations | جدول جدید + FKها | 🟡 متوسط |
+| ۱۷ | **Infra** | Database Migration | schema changes | 🟡 متوسط |
+| ۱۸ | **Proto** | `package.proto` | فیلدهای جدید پکیج | 🟢 کم |
+| ۱۹ | **Proto** | `clubmembership.proto` | PackageId در response | 🟢 کم |
+| ۲۰ | **Proto** | `commission.proto` | PackageId در pool/payout | 🟢 کم |
+| ۲۱ | **FrontOffice** | صفحه انتخاب پکیج | UI جدید (کاشیها) | 🔴 زیاد |
+| ۲۲ | **FrontOffice** | مدال پرداخت | دو مدال متفاوت | 🔴 زیاد |
+| ۲۳ | **FrontOffice** | `MyPackages.razor` | نمایش نوع پکیج | 🟡 متوسط |
+| ۲۴ | **FrontOffice** | `ActivateClubDialog.razor` | ارتباط با پکیج | 🟡 متوسط |
+| ۲۵ | **BackOffice** | صفحه مدیریت پکیجها | CRUD فیلدهای جدید | 🟡 متوسط |
+| ۲۶ | **BackOffice** | صفحه فیچر پکیجها — **جدید** | ماتریس پکیج×فیچر | 🔴 زیاد |
+| ۲۷ | **BackOffice** | `ActivateClubDialog.razor` | انتخاب پکیج | 🟡 متوسط |
+
+### ۷.۲ ریسکها
+
+| ریسک | احتمال | شدت | راهحل |
+|------|--------|-----|--------|
+| دادههای فعلی — کاربران بدون PackageId | قطعی | زیاد | Migration: کاربران فعلی → PackageId = پکیج پایه |
+| Commission Pool فعلی بدون PackageId | قطعی | زیاد | Migration: Poolهای موجود → PackageId = پکیج پایه |
+| SP تغییر → محاسبات اشتباه | متوسط | بحرانی | تست جامع + محیط staging |
+| مبالغ hardcoded در جاهای پراکنده | زیاد | متوسط | Audit کامل کدبیس |
+| عدم سازگاری FrontOffice/BackOffice | متوسط | متوسط | تست end-to-end |
+
+---
+
+## ۸. فازبندی پیادهسازی
+
+### فاز ۱ — زیرساخت (Domain + DB) ≈ ۳-۴ روز
+
+| تسک | شرح |
+|-----|------|
+| T1.1 | بروزرسانی `Package` entity (فیلدهای جدید) |
+| T1.2 | ایجاد `PackageFeature` entity + EF Configuration |
+| T1.3 | اضافه کردن `PackageId` به `ClubMembership` |
+| T1.4 | اضافه کردن `PackageId` به `ClubMembershipCycle` |
+| T1.5 | اضافه کردن `PackageId` به `WeeklyCommissionPool` |
+| T1.6 | Database Migration + Seed data (پکیج پایه + فیچرها) |
+| T1.7 | Migration: کاربران/Poolهای فعلی → PackageId = پکیج پایه |
+| T1.8 | بروزرسانی Protoها |
+
+### فاز ۲ — منطق کسبوکار (Application) ≈ ۴-۵ روز
+
+| تسک | شرح |
+|-----|------|
+| T2.1 | بروزرسانی `ActivateClubMembershipCommandHandler` — فیچر بر اساس پکیج |
+| T2.2 | بروزرسانی Verify handlers — شارژ کیف پول متناسب با پکیج |
+| T2.3 | حذف مقادیر hardcoded از `SystemConstants` → خوانش از Package |
+| T2.4 | بروزرسانی `InitiateBasePackagePayment` → Generic `InitiatePackagePayment` |
+| T2.5 | بروزرسانی `ManualPaymentCommandHandler` — پشتیبانی پکیج متغیر |
+| T2.6 | CRUD پکیج با فیلدهای جدید (gRPC handlers) |
+| T2.7 | CRUD `PackageFeature` (ماتریس پکیج×فیچر) |
+
+### فاز ۳ — محاسبه پورسانت ≈ ۳-۴ روز
+
+| تسک | شرح |
+|-----|------|
+| T3.1 | بروزرسانی `sp_CalculateWeeklyBalances` — فیلتر بر اساس PackageId |
+| T3.2 | بروزرسانی `sp_CalculateWeeklyCommissionPool` — Pool جداگانه |
+| T3.3 | بروزرسانی `WeeklyCommissionCalculationService` — Loop روی پکیجها |
+| T3.4 | تست محاسبات با داده واقعی |
+
+### فاز ۴ — UI (FrontOffice + BackOffice) ≈ ۴-۵ روز
+
+| تسک | شرح |
+|-----|------|
+| T4.1 | صفحه کاشیهای پکیج (FrontOffice) |
+| T4.2 | مدال پرداخت پکیج پایه (دایا + مستقیم) |
+| T4.3 | مدال پرداخت پکیجهای دیگر (فقط مستقیم) |
+| T4.4 | بروزرسانی `MyPackages.razor` — نمایش نوع پکیج |
+| T4.5 | بروزرسانی `ActivateClubDialog.razor` — ارتباط با پکیج |
+| T4.6 | BackOffice: CRUD پکیج با فیلدهای جدید |
+| T4.7 | BackOffice: صفحه ماتریس فیچرهای پکیج |
+| T4.8 | BackOffice: `ActivateClubDialog` — انتخاب پکیج |
+
+### فاز ۵ — تست و استقرار ≈ ۲-۳ روز
+
+| تسک | شرح |
+|-----|------|
+| T5.1 | تست end-to-end فلوی خرید هر پکیج |
+| T5.2 | تست محاسبه پورسانت جداگانه |
+| T5.3 | تست migration دادههای فعلی |
+| T5.4 | Deploy به staging + تست |
+| T5.5 | Deploy به production |
+
+---
+
+## ۹. Seed Data — پکیجهای اولیه
+
+```sql
+-- Migration: Seed packages
+INSERT INTO Packages (Title, Description, Price, IsActive, IsBasePackage,
+ SupportsDayaPurchase, SupportsDirectPurchase, ActivationFee, GiftValue,
+ DiscountMultiplier, SortOrder)
+VALUES
+ ('نقرهای', 'پکیج نقرهای باشگاه مشتریان', 5600000, 1, 0,
+ 0, 1, ???, ???, 2.0, 1),
+ ('طلایی', 'پکیج طلایی باشگاه مشتریان (پایه)', 56000000, 1, 1,
+ 1, 1, 25200000, 25200000, 2.0, 2);
+
+-- Migration: ربط فیچرها به پکیجها
+INSERT INTO PackageFeatures (PackageId, ClubFeatureId, IsIncluded) VALUES
+ -- نقرهای: فقط تریپ و لرن
+ (@silverId, @tripId, 1),
+ (@silverId, @learnId, 1),
+ -- طلایی: همه فیچرها
+ (@goldId, @chatikaId, 1),
+ (@goldId, @bimeId, 1),
+ (@goldId, @tripId, 1),
+ (@goldId, @learnId, 1);
+
+-- Migration: کاربران فعلی → پکیج پایه
+UPDATE ClubMemberships SET PackageId = @goldId WHERE PackageId IS NULL;
+UPDATE ClubMembershipCycles SET PackageId = @goldId WHERE PackageId IS NULL;
+UPDATE WeeklyCommissionPools SET PackageId = @goldId WHERE PackageId IS NULL;
+```
+
+---
+
+## ۱۰. سوالات باز (نیاز به تصمیمگیری)
+
+| # | سوال | گزینهها |
+|---|------|---------|
+| ۱ | `ActivationFee` و `GiftValue` پکیج نقرهای چقدر باشد؟ | نسبت به قیمت؟ مقدار ثابت؟ |
+| ۲ | آیا کاربر میتواند بعداً پکیج خود را ارتقا دهد (upgrade)؟ | بله → فقط مابهالتفاوت / خیر |
+| ۳ | `DiscountMultiplier` برای همه پکیجها ×۲ باشد؟ | یکسان / متفاوت به ازای هر پکیج |
+| ۴ | ضریب `MagicWallet` (×۲.۵) برای پکیجهای کوچکتر هم همان باشد؟ | بله / خیر |
+| ۵ | فیچرهای پکیج نقرهای دقیقاً کدامها هستند؟ | لرن+تریپ؟ فقط لرن؟ |
+| ۶ | آیا یک کاربر میتواند چند پکیج همزمان داشته باشد؟ | فقط یکی / امکان خرید چندتا |
+| ۷ | نام و تعداد دقیق پکیجها چیست؟ | نقرهای+طلایی؟ بیشتر؟ |
+| ۸ | کاربرانی که با دایا فعال شدن، چه پکیجی دارند؟ | طلایی (پایه) |
+
+---
+
+## ۱۱. تخمین زمانی
+
+| فاز | مدت | وابستگی |
+|-----|------|---------|
+| فاز ۱ — زیرساخت | ۳-۴ روز | — |
+| فاز ۲ — منطق | ۴-۵ روز | فاز ۱ |
+| فاز ۳ — پورسانت | ۳-۴ روز | فاز ۱ |
+| فاز ۴ — UI | ۴-۵ روز | فاز ۲ |
+| فاز ۵ — تست | ۲-۳ روز | فاز ۳, ۴ |
+| **مجموع** | **~۱۶-۲۱ روز کاری** | |
+
+> فازهای ۲ و ۳ قابل موازیسازی هستند.
diff --git a/business/BIZ-PACKAGE-BASED-SYSTEM.md b/business/BIZ-PACKAGE-BASED-SYSTEM.md
index 549dd4a..e199faa 100644
--- a/business/BIZ-PACKAGE-BASED-SYSTEM.md
+++ b/business/BIZ-PACKAGE-BASED-SYSTEM.md
@@ -1,131 +1,120 @@
# 📦 سیستم مبتنی بر پکیج (Package-Based System)
-> **وضعیت:** تحلیل و بررسی — منتظر تایید
-> **تاریخ:** اسفند ۱۴۰۴
-> **تاثیرگذاری:** زیاد — بخشهای متعدد سیستم تحت تاثیر قرار میگیرد
+> **وضعیت:** تاییدشده — آماده پیادهسازی
+> **تاریخ بروزرسانی:** ۶ اسفند ۱۴۰۴
+> **نسخه:** v2 (بازنویسی کامل بعد از تحلیل عمیق کدبیس)
+> **تاثیرگذاری:** زیاد — ۳۹ فایل در ۶ لایه
---
## ۱. خلاصه فیچر
-**وضعیت فعلی:** سیستم فقط یک پکیج پایه (۵۶ میلیون تومان) دارد و همه چیز حول آن میچرخد.
+**وضعیت فعلی:** سیستم فقط یک پکیج پایه (۵۶ میلیون تومان) دارد و همه چیز حول آن hardcode شده.
-**وضعیت هدف:** سیستم چندین پکیج با قیمتها و ویژگیهای متفاوت پشتیبانی میکند. هر پکیج روشهای پرداخت، محاسبه پورسانت، شارژ کیف پول و فیچرهای مختص خود را دارد.
-
-```
-مثال پکیجها:
-┌──────────────┬──────────────┬──────────────┬──────────────┐
-│ 🥈 نقرهای │ 🥇 طلایی │ 💎 الماسی │ ⭐ ویژه │
-│ ۵.۶M تومان │ ۵۶M تومان │ ؟؟ تومان │ ؟؟ تومان │
-│ │ (پکیج پایه) │ │ │
-│ فقط مستقیم │ دایا+مستقیم │ فقط مستقیم │ فقط مستقیم │
-│ فیچر محدود │ همه فیچرها │ همه فیچرها │ همه+اختصاصی │
-└──────────────┴──────────────┴──────────────┴──────────────┘
-```
+**وضعیت هدف:** سیستم چندین پکیج با قیمتها و ویژگیهای متفاوت پشتیبانی میکند. هر پکیج مقادیر مالی، فیچرها و Commission Pool مستقل خود را دارد. کاربر میتواند **N بار** پکیج بخرد (بعد از تکمیل چرخه Magic Wallet).
---
-## ۲. وضعیت فعلی سیستم (AS-IS)
+## ۲. تصمیمات تاییدشده
-### ۲.۱ فلوی فعلی فعالسازی
-
-```mermaid
-flowchart TD
- A["کاربر وارد سیستم میشود"] --> B{"روش پرداخت"}
- B -->|"خرید الماس دایا"| C["DayaLoan — ۵۶M"]
- B -->|"پرداخت مستقیم"| D["درگاه بانکی — ۵۶M"]
- C --> E["بررسی موفقیت پرداخت"]
- D --> E
- E --> F["شارژ کیف پول"]
- F --> G["مدال قرارداد باشگاه مشتریان"]
- G --> H["تایید OTP + امضا"]
- H --> I["فعالسازی عضویت باشگاه"]
- I --> J["اختصاص فیچرها"]
- I --> K["ایجاد Cycle"]
- I --> L["اضافه به Commission Pool"]
- J --> M["✅ کاربر فعال — لینک معرف"]
-```
-
-### ۲.۲ جریان پول فعلی
-
-```
-کاربر ۵۶M پرداخت میکند
- │
- ├── Balance (کیف پول عادی) += ۵۶,۰۰۰,۰۰۰ ریال
- ├── DiscountBalance (اعتباری) += ۱۱۲,۰۰۰,۰۰۰ ریال (×۲)
- │
- └── Club Activation:
- ├── CommissionPool += ۲۵,۲۰۰,۰۰۰ ریال (ClubActivationFee)
- └── GiftValue = ۲۵,۲۰۰,۰۰۰ ریال (اطلاعرسانی)
-```
-
-### ۲.۳ مقادیر Hardcoded فعلی (`SystemConstants.cs`)
-
-| ثابت | مقدار | کاربرد |
-|------|-------|--------|
-| `BasePackageAmount` | ۵۶,۰۰۰,۰۰۰ | قیمت پکیج |
-| `DayaLoanAmount` | ۵۶,۰۰۰,۰۰۰ | مبلغ وام دایا |
-| `ClubActivationFee` | ۲۵,۲۰۰,۰۰۰ | سهم هفتگی Commission Pool |
-| `ClubMembershipGiftValue` | ۲۵,۲۰۰,۰۰۰ | ارزش هدیه حق عضویت |
-| `MagicWalletMultiplier` | ×۲.۵ | ضریب کیف پول جادویی |
-
-### ۲.۴ مشکلات فعلی
-
-| # | مشکل | فایل |
-|---|------|------|
-| ۱ | پکیج ID=4 **hardcoded** در `InitiateBasePackagePaymentCommandHandler` | Application/Commands |
-| ۲ | مبلغ ۵۶M **hardcoded** در `SystemConstants` و چندین handler | Domain/Common |
-| ۳ | فیچرها **همه یکجا** assign میشن (۴ فیچر ثابت: چتیکا، بیمه، تریپ، لرن) | ActivateClubMembershipHandler |
-| ۴ | Commission Pool فقط با `ClubActivationFee` ثابت پر میشه | ActivateClubMembershipHandler |
-| ۵ | `DiscountBalance = Amount × 2` — ضریب hardcoded | VerifyPayment handlers |
-| ۶ | فرانتاند فقط یک مسیر خرید نشون میده | FrontOffice pages |
+| # | سوال | تصمیم |
+|---|------|-------|
+| Q1 | باگ DiscountBalance در VerifyGoldenPackagePurchase | ✅ **باگه — باید فیکس بشه** |
+| Q2 | سه مسیر پرداخت موازی | ✅ **ادغام به سرویس Generic** — نه به نام پکیج خاص |
+| Q3 | پکیجهای اولیه | ✅ **نقرهای (۵.۶M) + پایه (۵۶M)** — سیستم داینامیک |
+| Q4 | ActivationFee و GiftValue | ✅ **یک فیلد (ActivationFee)** — GiftValue حذف (تکراری بود) |
+| Q5 | DiscountMultiplier | ✅ **×2 برای همه** — ولی داینامیک در entity |
+| Q6 | Migration کاربران فعلی | ✅ **Pipeline/Script** — کاربران فعلی → PackageId = پکیج پایه |
+| Q7 | خرید چند پکیج | ✅ **N بار** — بعد تکمیل Magic Wallet و صفر شدن Balance |
+| Q8 | Commission Pool | ✅ **جدا برای هر پکیج** — تمام جداول Commission با PackageId |
+| Q9 | MagicWallet Multiplier | ✅ **داینامیک** بهازای هر پکیج (فعلاً همه ×2.5) |
+| Q10 | کاربران دایا | ✅ **پکیج پایه** گرفتن — "طلایی" اشتباه نامگذاری بوده |
+| Q11 | فیچرها | ✅ **داینامیک** — ادمین مدیریت میکند |
---
-## ۳. طراحی پیشنهادی (TO-BE)
+## ۳. تحلیل عمیق وضعیت فعلی (AS-IS)
-### ۳.۱ فلوی جدید فعالسازی
+### ۳.۱ باگهای کشفشده
-```mermaid
-flowchart TD
- A["کاربر وارد سیستم"] --> B["صفحه پکیجها
(کاشیهای نقرهای/طلایی/الماسی/...)"]
+| # | باگ | شدت | فایل |
+|---|-----|------|------|
+| **B1** | `VerifyGoldenPackagePurchase` → **DiscountBalance شارژ نمیشود** | 🔴 بحرانی | VerifyGoldenPackagePurchaseCommandHandler.cs |
+| **B2** | `VerifyGoldenPackagePurchase` → **UserPackagePurchase ساخته نمیشود** | 🔴 بحرانی | VerifyGoldenPackagePurchaseCommandHandler.cs |
+| **B3** | `VerifyPackagePurchase` → **UserPackagePurchase ساخته نمیشود** | 🔴 بحرانی | VerifyPackagePurchaseCommandHandler.cs |
+| **B4** | `VerifyBasePackagePayment` → **UserPackagePurchase ساخته نمیشود** | 🔴 بحرانی | VerifyBasePackagePaymentCommandHandler.cs |
+| **B5** | `PackageService.CustomerPurchasePackage` → **guard برای خرید تکراری ندارد** | 🟡 متوسط | PackageService.cs |
+| **B6** | EXIT Magic Mode → **PackagePurchaseMethod ریست نمیشود** (خرید مجدد مسدود) | 🔴 بحرانی | UserOrderService.cs |
+
+### ۳.۲ ناسازگاری مسیرهای پرداخت
+
+| مسیر | Balance | Discount | UserPackagePurchase | WalletChangeLog |
+|------|---------|----------|---------------------|-----------------|
+| **BFF/PYMS** (InitiateBase→VerifyBase) | ✅ | ✅ | ❌ | ✅ |
+| **ZarinPal Golden** (PurchaseGolden→VerifyGolden) | ✅ | ❌ | ❌ | ✅ (فقط Balance) |
+| **ZarinPal Generic** (Purchase→VerifyPurchase) | ✅ | ✅ | ❌ | ✅ |
+| **Daya Loan** (CheckAndProcess) | ✅ | ✅ | ✅ | ✅ |
+| **Manual** (CreateManualPayment) | ✅ | ✅ | ❌ | ✅ |
+
+> **فقط Daya Loan** همه مراحل را کامل انجام میدهد. بقیه مسیرها ناقص هستند.
+
+### ۳.۳ مقادیر Hardcoded (۱۵ مورد)
+
+| # | مکان | مقدار | باید بشه |
+|---|------|-------|----------|
+| H1 | `InitiateBasePackagePaymentCommandHandler` | `BasePackageId = 4` | خوانش از پکیج فعال |
+| H2 | `CheckAndProcessDayaLoansCommandHandler` | `p.Id == 4` | خوانش از پکیج پایه |
+| H3 | `SystemConstants.BasePackageAmount` | `56_000_000` | `Package.Price` |
+| H4 | `SystemConstants.DayaLoanAmount` | `56_000_000` | `Package.Price` |
+| H5 | `SystemConstants.ClubActivationFee` | `25_200_000` | `Package.ActivationFee` |
+| H6 | `SystemConstants.ClubMembershipGiftValue` | `25_200_000` | حذف (= ActivationFee) |
+| H7 | `VerifyPackagePurchaseCommandHandler` | `Amount * 2` (×3 جا) | `Package.DiscountMultiplier` |
+| H8 | `VerifyBasePackagePaymentCommandHandler` | `BasePackageAmount * 2` (×2) | `Package.DiscountMultiplier` |
+| H9 | `CreateManualPaymentCommandHandler` | `BasePackageAmount * 2` | `Package.DiscountMultiplier` |
+| H10 | `CheckAndProcessDayaLoansCommandHandler` | `DayaLoanAmount * 2` | `Package.DiscountMultiplier` |
+| H11 | `ActivateClubMembershipCommandHandler` | `PackageAmount = BasePackageAmount` | `Package.Price` |
+| H12 | `PurchaseGoldenPackageCommandHandler` | `Title.Contains("طلایی")` | حذف — Generic |
+| H13 | `ActivateClubMembershipCommandHandler` | `GetAllFeatureIds()` | `Package.PackageFeatures` |
+| H14 | `ActivationSection.razor` | `56_000_000 × months` | از پکیج خوانده شود |
+| H15 | `ClubMembershipContractDialog.razor` | متن قرارداد ۵۶M | داینامیک از پکیج |
+
+### ۳.۴ Guardهای مسدودکننده خرید مجدد
+
+| # | فایل | Guard | وضعیت | تغییر |
+|---|------|-------|-------|-------|
+| G1 | `PurchaseGoldenPackageCommandHandler` | `PackagePurchaseMethod != None` → throw | مسدود | ✅ اجازه بعد تکمیل چرخه |
+| G2 | `InitiateBasePackagePaymentCommandHandler` | `PackagePurchaseMethod != None` → fail | مسدود | ✅ اجازه بعد تکمیل چرخه |
+| G3 | `PurchasePackageCommandHandler` | `PackagePurchaseMethod != None` → throw | مسدود | ✅ اجازه بعد تکمیل چرخه |
+| G4 | `CheckAndProcessDayaLoansCommandHandler` | `hasPreviousCycle` → skip | عمدی ✅ | ❌ حفظ (دایا فقط دور اول) |
+| G5 | `AcceptClubMembershipContractCommandHandler` | `IsActive == true` → fail | مسدود | ✅ اجازه re-contract |
+| G6 | `ActivateClubMembershipCommandHandler` | `IsActive == true` → return true | short-circuit | ✅ باید چرخه جدید بسازه |
+| G7 | JWT Claim `HasPurchasedPackage` | permanent true | UI مسدود | ✅ اضافه `CanRepurchase` |
+
+### ۳.۵ Root Cause — خرید مجدد کار نمیکند
+
+```
+EXIT Magic Mode (UserOrderService.cs):
+ ✅ wallet.WalletMode = Normal
+ ✅ wallet.MagicCompletedAt = now
+ ✅ cycle.MagicCompletedAt = now
- B -->|"کلیک روی پکیج"| C{"نوع پکیج"}
-
- C -->|"پکیج پایه (طلایی)"| D["مدال با دو گزینه:
۱. خرید الماس دایا
۲. پرداخت مستقیم"]
- C -->|"پکیجهای دیگر"| E["مدال با یک گزینه:
فقط پرداخت مستقیم
+ توضیحات + قیمت"]
-
- D -->|"دایا"| F["فلوی دایا"]
- D -->|"مستقیم"| G["درگاه پرداخت"]
- E --> G
-
- F --> H["پرداخت موفق"]
- G --> H
-
- H --> I["شارژ کیف پول
(متناسب با قیمت پکیج)"]
- I --> J["مدال قرارداد باشگاه"]
- J --> K["OTP + امضا"]
- K --> L["فعالسازی
+ اختصاص فیچرهای پکیج"]
- L --> M["✅ کاربر فعال"]
+ ❌ MISSING: user.PackagePurchaseMethod = None ← Guards G1-G3 مسدود میمانند
+ ❌ MISSING: membership.IsActive = false ← Guards G5-G6 مسدود میمانند
+ ❌ MISSING: JWT CanRepurchase = true ← UI دکمه خرید نشان نمیدهد
```
-### ۳.۲ تغییرات Entity — Package
+**راهحل:** در EXIT Magic Mode، وضعیت کاربر ریست شود تا بتواند پکیج جدید بخرد.
+
+---
+
+## ۴. طراحی نهایی (TO-BE)
+
+### ۴.۱ تغییرات Entity — Package
-**فعلی:**
-```csharp
-public class Package : BaseAuditableEntity
-{
- public string Title { get; set; }
- public string Description { get; set; }
- public string ImagePath { get; set; }
- public long Price { get; set; }
-}
-```
-
-**پیشنهادی:**
```csharp
public class Package : BaseAuditableEntity
{
+ // === فیلدهای فعلی (حفظ) ===
public string Title { get; set; }
public string Description { get; set; }
public string ImagePath { get; set; }
@@ -134,27 +123,30 @@ public class Package : BaseAuditableEntity
// === فیلدهای جدید ===
public int SortOrder { get; set; } // ترتیب نمایش
public bool IsActive { get; set; } = true; // فعال/غیرفعال
- public bool IsBasePackage { get; set; } // آیا پکیج پایه است؟
+ public bool IsBasePackage { get; set; } // پکیج پایه؟ (فقط یکی true)
public bool SupportsDayaPurchase { get; set; } // پشتیبانی از خرید دایا
public bool SupportsDirectPurchase { get; set; } = true; // پشتیبانی از پرداخت مستقیم
// === محاسبات مالی ===
public long ActivationFee { get; set; } // سهم Commission Pool
- public long GiftValue { get; set; } // ارزش هدیه
public decimal DiscountMultiplier { get; set; } = 2.0m; // ضریب شارژ DiscountBalance
+ public decimal MagicWalletMultiplier { get; set; } = 2.5m; // ضریب کیفپول جادویی
// === Navigation ===
public virtual ICollection PackageFeatures { get; set; }
public virtual ICollection Purchases { get; set; }
+ public virtual ICollection UserOrders { get; set; }
}
```
-### ۳.۳ Entity جدید — PackageFeature (پل بین پکیج و فیچر)
+**نسبت به v1:**
+- ❌ `GiftValue` حذف (= ActivationFee — تکراری)
+- ✅ `MagicWalletMultiplier` اضافه (داینامیک)
+- ❌ `PackageType` enum نیاز نیست (`IsBasePackage` کافیست)
+
+### ۴.۲ Entity جدید — PackageFeature
```csharp
-///
-/// مشخص میکند هر پکیج چه فیچرهایی را فعال میکند
-///
public class PackageFeature : BaseAuditableEntity
{
public long PackageId { get; set; }
@@ -163,392 +155,429 @@ public class PackageFeature : BaseAuditableEntity
public long ClubFeatureId { get; set; }
public virtual ClubFeature ClubFeature { get; set; }
- public bool IsIncluded { get; set; } = true; // آیا این فیچر در پکیج هست؟
+ public bool IsIncluded { get; set; } = true;
}
```
-### ۳.۴ تغییرات Entity — ClubMembership
+### ۴.۳ تغییرات Entity — سایر
-```csharp
-public class ClubMembership : BaseAuditableEntity
-{
- // ... فیلدهای فعلی حفظ میشوند ...
+| Entity | فیلد جدید | توضیح |
+|--------|-----------|-------|
+| `ClubMembership` | `long? PackageId` + FK | آخرین پکیج خریداریشده |
+| `ClubMembershipCycle` | `long PackageId` + FK | پکیج این چرخه |
+| `WeeklyCommissionPool` | `long PackageId` + FK | Pool جداگانه هر پکیج |
+| `UserCommissionPayout` | `long PackageId` + FK | از کدام Pool |
+
+**Constraint جدید:** `WeeklyCommissionPool` → Unique(`WeekDefinitionId`, `PackageId`)
+
+### ۴.۴ حذف/تغییر SystemConstants
+
+| ثابت | تغییر | جایگزین |
+|------|-------|---------|
+| `ClubMembershipGiftValue` | ❌ حذف | تکراری بود |
+| `ClubActivationFee` | ❌ حذف | `Package.ActivationFee` |
+| `BasePackageAmount` | ❌ حذف | `Package.Price` |
+| `DayaLoanAmount` | ❌ حذف | `Package.Price` (base) |
+| `MagicWalletMultiplier` | ❌ حذف | `Package.MagicWalletMultiplier` |
+| `CommissionMaxWeeklyBalancesPerLeg` | ✅ حفظ | عمومی |
+| `CommissionMaxNetworkLevel` | ✅ حفظ | عمومی |
+| `ShopVAT` | ✅ حفظ | عمومی |
+
+### ۴.۵ فرمول مالی
+
+```
+ActivationFee = Price × 0.45
+
+پکیج نقرهای (۵,۶۰۰,۰۰۰ ریال):
+ ├── Balance += ۵,۶۰۰,۰۰۰ (Price)
+ ├── DiscountBalance += ۱۱,۲۰۰,۰۰۰ (Price × DiscountMultiplier)
+ └── CommissionPool += ۲,۵۲۰,۰۰۰ (ActivationFee)
+
+پکیج پایه (۵۶,۰۰۰,۰۰۰ ریال):
+ ├── Balance += ۵۶,۰۰۰,۰۰۰ (Price)
+ ├── DiscountBalance += ۱۱۲,۰۰۰,۰۰۰ (Price × DiscountMultiplier)
+ └── CommissionPool += ۲۵,۲۰۰,۰۰۰ (ActivationFee)
+```
+
+---
+
+## ۵. فلوی خرید مجدد (Re-Purchase)
+
+### ۵.۱ چرخه حیات کامل
+
+```mermaid
+stateDiagram-v2
+ [*] --> NoPurchase: کاربر ثبتنام کرده
- // === فیلد جدید ===
- public long PackageId { get; set; } // کدام پکیج خریداری شده
- public virtual Package Package { get; set; }
-}
-```
-
-### ۳.۵ تغییرات Entity — ClubMembershipCycle
-
-```csharp
-public class ClubMembershipCycle : BaseAuditableEntity
-{
- // ... فیلدهای فعلی حفظ میشوند ...
+ NoPurchase --> PackagePurchased: خرید پکیج\n(هر پکیجی)
- // === فیلد جدید ===
- public long PackageId { get; set; } // پکیج این سایکل
- public virtual Package Package { get; set; }
- // PackageAmount قبلاً وجود دارد — از Package.Price پر میشود
-}
-```
-
-### ۳.۶ تغییرات Entity — WeeklyCommissionPool
-
-```csharp
-public class WeeklyCommissionPool : BaseAuditableEntity
-{
- // ... فیلدهای فعلی حفظ میشوند ...
+ PackagePurchased --> ClubActivated: فعالسازی باشگاه\n(OTP + قرارداد)
- // === فیلد جدید ===
- public long PackageId { get; set; } // Pool جداگانه برای هر پکیج
- public virtual Package Package { get; set; }
-}
+ ClubActivated --> Shopping: خرج Balance\nدر فروشگاه
+
+ Shopping --> MagicMode: Balance == 0
+
+ MagicMode --> MagicCharging: شارژ + خرج\n(تا سقف 1B)
+
+ MagicCharging --> MagicMode: ادامه
+
+ MagicMode --> CycleComplete: Balance == 0\nAND Deposit ≥ 1B
+
+ CycleComplete --> NoPurchase: ریست وضعیت\nآماده خرید مجدد
```
-### ۳.۷ جریان پول جدید
+### ۵.۲ ریست وضعیت بعد تکمیل چرخه (EXIT Magic Mode)
-```
-پکیج نقرهای (۵.۶M):
- ├── Balance += ۵,۶۰۰,۰۰۰
- ├── DiscountBalance += ۱۱,۲۰۰,۰۰۰ (×۲)
- └── CommissionPool += ActivationFee مخصوص نقرهای
-
-پکیج طلایی/پایه (۵۶M):
- ├── Balance += ۵۶,۰۰۰,۰۰۰
- ├── DiscountBalance += ۱۱۲,۰۰۰,۰۰۰ (×۲)
- └── CommissionPool += ۲۵,۲۰۰,۰۰۰
-
-پکیج الماسی (??M):
- ├── Balance += ??
- ├── DiscountBalance += ?? (×۲)
- └── CommissionPool += ActivationFee مخصوص الماسی
-```
-
----
-
-## ۴. محاسبه پورسانت — تغییرات
-
-### ۴.۱ وضعیت فعلی
-
-```
-یک WeeklyCommissionPool برای کل هفته
- ↓
-TotalAmount = مجموع ActivationFee همه فعالسازیها
- ↓
-ValuePerBalance = TotalAmount ÷ مجموع Balanceها
- ↓
-همه یکسان محاسبه میشوند
-```
-
-### ۴.۲ وضعیت هدف
-
-```
-برای هر پکیج، یک WeeklyCommissionPool جداگانه:
-
-Pool_نقرهای:
- TotalAmount = مجموع ActivationFee خریداران نقرهای این هفته
- Balanceها = فقط از شبکه خریداران نقرهای
- ValuePerBalance = Pool_نقرهای ÷ Balance_نقرهای
-
-Pool_طلایی:
- TotalAmount = مجموع ActivationFee خریداران طلایی این هفته
- Balanceها = فقط از شبکه خریداران طلایی
- ValuePerBalance = Pool_طلایی ÷ Balance_طلایی
-```
-
-### ۴.۳ نکته مهم: ساختار شبکه یکی است
-
-```
- [Ali]
- / \
- [Sara] [Reza] ← شبکه باینری یکیست
- / \ / \
- [M1] [M2] [M3] [M4]
-
-ولی محاسبات جدا:
- - Ali با پکیج طلایی → پورسانت از Pool طلایی
- - Sara با پکیج نقرهای → پورسانت از Pool نقرهای
- - Reza با پکیج طلایی → پورسانت از Pool طلایی
-```
-
-### ۴.۴ تغییرات Stored Procedure
-
-**`sp_CalculateWeeklyBalances`** باید:
-- پارامتر `@PackageId` بگیرد
-- فقط کاربرانی که این پکیج را خریدهاند فیلتر کند
-- برای هر پکیج جداگانه اجرا شود
-
-**`sp_CalculateWeeklyCommissionPool`** باید:
-- پارامتر `@PackageId` بگیرد
-- Pool مخصوص آن پکیج را بخواند
-- پرداختها فقط به خریداران آن پکیج اختصاص یابد
-
----
-
-## ۵. فیچرهای باشگاه مشتریان بر اساس پکیج
-
-### ۵.۱ وضعیت فعلی
-
-وقتی کاربر فعال میشود، **همه ۴ فیچر** یکجا assign میشوند:
```csharp
-// ActivateClubMembershipCommandHandler — خط ~350
-var allFeatureIds = ClubFeatureTypeExtensions.GetAllFeatureIds();
-foreach (var featureId in allFeatureIds)
+// UserOrderService.cs — EXIT Magic Mode — تغییرات لازم:
+wallet.WalletMode = WalletMode.Normal;
+wallet.MagicCompletedAt = DateTime.UtcNow;
+cycle.MagicCompletedAt = DateTime.UtcNow;
+
+// ✅ اضافه شود:
+user.PackagePurchaseMethod = PackagePurchaseMethod.None; // اجازه خرید مجدد
+membership.IsActive = false; // اجازه re-contract
+cycle.IsCurrentCycle = false; // آماده چرخه جدید
+```
+
+### ۵.۳ نکات مهم
+
+1. **دایا فقط دور اول** — بعد از دور اول، فقط IPG مجاز
+2. **هر خرید = Commission contribution** — ActivationFee به Pool آن پکیج
+3. **PackagePurchaseMethod ریست** بعد تکمیل چرخه
+4. **فیچرها بر اساس پکیج جدید** — ممکنه متفاوت باشه
+
+---
+
+## ۶. Commission Pool — تغییرات
+
+### ۶.۱ ساختار جدید
+
+```
+هفته ۱:
+ Pool_نقرهای (PackageId=X): TotalAmount = Σ ActivationFee نقرهای
+ Pool_پایه (PackageId=Y): TotalAmount = Σ ActivationFee پایه
+
+هر Pool مستقل:
+ ValuePerBalance = TotalPoolAmount ÷ TotalBalances
+ (فقط کاربران همان پکیج)
+```
+
+### ۶.۲ ساختار شبکه یکیست
+
+```
+ [Ali - پایه]
+ / \
+ [Sara - نقرهای] [Reza - پایه]
+
+Commission:
+ Ali → پاداش از Pool_پایه
+ Sara → پاداش از Pool_نقرهای
+ Reza → پاداش از Pool_پایه
+```
+
+### ۶.۳ تغییرات SP
+
+| SP | تغییر |
+|----|-------|
+| `sp_CalculateWeeklyBalances` | پارامتر `@PackageId` — فیلتر کاربران بر اساس PackageId |
+| `sp_CalculateWeeklyCommissionPool` | پارامتر `@PackageId` — Pool مخصوص آن پکیج |
+
+### ۶.۴ تغییرات Service
+
+```csharp
+// WeeklyCommissionCalculationService — Loop روی پکیجها:
+var activePackages = await _context.Packages
+ .Where(p => p.IsActive && !p.IsDeleted)
+ .ToListAsync();
+
+foreach (var package in activePackages)
{
- userClubFeatures.Add(new UserClubFeature { ... });
+ await strategy.CalculateWeeklyBalancesAsync(weekId, package.Id);
+ await strategy.CalculateWeeklyPoolAsync(weekId, package.Id);
}
```
-### ۵.۲ وضعیت هدف
+---
-فیچرها بر اساس جدول `PackageFeature` تعیین میشوند:
+## ۷. Event-Driven Side Effects
-| فیچر | نقرهای | طلایی (پایه) | الماسی |
-|------|---------|-------------|--------|
-| چتیکا | ❌ | ✅ | ✅ |
-| بیمه | ❌ | ✅ | ✅ |
-| تریپ | ✅ | ✅ | ✅ |
-| لرن | ✅ | ✅ | ✅ |
-| فیچر VIP | ❌ | ❌ | ✅ |
-
-*مقادیر بالا نمونهای هستند — قابل تنظیم از BackOffice*
-
-### ۵.۳ تغییر در ActivateClubMembershipHandler
+### ۷.۱ ساخت پکیج جدید
+```mermaid
+flowchart LR
+ A["ساخت پکیج جدید
(از BackOffice)"] --> B["PackageCreatedEvent"]
+ B --> C["ایجاد Pool خالی
برای هفته جاری"]
+ B --> D["لاگ ادمین"]
```
-قبلی:
- GetAllFeatureIds() → assign all
-جدید:
- Package.PackageFeatures
- .Where(pf => pf.IsIncluded)
- .Select(pf => pf.ClubFeatureId)
- → assign only included features
+### ۷.۲ جدول رویدادها
+
+| رویداد | Side Effect |
+|--------|------------|
+| `PackageCreated` | ساخت WeeklyCommissionPool خالی هفته جاری |
+| `PackageDeactivated` | هشدار ادمین — Pool موجود تکمیل شود |
+| `PackagePurchased` | ActivationFee → Pool پکیج + شارژ wallets |
+| `MagicCycleCompleted` | ریست PackagePurchaseMethod + Deactivate membership |
+
+---
+
+## ۸. Seed Data
+
+```sql
+-- پکیج پایه (۵۶ میلیون تومان)
+INSERT INTO "CMS"."Packages" (
+ "Title", "Description", "Price", "IsActive", "IsBasePackage",
+ "SupportsDayaPurchase", "SupportsDirectPurchase",
+ "ActivationFee", "DiscountMultiplier", "MagicWalletMultiplier",
+ "SortOrder", "ImagePath"
+) VALUES (
+ 'پکیج پایه', 'پکیج اصلی باشگاه مشتریان کارا بازار سلامت',
+ 56000000, true, true,
+ true, true,
+ 25200000, 2.0, 2.5,
+ 2, ''
+);
+
+-- پکیج نقرهای (۵.۶ میلیون تومان)
+INSERT INTO "CMS"."Packages" (
+ "Title", "Description", "Price", "IsActive", "IsBasePackage",
+ "SupportsDayaPurchase", "SupportsDirectPurchase",
+ "ActivationFee", "DiscountMultiplier", "MagicWalletMultiplier",
+ "SortOrder", "ImagePath"
+) VALUES (
+ 'پکیج نقرهای', 'پکیج سطح نقرهای باشگاه مشتریان',
+ 5600000, true, false,
+ false, true,
+ 2520000, 2.0, 2.5,
+ 1, ''
+);
+
+-- فیچرهای پکیج پایه: همه فیچرها
+INSERT INTO "CMS"."PackageFeatures" ("PackageId", "ClubFeatureId", "IsIncluded")
+SELECT base."Id", cf."Id", true
+FROM "CMS"."Packages" base
+CROSS JOIN "CMS"."ClubFeatures" cf
+WHERE base."IsBasePackage" = true AND cf."IsDeleted" = false;
+
+-- فیچرهای پکیج نقرهای: تعیین میشود از BackOffice
```
---
-## ۶. تغییرات UI — FrontOffice
+## ۹. Migration دادههای فعلی
-### ۶.۱ صفحه پکیجها (کاشیها)
+```sql
+-- ========================================
+-- STEP 1: مشخص کردن ID پکیج پایه
+-- ========================================
+DO $$
+DECLARE base_pkg_id BIGINT;
+BEGIN
+ SELECT "Id" INTO base_pkg_id
+ FROM "CMS"."Packages" WHERE "IsBasePackage" = true LIMIT 1;
+
+ -- STEP 2: ClubMembership
+ UPDATE "CMS"."ClubMemberships"
+ SET "PackageId" = base_pkg_id WHERE "PackageId" IS NULL;
+
+ -- STEP 3: ClubMembershipCycle
+ UPDATE "CMS"."ClubMembershipCycles"
+ SET "PackageId" = base_pkg_id WHERE "PackageId" IS NULL;
+
+ -- STEP 4: WeeklyCommissionPool
+ UPDATE "CMS"."WeeklyCommissionPools"
+ SET "PackageId" = base_pkg_id WHERE "PackageId" IS NULL;
+
+ -- STEP 5: UserCommissionPayout
+ UPDATE "CMS"."UserCommissionPayouts"
+ SET "PackageId" = base_pkg_id WHERE "PackageId" IS NULL;
+
+ RAISE NOTICE 'Migration completed for PackageId=%', base_pkg_id;
+END $$;
-```
-┌─────────────────────────────────────────────────────┐
-│ انتخاب پکیج باشگاه مشتریان │
-├─────────────┬──────────────┬──────────────┬─────────┤
-│ │ │ │ │
-│ 🥈 نقرهای │ 🥇 طلایی │ 💎 الماسی │ ⭐ ویژه │
-│ ۵.۶M │ ۵۶M │ ؟؟M │ ؟؟M │
-│ │ │ │ │
-│ ● لرن │ ● چتیکا │ ● همه │ ● همه │
-│ ● تریپ │ ● بیمه │ ● + VIP │ ● +... │
-│ │ ● تریپ │ │ │
-│ │ ● لرن │ │ │
-│ │ │ │ │
-│ [انتخاب] │ [انتخاب] │ [انتخاب] │[انتخاب]│
-└─────────────┴──────────────┴──────────────┴─────────┘
-```
-
-### ۶.۲ مدال پرداخت — پکیج پایه (طلایی)
-
-```
-┌─────────────────────────────────────────┐
-│ خرید پکیج طلایی — ۵۶M تومان │
-│ │
-│ توضیحات: ... │
-│ │
-│ روشهای پرداخت: │
-│ ┌─────────────────────────────────┐ │
-│ │ 💎 خرید از طریق الماس دایا │ │
-│ └─────────────────────────────────┘ │
-│ ┌─────────────────────────────────┐ │
-│ │ 💳 پرداخت مستقیم (درگاه بانکی) │ │
-│ └─────────────────────────────────┘ │
-└─────────────────────────────────────────┘
-```
-
-### ۶.۳ مدال پرداخت — پکیجهای دیگر (نقرهای و بالاتر)
-
-```
-┌─────────────────────────────────────────┐
-│ خرید پکیج نقرهای — ۵.۶M تومان │
-│ │
-│ توضیحات: ... │
-│ ویژگیها: لرن، تریپ │
-│ │
-│ ┌─────────────────────────────────┐ │
-│ │ 💳 پرداخت و فعالسازی │ │
-│ └─────────────────────────────────┘ │
-└─────────────────────────────────────────┘
+-- STEP 6: Verify — همه باید 0 باشند
+SELECT 'ClubMemberships' AS tbl, COUNT(*) FROM "CMS"."ClubMemberships" WHERE "PackageId" IS NULL
+UNION ALL
+SELECT 'Cycles', COUNT(*) FROM "CMS"."ClubMembershipCycles" WHERE "PackageId" IS NULL
+UNION ALL
+SELECT 'Pools', COUNT(*) FROM "CMS"."WeeklyCommissionPools" WHERE "PackageId" IS NULL
+UNION ALL
+SELECT 'Payouts', COUNT(*) FROM "CMS"."UserCommissionPayouts" WHERE "PackageId" IS NULL;
```
---
-## ۷. بخشهای تحت تاثیر (Impact Analysis)
+## ۱۰. Impact Analysis — ۳۹ تغییر در ۶ لایه
-### ۷.۱ جدول تاثیرپذیری
+### ۱۰.۱ لایه Domain (۸ تغییر)
-| # | لایه | فایل/بخش | نوع تغییر | شدت |
-|---|------|----------|-----------|-----|
-| ۱ | **Domain** | `Package.cs` | اضافه فیلد | 🟡 متوسط |
-| ۲ | **Domain** | `PackageFeature.cs` — **جدید** | Entity جدید | 🔴 زیاد |
-| ۳ | **Domain** | `ClubMembership.cs` | اضافه `PackageId` | 🟡 متوسط |
-| ۴ | **Domain** | `ClubMembershipCycle.cs` | اضافه `PackageId` | 🟡 متوسط |
-| ۵ | **Domain** | `WeeklyCommissionPool.cs` | اضافه `PackageId` | 🔴 زیاد |
-| ۶ | **Domain** | `SystemConstants.cs` | حذف hardcodeها → خوانش از Package | 🟡 متوسط |
-| ۷ | **Application** | `ActivateClubMembershipCommandHandler` | فیچر بر اساس پکیج | 🔴 زیاد |
-| ۸ | **Application** | `InitiateBasePackagePaymentCommandHandler` | حذف ID=4 hardcoded | 🟡 متوسط |
-| ۹ | **Application** | `VerifyBasePackagePaymentCommandHandler` | شارژ متناسب با پکیج | 🔴 زیاد |
-| ۱۰ | **Application** | `VerifyPackagePurchasePaymentCommandHandler` | شارژ متناسب با پکیج | 🔴 زیاد |
-| ۱۱ | **Application** | `ManualPaymentCommandHandler` | شارژ متناسب با پکیج | 🟡 متوسط |
-| ۱۲ | **Application** | `CustomerPurchasePackageCommandHandler` | پشتیبانی روشهای پرداخت پکیج | 🟡 متوسط |
-| ۱۳ | **Infra** | `sp_CalculateWeeklyBalances` | پارامتر PackageId | 🔴 زیاد |
-| ۱۴ | **Infra** | `sp_CalculateWeeklyCommissionPool` | Pool جداگانه هر پکیج | 🔴 زیاد |
-| ۱۵ | **Infra** | `WeeklyCommissionCalculationService` | Loop روی پکیجها | 🟡 متوسط |
-| ۱۶ | **Infra** | EF Configurations | جدول جدید + FKها | 🟡 متوسط |
-| ۱۷ | **Infra** | Database Migration | schema changes | 🟡 متوسط |
-| ۱۸ | **Proto** | `package.proto` | فیلدهای جدید پکیج | 🟢 کم |
-| ۱۹ | **Proto** | `clubmembership.proto` | PackageId در response | 🟢 کم |
-| ۲۰ | **Proto** | `commission.proto` | PackageId در pool/payout | 🟢 کم |
-| ۲۱ | **FrontOffice** | صفحه انتخاب پکیج | UI جدید (کاشیها) | 🔴 زیاد |
-| ۲۲ | **FrontOffice** | مدال پرداخت | دو مدال متفاوت | 🔴 زیاد |
-| ۲۳ | **FrontOffice** | `MyPackages.razor` | نمایش نوع پکیج | 🟡 متوسط |
-| ۲۴ | **FrontOffice** | `ActivateClubDialog.razor` | ارتباط با پکیج | 🟡 متوسط |
-| ۲۵ | **BackOffice** | صفحه مدیریت پکیجها | CRUD فیلدهای جدید | 🟡 متوسط |
-| ۲۶ | **BackOffice** | صفحه فیچر پکیجها — **جدید** | ماتریس پکیج×فیچر | 🔴 زیاد |
-| ۲۷ | **BackOffice** | `ActivateClubDialog.razor` | انتخاب پکیج | 🟡 متوسط |
+| # | فایل | نوع | شدت |
+|---|------|-----|------|
+| D1 | `Package.cs` | اضافه ۷ فیلد جدید | 🟡 |
+| D2 | `PackageFeature.cs` | Entity جدید + EF Config | 🔴 |
+| D3 | `ClubMembership.cs` | اضافه `PackageId` | 🟡 |
+| D4 | `ClubMembershipCycle.cs` | اضافه `PackageId` | 🟡 |
+| D5 | `WeeklyCommissionPool.cs` | اضافه `PackageId` + Unique | 🔴 |
+| D6 | `UserCommissionPayout.cs` | اضافه `PackageId` | 🟡 |
+| D7 | `SystemConstants.cs` | حذف ۵ ثابت، حفظ بقیه | 🟡 |
+| D8 | EF Migration + Seed | schema + data migration | 🔴 |
-### ۷.۲ ریسکها
+### ۱۰.۲ لایه Application (۱۲ تغییر)
+
+| # | فایل | نوع | شدت |
+|---|------|-----|------|
+| A1 | `ActivateClubMembershipCommandHandler` | فیچر از PackageFeature + ActivationFee + re-activate | 🔴 |
+| A2 | `VerifyPackagePurchaseCommandHandler` | DiscountMultiplier + UserPackagePurchase + generic | 🔴 |
+| A3 | `VerifyBasePackagePaymentCommandHandler` | DiscountMultiplier + UserPackagePurchase + generic | 🔴 |
+| A4 | `VerifyGoldenPackagePurchaseCommandHandler` | فیکس DiscountBalance + UserPackagePurchase + generic | 🔴 |
+| A5 | `InitiateBasePackagePaymentCommandHandler` | حذف ID=4 + generic | 🟡 |
+| A6 | `PurchaseGoldenPackageCommandHandler` | حذف فیلتر "طلایی" + generic | 🟡 |
+| A7 | `PurchasePackageCommandHandler` | اجازه re-purchase | 🟡 |
+| A8 | `CreateManualPaymentCommandHandler` | DiscountMultiplier از Package | 🟡 |
+| A9 | `CheckAndProcessDayaLoansCommandHandler` | حذف ID=4 + DiscountMultiplier | 🟡 |
+| A10 | `AcceptClubMembershipContractCommandHandler` | اجازه re-contract بعد چرخه | 🟡 |
+| A11 | `UserOrderService` (EXIT Magic) | ریست PackagePurchaseMethod + Deactivate | 🔴 |
+| A12 | Package CRUD handlers | فیلدهای جدید + PackageFeature CRUD | 🟡 |
+
+### ۱۰.۳ لایه Infrastructure (۵ تغییر)
+
+| # | فایل | نوع | شدت |
+|---|------|-----|------|
+| I1 | `sp_CalculateWeeklyBalances` | پارامتر `@PackageId` | 🔴 |
+| I2 | `sp_CalculateWeeklyCommissionPool` | پارامتر `@PackageId` | 🔴 |
+| I3 | `WeeklyCommissionCalculationService` | Loop روی پکیجها | 🟡 |
+| I4 | `OrmCommissionCalculationStrategy` | فیلتر PackageId | 🔴 |
+| I5 | `SpCommissionCalculationStrategy` | پاس دادن PackageId | 🟡 |
+
+### ۱۰.۴ لایه Proto/gRPC (۴ تغییر)
+
+| # | فایل | نوع | شدت |
+|---|------|-----|------|
+| P1 | `package.proto` | فیلدهای جدید Package | 🟡 |
+| P2 | `clubmembership.proto` | `package_id` در request/response | 🟡 |
+| P3 | `commission.proto` | `package_id` در pool/payout | 🟡 |
+| P4 | `PackageGrpcService.cs` | Generic purchase + CRUD | 🟡 |
+
+### ۱۰.۵ لایه FrontOffice (۶ تغییر)
+
+| # | فایل | نوع | شدت |
+|---|------|-----|------|
+| F1 | `Packages.razor` | کاشیهای پکیج از API | 🔴 |
+| F2 | `PackageDetail.razor` | فیچرها از PackageFeature | 🟡 |
+| F3 | `ActivationSection.razor` | حذف hardcoded 56M | 🟡 |
+| F4 | `ClubMembershipContractDialog.razor` | متن قرارداد داینامیک | 🟡 |
+| F5 | `MyPackages.razor` | نمایش نوع پکیج + re-purchase | 🟡 |
+| F6 | `PackageService.cs` | فیکس stub GetPurchaseHistory | 🟡 |
+
+### ۱۰.۶ لایه BackOffice (۴ تغییر)
+
+| # | فایل | نوع | شدت |
+|---|------|-----|------|
+| BO1 | `PackageCreateDialog.razor` | فیلدهای جدید | 🟡 |
+| BO2 | `PackageEditDialog.razor` | فیلدهای جدید | 🟡 |
+| BO3 | `PackageFeatureMatrixPage` — **جدید** | ماتریس پکیج×فیچر | 🔴 |
+| BO4 | `ActivateClubDialog.razor` | dropdown انتخاب پکیج | 🟡 |
+
+---
+
+## ۱۱. فازبندی پیادهسازی
+
+### فاز ۰ — فیکس باگهای فوری ≈ ۱ روز
+
+| تسک | شرح |
+|-----|------|
+| **T0.1** | فیکس `VerifyGoldenPackagePurchase` — اضافه DiscountBalance (`Amount × 2`) |
+| **T0.2** | فیکس `VerifyGoldenPackagePurchase` — ساخت `UserPackagePurchase` |
+| **T0.3** | فیکس `VerifyPackagePurchase` — ساخت `UserPackagePurchase` |
+| **T0.4** | فیکس `VerifyBasePackagePayment` — ساخت `UserPackagePurchase` |
+
+### فاز ۱ — زیرساخت (Domain + DB) ≈ ۳ روز
+
+| تسک | شرح |
+|-----|------|
+| **T1.1** | بروزرسانی `Package` entity (۷ فیلد جدید) |
+| **T1.2** | ایجاد `PackageFeature` entity + EF Config |
+| **T1.3** | اضافه `PackageId` به `ClubMembership` |
+| **T1.4** | اضافه `PackageId` به `ClubMembershipCycle` |
+| **T1.5** | اضافه `PackageId` به `WeeklyCommissionPool` + Unique |
+| **T1.6** | اضافه `PackageId` به `UserCommissionPayout` |
+| **T1.7** | حذف ۵ ثابت از `SystemConstants` |
+| **T1.8** | Database Migration + Seed Data (۲ پکیج + فیچرها) |
+| **T1.9** | Data Migration: کاربران فعلی → PackageId = پکیج پایه |
+| **T1.10** | بروزرسانی Protoها |
+
+### فاز ۲ — منطق کسبوکار ≈ ۴ روز
+
+| تسک | شرح |
+|-----|------|
+| **T2.1** | ادغام Verify handlers → Generic (DiscountMultiplier + UserPackagePurchase) |
+| **T2.2** | ادغام Purchase handlers → Generic (حذف "طلایی"، حذف ID=4) |
+| **T2.3** | بروزرسانی `ActivateClubMembership` — فیچر از PackageFeature |
+| **T2.4** | بروزرسانی `ActivateClubMembership` — ActivationFee از Package |
+| **T2.5** | اجازه re-purchase در Guards (G1–G3) |
+| **T2.6** | ریست وضعیت در EXIT Magic Mode |
+| **T2.7** | اجازه re-contract (G5) + بروزرسانی JWT (G7) |
+| **T2.8** | بروزرسانی `CreateManualPayment` + `DayaLoan` |
+| **T2.9** | PackageFeature CRUD |
+| **T2.10** | Event: PackageCreated → ساخت Pool خالی |
+
+### فاز ۳ — محاسبه پورسانت ≈ ۳ روز (موازی با فاز ۲)
+
+| تسک | شرح |
+|-----|------|
+| **T3.1** | بروزرسانی `sp_CalculateWeeklyBalances` — `@PackageId` |
+| **T3.2** | بروزرسانی `sp_CalculateWeeklyCommissionPool` — `@PackageId` |
+| **T3.3** | بروزرسانی `WeeklyCommissionCalculationService` — Loop |
+| **T3.4** | بروزرسانی `OrmCommissionCalculationStrategy` — فیلتر |
+| **T3.5** | تست محاسبات با داده واقعی |
+
+### فاز ۴ — UI ≈ ۴ روز
+
+| تسک | شرح |
+|-----|------|
+| **T4.1** | FrontOffice: کاشیهای پکیج (داینامیک) |
+| **T4.2** | FrontOffice: مدال پرداخت (دایا+مستقیم / فقط مستقیم) |
+| **T4.3** | FrontOffice: MyPackages — re-purchase |
+| **T4.4** | FrontOffice: ActivationSection + Contract — داینامیک |
+| **T4.5** | BackOffice: CRUD پکیج — فیلدهای جدید |
+| **T4.6** | BackOffice: ماتریس PackageFeature |
+| **T4.7** | BackOffice: ActivateClubDialog — dropdown |
+
+### فاز ۵ — تست و استقرار ≈ ۲ روز
+
+| تسک | شرح |
+|-----|------|
+| **T5.1** | تست خرید هر پکیج |
+| **T5.2** | تست re-purchase بعد تکمیل چرخه |
+| **T5.3** | تست Commission Pool جداگانه |
+| **T5.4** | تست Migration |
+| **T5.5** | Deploy staging → production |
+
+---
+
+## ۱۲. ریسکها
| ریسک | احتمال | شدت | راهحل |
|------|--------|-----|--------|
-| دادههای فعلی — کاربران بدون PackageId | قطعی | زیاد | Migration: کاربران فعلی → PackageId = پکیج پایه |
-| Commission Pool فعلی بدون PackageId | قطعی | زیاد | Migration: Poolهای موجود → PackageId = پکیج پایه |
-| SP تغییر → محاسبات اشتباه | متوسط | بحرانی | تست جامع + محیط staging |
-| مبالغ hardcoded در جاهای پراکنده | زیاد | متوسط | Audit کامل کدبیس |
-| عدم سازگاری FrontOffice/BackOffice | متوسط | متوسط | تست end-to-end |
+| Migration دادهها — PackageId اشتباه | کم | بحرانی | Verify query + بکاپ |
+| SP تغییر → محاسبات اشتباه | متوسط | بحرانی | تست staging قبل production |
+| ادغام handlers → رگرسیون | متوسط | زیاد | E2E test |
+| خرید مجدد بدون تکمیل چرخه | کم | زیاد | Validation: چرخه قبلی MagicCompletedAt |
+| Proto breaking change | قطعی | کم | backward compatible fields |
---
-## ۸. فازبندی پیادهسازی
-
-### فاز ۱ — زیرساخت (Domain + DB) ≈ ۳-۴ روز
-
-| تسک | شرح |
-|-----|------|
-| T1.1 | بروزرسانی `Package` entity (فیلدهای جدید) |
-| T1.2 | ایجاد `PackageFeature` entity + EF Configuration |
-| T1.3 | اضافه کردن `PackageId` به `ClubMembership` |
-| T1.4 | اضافه کردن `PackageId` به `ClubMembershipCycle` |
-| T1.5 | اضافه کردن `PackageId` به `WeeklyCommissionPool` |
-| T1.6 | Database Migration + Seed data (پکیج پایه + فیچرها) |
-| T1.7 | Migration: کاربران/Poolهای فعلی → PackageId = پکیج پایه |
-| T1.8 | بروزرسانی Protoها |
-
-### فاز ۲ — منطق کسبوکار (Application) ≈ ۴-۵ روز
-
-| تسک | شرح |
-|-----|------|
-| T2.1 | بروزرسانی `ActivateClubMembershipCommandHandler` — فیچر بر اساس پکیج |
-| T2.2 | بروزرسانی Verify handlers — شارژ کیف پول متناسب با پکیج |
-| T2.3 | حذف مقادیر hardcoded از `SystemConstants` → خوانش از Package |
-| T2.4 | بروزرسانی `InitiateBasePackagePayment` → Generic `InitiatePackagePayment` |
-| T2.5 | بروزرسانی `ManualPaymentCommandHandler` — پشتیبانی پکیج متغیر |
-| T2.6 | CRUD پکیج با فیلدهای جدید (gRPC handlers) |
-| T2.7 | CRUD `PackageFeature` (ماتریس پکیج×فیچر) |
-
-### فاز ۳ — محاسبه پورسانت ≈ ۳-۴ روز
-
-| تسک | شرح |
-|-----|------|
-| T3.1 | بروزرسانی `sp_CalculateWeeklyBalances` — فیلتر بر اساس PackageId |
-| T3.2 | بروزرسانی `sp_CalculateWeeklyCommissionPool` — Pool جداگانه |
-| T3.3 | بروزرسانی `WeeklyCommissionCalculationService` — Loop روی پکیجها |
-| T3.4 | تست محاسبات با داده واقعی |
-
-### فاز ۴ — UI (FrontOffice + BackOffice) ≈ ۴-۵ روز
-
-| تسک | شرح |
-|-----|------|
-| T4.1 | صفحه کاشیهای پکیج (FrontOffice) |
-| T4.2 | مدال پرداخت پکیج پایه (دایا + مستقیم) |
-| T4.3 | مدال پرداخت پکیجهای دیگر (فقط مستقیم) |
-| T4.4 | بروزرسانی `MyPackages.razor` — نمایش نوع پکیج |
-| T4.5 | بروزرسانی `ActivateClubDialog.razor` — ارتباط با پکیج |
-| T4.6 | BackOffice: CRUD پکیج با فیلدهای جدید |
-| T4.7 | BackOffice: صفحه ماتریس فیچرهای پکیج |
-| T4.8 | BackOffice: `ActivateClubDialog` — انتخاب پکیج |
-
-### فاز ۵ — تست و استقرار ≈ ۲-۳ روز
-
-| تسک | شرح |
-|-----|------|
-| T5.1 | تست end-to-end فلوی خرید هر پکیج |
-| T5.2 | تست محاسبه پورسانت جداگانه |
-| T5.3 | تست migration دادههای فعلی |
-| T5.4 | Deploy به staging + تست |
-| T5.5 | Deploy به production |
-
----
-
-## ۹. Seed Data — پکیجهای اولیه
-
-```sql
--- Migration: Seed packages
-INSERT INTO Packages (Title, Description, Price, IsActive, IsBasePackage,
- SupportsDayaPurchase, SupportsDirectPurchase, ActivationFee, GiftValue,
- DiscountMultiplier, SortOrder)
-VALUES
- ('نقرهای', 'پکیج نقرهای باشگاه مشتریان', 5600000, 1, 0,
- 0, 1, ???, ???, 2.0, 1),
- ('طلایی', 'پکیج طلایی باشگاه مشتریان (پایه)', 56000000, 1, 1,
- 1, 1, 25200000, 25200000, 2.0, 2);
-
--- Migration: ربط فیچرها به پکیجها
-INSERT INTO PackageFeatures (PackageId, ClubFeatureId, IsIncluded) VALUES
- -- نقرهای: فقط تریپ و لرن
- (@silverId, @tripId, 1),
- (@silverId, @learnId, 1),
- -- طلایی: همه فیچرها
- (@goldId, @chatikaId, 1),
- (@goldId, @bimeId, 1),
- (@goldId, @tripId, 1),
- (@goldId, @learnId, 1);
-
--- Migration: کاربران فعلی → پکیج پایه
-UPDATE ClubMemberships SET PackageId = @goldId WHERE PackageId IS NULL;
-UPDATE ClubMembershipCycles SET PackageId = @goldId WHERE PackageId IS NULL;
-UPDATE WeeklyCommissionPools SET PackageId = @goldId WHERE PackageId IS NULL;
-```
-
----
-
-## ۱۰. سوالات باز (نیاز به تصمیمگیری)
-
-| # | سوال | گزینهها |
-|---|------|---------|
-| ۱ | `ActivationFee` و `GiftValue` پکیج نقرهای چقدر باشد؟ | نسبت به قیمت؟ مقدار ثابت؟ |
-| ۲ | آیا کاربر میتواند بعداً پکیج خود را ارتقا دهد (upgrade)؟ | بله → فقط مابهالتفاوت / خیر |
-| ۳ | `DiscountMultiplier` برای همه پکیجها ×۲ باشد؟ | یکسان / متفاوت به ازای هر پکیج |
-| ۴ | ضریب `MagicWallet` (×۲.۵) برای پکیجهای کوچکتر هم همان باشد؟ | بله / خیر |
-| ۵ | فیچرهای پکیج نقرهای دقیقاً کدامها هستند؟ | لرن+تریپ؟ فقط لرن؟ |
-| ۶ | آیا یک کاربر میتواند چند پکیج همزمان داشته باشد؟ | فقط یکی / امکان خرید چندتا |
-| ۷ | نام و تعداد دقیق پکیجها چیست؟ | نقرهای+طلایی؟ بیشتر؟ |
-| ۸ | کاربرانی که با دایا فعال شدن، چه پکیجی دارند؟ | طلایی (پایه) |
-
----
-
-## ۱۱. تخمین زمانی
+## ۱۳. تخمین زمانی
| فاز | مدت | وابستگی |
|-----|------|---------|
-| فاز ۱ — زیرساخت | ۳-۴ روز | — |
-| فاز ۲ — منطق | ۴-۵ روز | فاز ۱ |
-| فاز ۳ — پورسانت | ۳-۴ روز | فاز ۱ |
-| فاز ۴ — UI | ۴-۵ روز | فاز ۲ |
-| فاز ۵ — تست | ۲-۳ روز | فاز ۳, ۴ |
-| **مجموع** | **~۱۶-۲۱ روز کاری** | |
+| فاز ۰ — فیکس باگها | ۱ روز | — |
+| فاز ۱ — زیرساخت | ۳ روز | فاز ۰ |
+| فاز ۲ — منطق | ۴ روز | فاز ۱ |
+| فاز ۳ — پورسانت | ۳ روز | فاز ۱ |
+| فاز ۴ — UI | ۴ روز | فاز ۲ |
+| فاز ۵ — تست | ۲ روز | فاز ۳, ۴ |
+| **مجموع** | **~۱۷ روز** | |
-> فازهای ۲ و ۳ قابل موازیسازی هستند.
+> فاز ۲ و ۳ **موازی** → مسیر بحرانی: ۰→۱→۲→۴→۵ = **~۱۴ روز**