Files
docs/roadmap/PACKAGE-TRANSFORMATION-TASKS.md
T
2026-02-26 03:36:33 +03:30

27 KiB
Raw Blame History

🔄 نقشه‌راه تحول پکیج‌بیس — تسک‌های گام‌به‌گام

وضعیت: در حال اجرا — فاز ۰-۷c تکمیل | فاز 8a تکمیل | NuGet + UI باقی‌مانده
تاریخ: ۱۴۰۴/۱۲/۰۹
پیش‌نیاز: BIZ-PACKAGE-BASED-SYSTEM.md v3 (تکمیل پورسانت per-package)
هدف: شکستن ۴۸+ تغییر به تسک‌های اتمیک با ترتیب اجرا و وابستگی‌ها
کامیت‌ها:
CMS: 8b9c317fe3edd1ae92ab8a9cd2fd8e5c7c5ccb938e0002a5a607f7917176fe4d19c569469d97b161f7968446e0e
FrontOffice: b82cac471f391a0bbc11e
BackOffice: f1b0085

⚠️ تغییرات v3: پورسانت per-package، carryover مجزا، SP parameters داینامیک، گزارش‌دهی FO/BO per-package


📊 نمای کلی

مرحله ۰: فیکس باگ فوری (۱ روز)                           ✅ `8b9c317` + `fe3edd1`
    └─→ مرحله ۱: زیرساخت Domain + DB (۴ روز)         ✅ `ae92ab8`
        └─→ مرحله ۱.۵: Migration + Seed                    ✅ `a9cd2fd`
            ├─→ مرحله ۲: منطق کسب‌وکار (۴ روز)           ✅ `8e5c7c5`
            │       └─→ مرحله ۳: بازسازی لایه Package       ✅ `ccb938e`
            │               └─→ مرحله ۴: CRUD + Legacy Fixes   ✅ `0002a5a`
            │                       └─→ مرحله UI (۵ روز)  ⬜
            └─→ مرحله ۳: پورسانت (۴ روز)               ✅ `607f791`+`7176fe4`
                    └─→ مرحله ۶: Deprecation cleanup    ✅ `d19c569`
                            └─→ مرحله ۷: Migration + Cleanup  ✅
                                ├─→ 7a: Cosmetic cleanup      ✅ CMS:`469d97b` FO:`b82cac4` BO:`f1b0085`
                                ├─→ 7b: FO RPC migration      ✅ CMS:`161f796` FO:`71f391a`
                                └─→ 7c: Delete deprecated      ✅ CMS:`8446e0e`
                                        └─→ مرحله ۸: FO Checkout + NuGet
                                            └─→ 8a: Checkout wire-up   ✅ FO:`0bbc11e`
                                                └─→ مرحله ۵: تست + استقرار (۳ روز)  ⬜

مسیر بحرانی: ۰→۱→۱.۵→۲→۳→۴→UI→۵ = ~۱۷ روز | انجام‌شده: ۰→8a (~۱۴ روز)

مرحله ۰ — فیکس باگ‌های فوری

تکمیل‌شده | کامیت: 8b9c317 + fe3edd1

وضعیت باگ‌ها (بررسی اولیه لازم)

# باگ Handler شرح فیکس
B1 DiscountBalance شارژ نمی‌شود VerifyGoldenPackagePurchaseCommandHandler اضافه DiscountBalance += Amount × 2 + WalletChangeLog
B2 UserPackagePurchase ساخته نمی‌شود VerifyGoldenPackagePurchaseCommandHandler ساخت record بعد verify موفق
B3 UserPackagePurchase ساخته نمی‌شود VerifyPackagePurchaseCommandHandler ساخت record بعد verify موفق
B4 UserPackagePurchase ساخته نمی‌شود VerifyBasePackagePaymentCommandHandler ساخت record بعد verify موفق

دستور کار B1:

1. باز کردن VerifyGoldenPackagePurchaseCommandHandler.cs
2. پیدا کردن جایی که Balance شارژ می‌شود
3. اضافه کردن:
   wallet.DiscountBalance += command.Amount * 2;
   // + ساخت WalletChangeLog برای DiscountBalance
4. تست: verify → چک DiscountBalance در DB

دستور کار B2-B4 (الگوی مشترک):

1. بعد از verify موفق و شارژ wallet:
   var purchase = new UserPackagePurchase
   {
       UserId = userId,
       PackageId = packageId,         // فعلاً BasePackageId = 4
       PurchaseDate = DateTime.UtcNow,
       Amount = amount,
       PurchaseMethod = purchaseMethod, // ZarinPal, BFF, etc.
       TransactionId = transactionId,
       IsVerified = true
   };
   _context.UserPackagePurchases.Add(purchase);
2. تست: verify → چک UserPackagePurchases table

مرحله ۱ — زیرساخت (Domain + DB)

تکمیل‌شده | کامیت: ae92ab8 (Phase 1) + a9cd2fd (Phase 1.5 Migration)

T1.1 — بروزرسانی Package Entity (۱۱ فیلد جدید — v3)

فایل: CMS/src/CMSMicroservice.Domain/Entities/Package.cs

+ 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; }
+ public decimal DiscountMultiplier { get; set; } = 2.0m;
+ public decimal MagicWalletMultiplier { get; set; } = 2.5m;
+ // === v3: تنظیمات پورسانت per-package ===
+ public int MaxBalancesPerLeg { get; set; } = 300;         // نقره‌ای=۳۰
+ public int MaxNetworkLevel { get; set; } = 15;
+ // === v3: سقف کیف‌پول جادویی per-package ===
+ public long MagicWalletMaxDeposit { get; set; } = 1_000_000_000;
+ public long MagicWalletMaxCredit { get; set; } = 2_500_000_000;
+
+ public virtual ICollection<PackageFeature> PackageFeatures { get; set; }

EF Config: PackageConfiguration.cs

  • حداکثر یک IsBasePackage = true (Index filter)
  • Precision for decimal fields

T1.2 — ایجاد PackageFeature Entity

فایل جدید: CMS/src/CMSMicroservice.Domain/Entities/PackageFeature.cs

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

T1.3-T1.6 — اضافه PackageId به entityها

Entity فیلد Required? توضیح v3?
ClubMembership long? PackageId nullable (بعد migration → required) آخرین پکیج
ClubMembershipCycle long PackageId required پکیج این چرخه
WeeklyCommissionPool long PackageId required + Unique(WeekDefId, PkgId) Pool هر پکیج
UserCommissionPayout long? PackageId nullable + Unique(UserId, WeekId, PkgId) ردیابی 🔄
NetworkWeeklyBalance long PackageId required + Unique(UserId, WeekId, PkgId) تعادل per-package 🆕

T1.7 — حذف SystemConstants (v3: ۷ ثابت)

فایل: CMS/src/CMSMicroservice.Domain/Common/SystemConstants.cs

- public const long BasePackageAmount = 56_000_000;
- public const long DayaLoanAmount = 56_000_000;
- public const long ClubActivationFee = 25_200_000;
- public const long ClubMembershipGiftValue = 25_200_000;
- public const decimal MagicWalletMultiplier = 2.5m;
- // === v3: انتقال به Package entity ===
- public const int CommissionMaxWeeklyBalancesPerLeg = 300;
- public const int CommissionMaxNetworkLevel = 15;

⚠️ قبل از حذف: grep تمام مصرف‌کننده‌ها → جایگزین با Package.Property
⚠️ v3: CommissionMaxWeeklyBalancesPerLeg و CommissionMaxNetworkLevel هم باید per-package شوند

T1.8 — Database Migration

dotnet ef migrations add AddPackageBasedSystem

شامل:

  • ستون‌های جدید Package
  • جدول PackageFeatures
  • FKها در 4 entity
  • Unique constraint

T1.9 — Data Migration Script

-- 1. بروزرسانی پکیج فعلی (ID=4 → اضافه فیلدهای جدید)
UPDATE "CMS"."Packages" SET
    "SortOrder" = 2,
    "IsActive" = true,
    "IsBasePackage" = true,
    "SupportsDayaPurchase" = true,
    "SupportsDirectPurchase" = true,
    "ActivationFee" = 25200000,
    "DiscountMultiplier" = 2.0,
    "MagicWalletMultiplier" = 2.5,
    -- v3: تنظیمات پورسانت
    "MaxBalancesPerLeg" = 300,
    "MaxNetworkLevel" = 15,
    "MagicWalletMaxDeposit" = 1000000000,
    "MagicWalletMaxCredit" = 2500000000
WHERE "Id" = 4;

-- 2. Link existing data to base package
UPDATE "CMS"."ClubMemberships" SET "PackageId" = 4 WHERE "PackageId" IS NULL;
UPDATE "CMS"."ClubMembershipCycles" SET "PackageId" = 4 WHERE "PackageId" IS NULL;
UPDATE "CMS"."WeeklyCommissionPools" SET "PackageId" = 4 WHERE "PackageId" IS NULL;
UPDATE "CMS"."UserCommissionPayouts" SET "PackageId" = 4 WHERE "PackageId" IS NULL;
-- v3: NetworkWeeklyBalance هم PackageId می‌گیره
UPDATE "CMS"."NetworkWeeklyBalances" SET "PackageId" = 4 WHERE "PackageId" IS NULL;

-- 3. Seed Silver package (شامل فیلدهای v3)
INSERT INTO "CMS"."Packages" (..., "MaxBalancesPerLeg", "MaxNetworkLevel",
    "MagicWalletMaxDeposit", "MagicWalletMaxCredit", ...)
VALUES ('پکیج نقره‌ای', 5600000, ..., 30, 15, 100000000, 250000000, ...);

T1.10 — بروزرسانی Protoها

Proto File تغیر v3?
package.proto فیلدهای جدید Package message (۱۱ فیلد) 🔄
clubmembership.proto package_id در request/response
commission.proto package_id + package_title در ۴ message 🆕
commission.proto Message جدید: CustomerCommissionPackageSummary 🆕
commission.proto فیلتر package_id در Requestها 🆕

T1.11 — اضافه PackageId به NetworkWeeklyBalance (🆕 v3)

فایل: CMS/src/CMSMicroservice.Domain/Entities/NetworkWeeklyBalance.cs

+ public long PackageId { get; set; }
+ public virtual Package Package { get; set; }

EF Config: اضافه Unique Index:

builder.HasIndex(e => new { e.UserId, e.WeekDefinitionId, e.PackageId }).IsUnique();
builder.HasOne(e => e.Package).WithMany().HasForeignKey(e => e.PackageId);

⚠️ تاثیر حجم: رکوردهای تعادل ×N (تعداد پکیج). مثلاً ۱۰۰۰ کاربر × ۲ پکیج = ۲۰۰۰ رکورد هفتگی

T1.12 — Data Migration: NetworkWeeklyBalance (🆕 v3)

-- رکوردهای موجود → پکیج پایه
UPDATE "CMS"."NetworkWeeklyBalances" 
SET "PackageId" = (SELECT "Id" FROM "CMS"."Packages" WHERE "IsBasePackage" = true LIMIT 1)
WHERE "PackageId" IS NULL;

مرحله ۲ — منطق کسب‌وکار

تکمیل‌شده | کامیت: 8e5c7c5 (Phase 2) + ccb938e (Phase 3) + 0002a5a (Phase 4)

T2.1 — Generic Verify Handler

هدف: ادغام VerifyGolden + VerifyBase + VerifyGeneric → یک handler

الگوریتم:

1. دریافت TransactionId از request
2. خواندن Transaction → PackageId → Package entity
3. verify با درگاه (ZarinPal/BFF/...)
4. اگر موفق:
   a. wallet.Balance += Package.Price
   b. wallet.DiscountBalance += Package.Price × Package.DiscountMultiplier
   c. ساخت WalletChangeLog (Balance)
   d. ساخت WalletChangeLog (DiscountBalance)
   e. ساخت UserPackagePurchase record
   f. اگر اولین خرید: JoinNetwork
   g. بروزرسانی ClubMembershipCycle.PackageId
5. return success + receipt

T2.2 — Generic Purchase Handler

هدف: ادغام PurchaseGolden + PurchasePackage + InitiateBase → یک handler

تغییرات:

  • حذف فیلتر Title.Contains("طلایی")
  • حذف BasePackageId = 4
  • خواندن Package entity از DB بر اساس request.PackageId
  • Gateway URL + Amount از Package.Price

T2.3-T2.4 — ActivateClubMembership بهبود

تغییرات:

- var features = await GetAllFeatureIds();        // همه فیچرها
+ var features = await GetPackageFeatures(packageId);  // فیچرهای پکیج

- membership.PackageAmount = SystemConstants.BasePackageAmount;
+ membership.PackageAmount = package.Price;

- var activationFee = SystemConstants.ClubActivationFee;
+ var activationFee = package.ActivationFee;

T2.5-T2.6 — Re-Purchase Logic

EXIT Magic Mode — تغییرات:

  wallet.WalletMode = WalletMode.Normal;
  wallet.MagicCompletedAt = DateTime.UtcNow;
  cycle.MagicCompletedAt = DateTime.UtcNow;
+ user.PackagePurchaseMethod = PackagePurchaseMethod.None;
+ membership.IsActive = false;
+ cycle.IsCurrentCycle = false;

Guard تغییرات:

- if (user.PackagePurchaseMethod != PackagePurchaseMethod.None)
-     throw new RpcException("قبلاً پکیج خریداری شده");
+ if (user.PackagePurchaseMethod != PackagePurchaseMethod.None 
+     && !HasCompletedMagicCycle(membership))
+     throw new RpcException("چرخه جاری هنوز تکمیل نشده");

T2.7 — JWT Claims جدید

  claims.Add("HasPurchasedPackage", "true");
+ claims.Add("CanRepurchase", HasCompletedMagicCycle(membership).ToString());
+ claims.Add("PackageId", membership.PackageId?.ToString() ?? "");
+ claims.Add("PackageTitle", package?.Title ?? "");

مرحله ۳ — محاسبه پورسانت (موازی با مرحله ۲)

تکمیل‌شده | کامیت: 607f791 + 7176fe4 | ⏱️ ۴ روز | ریسک: بحرانی (مالی)

T3.1-T3.2 — SPs + PackageId + پارامترهای داینامیک (🔄 v3)

-- sp_CalculateWeeklyBalances — v3: حذف hardcode
ALTER PROCEDURE sp_CalculateWeeklyBalances
    @WeekDefinitionId BIGINT,
    @PackageId BIGINT,
    @MaxBalancesPerLeg INT,          -- v3: از Package entity (نه ۳۰۰ hardcode!)
    @MaxNetworkLevel INT             -- v3: از Package entity (نه ۱۵ hardcode!)
AS
BEGIN
    -- فیلتر: فقط کاربرانی که این پکیج را دارند
    -- carryover: فقط رکوردهای PackageId = @PackageId
    -- cap: از @MaxBalancesPerLeg (نه ۳۰۰)
    -- depth: CTE تا @MaxNetworkLevel (نه ۱۵)
    INSERT INTO "CMS"."NetworkWeeklyBalances" ("PackageId", ...)
    SELECT @PackageId, ...
    FROM "CMS"."UserWallets" w
    INNER JOIN "CMS"."ClubMemberships" m ON m."UserId" = w."UserId"
    WHERE m."PackageId" = @PackageId
      AND m."IsActive" = true;
END;

T3.3 — Loop Service (🔄 v3: ارسال تنظیمات پکیج)

// WeeklyCommissionCalculationService.cs
var activePackages = await _context.Packages
    .Where(p => p.IsActive && !p.IsDeleted)
    .ToListAsync();

foreach (var package in activePackages)
{
    _logger.LogInformation(
        "Calculating commission for package {Id}: {Title} " +
        "(MaxBalances={Max}, MaxLevel={Level})", 
        package.Id, package.Title, 
        package.MaxBalancesPerLeg, package.MaxNetworkLevel);
    
    // v3: پاس دادن تنظیمات پکیج
    await strategy.CalculateWeeklyBalancesAsync(
        weekId, package.Id, 
        package.MaxBalancesPerLeg, package.MaxNetworkLevel);
    
    await strategy.CalculateWeeklyPoolAsync(weekId, package.Id);
}

T3.4 — OrmCommissionCalculationStrategy (🔄 v3)

تغییرات:

- var maxBalances = SystemConstants.CommissionMaxWeeklyBalancesPerLeg; // 300
- var maxLevel = SystemConstants.CommissionMaxNetworkLevel;           // 15
+ // پارامتر از بیرون — per-package
+ int maxBalances = maxBalancesPerLeg;  // e.g., نقره‌ای=30, پایه=300
+ int maxLevel = maxNetworkLevel;

- // فیلتر کاربران
+ // فیلتر کاربران بر اساس پکیج
+ .Where(m => m.PackageId == packageId && m.IsActive)

- // carryover
+ // carryover: فقط رکوردهای همان PackageId
+ .Where(b => b.PackageId == packageId && b.WeekDefinitionId == prevWeekId)

T3.5 — SpCommissionCalculationStrategy (🆕 v3)

// قبل: فقط WeekDefinitionId
await connection.ExecuteAsync("CMS.sp_CalculateWeeklyBalances",
    new { WeekDefinitionId = weekId, ForceRecalculate = true });

// بعد (v3): پکیج + تنظیمات داینامیک
await connection.ExecuteAsync("CMS.sp_CalculateWeeklyBalances",
    new { 
        WeekDefinitionId = weekId, 
        PackageId = package.Id,
        MaxBalancesPerLeg = package.MaxBalancesPerLeg,
        MaxNetworkLevel = package.MaxNetworkLevel,
        ForceRecalculate = true 
    });

T3.6 — Carryover per-package (🆕 v3)

⚠️ بحرانی: week-shifting باید فقط رکوردهای همان PackageId را shift کند

هفته ۱۰ → هفته ۱۱:
    علی: carryover_پایه = {Left: surplus, Right: surplus}    ← جداگانه
    علی: carryover_نقره‌ای = {Left: 0, Right: 0}             ← جداگانه
    
    ✖ اشتباه: قاطی کردن carryover پایه و نقره‌ای!
    ✔ صحیح: هر PackageId فقط carryover خودش را می‌بینه

⚠️ نکته بحرانی

پورسانت = پول واقعی. هر تغییر در SPs باید:

  1. ابتدا در staging با داده واقعی تست شود
  2. نتایج قبل و بعد مقایسه شوند
  3. Rollback plan آماده باشد
  4. در production ابتدا read-only اجرا شود (بدون commit)

مرحله ۷ — Migration + Cleanup (سه‌گانه)

تکمیل‌شده | ⏱️ ۱ روز | ریسک: پایین

فاز 7a — Cosmetic Cleanup

کامیت: CMS 469d97b | FO b82cac4 | BO f1b0085

CMS:

  • حذف orphaned PurchasePackage handler (۳ فایل، بدون caller)
  • فیکس doc-comments: طلاییپکیج در ۶ فایل (enums, entities, handlers)

FrontOffice:

  • حذف hardcoded پکیج طلایی از MyPackages.razor و Packages.razor
  • اضافه PackageTitle property به UserPackageStatusDto record

BackOffice:

  • تغییر label پکیج طلاییخرید پکیج در UserNetworkInfo.razor

فاز 7b — FrontOffice RPC Migration

کامیت: CMS 161f796 | FO 71f391a

CMS:

  • CustomerPurchasePackage: embed orderId در callback URL قبل از ارسال به درگاه
    • $"{request.CallbackUrl}{separator}orderId={purchase.Id}"

FrontOffice:

  • Profile/Index.razor.cs: مهاجرت InitiateBasePackagePaymentAsyncCustomerPurchasePackageAsync
  • Profile/PaymentCallback.razor: مهاجرت VerifyBasePackagePaymentAsyncCustomerVerifyPackagePurchaseAsync
  • پارامترهای جدید: PackageId, CallbackUrl, PurchaseMethod, OrderId, Authority, Status

فاز 7c — Delete Deprecated Handlers

کامیت: CMS 8446e0e (14 فایل، 1125 حذف)

حذف ۴ handler CQRS (۱۲ فایل):

  • PurchaseGoldenPackage/ (Command, Handler, Validator)
  • VerifyGoldenPackagePurchase/ (Command, Handler, Validator)
  • InitiateBasePackagePayment/ (Command, Handler, Validator)
  • VerifyBasePackagePayment/ (Command, Handler, Validator)

Cleanup:

  • PackageService.cs: حذف ۴ gRPC override method (proto RPCs حالا auto-throw Unimplemented)
  • PackageProfile.cs: حذف ۶ Mapster mapping block + ۴ using directive
  • Build: 0 Error

مرحله ۸ — FrontOffice Checkout + NuGet

🔄 در حال اجرا | فاز 8a تکمیل

فاز 8a — Checkout Wire-up

کامیت: FO 0bbc11e

Checkout.razor.cs:

  • حذف dead code: ProcessPayment() از flow قدیمی TransactionsContract + UserOrderContract استفاده می‌کرد
  • Rewrite با CustomerPurchasePackageAsync (مثل Profile/Index.razor.cs)
  • Callback URL → /profile/payment-callback (از صفحه verify موجود استفاده مجدد)
  • حذف DI بلااستفاده: UserOrderContract, TransactionContract
  • حذف usings: Transactions, UserOrder, WellKnownTypes

Profile/Index.razor.cs (cosmetic):

  • Rename basePackageselectedPackage, tempCallbackUrlcallbackUrl

فاز 8b — NuGet Rebuild + BackOffice Fix

بلاکر: BackOffice هنوز از HasPurchasedGoldenPackage استفاده می‌کند (NuGet v0.0.184) Proto field renamed: has_purchased_golden_packagehas_purchased_package (field 35) FrontOffice هم روی v0.0.182 هست (۲ نسخه عقب)

تسک‌ها:

  1. Bump proto version → 0.0.185
  2. Pack + Push NuGet
  3. BackOffice: HasPurchasedGoldenPackageHasPurchasedPackage در UserNetworkInfo.razor
  4. FrontOffice: Bump NuGet 0.0.1820.0.185

مرحله ۴ — UI (FrontOffice + BackOffice)

⏱️ ۵ روز (v3: +۲) | وابستگی: مرحله ۲ + ۳ | ریسک: متوسط

T4.1 — کاشی‌های پکیج داینامیک

فایل: FrontOffice/src/.../Pages/Package/Packages.razor

@* قبل: hardcoded *@
@* بعد: *@
@foreach (var package in _packages.OrderBy(p => p.SortOrder))
{
    <PackageCard Package="@package" 
                 OnPurchase="StartPurchase"
                 ShowFeatures="true"
                 ShowPV="true" />
}

T4.2 — مودال پرداخت شرطی

@if (_selectedPackage.SupportsDirectPurchase)
{
    <MudButton OnClick="PayOnline">پرداخت آنلاین</MudButton>
}
@if (_selectedPackage.SupportsDayaPurchase)
{
    <MudButton OnClick="PayDaya">اقساط دایا</MudButton>
}

T4.3 — MyPackages + Re-Purchase

@if (_canRepurchase)
{
    <MudAlert Severity="Severity.Success">
        🎉 چرخه جادویی تکمیل شد! می‌توانید پکیج جدید بخرید.
    </MudAlert>
    <MudButton Href="/packages">خرید پکیج جدید</MudButton>
}
else
{
    <MagicWalletProgress Wallet="@_wallet" Cycle="@_currentCycle" />
}

T4.8 — FrontOffice: CommissionDashboard per-package (🆕 v3)

فایل: FrontOffice/src/.../Pages/Commission/CommissionDashboardPage.razor

@* v3: کارت‌های خلاصه بر اساس پکیج *@
<MudGrid>
    @foreach (var summary in _packageSummaries)
    {
        <MudItem xs="12" md="6">
            <MudCard>
                <MudCardHeader>@summary.PackageTitle</MudCardHeader>
                <MudCardContent>
                    <p>زیرمجموعه: @summary.DownlineCount نفر</p>
                    <p>تیم اول: @summary.LeftLegMembers | تیم دوم: @summary.RightLegMembers</p>
                    <p>تعادل: @summary.TotalBalances</p>
                    <p>💰 پاداش: @summary.CommissionFormatted</p>
                </MudCardContent>
            </MudCard>
        </MudItem>
    }
</MudGrid>
<MudAlert>📦 مجموع پاداش هفته: @_totalCommission</MudAlert>

T4.9 — FrontOffice: WeeklyBalance per-package (🆕 v3)

تعادل تیم اول/دوم به تفکیک هر پکیج نمایش داده شود

T4.10-T4.12 — BackOffice: گزارش‌های پورسانت per-package (🆕 v3)

تغییرات مشترک:

  • اضافه dropdown فیلتر پکیج (همه / پایه / نقره‌ای / ...)
  • اضافه ستون پکیج در جدول‌ها
  • CSV export شامل ستون پکیج
صفحه تغییر
Commission Dashboard فیلتر + خلاصه per-package
Weekly Reports فیلتر + CSV per-package
Balances Report فیلتر + ستون پکیج
User Payouts ستون پکیج + فیلتر

T4.13 — BackOffice: Package CRUD + Quick Access فیچرها (🆕 v3)

ادمین موقع ساخت/ویرایش پکیج، فیچرها را با checkbox در همان فرم تعیین کند

@* در PackageCreateDialog / PackageEditDialog: *@
<MudText Typo="Typo.h6">فیچرهای پکیج</MudText>
@foreach (var feature in _allFeatures)
{
    <MudCheckBox @bind-Checked="feature.IsIncluded" Label="@feature.Title" />
}

مرحله ۵ — تست و استقرار

⏱️ ۳ روز (v3: +۱) | وابستگی: مرحله ۴

Checklist تست

خرید + فعال‌سازی:

  • خرید پکیج نقره‌ای (ZarinPal)
  • خرید پکیج پایه (ZarinPal)
  • خرید پکیج پایه (Daya Loan)
  • خرید پکیج پایه (Manual Payment)
  • فعالسازی باشگاه با پکیج نقره‌ای → فیچرهای محدود
  • فعالسازی باشگاه با پکیج پایه → همه فیچرها

چرخه Magic + خرید مجدد:

  • تکمیل چرخه Magic → ریست وضعیت
  • خرید مجدد بعد تکمیل چرخه (همان پکیج)
  • خرید مجدد با پکیج متفاوت (پایه → نقره‌ای)

پورسانت per-package (v3):

  • Commission Pool جداگانه هر پکیج
  • تعادل per-package: MaxBalancesPerLeg متفاوت (پایه=۳۰۰, نقره‌ای=۳۰)
  • Carryover مجزا: shift فقط رکوردهای همان PackageId
  • SP پارامترها صحیح: @MaxBalancesPerLeg و @MaxNetworkLevel از Package
  • NetworkWeeklyBalance رکوردها: ۲ پکیج = ۲× رکورد

گزارش per-package (v3):

  • FO: مشتری کارت‌های خلاصه per-package را می‌بیند
  • FO: مجموع پاداش = جمع همه پکیج‌ها
  • BO: فیلتر dropdown پکیج کار می‌کند
  • BO: CSV export شامل ستون پکیج

Migration + سایر:

  • Data Migration — PackageId در رکوردهای قبلی (شامل NetworkWeeklyBalance)
  • JWT claims جدید (CanRepurchase, PackageId)
  • UI: کاشی‌های داینامیک FrontOffice
  • UI: ماتریس فیچر + Quick Access BackOffice
  • Rollback: بدون data loss

📅 تقویم پیشنهادی (v3)

هفته روز تسک
هفته ۱ روز ۱ مرحله ۰: فیکس ۴ باگ
روز ۲-۳ مرحله ۱: Package entity (۱۱ فیلد) + PackageFeature
روز ۴-۵ مرحله ۱: FKها + NetworkWeeklyBalance + Migration
هفته ۲ روز ۶-۷ مرحله ۲: Generic handlers + re-purchase
روز ۶-۸ مرحله ۳: SP params + carryover per-package (موازی)
روز ۸-۱۰ مرحله ۲: Guards + JWT + Manual
هفته ۳ روز ۱۱-۱۲ مرحله ۴: FrontOffice UI + گزارش per-package
روز ۱۳-۱۴ مرحله ۴: BackOffice UI + گزارش per-package
روز ۱۵-۱۷ مرحله ۵: تست + deploy

🔗 ارجاعات

مستند محتوا
BIZ-PACKAGE-BASED-SYSTEM.md طراحی فنی — ۴۸+ تغییر (v3) + باگ‌ها
PACKAGE-TRANSFORMATION-UX.md تاثیر UX بر فرانت‌ها
FEATURE-BACKLOG.md بکلاگ ۱۲ RPC آماده
GRPC-SERVICES-AUDIT.md آدیت ۳۴۲ RPC

آخرین بروزرسانی: ۱۴۰۴/۱۲/۰۹ — فاز ۰-۸a تکمیل (۱۷ کامیت: ۱۳ CMS + ۳ FO + ۱ BO) | Checkout فعال‌شده | ۴ deprecated handler حذف | FO کامل مهاجرت به Customer RPCs*