# مستندات سیستم کمیسیون (Commission System)

> **آخرین بروزرسانی**: ۲۹ آذر ۱۴۰۴ (19 December 2025)

---

## 📋 خلاصه

سیستم کمیسیون مسئول محاسبه، ذخیره و پرداخت کمیسیون‌های کاربران بر اساس ساختار شبکه بازاریابی است.

---

## 🗄️ موجودیت‌ها (Entities)

### WeekDefinition
جدول مرجع برای تعریف هفته‌های مالی:

```csharp
public class WeekDefinition : BaseAuditableEntity
{
    public int WeekOrder { get; set; }        // شماره ترتیبی هفته
    public int Year { get; set; }             // سال میلادی
    public int PersianYear { get; set; }      // سال شمسی
    public DateTime StartDate { get; set; }    // تاریخ شروع
    public DateTime EndDate { get; set; }      // تاریخ پایان
    public string StartDatePersian { get; set; }  // تاریخ شروع شمسی
    public string EndDatePersian { get; set; }    // تاریخ پایان شمسی
    public bool IsActive { get; set; }         // آیا هفته جاری است
}
```

### NetworkWeeklyBalance
تعادل هفتگی شاخه چپ و راست کاربر:

```csharp
public class NetworkWeeklyBalance : BaseAuditableEntity
{
    public long UserId { get; set; }
    public long WeekDefinitionId { get; set; }  // FK به WeekDefinition
    public long LeftBalance { get; set; }       // امتیاز شاخه چپ
    public long RightBalance { get; set; }      // امتیاز شاخه راست
    
    // Navigation Properties
    public virtual User User { get; set; }
    public virtual WeekDefinition WeekDefinition { get; set; }
}
```

**Index**: `(UserId, WeekDefinitionId)` - Unique

### WeeklyCommissionPool
استخر کمیسیون هفتگی:

```csharp
public class WeeklyCommissionPool : BaseAuditableEntity
{
    public long WeekDefinitionId { get; set; }  // FK به WeekDefinition
    public long TotalPoolAmount { get; set; }   // مجموع استخر
    public long DistributedAmount { get; set; } // مقدار توزیع شده
    public int TotalBalances { get; set; }      // تعداد کل تعادل‌ها
    public long PerBalanceAmount { get; set; }  // مبلغ هر تعادل
    public bool IsFinalized { get; set; }       // آیا نهایی شده
    
    // Navigation Property
    public virtual WeekDefinition WeekDefinition { get; set; }
}
```

### UserCommissionPayout
رکورد پرداخت کمیسیون به کاربر:

```csharp
public class UserCommissionPayout : BaseAuditableEntity
{
    public long UserId { get; set; }
    public long WeekDefinitionId { get; set; }  // FK به WeekDefinition
    public int BalancesEarned { get; set; }     // تعداد تعادل‌های کسب شده
    public long Amount { get; set; }            // مبلغ کمیسیون
    public CommissionPayoutStatus Status { get; set; }  // وضعیت پرداخت
    public DateTime? PaidAt { get; set; }       // تاریخ پرداخت
    
    // Navigation Properties
    public virtual User User { get; set; }
    public virtual WeekDefinition WeekDefinition { get; set; }
}
```

**وضعیت‌ها (Status)**:
- `Created` - ایجاد شده
- `Paid` - پرداخت به کیف پول
- `WithdrawalRequested` - درخواست برداشت
- `Withdrawn` - برداشت شده
- `Cancelled` - لغو شده

### CommissionPayoutHistory
تاریخچه تغییرات وضعیت پرداخت:

```csharp
public class CommissionPayoutHistory : BaseAuditableEntity
{
    public long UserCommissionPayoutId { get; set; }
    public long WeekDefinitionId { get; set; }  // FK به WeekDefinition
    public CommissionPayoutStatus FromStatus { get; set; }
    public CommissionPayoutStatus ToStatus { get; set; }
    public string? Notes { get; set; }
    
    // Navigation Properties
    public virtual UserCommissionPayout UserCommissionPayout { get; set; }
    public virtual WeekDefinition WeekDefinition { get; set; }
}
```

### WorkerExecutionLog
لاگ اجرای Worker های محاسبه کمیسیون:

```csharp
public class WorkerExecutionLog : BaseAuditableEntity
{
    public string WorkerName { get; set; }       // نام Worker
    public long? WeekDefinitionId { get; set; }  // FK به WeekDefinition (nullable)
    public DateTime StartedAt { get; set; }       // زمان شروع
    public DateTime? CompletedAt { get; set; }    // زمان پایان
    public bool IsSuccess { get; set; }           // موفقیت
    public string? ErrorMessage { get; set; }     // پیام خطا
    public int ProcessedCount { get; set; }       // تعداد پردازش شده
    
    // Navigation Property
    public virtual WeekDefinition? WeekDefinition { get; set; }
}
```

---

## 🔗 روابط (Relationships)

```
WeekDefinition (1) ─────┬──── (*) NetworkWeeklyBalance
                        ├──── (*) WeeklyCommissionPool
                        ├──── (*) UserCommissionPayout
                        ├──── (*) CommissionPayoutHistory
                        └──── (*) WorkerExecutionLog

User (1) ───────────────┬──── (*) NetworkWeeklyBalance
                        └──── (*) UserCommissionPayout

UserCommissionPayout (1) ──── (*) CommissionPayoutHistory
```

---

## 📡 Proto Models

### UserCommissionPayoutModel
```protobuf
message UserCommissionPayoutModel {
    int64 id = 1;
    int64 user_id = 2;
    string user_full_name = 3;
    int64 week_definition_id = 4;      // شناسه هفته
    int32 balances_earned = 5;          // تعداد تعادل
    int64 amount = 6;                   // مبلغ
    int32 status = 7;                   // وضعیت
    google.protobuf.Timestamp paid_at = 8;
    google.protobuf.Timestamp created = 9;
    string mobile = 10;
    string week_display_name = 11;      // نام نمایشی هفته
}
```

### UserWeeklyBalanceModel
```protobuf
message UserWeeklyBalanceModel {
    int64 user_id = 1;
    int64 week_definition_id = 2;       // شناسه هفته
    int64 left_balance = 3;
    int64 right_balance = 4;
    string start_date_persian = 5;
    string end_date_persian = 6;
    int32 year = 7;
    int32 week_order = 8;
    bool is_active = 9;
    string week_display_name = 10;      // نام نمایشی هفته
}
```

---

## 🎯 نام‌گذاری فیلدها

### قبل از مایگریشن (Legacy)
```
WeekNumber: "2025-01" (string)
GregorianWeekNumber: "2025-01" (string)
PersianWeekNumber: "1403-40" (string)
WeekLabel: "هفته 1 - 1403/10/01"
```

### بعد از مایگریشن (Current)
```
WeekDefinitionId: 42 (long)           // FK به جدول WeekDefinition
WeekDisplayName: "هفته 1 - 1403/10/01" // ساخته شده از WeekDefinition
```

**فرمول WeekDisplayName**:
```csharp
$"هفته {WeekDefinition.WeekOrder} - {WeekDefinition.StartDatePersian}"
```

---

## 📊 Query Examples

### دریافت کمیسیون‌های کاربر
```csharp
var payouts = await _context.UserCommissionPayouts
    .Include(p => p.WeekDefinition)
    .Where(p => p.UserId == userId)
    .OrderByDescending(p => p.WeekDefinition.WeekOrder)
    .Select(p => new {
        p.Id,
        p.WeekDefinitionId,
        WeekDisplayName = $"هفته {p.WeekDefinition.WeekOrder} - {p.WeekDefinition.StartDatePersian}",
        p.BalancesEarned,
        p.Amount,
        p.Status
    })
    .ToListAsync();
```

### دریافت تعادل هفتگی
```csharp
var balance = await _context.NetworkWeeklyBalances
    .Include(b => b.WeekDefinition)
    .Where(b => b.UserId == userId && b.WeekDefinitionId == weekDefinitionId)
    .Select(b => new {
        b.WeekDefinitionId,
        WeekDisplayName = $"هفته {b.WeekDefinition.WeekOrder} - {b.WeekDefinition.StartDatePersian}",
        b.LeftBalance,
        b.RightBalance,
        b.WeekDefinition.StartDatePersian,
        b.WeekDefinition.EndDatePersian
    })
    .FirstOrDefaultAsync();
```

---

## ⚠️ ملاحظات مایگریشن

### EF Migration
```bash
# ایجاد migration
dotnet ef migrations add MigrateWeekNumberToWeekDefinitionId \
    -p CMSMicroservice.Infrastructure \
    -s CMSMicroservice.WebApi

# اجرای migration
dotnet ef database update \
    -p CMSMicroservice.Infrastructure \
    -s CMSMicroservice.WebApi
```

### Data Migration Script
```sql
-- Step 1: Add new column
ALTER TABLE NetworkWeeklyBalances ADD WeekDefinitionId BIGINT NULL;

-- Step 2: Populate from WeekDefinitions
UPDATE nwb
SET nwb.WeekDefinitionId = wd.Id
FROM NetworkWeeklyBalances nwb
INNER JOIN WeekDefinitions wd ON 
    CONCAT(wd.Year, '-', RIGHT('0' + CAST(wd.WeekOrder AS VARCHAR), 2)) = nwb.WeekNumber;

-- Step 3: Add FK constraint
ALTER TABLE NetworkWeeklyBalances 
ADD CONSTRAINT FK_NetworkWeeklyBalances_WeekDefinitions 
FOREIGN KEY (WeekDefinitionId) REFERENCES WeekDefinitions(Id);

-- Step 4: Drop old column (after verification)
ALTER TABLE NetworkWeeklyBalances DROP COLUMN WeekNumber;
```

---

## 📝 تغییرات API

### Request Changes
```
// قبل
GET /api/commission/payouts?weekNumber=2025-01

// بعد
GET /api/commission/payouts?weekDefinitionId=42
```

### Response Changes
```json
// قبل
{
  "weekNumber": "2025-01",
  "weekLabel": "هفته 1 - 1403/10/01"
}

// بعد
{
  "weekDefinitionId": 42,
  "weekDisplayName": "هفته 1 - 1403/10/01"
}
```
