# 📝 Changelog - ۷ دی ۱۴۰۴ (27 December 2025)

> **Session**: بهینه‌سازی‌های Mapping + SystemConstants + SMS Templates + AppVersion UI + Commission System Fixes

---

## 🎯 خلاصه Session

این session شامل موارد زیر بود:
1. **SystemConstants** - انتقال مقادیر ثابت از hardcode به کلاس مرکزی
2. **SMS Templates** - متمرکز کردن همه قالب‌های پیامک
3. **SMS for Daya Loan** - ارسال پیامک هنگام تأیید وام دایا
4. **AppVersion UI** - تکمیل صفحه مدیریت نسخه در BackOffice
5. **Mapping Fixes** - رفع مشکلات Mapster
6. **Commission Status Refactoring** - انتقال تبدیل Status از BFF به FrontOffice
7. **ProcessWithdrawal Fix** - رفع خطای "PayoutId invalid" در BackOffice
8. **WeekDisplayName Fix** - نمایش صحیح نام هفته به جای فرمت 1404-W40
9. **Withdrawals Page Fix** - رفع مشکل لود نشدن صفحه تأیید برداشت‌ها
10. **Network Balances Enhancement** - افزودن نام کاربر و جزئیات Carryover به صفحه balance‌ها
11. **WeekDefinitionId Mapping Fix** - رفع مشکل ارسال WeekDefinitionId=0 در FrontOffice.BFF

---

## ✨ تغییرات

### 1. 💰 SystemConstants - مقادیر ثابت ✅

**فایل**: `CMSMicroservice.Domain/Common/SystemConstants.cs`

```csharp
public static class SystemConstants
{
    // Club Configuration
    public const decimal ClubJoiningPercentage = 0.35m;    // 35% کمیسیون پیوستن به باشگاه
    public const decimal ClubActivationThreshold = 0.5m;   // 50% آستانه فعال‌سازی
    
    // Commission Configuration
    public const int MaxCalculationAttempts = 3;           // حداکثر تلاش محاسبه
    public const int DefaultCommissionPoolDays = 7;        // روزهای استخر کمیسیون
    
    // Package Amounts
    public const long GoldenPackageAmount = 56_000_000;    // 56 میلیون - پکیج طلایی
    public const long DayaLoanAmount = 56_000_000;         // 56 میلیون - وام دایا
}
```

**Handlers آپدیت شده**:
| Handler | تغییر |
|---------|-------|
| `ProcessDayaLoanApprovalCommandHandler` | استفاده از `SystemConstants.DayaLoanAmount` |
| `ValidateGoldenPackagePurchaseQueryHandler` | استفاده از `SystemConstants.GoldenPackageAmount` |
| سایر handlers با 56_000_000 | همه به ثابت تبدیل شدند |

---

### 2. 📱 SmsTemplates - قالب‌های متمرکز پیامک ✅

**فایل جدید**: `CMSMicroservice.Domain/Common/SmsTemplates.cs`

```csharp
public static class SmsTemplates
{
    private static string GetUserName(string? firstName) 
        => string.IsNullOrWhiteSpace(firstName) ? "کاربر" : firstName;

    public static string DayaLoanReceived(string? firstName, long amount)
        => $"{GetUserName(firstName)} عزیز، مبلغ {amount:N0} ریال وام دایا به کیف پول شما واریز شد. کارابازار";

    public static string ClubActivated(string? firstName)
        => $"{GetUserName(firstName)} عزیز، حساب باشگاه شما فعال شد. کارابازار";

    public static string PackagePurchased(string? firstName, string packageName)
        => $"{GetUserName(firstName)} عزیز، پکیج {packageName} با موفقیت خریداری شد. کارابازار";

    public static string CommissionDeposited(string? firstName, long amount)
        => $"{GetUserName(firstName)} عزیز، مبلغ {amount:N0} ریال کمیسیون به کیف پول شما واریز شد. کارابازار";

    public static string WithdrawalSuccess(string? firstName, long amount)
        => $"{GetUserName(firstName)} عزیز، درخواست برداشت {amount:N0} ریال با موفقیت انجام شد. کارابازار";

    public static string NetworkJoined(string? firstName, string referrerName)
        => $"{GetUserName(firstName)} عزیز، به شبکه {referrerName} پیوستید. کارابازار";

    public static string NewDownline(string? firstName, string newMemberName)
        => $"{GetUserName(firstName)} عزیز، {newMemberName} به زیرمجموعه شما اضافه شد. کارابازار";

    public static string OtpCode(string code)
        => $"کد تأیید شما: {code}\nکارابازار";

    public static string Welcome(string? firstName)
        => $"{GetUserName(firstName)} عزیز، به کارابازار خوش آمدید!";
}
```

---

### 3. 📲 ارسال SMS هنگام تأیید وام دایا ✅

**فایل**: `CMSMicroservice.Application/FinancialCQ/Commands/ProcessDayaLoanApproval/ProcessDayaLoanApprovalCommandHandler.cs`

**تغییرات**:
```csharp
public class ProcessDayaLoanApprovalCommandHandler : IRequestHandler<ProcessDayaLoanApprovalCommand, Unit>
{
    private readonly IKavenegarService _smsService;  // جدید
    private readonly ILogger<ProcessDayaLoanApprovalCommandHandler> _logger;  // جدید

    // بعد از واریز موفق به کیف پول
    private async Task SendDayaLoanSmsAsync(User user)
    {
        try
        {
            var message = SmsTemplates.DayaLoanReceived(
                user.FirstName, 
                SystemConstants.DayaLoanAmount);
            
            await _smsService.SendAsync(user.PhoneNumber, message);
            _logger.LogInformation("Daya loan SMS sent to user {UserId}", user.Id);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Failed to send Daya loan SMS to user {UserId}", user.Id);
            // خطای SMS مانع عملیات اصلی نمی‌شود
        }
    }
}
```

---

### 4. 🖥️ BackOffice - صفحه مدیریت نسخه اپلیکیشن ✅

#### 4.1 اضافه شدن به منو

**فایل**: `BackOffice/Shared/NavMenu.razor`

```razor
@if (CanViewSettings)
{
    <MudNavLink Match="NavLinkMatch.Prefix"
                Href="/settings/app-versions"
                Icon="@Icons.Material.Filled.PhoneAndroid">
        نسخه اپلیکیشن‌ها
    </MudNavLink>
}
```

**Permission**: `settings.view`

#### 4.2 دکمه افزودن نسخه جدید

**فایل**: `BackOffice/Pages/Settings/AppVersions.razor`

```razor
<MudButton Variant="Variant.Filled" 
           Color="Color.Primary" 
           StartIcon="@Icons.Material.Filled.Add"
           OnClick="@OpenCreateDialog">
    افزودن نسخه جدید
</MudButton>
```

#### 4.3 Dialog با حالت جدید/ویرایش

**فایل**: `BackOffice/Pages/Settings/Components/AppVersionEditDialog.razor`

```razor
[Parameter]
public bool IsNew { get; set; } = false;

@if (IsNew)
{
    <MudSelect @bind-Value="Model.AppName"
               Label="نام اپلیکیشن"
               Required="true">
        <MudSelectItem Value="@("KaraBazarApp")">کارابازار</MudSelectItem>
        <MudSelectItem Value="@("KaraBazarAdminApp")">ادمین کارابازار</MudSelectItem>
    </MudSelect>
}
else
{
    <MudTextField @bind-Value="Model.AppName" 
                  ReadOnly="true" Disabled="true" />
}
```

#### 4.4 آیکون و رنگ اپلیکیشن‌ها

```csharp
private string GetAppIcon(string appName) => appName switch
{
    "KaraBazarApp" => Icons.Material.Filled.ShoppingCart,
    "KaraBazarAdminApp" => Icons.Material.Filled.AdminPanelSettings,
    _ => Icons.Material.Filled.PhoneAndroid
};

private Color GetAppColor(string appName) => appName switch
{
    "KaraBazarApp" => Color.Primary,
    "KaraBazarAdminApp" => Color.Secondary,
    _ => Color.Default
};
```

---

### 5. 🔧 Mapping Fixes ✅

#### 5.1 CMS - AppVersionProfile

**فایل جدید**: `CMSMicroservice.WebApi/Common/Mappings/AppVersionProfile.cs`

```csharp
public class AppVersionProfile : IRegister
{
    public void Register(TypeAdapterConfig config)
    {
        // Map List<AppVersionItemDto> to GetAllAppVersionsResponse
        config.NewConfig<List<AppVersionItemDto>, GetAllAppVersionsResponse>()
            .MapWith(src => CreateResponse(src));

        // Map AppVersionItemDto to AppVersionItem (proto message)
        config.NewConfig<AppVersionItemDto, AppVersionItem>()
            .Map(dest => dest.Id, src => src.Id)
            .Map(dest => dest.AppName, src => src.AppName)
            // ... other mappings
    }

    private static GetAllAppVersionsResponse CreateResponse(List<AppVersionItemDto> items)
    {
        var response = new GetAllAppVersionsResponse();
        foreach (var item in items)
        {
            response.Items.Add(item.Adapt<AppVersionItem>());
        }
        return response;
    }
}
```

#### 5.2 BackOffice.BFF - CommissionProfile

**فایل**: `BackOffice.BFF.Application/Common/Mappings/CommissionProfile.cs`

```csharp
// CMS GetAllWeeklyPoolsResponse -> GetAllWeeklyPoolsResponseDto
config.NewConfig<GetAllWeeklyPoolsResponse, GetAllWeeklyPoolsResponseDto>()
    .MapWith(src => new GetAllWeeklyPoolsResponseDto
    {
        MetaData = new MetaDataDto
        {
            TotalCount = (int)src.MetaData.TotalCount,
            PageSize = (int)src.MetaData.PageSize,
            CurrentPage = (int)src.MetaData.CurrentPage,
            TotalPages = (int)src.MetaData.TotalPage
        },
        Models = src.Models.Select(m => new WeeklyCommissionPoolDto
        {
            Id = m.Id,
            WeekDefinitionId = m.WeekDefinitionId,
            // ... other mappings
        }).ToList()
    });
```

#### 5.3 BackOffice.BFF - GeneralMapping (Unit to Empty)

**فایل**: `BackOffice.BFF.WebApi/Common/Mappings/GeneralMapping.cs`

```csharp
// MediatR Unit to Google.Protobuf.Empty
config.NewConfig<MediatR.Unit, Google.Protobuf.WellKnownTypes.Empty>()
    .MapWith(_ => new Google.Protobuf.WellKnownTypes.Empty());
```

---

### 6. 🔄 Commission Status Refactoring ✅

**مشکل**: تبدیل enum عددی `CommissionPayoutStatus` به متن فارسی در BFF gateway انجام می‌شد.

**راه‌حل**: انتقال منطق به FrontOffice client برای معماری بهتر.

#### 6.1 FrontOffice.BFF - Simplify Response

**فایل**: `FrontOffice.BFF.Application/.../GetMyWeeklyBalancesQueryHandler.cs`

**قبل**:
```csharp
Status = MapStatusToString(x.Status)
```

**بعد**:
```csharp
Status = x.Status  // Return int directly
```

#### 6.2 FrontOffice - CommissionService

**فایل**: `FrontOffice.Main/Utilities/CommissionService.cs`

```csharp
public static string MapStatus(int status) => status switch
{
    0 => "در انتظار",      // Pending
    1 => "پرداخت شده",     // Paid
    2 => "درخواست برداشت", // WithdrawRequested
    3 => "برداشت شده",     // Withdrawn
    4 => "خطای پرداخت",    // PaymentFailed
    5 => "لغو شده",        // Cancelled
    _ => "نامشخص"
};

public static string GetStatusColor(int status) => status switch
{
    0 => "warning",   // Pending - زرد
    1 => "success",   // Paid - سبز
    2 => "info",      // WithdrawRequested - آبی
    3 => "success",   // Withdrawn - سبز
    4 => "error",     // PaymentFailed - قرمز
    5 => "default",   // Cancelled - خاکستری
    _ => "default"
};
```

**Enum مرجع** (`CommissionPayoutStatus`):
```csharp
public enum CommissionPayoutStatus
{
    Pending = 0,
    Paid = 1,
    WithdrawRequested = 2,
    Withdrawn = 3,
    PaymentFailed = 4,
    Cancelled = 5
}
```

---

### 7. 🛠️ ProcessWithdrawal Fix ✅

**مشکل**: خطای "PayoutId invalid" هنگام تأیید/رد برداشت در BackOffice

**علت**: `PayoutId` در mapping از CMS request به BackOffice.BFF command map نمی‌شد.

**فایل**: `BackOffice.BFF.Application/Common/Mappings/CommissionProfile.cs`

**قبل**:
```csharp
config.NewConfig<ProcessWithdrawalRequest, ProcessWithdrawalCommand>();
// PayoutId ignored!
```

**بعد**:
```csharp
config.NewConfig<ProcessWithdrawalRequest, ProcessWithdrawalCommand>()
    .Map(dest => dest.PayoutId, src => src.PayoutId)
    .Map(dest => dest.Approve, src => src.Approve)
    .Map(dest => dest.RejectionReason, src => src.RejectionReason);
```

---

### 8. 📅 WeekDisplayName Fix ✅

**مشکل**: نمایش "1404-W40" به جای "هفته چهلم" در dropdown انتخاب هفته

**علت**: استفاده از `PersianWeekNumber` به جای `DisplayName`

**فایل**: `CMS.Application/.../GetAllWeeklyPoolsQueryHandler.cs`

**قبل**:
```csharp
WeekDisplayName = x.WeekDefinition.PersianWeekNumber  // "1404-W40"
```

**بعد**:
```csharp
WeekDisplayName = x.WeekDefinition.DisplayName  // "هفته چهلم"
```

---

### 9. 📋 Withdrawals Page Fix ✅

**مشکل**: صفحه تأیید برداشت‌ها در BackOffice لود نمی‌شد

**علت**: mismatch بین نام propertyها در CMS proto و BackOffice.BFF DTO

**فایل**: `BackOffice.BFF.Application/.../GetWithdrawalRequestsResponseDto.cs`

**قبل**:
```csharp
public int TotalPages { get; set; }
public int TotalCount { get; set; }
```

**بعد**:
```csharp
public int TotalPage { get; set; }   // Match CMS proto
public int TotalCount { get; set; }
```

**فایل**: `BackOffice.BFF.Application/.../GetWithdrawalRequestsQueryHandler.cs`

**قبل**:
```csharp
return response.Adapt<GetWithdrawalRequestsResponseDto>();
```

**بعد**:
```csharp
return new GetWithdrawalRequestsResponseDto
{
    TotalCount = (int)response.MetaData.TotalCount,
    TotalPage = (int)response.MetaData.TotalPage,
    // ... explicit mapping
};
```

---

### 10. 👤 Network Balances Enhancement ✅

**نیاز**: نمایش نام کامل کاربر و جزئیات breakdown پای چپ/راست در صفحه balance‌های شبکه

#### 10.1 CMS Proto Update

**فایل**: `CMS/Protobufs/Protos/commission.proto`

```protobuf
message UserWeeklyBalanceModel {
    // ... existing fields
    string user_full_name = 13;
    int64 left_leg_new_members = 14;
    int64 left_leg_carryover = 15;
    int64 left_leg_total = 16;
    int64 right_leg_new_members = 17;
    int64 right_leg_carryover = 18;
    int64 right_leg_total = 19;
}
```

#### 10.2 CMS Handler Update

**فایل**: `CMS.Application/.../GetUserWeeklyBalancesQueryHandler.cs`

```csharp
var query = _dbContext.NetworkWeeklyBalances
    .Include(x => x.User)  // NEW: Include User
    .Include(x => x.WeekDefinition)
    .Where(x => x.WeekDefinitionId == request.WeekDefinitionId);

// In projection:
UserFullName = $"{x.User.FirstName} {x.User.LastName}".Trim(),
LeftLegNewMembers = x.LeftLegNewMembers,
LeftLegCarryover = x.LeftLegCarryover,
LeftLegTotal = x.LeftLegTotal,
RightLegNewMembers = x.RightLegNewMembers,
RightLegCarryover = x.RightLegCarryover,
RightLegTotal = x.RightLegTotal,
```

#### 10.3 BackOffice UI Update

**فایل**: `BackOffice/Pages/Network/BalancesReport.razor`

```razor
@* ستون نام کاربر *@
<PropertyColumn Property="x => x.UserFullName" Title="نام کاربر" />

@* ستون پای چپ با Tooltip *@
<TemplateColumn Title="پای چپ">
    <CellTemplate>
        <MudTooltip Text="@($"جدید: {FormatNumber(context.Item.LeftLegNewMembers)} | انتقالی: {FormatNumber(context.Item.LeftLegCarryover)}")">
            <MudText>@FormatNumber(context.Item.LeftLegTotal)</MudText>
        </MudTooltip>
    </CellTemplate>
</TemplateColumn>

@* ستون پای راست با Tooltip *@
<TemplateColumn Title="پای راست">
    <CellTemplate>
        <MudTooltip Text="@($"جدید: {FormatNumber(context.Item.RightLegNewMembers)} | انتقالی: {FormatNumber(context.Item.RightLegCarryover)}")">
            <MudText>@FormatNumber(context.Item.RightLegTotal)</MudText>
        </MudTooltip>
    </CellTemplate>
</TemplateColumn>
```

#### 10.4 Proto Package Update

```bash
# Publish new proto package
cd CMS/Protobufs
# Update version in .csproj to 0.0.14
dotnet pack
dotnet nuget push ...

# Update BackOffice
cd BackOffice/src/BackOffice
# Update package reference in .csproj
<PackageReference Include="Foursat.BackOffice.BFF.Commission.Protobuf" Version="0.0.14" />
```

---

### 11. 🔢 WeekDefinitionId Mapping Fix ✅

**مشکل**: `WeekDefinitionId` همیشه `0` به BFF ارسال می‌شد، حتی اگر در client مقدار صحیح ست شده بود.

**علت**: در protobuf، فیلد `week_definition_id` از نوع `google.protobuf.Int64Value` است که یک wrapper type هست. در mapping مستقیم assign می‌شد بدون extract کردن `.Value`.

**فایل**: `FrontOffice.BFF/src/FrontOffice.BFF.WebApi/Common/Mappings/CommissionProfile.cs`

**Proto Definition**:
```protobuf
message GetMyWeeklyBalancesRequest {
    google.protobuf.Int64Value week_definition_id = 3;
}
```

**قبل**:
```csharp
config.NewConfig<GetMyWeeklyBalancesRequest, GetMyWeeklyBalancesQuery>()
    .Map(dest => dest.WeekDefinitionId, src => src.WeekDefinitionId);  // BUG: assigns Int64Value object, not the value
```

**بعد**:
```csharp
config.NewConfig<GetMyWeeklyBalancesRequest, GetMyWeeklyBalancesQuery>()
    .Map(dest => dest.WeekDefinitionId, 
         src => src.WeekDefinitionId != null ? src.WeekDefinitionId.Value : null);
```

**توضیح**:
- `Int64Value` یک wrapper class در protobuf هست برای nullable long
- وقتی مستقیم assign کنید، implicit conversion اتفاق نمیفته
- باید explicit از `.Value` استفاده کنید

---

## 📦 فایل‌های تغییر یافته

### CMS
| فایل | نوع تغییر |
|------|-----------|
| `Domain/Common/SystemConstants.cs` | Modified - اضافه شدن GoldenPackageAmount, DayaLoanAmount |
| `Domain/Common/SmsTemplates.cs` | **New** - قالب‌های پیامک |
| `Application/.../ProcessDayaLoanApprovalCommandHandler.cs` | Modified - اضافه شدن SMS |
| `WebApi/Common/Mappings/AppVersionProfile.cs` | **New** - Mapster profile |
| `Application/.../GetAllWeeklyPoolsQueryHandler.cs` | Modified - تغییر WeekDisplayName از PersianWeekNumber به DisplayName |
| `Application/.../GetUserWeeklyBalancesQueryHandler.cs` | Modified - اضافه شدن User include و فیلدهای جدید |
| `Application/.../GetUserWeeklyBalancesResponseDto.cs` | Modified - اضافه شدن UserFullName و breakdown fields |
| `WebApi/Common/Mappings/CommissionProfile.cs` | Modified - mapping جدید برای UserWeeklyBalanceModel |
| `Protobufs/Protos/commission.proto` | Modified - اضافه شدن فیلدهای جدید به UserWeeklyBalanceModel |

### BackOffice.BFF
| فایل | نوع تغییر |
|------|-----------|
| `Application/Common/Mappings/CommissionProfile.cs` | Modified - اضافه شدن GetAllWeeklyPools mapping + ProcessWithdrawal mapping |
| `WebApi/Common/Mappings/GeneralMapping.cs` | Modified - اضافه شدن Unit to Empty |
| `Application/.../GetWithdrawalRequestsQueryHandler.cs` | Modified - explicit mapping به جای Adapt<> |
| `Application/.../GetWithdrawalRequestsResponseDto.cs` | Modified - تطابق با CMS proto |
| `Protobufs/Protos/commission.proto` | Modified - اضافه شدن فیلدهای جدید |

### BackOffice
| فایل | نوع تغییر |
|------|-----------|
| `Shared/NavMenu.razor` | Modified - اضافه شدن لینک app-versions |
| `Pages/Settings/AppVersions.razor` | Modified - دکمه افزودن + OpenCreateDialog |
| `Pages/Settings/Components/AppVersionEditDialog.razor` | Modified - پارامتر IsNew + Select |
| `Pages/Network/BalancesReport.razor` | Modified - ستون‌های جدید با MudTooltip |
| `BackOffice.csproj` | Modified - آپدیت proto package به v0.0.14 |

### FrontOffice.BFF
| فایل | نوع تغییر |
|------|-----------|
| `WebApi/Common/Mappings/CommissionProfile.cs` | Modified - رفع WeekDefinitionId mapping (Int64Value.Value) |
| `Application/.../GetMyWeeklyBalancesQueryHandler.cs` | Modified - simplify status handling |

### FrontOffice
| فایل | نوع تغییر |
|------|-----------|
| `Utilities/CommissionService.cs` | Modified - اضافه شدن GetStatusColor و MapStatus (انتقال از BFF) |
| `Pages/Commission/WeeklyBalancePage.razor.cs` | Modified - استفاده از متدهای جدید CommissionService |

---

## ✅ Build Status

```bash
# CMS
dotnet build CMSMicroservice.WebApi/CMSMicroservice.WebApi.csproj
# Build succeeded. 0 Error(s)

# BackOffice.BFF
dotnet build BackOffice.BFF.WebApi/BackOffice.BFF.WebApi.csproj
# Build succeeded. 0 Error(s)

# BackOffice
dotnet build BackOffice/BackOffice.csproj
# Build succeeded. 0 Error(s)

# FrontOffice.BFF
dotnet build FrontOffice.BFF.WebApi/FrontOffice.BFF.WebApi.csproj
# Build succeeded. 0 Error(s)

# FrontOffice
dotnet build FrontOffice.Main/FrontOffice.Main.csproj
# Build succeeded. 0 Error(s)
```

---

## 📊 آمار Session

| متریک | مقدار |
|-------|-------|
| فایل‌های جدید | 2 |
| فایل‌های تغییر یافته | 18 |
| خطوط کد اضافه شده | ~500 |
| باگ‌های Mapping رفع شده | 5 |
| پروژه‌های تأثیرگذار | 5 (CMS, BackOffice, BackOffice.BFF, FrontOffice, FrontOffice.BFF) |

---

## 🔗 Related Changelogs

- [CHANGELOG-2025-12-26.md](CHANGELOG-2025-12-26.md) - App Version Management + ReferralCode in Tree
- [CHANGELOG-2025-12-25.md](CHANGELOG-2025-12-25.md) - درخت شبکه BackOffice
