# 📋 Task List - توضیحات جدید بیزینس 2025-12-08

**تاریخ ایجاد**: 2025-12-08  
**آخرین به‌روزرسانی**: 2025-12-09  
**منبع**: تحلیل توضیحات شفاهی جدید بیزینس  
**وضعیت**: ✅ Task #0 Complete, بقیه آماده برای اجرا

---

## ✅ Completed Tasks

### ~~Task #0: اصلاح محاسبات تعادل و فلش~~ ✅

**شرح**:
کد محاسبه تعادل با توضیحات دقیق بیزینس تطبیق داده شد.

**انجام شده**:
- ✅ ترتیب محاسبات اصلاح شد (تعادل → باقیمانده → سقف → فلش)
- ✅ فلش از هر دو طرف محاسبه می‌شود
- ✅ باقیمانده جداگانه ذخیره می‌شود (چپ و راست)
- ✅ Documentation به‌روزرسانی شد
- ✅ مثال‌های 5 لول عمقی اضافه شد

**فایل‌های تغییر یافته**:
```
CMS/src/.../CalculateWeeklyBalancesCommandHandler.cs (اصلاح شد)
totalDoc/01-BUSINESS/balance-calculation-rules.md (به‌روزرسانی شد)
totalDoc/01-BUSINESS/balance-calculation-examples-5-levels.md (جدید)
```

**تاریخ اتمام**: 2025-12-09

---

## 🔥 Priority 1: Critical Tasks

### Task #1: پیاده‌سازی Worker حذف خودکار کاربران غیرفعال

**شرح**:
کاربرانی که تا 2 هفته بعد از ثبت نام هیچکدام از موارد زیر را انجام ندادند باید به صورت خودکار حذف شوند:
- وام دایا نگرفتند
- پرداخت مستقیم 56 میلیون نکردند

**Acceptance Criteria**:
- [ ] Worker روزانه یک بار اجرا شود (مثلاً ساعت 3 صبح)
- [ ] کاربرانی با `CreatedAt < Now - 14 days` و `IsActive = false` و `ClubMembershipId = null` شناسایی شوند
- [ ] کاربر به صورت Soft Delete حذف شود (یا Hard Delete بر اساس تصمیم)
- [ ] جایگاه شبکه (Network Position) آزاد شود
- [ ] معرف (Parent) بتواند دوباره کاربر جدید جذب کند
- [ ] Log کامل عملیات حذف ثبت شود

**فایل‌های نیاز به ایجاد/تغییر**:
```
CMS/src/CMSMicroservice.WebApi/BackgroundWorkers/
  └── DeleteInactiveUsersJob.cs (جدید)

CMS/src/CMSMicroservice.Application/UserCQ/Commands/
  └── DeleteInactiveUser/
      ├── DeleteInactiveUserCommand.cs (جدید)
      └── DeleteInactiveUserCommandHandler.cs (جدید)

CMS/src/CMSMicroservice.WebApi/Program.cs
  └── services.AddHostedService<DeleteInactiveUsersJob>();
```

**کد پیشنهادی**:
```csharp
public class DeleteInactiveUsersJob : BackgroundService
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ILogger<DeleteInactiveUsersJob> _logger;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            // محاسبه زمان اجرا (3 صبح)
            var now = DateTime.Now;
            var next3AM = now.Date.AddDays(1).AddHours(3);
            var delay = next3AM - now;
            
            await Task.Delay(delay, stoppingToken);
            
            using var scope = _serviceProvider.CreateScope();
            var context = scope.ServiceProvider.GetRequiredService<IApplicationDbContext>();
            
            var twoWeeksAgo = DateTime.Now.AddDays(-14);
            
            var inactiveUsers = await context.Users
                .Where(u => u.Created < twoWeeksAgo
                         && u.ClubMembershipId == null
                         && !u.IsActive)
                .ToListAsync(stoppingToken);
            
            _logger.LogInformation($"🧹 حذف {inactiveUsers.Count} کاربر غیرفعال بیش از 2 هفته");
            
            foreach (var user in inactiveUsers)
            {
                // حذف کاربر
                user.IsDeleted = true; // Soft Delete
                user.DeletedAt = DateTime.Now;
                
                // آزادسازی جایگاه شبکه
                // (NetworkParentId را null نکنید چون تاریخچه نیاز دارد)
                
                _logger.LogWarning($"❌ حذف کاربر: {user.Id} - {user.UserName}");
            }
            
            await context.SaveChangesAsync(stoppingToken);
        }
    }
}
```

**تست**:
1. کاربر جدید با `CreatedAt = DateTime.Now.AddDays(-15)` ایجاد کنید
2. `IsActive = false`, `ClubMembershipId = null`
3. Worker را مجبور به اجرا کنید (یا زمان را تغییر دهید)
4. چک کنید: `user.IsDeleted = true`

**تخمین زمان**: 4-6 ساعت

---

### Task #2: الزامی کردن دیالوگ باشگاه مشتریان

**شرح**:
بعد از پرداخت موفق 56 میلیون تومان، کاربر **باید** قرارداد باشگاه مشتریان را امضا کند. تا زمانی که امضا نکند، نمی‌تواند به سایر بخش‌های سیستم دسترسی داشته باشد و لینک معرفی خود را ببیند.

**Acceptance Criteria**:
- [ ] بعد از تأیید پرداخت، Modal/Dialog باشگاه مشتریان باز شود
- [ ] دکمه Close غیرفعال باشد (یا Modal با `disableBackdropClick` باز شود)
- [ ] کاربر نتواند از دیالوگ خارج شود (ESC هم کار نکند)
- [ ] بعد از امضای قرارداد:
  - `ClubMembership` record ایجاد شود
  - `User.ClubMembershipId` Set شود
  - 25 میلیون تومان به `WeeklyCommissionPool` اضافه شود
- [ ] بعد از امضا، redirect به Dashboard
- [ ] در Dashboard لینک معرفی نمایش داده شود

**فایل‌های نیاز به تغییر**:
```
FrontOffice/src/FrontOffice.Main/Pages/
  └── Payment/PaymentSuccess.razor
  └── Payment/PaymentSuccess.razor.cs

FrontOffice/src/FrontOffice.Main/Components/
  └── ClubMembershipDialog.razor (جدید یا اصلاح)

CMS/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/
  └── CreateClubMembership/
      ├── CreateClubMembershipCommand.cs
      └── CreateClubMembershipCommandHandler.cs
```

**کد پیشنهادی (Frontend)**:
```razor
@* PaymentSuccess.razor *@
@if (_showClubDialog)
{
    <MudDialog @bind-IsVisible="_showClubDialog" 
               Options="@(new DialogOptions { 
                   DisableBackdropClick = true, 
                   CloseButton = false 
               })">
        <DialogContent>
            <h3>عضویت در باشگاه مشتریان</h3>
            <p>برای ادامه، لطفاً قرارداد باشگاه مشتریان را مطالعه و امضا کنید.</p>
            
            <MudPaper Class="pa-4 my-4" Elevation="2">
                <p>متن قرارداد...</p>
            </MudPaper>
            
            <MudCheckBox @bind-Checked="_agreedToTerms">
                متن قرارداد را مطالعه کردم و با آن موافقم
            </MudCheckBox>
        </DialogContent>
        <DialogActions>
            <MudButton Variant="Variant.Filled" 
                       Color="Color.Primary" 
                       Disabled="!_agreedToTerms"
                       OnClick="SignContract">
                امضای قرارداد
            </MudButton>
        </DialogActions>
    </MudDialog>
}
```

```csharp
// PaymentSuccess.razor.cs
private bool _showClubDialog = false;
private bool _agreedToTerms = false;

protected override async Task OnInitializedAsync()
{
    // بعد از تأیید پرداخت
    if (PaymentConfirmed && !User.ClubMembershipId.HasValue)
    {
        _showClubDialog = true;
    }
}

private async Task SignContract()
{
    var request = new CreateClubMembershipRequest
    {
        UserId = User.Id,
        InitialContribution = 25000000
    };
    
    await ClubMembershipContract.CreateClubMembershipAsync(request);
    
    _showClubDialog = false;
    NavigationManager.NavigateTo("/dashboard");
}
```

**تست**:
1. پرداخت 56M انجام دهید
2. بعد از موفقیت، باید Dialog باز شود
3. سعی کنید Close کنید → نشود
4. بدون tick نزدن → دکمه غیرفعال باشد
5. tick بزنید و امضا کنید → redirect به Dashboard
6. لینک معرفی نمایش داده شود

**تخمین زمان**: 6-8 ساعت

---

### Task #3: شرط نمایش لینک معرفی

**شرح**:
لینک معرفی فقط باید برای کاربرانی نمایش داده شود که:
1. پرداخت کرده‌اند (`IsActive = true`)
2. عضو باشگاه مشتریان شده‌اند (`ClubMembershipId != null`)
3. عضویت باشگاه فعال است (`ClubMembership.IsActive = true`)

**Acceptance Criteria**:
- [ ] در صفحه Dashboard یا Profile، شرط بالا چک شود
- [ ] اگر شرایط برقرار نیست:
  - پیغام: "برای دریافت لینک معرفی، ابتدا عضو باشگاه مشتریان شوید"
  - دکمه "عضویت در باشگاه" (در صورت عدم عضویت)
- [ ] اگر شرایط برقرار است:
  - لینک معرفی نمایش داده شود
  - دکمه کپی
  - QR Code (اختیاری)

**فایل‌های نیاز به تغییر**:
```
FrontOffice/src/FrontOffice.Main/Pages/
  └── Dashboard/Dashboard.razor
  └── Dashboard/Dashboard.razor.cs

یا

FrontOffice/src/FrontOffice.Main/Pages/
  └── Profile/MyProfile.razor
```

**کد پیشنهادی**:
```razor
@if (CanShowReferralLink)
{
    <MudCard Class="my-4">
        <MudCardHeader>
            <CardHeaderContent>
                <MudText Typo="Typo.h6">🔗 لینک معرفی شما</MudText>
            </CardHeaderContent>
        </MudCardHeader>
        <MudCardContent>
            <MudTextField @bind-Value="_referralLink" 
                          ReadOnly="true" 
                          Adornment="Adornment.End"
                          AdornmentIcon="@Icons.Material.Filled.ContentCopy"
                          OnAdornmentClick="CopyLink"/>
        </MudCardContent>
    </MudCard>
}
else
{
    <MudAlert Severity="Severity.Warning" Class="my-4">
        برای دریافت لینک معرفی، ابتدا عضو باشگاه مشتریان شوید.
        @if (!User.ClubMembershipId.HasValue)
        {
            <MudButton Color="Color.Primary" 
                       Variant="Variant.Filled" 
                       Class="mt-2"
                       OnClick="OpenClubDialog">
                عضویت در باشگاه
            </MudButton>
        }
    </MudAlert>
}
```

```csharp
private bool CanShowReferralLink => 
    User.IsActive 
    && User.ClubMembershipId.HasValue 
    && User.ClubMembership?.IsActive == true;
```

**تست**:
1. کاربر بدون `ClubMembership` → Alert نمایش داده شود
2. کاربر با `ClubMembership` فعال → لینک نمایش داده شود
3. دکمه کپی کار کند

**تخمین زمان**: 3-4 ساعت

---

## ⚠️ Priority 2: Medium Tasks

### Task #4: Validation دقیق‌تر محدودیت 2 فرزند فعال

**شرح**:
در هنگام ثبت نام با کد معرف، باید بررسی شود که آیا Parent حداکثر **2 فرزند فعال** دارد یا نه (نه فقط 2 فرزند).

**Acceptance Criteria**:
- [ ] Validation در `CreateUserCommandHandler` یا `NetworkPlacementService`
- [ ] شمارش فرزندان با شرط:
  ```csharp
  u.NetworkParentId == parentId 
  && u.IsActive 
  && u.ClubMembershipId != null
  ```
- [ ] اگر `activeChildCount >= 2`:
  - Exception: "این کاربر تعداد زیرمجموعه‌هاش پر شده و شما نمی‌تونید جزو زیرمجموعه این آدم بشید"
  - یا Auto-Placement (بر اساس تصمیم)

**فایل‌های نیاز به تغییر**:
```
CMS/src/CMSMicroservice.Application/Services/
  └── NetworkPlacementService.cs

CMS/src/CMSMicroservice.Application/UserCQ/Commands/CreateUser/
  └── CreateUserCommandHandler.cs
  └── CreateUserCommandValidator.cs
```

**کد پیشنهادی**:
```csharp
public async Task<NetworkLeg?> CalculateLegPositionAsync(long parentId, CancellationToken cancellationToken)
{
    var activeChildrenCount = await _context.Users
        .CountAsync(u => u.NetworkParentId == parentId 
                      && u.IsActive 
                      && u.ClubMembershipId != null, 
                    cancellationToken);
    
    if (activeChildrenCount >= 2)
    {
        throw new InvalidOperationException(
            "این کاربر تعداد زیرمجموعه‌هاش پر شده و شما نمی‌تونید جزو زیرمجموعه این آدم بشید");
    }
    
    // بررسی Left و Right
    var hasLeft = await _context.Users
        .AnyAsync(u => u.NetworkParentId == parentId 
                    && u.LegPosition == NetworkLeg.Left
                    && u.IsActive 
                    && u.ClubMembershipId != null, 
                  cancellationToken);
    
    if (!hasLeft) return NetworkLeg.Left;
    
    var hasRight = await _context.Users
        .AnyAsync(u => u.NetworkParentId == parentId 
                    && u.LegPosition == NetworkLeg.Right
                    && u.IsActive 
                    && u.ClubMembershipId != null, 
                  cancellationToken);
    
    if (!hasRight) return NetworkLeg.Right;
    
    return null; // هر دو پر است
}
```

**تست**:
1. Parent با 2 فرزند فعال
2. ثبت نام با کد این Parent
3. باید Exception بیاید

**تخمین زمان**: 3-4 ساعت

---

### Task #5: بهبود پیغام خطای کد معرف پر

**شرح**:
در صفحه ثبت نام، اگر کاربر کد معرفی وارد کند که ظرفیتش پر است، باید پیغام خطای واضح و فارسی نمایش داده شود.

**Acceptance Criteria**:
- [ ] در Frontend، بعد از وارد کردن کد معرف، validation شود
- [ ] اگر کد پر بود، پیغام:
  > "این کد معرف ظرفیتش پر شده، لطفا از کد معرف دیگری استفاده کنید"
- [ ] Snackbar یا Alert با Severity.Warning
- [ ] فیلد کد معرف هایلایت شود (قرمز)

**فایل‌های نیاز به تغییر**:
```
FrontOffice/src/FrontOffice.Main/Pages/
  └── Register.razor
  └── Register.razor.cs
```

**کد پیشنهادی**:
```csharp
private async Task ValidateReferralCode()
{
    if (string.IsNullOrWhiteSpace(_referralCode))
        return;
    
    try
    {
        var request = new ValidateReferralCodeRequest
        {
            ReferralCode = _referralCode
        };
        
        var response = await UserContract.ValidateReferralCodeAsync(request);
        
        if (!response.IsValid)
        {
            _referralCodeError = "کد معرف نامعتبر است";
        }
        else if (response.IsFull)
        {
            _referralCodeError = "این کد معرف ظرفیتش پر شده، لطفا از کد معرف دیگری استفاده کنید";
            Snackbar.Add(_referralCodeError, Severity.Warning);
        }
    }
    catch (Exception ex)
    {
        _referralCodeError = "خطا در بررسی کد معرف";
    }
}
```

**تست**:
1. Parent پر را پیدا کنید
2. کد معرف او را در Register وارد کنید
3. پیغام واضح نمایش داده شود

**تخمین زمان**: 2-3 ساعت

---

## 📝 Priority 3: Documentation Tasks

### Task #6: به‌روزرسانی مستندات

**شرح**:
با توجه به توضیحات جدید، داکیومنت‌های زیر باید Update شوند.

**فایل‌های نیاز به تغییر**:

#### 1. `totalDoc/01-BUSINESS/network-commission-system.md`
```markdown
# اضافه کردن بخش جدید:

## ۱۰. حذف خودکار کاربران غیرفعال

کاربرانی که تا 2 هفته بعد از ثبت نام:
- وام دایا نگرفته‌اند
- پرداخت مستقیم 56 میلیون نکرده‌اند

به صورت خودکار حذف می‌شوند.

**Worker**: `DeleteInactiveUsersJob`
**زمان اجرا**: روزانه ساعت 3 صبح
**منطق**: `CreatedAt < Now - 14 days && !IsActive && ClubMembershipId == null`

---

## ۱۱. شرایط نمایش لینک معرفی

لینک معرفی فقط برای کاربرانی نمایش داده می‌شود که:
1. پرداخت کرده‌اند (IsActive = true)
2. عضو باشگاه مشتریان شده‌اند (ClubMembershipId != null)
3. عضویت باشگاه فعال است (ClubMembership.IsActive = true)

**تا زمانی که این شرایط برقرار نباشد، کاربر نمی‌تواند لینک معرفی خود را ببیند.**

---

## ۱۲. الزامی بودن دیالوگ باشگاه مشتریان

بعد از پرداخت موفق 56 میلیون تومان، کاربر **باید** قرارداد باشگاه مشتریان را امضا کند.

**فرآیند**:
1. پرداخت موفق
2. Dialog باشگاه مشتریان باز می‌شود
3. کاربر نمی‌تواند Dialog را ببندد
4. باید قرارداد را بخواند و امضا کند
5. بعد از امضا → redirect به Dashboard
6. لینک معرفی نمایش داده می‌شود
```

#### 2. `totalDoc/01-BUSINESS/binary-tree-guide.md`
```markdown
# اصلاح بخش Validation:

### محدودیت 2 فرزند **فعال**

هر Parent فقط می‌تواند **2 فرزند فعال** داشته باشد.

**تعریف فعال**:
- IsActive = true
- ClubMembershipId != null
- عضویت باشگاه فعال است

**نکته مهم**: کاربرانی که ثبت نام کرده‌اند اما هنوز فعال نشده‌اند، در شمارش 2 فرزند محسوب نمی‌شوند.
```

#### 3. `totalDoc/03-BACKEND/CMS/implementation-status.md`
```markdown
# افزودن به بخش Background Workers:

### ✅ DeleteInactiveUsersWorker (NEW - 2025-12-08)

**وضعیت**: 🔴 نیاز به پیاده‌سازی

**شرح**: حذف خودکار کاربران غیرفعال بعد از 2 هفته

**منطق**:
- روزانه ساعت 3 صبح اجرا می‌شود
- کاربرانی که `CreatedAt < Now - 14 days`
- و `IsActive = false`
- و `ClubMembershipId = null`
- به صورت Soft Delete حذف می‌شوند

**فایل**: `CMS/BackgroundWorkers/DeleteInactiveUsersJob.cs`

**Dependencies**:
- IApplicationDbContext
- ILogger
```

#### 4. `totalDoc/05-TASKS/BACKLOG.md`
```markdown
# اضافه کردن این 5 Task به Backlog

## 🔥 Critical

- [ ] Task #1: پیاده‌سازی DeleteInactiveUsersWorker (6h)
- [ ] Task #2: الزامی کردن دیالوگ باشگاه (8h)
- [ ] Task #3: شرط نمایش لینک معرفی (4h)

## ⚠️ Medium

- [ ] Task #4: Validation 2 فرزند فعال (4h)
- [ ] Task #5: پیغام خطای کد معرف پر (3h)

## 📝 Low

- [ ] Task #6: Update Documentation (2h)

**زمان کل**: 27 ساعت (~4 روز کاری)
```

**تخمین زمان**: 2-3 ساعت

---

## 📊 خلاصه Task ها

| # | عنوان | Priority | زمان | وضعیت |
|---|--------|----------|------|--------|
| 1 | DeleteInactiveUsersWorker | 🔥 Critical | 6h | ⬜ Todo |
| 2 | الزامی دیالوگ باشگاه | 🔥 Critical | 8h | ⬜ Todo |
| 3 | شرط لینک معرفی | 🔥 Critical | 4h | ⬜ Todo |
| 4 | Validation 2 فرزند فعال | ⚠️ Medium | 4h | ⬜ Todo |
| 5 | پیغام کد معرف پر | ⚠️ Medium | 3h | ⬜ Todo |
| 6 | Update Documentation | 📝 Low | 3h | ⬜ Todo |

**مجموع زمان**: 28 ساعت (~4 روز کاری)

---

## 🎯 پلان اجرا (پیشنهادی)

### روز 1 (8 ساعت):
- [ ] Task #1: DeleteInactiveUsersWorker (6h)
- [ ] شروع Task #2 (2h)

### روز 2 (8 ساعت):
- [ ] ادامه Task #2: Dialog الزامی (6h)
- [ ] شروع Task #3 (2h)

### روز 3 (8 ساعت):
- [ ] ادامه Task #3: شرط لینک (2h)
- [ ] Task #4: Validation (4h)
- [ ] شروع Task #5 (2h)

### روز 4 (4 ساعت):
- [ ] ادامه Task #5 (1h)
- [ ] Task #6: Documentation (3h)

---

## ✅ Definition of Done

هر Task زمانی Complete حساب می‌شود که:
1. ✅ کد نوشته شده و Build موفق
2. ✅ Unit Test / Manual Test انجام شده
3. ✅ Code Review شده
4. ✅ Documentation به‌روز شده
5. ✅ Merge به Main Branch

---

**تهیه‌کننده**: AI Assistant  
**تاریخ**: 2025-12-08  
**نسخه**: 1.0
