# اصلاحات سیستم کمیسیون هفتگی ## 📋 خلاصه تغییرات سیستم کمیسیون هفتگی از **3 مرحله به 2 مرحله** ساده‌سازی شد: ### ❌ قبل (3 مرحله): 1. `CalculateWeeklyBalances` - محاسبه تعادل‌ها 2. `CalculateWeeklyCommissionPool` - محاسبه استخر 3. `ProcessUserPayouts` - پردازش پرداخت‌ها (تکراری!) ### ✅ بعد (2 مرحله): 1. `CalculateWeeklyBalances` - محاسبه تعادل‌ها تا 15 لول 2. `CalculateWeeklyCommissionPool` - محاسبه استخر + پردازش پرداخت‌ها --- ## 🔧 تغییرات جزئی ### 1️⃣ اضافه شدن فیلدها به `NetworkWeeklyBalance` **فیلدهای جدید:** ```csharp /// /// مقدار فلش هر طرف (بعد از اعمال Cap) /// public int FlushedPerSide { get; set; } /// /// مجموع فلش از دو طرف (از دست رفته) /// public int TotalFlushed { get; set; } ``` **Migration:** `AddFlushedFieldsToNetworkWeeklyBalance` --- ### 2️⃣ اصلاح `CalculateWeeklyBalances` **تغییرات:** - ✅ فیلدهای `FlushedPerSide` و `TotalFlushed` ذخیره می‌شوند - ✅ `WeeklyPoolContribution = 0` (دیگر در این مرحله محاسبه نمیشه) - ✅ محدودیت 15 لول قبلاً موجود بود و درست کار می‌کند **کد:** ```csharp // محاسبه فلش var flushedPerSide = totalBalances - cappedBalances; var totalFlushed = flushedPerSide * 2; // ذخیره balance.FlushedPerSide = flushedPerSide; balance.TotalFlushed = totalFlushed; balance.WeeklyPoolContribution = 0; // Pool در مرحله بعد محاسبه میشه ``` --- ### 3️⃣ اصلاح کامل `CalculateWeeklyCommissionPool` **منطق جدید Pool:** ```csharp // 1. Pool از فعالسازی‌های باشگاه این هفته میاد (نه از تعادل‌ها) var newClubMembersCount = await _context.ClubMemberships .Where(c => c.ActivatedAt >= startDate && c.ActivatedAt <= endDate) .CountAsync(); var totalPoolAmount = newClubMembersCount * activationFee; // 2. ارزش هر امتیاز var totalBalancesInNetwork = weeklyBalances.Sum(x => x.TotalBalances); var valuePerBalance = totalPoolAmount / totalBalancesInNetwork; ``` **افزوده شدن محاسبه تعادل زیرمجموعه:** ```csharp // برای هر کاربر: // 1. تعادل خودش var directBalances = balance.TotalBalances; // 2. تعادل زیرمجموعه (تا 15 لول) var subordinateBalances = await CalculateSubordinateBalancesAsync( balance.UserId, request.WeekNumber, maxLevels: 15 ); var totalBalancesForUser = directBalances + subordinateBalances; ``` **ایجاد UserCommissionPayout:** ```csharp var payout = new UserCommissionPayout { UserId = balance.UserId, WeekNumber = request.WeekNumber, WeeklyPoolId = existingPool.Id, BalancesEarned = totalBalancesForUser, ValuePerBalance = valuePerBalance, TotalAmount = totalBalancesForUser * valuePerBalance, Status = CommissionPayoutStatus.Pending, // ... subordinate fields }; ``` **ثبت تاریخچه:** ```csharp var history = new CommissionPayoutHistory { UserId = payout.UserId, PayoutId = payout.Id, Amount = payout.TotalAmount, Status = CommissionPayoutStatus.Pending, ChangeReason = "محاسبه اولیه کمیسیون هفتگی" }; ``` --- ### 4️⃣ ساده‌سازی `TriggerWeeklyCalculation` **قبل:** ```csharp // Step 1 await _mediator.Send(new CalculateWeeklyBalancesCommand { ... }); // Step 2 await _mediator.Send(new CalculateWeeklyCommissionPoolCommand { ... }); // Step 3 await _mediator.Send(new ProcessUserPayoutsCommand { ... }); ``` **بعد:** ```csharp // Step 1: محاسبه تعادل‌ها if (!request.SkipBalances) { await _mediator.Send(new CalculateWeeklyBalancesCommand { ... }); } // Step 2: محاسبه Pool و پرداخت‌ها if (!request.SkipPayouts) { await _mediator.Send(new CalculateWeeklyCommissionPoolCommand { ... }); } ``` **حذف شد:** - ❌ `SkipPool` flag - ❌ Step 3 کاملاً حذف شد --- ## 🎯 فرآیند نهایی ### مرحله 1: محاسبه تعادل‌ها ``` 1. برای هر کاربر در شبکه 2. تا 15 لول پایین‌تر شمارش کن 3. محاسبه تعادل (MIN of left/right) 4. محاسبه باقیمانده 5. محاسبه فلش 6. ذخیره در NetworkWeeklyBalance ``` ### مرحله 2: محاسبه Pool و توزیع ``` 1. شمارش فعالسازی‌های باشگاه این هفته 2. Pool = تعداد × ActivationFee 3. ارزش هر امتیاز = Pool ÷ مجموع تعادل‌ها 4. برای هر کاربر: a. تعادل خودش + تعادل زیرمجموعه (تا 15 لول) b. سهم = تعادل × ارزش c. ثبت در UserCommissionPayout d. ثبت تاریخچه ``` --- ## 📊 جداول درگیر ### `NetworkWeeklyBalance` (فیلدهای جدید) ```sql ALTER TABLE [Network].[NetworkWeeklyBalances] ADD [FlushedPerSide] INT NOT NULL DEFAULT 0, [TotalFlushed] INT NOT NULL DEFAULT 0; ``` ### `WeeklyCommissionPool` ``` - TotalPoolAmount: از فعالسازی‌های باشگاه - TotalBalances: مجموع تعادل‌های شبکه - ValuePerBalance: Pool ÷ TotalBalances ``` ### `UserCommissionPayout` ``` - BalancesEarned: تعادل خودش + زیرمجموعه - DirectBalances: فقط تعادل خودش - SubordinateBalances: فقط زیرمجموعه - TotalAmount: BalancesEarned × ValuePerBalance - Status: Pending ``` ### `CommissionPayoutHistory` ``` - PayoutId: شناسه UserCommissionPayout - Status: Pending (در این مرحله) - ChangeReason: "محاسبه اولیه کمیسیون هفتگی" ``` --- ## ✅ مزایا 1. **ساده‌تر**: 2 مرحله به جای 3 2. **بدون تکرار**: دیگر UserCommissionPayout دوبار ساخته نمیشه 3. **واضح‌تر**: Pool از کجا میاد مشخصه 4. **قابل نگهداری**: منطق مشابه یکجا هست 5. **کامل**: تاریخچه + subordinate balances همه جا هست --- ## 🔄 مراحل بعدی (اختیاری) ### مرحله 3: پرداخت واقعی (جدا از محاسبه) می‌توان یک Command جدید داشت که: 1. `UserCommissionPayout` با status=Pending رو بخونه 2. به کیف پول واریز کنه 3. Status رو به Paid تغییر بده 4. تاریخچه اضافه کنه این مرحله **جدا از محاسبات** است و می‌تواند: - دستی توسط ادمین اجرا شود - یا به صورت خودکار بعد از تایید --- ## 📝 نکات مهم ### Pool چطور پُر میشه؟ ``` 1. کاربر عضو Club میشه 2. در ActivateClubMembership مبلغی کسر میشه 3. این مبلغ به Pool اضافه **نمیشه** (فقط شمارش میشه) 4. در محاسبه Pool: تعداد × ActivationFee ``` ### چرا subordinate balances؟ ``` در سیستم باینری، کاربر از تعادل زیرمجموعه‌های خود (تا 15 لول پایین‌تر) هم کمیسیون می‌گیرد. ``` ### چرا 15 لول؟ ``` محدودیت عمق برای جلوگیری از بارگذاری بیش از حد و تشویق به ایجاد شبکه متعادل ``` --- ## 🧪 تست ### تست مرحله 1 ```csharp // 1. ایجاد کاربران در شبکه // 2. فعالسازی Club برای برخی // 3. اجرای CalculateWeeklyBalances // 4. بررسی NetworkWeeklyBalance // - TotalBalances // - FlushedPerSide // - TotalFlushed ``` ### تست مرحله 2 ```csharp // 1. اجرای مرحله 1 // 2. اجرای CalculateWeeklyCommissionPool // 3. بررسی WeeklyCommissionPool // - TotalPoolAmount = تعداد فعالسازی‌ها × ActivationFee // - ValuePerBalance صحیح باشد // 4. بررسی UserCommissionPayout // - برای هر کاربر ایجاد شده // - BalancesEarned شامل subordinate هم هست // - TotalAmount = BalancesEarned × ValuePerBalance // 5. بررسی CommissionPayoutHistory // - برای هر پرداخت ثبت شده ``` --- ## 📚 فایل‌های تغییر یافته 1. ✅ `NetworkWeeklyBalance.cs` - اضافه شدن فیلدها 2. ✅ `CalculateWeeklyBalancesCommandHandler.cs` - ذخیره فلش 3. ✅ `CalculateWeeklyCommissionPoolCommandHandler.cs` - منطق کامل جدید 4. ✅ `TriggerWeeklyCalculationCommandHandler.cs` - حذف مرحله 3 5. ✅ `TriggerWeeklyCalculationCommand.cs` - حذف SkipPool flag 6. ✅ Migration: `AddFlushedFieldsToNetworkWeeklyBalance` --- ## 🎉 نتیجه سیستم کمیسیون هفتگی حالا: - ✅ **ساده‌تر** و قابل فهم‌تر - ✅ **بدون تکرار** در کد - ✅ **Pool از منبع صحیح** (فعالسازی‌های Club) - ✅ **تعادل زیرمجموعه** محاسبه میشه - ✅ **تاریخچه کامل** ثبت میشه - ✅ **فلش دقیق** ذخیره میشه آماده برای استفاده در Production! 🚀