diff --git a/src/BackOffice.Main/Pages/Payment/ManualMembershipPayment.razor b/src/BackOffice.Main/Pages/Payment/ManualMembershipPayment.razor
new file mode 100644
index 0000000..18e7710
--- /dev/null
+++ b/src/BackOffice.Main/Pages/Payment/ManualMembershipPayment.razor
@@ -0,0 +1,131 @@
+@page "/payment/membership"
+@using BackOffice.BFF.ManualPayment.Protobuf
+@using BackOffice.Main.Pages.AutoComplete
+@attribute [Authorize(Roles = "Admin,SuperAdmin")]
+
+پرداخت دستی عضویت
+
+
+
+
+
+
+
+ پرداخت دستی عضویت
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @if (_showResult)
+ {
+
+ @_resultMessage
+ شماره تراکنش: @_transactionId
+ شماره سفارش: @_orderId
+ موجودی جدید کیف پول: @_newWalletBalance.ToString("N0") ریال
+
+ }
+
+
+
+
+ @if (_isProcessing)
+ {
+
+ در حال ثبت...
+ }
+ else
+ {
+ ثبت پرداخت
+ }
+
+
+
+ پاک کردن فرم
+
+
+
+
+
+
+
+
+
+
+ راهنما
+
+
+
+
+ این صفحه برای ثبت پرداختهای دستی عضویت استفاده میشود. پس از ثبت:
+
+
+ مبلغ به کیف پول کاربر (Balance و DiscountBalance) اضافه میشود
+
+
+ لاگ تغییرات کیف پول ثبت میشود
+
+
+ تراکنش با وضعیت موفق ثبت میشود
+
+
+ سفارش خرید پکیج عضویت ثبت میشود
+
+
+
+
+
diff --git a/src/BackOffice.Main/Pages/Payment/ManualMembershipPayment.razor.cs b/src/BackOffice.Main/Pages/Payment/ManualMembershipPayment.razor.cs
new file mode 100644
index 0000000..0f4b079
--- /dev/null
+++ b/src/BackOffice.Main/Pages/Payment/ManualMembershipPayment.razor.cs
@@ -0,0 +1,103 @@
+using BackOffice.BFF.ManualPayment.Protobuf;
+using BackOffice.Main.Components;
+using Grpc.Core;
+using Microsoft.AspNetCore.Components;
+
+namespace BackOffice.Main.Pages.Payment;
+
+public partial class ManualMembershipPayment
+{
+ [Inject] private ManualPaymentContract.ManualPaymentContractClient ManualPaymentClient { get; set; } = default!;
+ [Inject] private ISnackbar Snackbar { get; set; } = default!;
+
+ private long? _userId;
+ private long _amount = 0;
+ private string _referenceNumber = string.Empty;
+ private string? _description;
+
+ private bool _isProcessing = false;
+ private bool _showResult = false;
+ private string _resultMessage = string.Empty;
+ private long _transactionId = 0;
+ private long _orderId = 0;
+ private long _newWalletBalance = 0;
+
+ private async Task ProcessPayment()
+ {
+ // Validation
+ if (!_userId.HasValue || _userId.Value <= 0)
+ {
+ Snackbar.Add("لطفا کاربر را انتخاب کنید", Severity.Warning);
+ return;
+ }
+
+ if (_amount <= 0)
+ {
+ Snackbar.Add("لطفا مبلغ معتبری وارد کنید", Severity.Warning);
+ return;
+ }
+
+ if (string.IsNullOrWhiteSpace(_referenceNumber))
+ {
+ Snackbar.Add("لطفا شماره مرجع را وارد کنید", Severity.Warning);
+ return;
+ }
+
+ try
+ {
+ _isProcessing = true;
+ _showResult = false;
+
+ var request = new ProcessManualMembershipPaymentRequest
+ {
+ UserId = _userId.Value,
+ Amount = _amount,
+ ReferenceNumber = _referenceNumber
+ };
+
+ if (!string.IsNullOrWhiteSpace(_description))
+ {
+ request.Description = _description;
+ }
+
+ var response = await ManualPaymentClient.ProcessManualMembershipPaymentAsync(request);
+
+ _resultMessage = response.Message;
+ _transactionId = response.TransactionId;
+ _orderId = response.OrderId;
+ _newWalletBalance = response.NewWalletBalance;
+ _showResult = true;
+
+ Snackbar.Add("پرداخت دستی با موفقیت ثبت شد", Severity.Success);
+
+ // Reset form
+ await Task.Delay(2000);
+ ResetForm();
+ }
+ catch (RpcException ex)
+ {
+ Snackbar.Add($"خطا در ثبت پرداخت: {ex.Status.Detail}", Severity.Error);
+ }
+ catch (Exception ex)
+ {
+ Snackbar.Add($"خطای غیرمنتظره: {ex.Message}", Severity.Error);
+ }
+ finally
+ {
+ _isProcessing = false;
+ }
+ }
+
+ private void ResetForm()
+ {
+ _userId = null;
+ _amount = 0;
+ _referenceNumber = string.Empty;
+ _description = null;
+ _showResult = false;
+ _resultMessage = string.Empty;
+ _transactionId = 0;
+ _orderId = 0;
+ _newWalletBalance = 0;
+ }
+}
diff --git a/src/BackOffice/BackOffice.csproj b/src/BackOffice/BackOffice.csproj
index 679a99d..b204838 100644
--- a/src/BackOffice/BackOffice.csproj
+++ b/src/BackOffice/BackOffice.csproj
@@ -118,7 +118,7 @@
-
+
@@ -134,7 +134,7 @@
-
+
diff --git a/src/BackOffice/Pages/AutoComplete/WeekNumberPicker.razor b/src/BackOffice/Pages/AutoComplete/WeekNumberPicker.razor
new file mode 100644
index 0000000..72ab58e
--- /dev/null
+++ b/src/BackOffice/Pages/AutoComplete/WeekNumberPicker.razor
@@ -0,0 +1,21 @@
+@using Foursat.BackOffice.BFF.Commission.Protos
+
+
+
+
+ @week.DisplayText
+
+
+
diff --git a/src/BackOffice/Pages/AutoComplete/WeekNumberPicker.razor.cs b/src/BackOffice/Pages/AutoComplete/WeekNumberPicker.razor.cs
new file mode 100644
index 0000000..e379674
--- /dev/null
+++ b/src/BackOffice/Pages/AutoComplete/WeekNumberPicker.razor.cs
@@ -0,0 +1,107 @@
+using BackOffice.Services;
+using Foursat.BackOffice.BFF.Commission.Protos;
+using Grpc.Core;
+using Microsoft.AspNetCore.Components;
+
+namespace BackOffice.Pages.AutoComplete;
+
+public partial class WeekNumberPicker
+{
+ [Inject] public CommissionContract.CommissionContractClient CommissionContract { get; set; } = default!;
+ [Inject] public IPersianDateTimeService PersianDateTime { get; set; } = default!;
+
+ [Parameter] public string Label { get; set; } = "انتخاب هفته";
+ [Parameter] public string? SelectedWeekNumber { get; set; }
+ [Parameter] public EventCallback SelectedWeekNumberChanged { get; set; }
+ [Parameter] public bool CoerceText { get; set; } = true; // اجازه ورود دستی
+ [Parameter] public int FutureWeeksCount { get; set; } = 4;
+ [Parameter] public int PastWeeksCount { get; set; } = 12;
+
+ private WeekInfo? _selectedWeek;
+ private List _allWeeks = new();
+ private bool _isLoaded = false;
+
+ protected override async Task OnInitializedAsync()
+ {
+ await LoadWeeks();
+
+ if (!string.IsNullOrWhiteSpace(SelectedWeekNumber) && _allWeeks.Any())
+ {
+ _selectedWeek = _allWeeks.FirstOrDefault(w => w.WeekNumber == SelectedWeekNumber);
+ }
+ }
+
+ protected override async Task OnParametersSetAsync()
+ {
+ if (!string.IsNullOrWhiteSpace(SelectedWeekNumber) &&
+ _selectedWeek?.WeekNumber != SelectedWeekNumber &&
+ _isLoaded)
+ {
+ _selectedWeek = _allWeeks.FirstOrDefault(w => w.WeekNumber == SelectedWeekNumber);
+ }
+ }
+
+ private async Task LoadWeeks()
+ {
+ try
+ {
+ var request = new GetAvailableWeeksRequest
+ {
+ FutureWeeksCount = FutureWeeksCount,
+ PastWeeksCount = PastWeeksCount
+ };
+
+ var response = await CommissionContract.GetAvailableWeeksAsync(request);
+
+ _allWeeks = new List();
+
+ // ترتیب: هفته جاری، محاسبه شده، در انتظار، آینده
+ if (response.CurrentWeek != null)
+ _allWeeks.Add(response.CurrentWeek);
+
+ if (response.CalculatedWeeks != null)
+ _allWeeks.AddRange(response.CalculatedWeeks);
+
+ if (response.PendingWeeks != null)
+ _allWeeks.AddRange(response.PendingWeeks);
+
+ if (response.FutureWeeks != null)
+ _allWeeks.AddRange(response.FutureWeeks);
+
+ _isLoaded = true;
+ }
+ catch
+ {
+ _allWeeks = new List();
+ _isLoaded = true;
+ }
+ }
+
+ private async Task> Search(string value, CancellationToken cancellationToken)
+ {
+ if (!_isLoaded)
+ await LoadWeeks();
+
+ if (string.IsNullOrWhiteSpace(value))
+ return _allWeeks;
+
+ // جستجو در شماره هفته
+ return _allWeeks
+ .Where(w => w.WeekNumber.Contains(value, StringComparison.OrdinalIgnoreCase))
+ .ToList();
+ }
+
+ private async Task OnSelected(WeekInfo? selected)
+ {
+ _selectedWeek = selected;
+ SelectedWeekNumber = selected?.WeekNumber;
+ await SelectedWeekNumberChanged.InvokeAsync(SelectedWeekNumber);
+ }
+
+ private async Task OnClear()
+ {
+ _selectedWeek = null;
+ SelectedWeekNumber = null;
+ await SelectedWeekNumberChanged.InvokeAsync(null);
+ }
+}
diff --git a/src/BackOffice/Pages/Commission/Dashboard.razor b/src/BackOffice/Pages/Commission/Dashboard.razor
index 510092a..7a11ef4 100644
--- a/src/BackOffice/Pages/Commission/Dashboard.razor
+++ b/src/BackOffice/Pages/Commission/Dashboard.razor
@@ -3,6 +3,7 @@
@using Foursat.BackOffice.BFF.Commission.Protos
@using MudBlazor
+@using BackOffice.Pages.AutoComplete
داشبورد کمیسیون
@@ -151,12 +152,10 @@
}
else
{
-
+
}
- Payout های کاربران
+ پرداخت های کاربران
- لیست Payout ها
+ لیست پرداخت ها
-
-
+
+
0)
{
- request.UserId = _filterUserId.Value;
+ request.Filter.UserId = _filterUserId.Value;
}
- if (!string.IsNullOrEmpty(_filterWeekNumber))
+ if (!string.IsNullOrWhiteSpace(_filterWeekNumber))
{
- request.WeekNumber = _filterWeekNumber;
+ request.Filter.WeekNumber = _filterWeekNumber;
}
if (_filterStatus.HasValue)
{
- request.Status = _filterStatus.Value;
+ request.Filter.Status = _filterStatus.Value;
}
var result = await CommissionContract.GetUserCommissionPayoutsAsync(request);
diff --git a/src/BackOffice/Pages/Commission/WithdrawalReports.razor b/src/BackOffice/Pages/Commission/WithdrawalReports.razor
index 0c08c56..b67bd41 100644
--- a/src/BackOffice/Pages/Commission/WithdrawalReports.razor
+++ b/src/BackOffice/Pages/Commission/WithdrawalReports.razor
@@ -6,6 +6,7 @@
@using Google.Protobuf.WellKnownTypes
@using Microsoft.JSInterop
@using System.Text
+@using BackOffice.Pages.AutoComplete
گزارش برداشتها
@@ -41,10 +42,8 @@
-
+
diff --git a/src/BackOffice/Pages/Commission/WithdrawalRequests.razor b/src/BackOffice/Pages/Commission/WithdrawalRequests.razor
index 298817f..aec0406 100644
--- a/src/BackOffice/Pages/Commission/WithdrawalRequests.razor
+++ b/src/BackOffice/Pages/Commission/WithdrawalRequests.razor
@@ -2,6 +2,7 @@
@attribute [Authorize]
@using Foursat.BackOffice.BFF.Commission.Protos
+@using BackOffice.Pages.AutoComplete
درخواستهای برداشت
@@ -15,14 +16,8 @@
درخواستهای برداشت
-
+
-
+
-
+
کنترل Worker محاسبات
@@ -59,10 +60,9 @@
-
+
-
+
}
+
+ @if (CanManagePayments)
+ {
+
+
+ پرداختهای دستی
+
+
+ پرداخت دستی عضویت
+
+
+ }
@@ -273,6 +289,7 @@
private bool CanManageTags;
private bool CanManageUsers;
private bool CanManageRoles;
+ private bool CanManagePayments;
private bool CanManageDiscountShop;
private bool CanManagePublicMessages;
private bool CanViewSystemAlerts;
@@ -300,6 +317,7 @@
CanManageTags = await AuthorizationService.HasPermissionAsync("tags.manage");
CanManageUsers = await AuthorizationService.HasPermissionAsync("users.view");
CanManageRoles = await AuthorizationService.HasPermissionAsync("roles.manage");
+ CanManagePayments = await AuthorizationService.HasPermissionAsync("manualpayments.create");
CanManageDiscountShop = await AuthorizationService.HasPermissionAsync("discountshop.manage");
CanManagePublicMessages = await AuthorizationService.HasPermissionAsync("publicmessages.view");
CanViewSystemAlerts = await AuthorizationService.HasPermissionAsync("system.alerts.view");