Compare commits

...

2 Commits

Author SHA1 Message Date
masoodafar-web 4115f64fff feat(payment): add manual credit charge functionality and update related components
Build and Deploy to Kubernetes / build-and-deploy (push) Has been cancelled
- Introduced new manual credit charge endpoints in ADMIN-SERVICES.md.
- Updated ReportLabels.cs and LedgerTransactions.razor to include manual credit wallet charge options.
- Enhanced NavMenu.razor with a link to the manual credit charges page.
- Bumped Foursat.CMSMicroservice.Protobuf version to 0.0.205 for compatibility with new features.
2026-07-25 23:54:57 +03:30
masoodafar-web d757c8220c docs: update ADMIN-SERVICES.md with new ledger transaction reports and additional details
- Added new transaction report paths and clarified their purposes in the documentation.
- Updated the proto version to 0.0.203 and included deployment notes for the new ledger transactions feature.
- Enhanced read-only report descriptions to include CSV export capabilities.
2026-06-26 19:30:20 +03:30
7 changed files with 292 additions and 13 deletions
+28 -12
View File
@@ -4,7 +4,7 @@
- **مسیر wrapper سرویس‌ها:** `BackOffice/src/BackOffice/Services/` - **مسیر wrapper سرویس‌ها:** `BackOffice/src/BackOffice/Services/`
- **ثبت gRPC:** `BackOffice/src/BackOffice/Common/Configure/ConfigureService.cs` - **ثبت gRPC:** `BackOffice/src/BackOffice/Common/Configure/ConfigureService.cs`
- **تاریخ:** ۱۴۰۵/۰۳/۱۷ (بروز: خرداد ۱۴۰۵ — گزارش‌های read-only + proto 0.0.201) - **تاریخ:** ۱۴۰۵/۰۳/۱۷ (بروز: خرداد ۱۴۰۵ — گزارش‌های read-only + proto 0.0.201؛ **بروز ۱۴۰۵/۰۳/۱۸ — دفتر تراکنش‌های مالی proto 0.0.203**)
--- ---
@@ -138,16 +138,26 @@
## ۱۰. پرداخت دستی و تراکنش‌ها ## ۱۰. پرداخت دستی و تراکنش‌ها
> **دو گزارش تراکنش — اشتباه نگیرید:**
>
> | مسیر | جدول DB | کاربرد |
> |------|---------|--------|
> | `/payment/transactions` | `PaymentTransactions` | ممیزی درگاه (Authority، ZarinPal، کارت) |
> | `/payment/ledger-transactions` | `Transactions` | دفتر مالی داخلی (نوع تراکنش، وضعیت، RefId) |
| # | سرویس / متد | gRPC Contract | صفحه / کامپوننت | کاربرد | | # | سرویس / متد | gRPC Contract | صفحه / کامپوننت | کاربرد |
|---|-------------|---------------|-----------------|--------| |---|-------------|---------------|-----------------|--------|
| 10.1 | `ManualPaymentContract.GetAllManualPayments` | `ManualPaymentContract` | `/payment/manual-payments` | لیست پرداخت‌های دستی | | 10.1 | `ManualPaymentContract.GetAllManualPayments` | `ManualPaymentContract` | `/payment/manual-payments` | لیست پرداخت‌های دستی |
| 10.2 | `ManualPaymentContract.CreateManualPayment` | `ManualPaymentContract` | `ManualPaymentDialog` | ثبت پرداخت دستی (مثلاً Daya) | | 10.2 | `ManualPaymentContract.CreateManualPayment` | `ManualPaymentContract` | `ManualPaymentDialog` | ثبت پرداخت دستی (مثلاً Daya) |
| 10.3 | `ManualPaymentContract.ApproveManualPayment` | `ManualPaymentContract` | ManualPayments, Dialog | تأیید پرداخت | | 10.3 | `ManualPaymentContract.ApproveManualPayment` | `ManualPaymentContract` | ManualPayments, Dialog | تأیید پرداخت |
| 10.4 | `ManualPaymentContract.RejectManualPayment` | `ManualPaymentContract` | ManualPayments, Dialog | رد پرداخت | | 10.4 | `ManualPaymentContract.RejectManualPayment` | `ManualPaymentContract` | ManualPayments, Dialog | رد پرداخت |
| 10.5 | `PaymentTransactionContract.GetAllPaymentTransactionByFilter` | `PaymentTransactionContract` | `/payment/transactions` | گزارش read-only تراکنش درگاه | | 10.5 | `PaymentTransactionContract.GetAllPaymentTransactionByFilter` | `PaymentTransactionContract` | `/payment/transactions` | گزارش read-only تراکنش **درگاه** (`PaymentTransactions`) |
| 10.6 | `UserPackagePurchaseContract.GetAllUserPackagePurchaseByFilter` | `UserPackagePurchaseContract` | `/club/purchases` | گزارش خرید پکیج | | 10.5b | `PaymentTransactionContract.GetPaymentTransactionSummary` | `PaymentTransactionContract` | `/payment/transactions` | کارت خلاصه گزارش درگاه |
| 10.7 | `ClubMembershipCycleContract.GetAllClubMembershipCycleByFilter` | `ClubMembershipCycleContract` | `/club/cycles` | گزارش دوره عضویت | | 10.6 | `TransactionsContract.GetAllTransactionsByFilter` | `TransactionsContract` | `/payment/ledger-transactions` | گزارش read-only **دفتر مالی** (`Transactions`) |
| 10.8 | `ClubMembershipCycleHistoryContract.GetAllClubMembershipCycleHistoryByFilter` | `ClubMembershipCycleHistoryContract` | `/club/history` (زیرتب دوره) | تاریخچه تغییرات دوره | | 10.7 | `TransactionsContract.GetTransactionSummary` | `TransactionsContract` | `/payment/ledger-transactions` | کارت خلاصه دفتر تراکنش |
| 10.8 | `UserPackagePurchaseContract.GetAllUserPackagePurchaseByFilter` | `UserPackagePurchaseContract` | `/club/purchases` | گزارش خرید پکیج |
| 10.9 | `ClubMembershipCycleContract.GetAllClubMembershipCycleByFilter` | `ClubMembershipCycleContract` | `/club/cycles` | گزارش دوره عضویت |
| 10.10 | `ClubMembershipCycleHistoryContract.GetAllClubMembershipCycleHistoryByFilter` | `ClubMembershipCycleHistoryContract` | `/club/history` (زیرتب دوره) | تاریخچه تغییرات دوره |
--- ---
@@ -157,6 +167,8 @@
|---|-------------|---------------|-----------------|--------| |---|-------------|---------------|-----------------|--------|
| 11.1 | `UserWalletContract.GetAllUserWalletByFilter` | `UserWalletContract` | `/wallets` | لیست کیف پول کاربران | | 11.1 | `UserWalletContract.GetAllUserWalletByFilter` | `UserWalletContract` | `/wallets` | لیست کیف پول کاربران |
| 11.2 | `UserWalletHistoryContract.GetAllUserWalletHistoryByFilter` | `UserWalletHistoryContract` | `/wallets` (تب تاریخچه) | لاگ تغییرات کیف پول | | 11.2 | `UserWalletHistoryContract.GetAllUserWalletHistoryByFilter` | `UserWalletHistoryContract` | `/wallets` (تب تاریخچه) | لاگ تغییرات کیف پول |
| 11.3 | `UserWalletContract.AdminManualCreditCharge` | `UserWalletContract` | `/payment/manual-credit-charges` | شارژ دستی کیف پول اصلی (Balance) |
| 11.4 | `UserWalletContract.GetManualCreditCharges` | `UserWalletContract` | `/payment/manual-credit-charges` | لیست شارژهای دستی (Type=17) |
--- ---
@@ -319,7 +331,6 @@
| سرویس / صفحه | وضعیت | | سرویس / صفحه | وضعیت |
|--------------|--------| |--------------|--------|
| `IBlogPostImageService` (`BlogPostImageContract`) | ثبت در DI؛ **هیچ صفحه‌ای inject نمی‌کند** | | `IBlogPostImageService` (`BlogPostImageContract`) | ثبت در DI؛ **هیچ صفحه‌ای inject نمی‌کند** |
| `/payment/transactions` | UI آماده؛ **LoadData خالی — API وصل نشده** |
| `RolePermissionsDialog` | UI permission؛ **ذخیره در backend TODO** | | `RolePermissionsDialog` | UI permission؛ **ذخیره در backend TODO** |
| `AuthorizationService` | فقط JWT role؛ **Permission API از CMS/BFF TODO** | | `AuthorizationService` | فقط JWT role؛ **Permission API از CMS/BFF TODO** |
| `/system/alerts` | داده mock؛ **AlertContract وجود ندارد** | | `/system/alerts` | داده mock؛ **AlertContract وجود ندارد** |
@@ -352,6 +363,11 @@ flowchart LR
| `CategoryContract` | Categories | | `CategoryContract` | Categories |
| `UserOrderContract` | Orders | | `UserOrderContract` | Orders |
| `ManualPaymentContract` | Manual payments | | `ManualPaymentContract` | Manual payments |
| `PaymentTransactionContract` | Gateway transaction report (`/payment/transactions`) |
| `TransactionsContract` | Ledger transaction report (`/payment/ledger-transactions`) |
| `UserPackagePurchaseContract` | Club package purchase report |
| `ClubMembershipCycleContract` | Club cycle report |
| `ClubMembershipCycleHistoryContract` | Club cycle history report |
| `UserWalletContract` | Wallet management | | `UserWalletContract` | Wallet management |
| `UserWalletHistoryContract` | Wallet change log | | `UserWalletHistoryContract` | Wallet change log |
| `UserContractContract` | Signed contracts | | `UserContractContract` | Signed contracts |
@@ -395,11 +411,11 @@ flowchart LR
| گام | جزئیات | | گام | جزئیات |
|-----|--------| |-----|--------|
| Proto | `Foursat.CMSMicroservice.Protobuf` **0.0.201** | | Proto | `Foursat.CMSMicroservice.Protobuf` **0.0.203** (گزارش دفتر تراکنش) |
| CMS | deploy WebApi (شامل CQRS + gRPC گزارش‌ها) | | CMS | deploy WebApi (شامل `GetTransactionSummary` + فیلتر تاریخ) |
| BackOffice | NuGet 0.0.201 یا ProjectReference در monorepo | | BackOffice | NuGet 0.0.203 یا ProjectReference در monorepo |
| FrontOffice | **redeploy لازم نیست** — روی 0.0.199 می‌ماند؛ RPCهای admin در FO register نشده | | FrontOffice | **redeploy لازم نیست** برای گزارش ledger؛ برای شارژ کیف اصلی proto **0.0.202** لازم است |
گزارش‌های read-only (بخش ۱۰ و ۱۵): فقط فیلتر + grid + summary — بدون CRUD، detail dialog، یا لینک cross-page. گزارش‌های read-only (بخش ۱۰ و ۱۵): فقط فیلتر + grid + summary + CSV — بدون CRUD یا detail dialog.
مستندات فنی کامل: [`totalDoc/technical/TECH-08-SESSION-LOG-202606.md`](../../totalDoc/technical/TECH-08-SESSION-LOG-202606.md) مستندات فنی کامل: [`totalDoc/technical/TECH-08-SESSION-LOG-202606.md`](../../totalDoc/technical/TECH-08-SESSION-LOG-202606.md) — §10 Ledger transactions
+1 -1
View File
@@ -143,7 +143,7 @@
<ProjectReference Include="../../../CMS/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj" /> <ProjectReference Include="../../../CMS/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="!Exists('../../../CMS/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj')"> <ItemGroup Condition="!Exists('../../../CMS/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj')">
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.203" /> <PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.205" />
</ItemGroup> </ItemGroup>
<!-- ============================================================ --> <!-- ============================================================ -->
@@ -36,6 +36,7 @@ public static class ReportLabels
TransactionType.MagicWalletDeposit => "واریز کیف جادویی", TransactionType.MagicWalletDeposit => "واریز کیف جادویی",
TransactionType.MagicWalletBonus => "بونوس کیف جادویی", TransactionType.MagicWalletBonus => "بونوس کیف جادویی",
TransactionType.CreditWalletCharge => "شارژ کیف اصلی", TransactionType.CreditWalletCharge => "شارژ کیف اصلی",
TransactionType.ManualCreditWalletCharge => "شارژ دستی کیف اصلی",
_ => type?.ToString() ?? "—" _ => type?.ToString() ?? "—"
}; };
@@ -0,0 +1,106 @@
@using BackOffice.Pages.AutoComplete
@using CMSMicroservice.Protobuf.Protos.UserWallet
@inject UserWalletContract.UserWalletContractClient WalletClient
<MudDialog>
<DialogContent>
<MudStack Spacing="3">
<MudAlert Severity="Severity.Warning" Dense="true" Variant="Variant.Outlined">
مبلغ مستقیماً به کیف پول اصلی (Balance) کاربر اضافه می‌شود و در دفتر مالی با نوع «شارژ دستی کیف اصلی» ثبت می‌گردد.
</MudAlert>
<UserAutoComplete Label="کاربر" @bind-SelectedUserId="_userId" />
<MudNumericField T="long" @bind-Value="_amount"
Label="مبلغ (تومان)"
Variant="Variant.Outlined"
Min="10000"
HelperText="حداقل ۱۰,۰۰۰ تومان" />
<MudTextField T="string" @bind-Value="_note"
Label="توضیحات ادمین (اختیاری)"
Variant="Variant.Outlined"
Lines="2" />
</MudStack>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel" Disabled="_isSubmitting">انصراف</MudButton>
<MudButton Color="Color.Primary" Variant="Variant.Filled"
OnClick="Submit" Disabled="_isSubmitting || !_userId.HasValue || _amount < 10000">
@if (_isSubmitting)
{
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
}
ثبت شارژ
</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter] IMudDialogInstance MudDialog { get; set; } = default!;
private long? _userId;
private long _amount = 10000;
private string? _note;
private bool _isSubmitting;
private void Cancel() => MudDialog.Cancel();
private async Task Submit()
{
if (!_userId.HasValue || _userId.Value <= 0)
{
Snackbar.Add("لطفاً کاربر را انتخاب کنید", Severity.Warning);
return;
}
if (_amount < 10000)
{
Snackbar.Add("حداقل مبلغ شارژ ۱۰,۰۰۰ تومان است", Severity.Warning);
return;
}
bool? confirm = await DialogService.ShowMessageBox(
"تأیید شارژ دستی",
$"آیا از شارژ {_amount:N0} تومان به کیف پول اصلی کاربر #{_userId.Value} مطمئن هستید؟",
yesText: "بله، شارژ شود", cancelText: "لغو");
if (confirm != true)
return;
_isSubmitting = true;
try
{
var request = new AdminManualCreditChargeRequest
{
UserId = _userId.Value,
Amount = _amount
};
if (!string.IsNullOrWhiteSpace(_note))
request.Note = _note.Trim();
var response = await WalletClient.AdminManualCreditChargeAsync(request);
if (response.Success)
{
Snackbar.Add(
$"شارژ موفق — تراکنش #{response.TransactionId}، موجودی جدید: {response.NewBalance:N0}",
Severity.Success);
MudDialog.Close(DialogResult.Ok(true));
}
else
{
Snackbar.Add(response.Message ?? "شارژ ناموفق بود", Severity.Error);
}
}
catch (Exception ex)
{
Snackbar.Add($"خطا: {ex.Message}", Severity.Error);
}
finally
{
_isSubmitting = false;
}
}
}
@@ -44,6 +44,7 @@
<MudSelectItem T="TransactionType?" Value="TransactionType.MagicWalletDeposit">واریز کیف جادویی</MudSelectItem> <MudSelectItem T="TransactionType?" Value="TransactionType.MagicWalletDeposit">واریز کیف جادویی</MudSelectItem>
<MudSelectItem T="TransactionType?" Value="TransactionType.MagicWalletBonus">بونوس کیف جادویی</MudSelectItem> <MudSelectItem T="TransactionType?" Value="TransactionType.MagicWalletBonus">بونوس کیف جادویی</MudSelectItem>
<MudSelectItem T="TransactionType?" Value="TransactionType.CreditWalletCharge">شارژ کیف اصلی</MudSelectItem> <MudSelectItem T="TransactionType?" Value="TransactionType.CreditWalletCharge">شارژ کیف اصلی</MudSelectItem>
<MudSelectItem T="TransactionType?" Value="TransactionType.ManualCreditWalletCharge">شارژ دستی کیف اصلی</MudSelectItem>
</MudSelect> </MudSelect>
</MudItem> </MudItem>
<MudItem xs="12" sm="6" md="4"> <MudItem xs="12" sm="6" md="4">
@@ -0,0 +1,149 @@
@page "/payment/manual-credit-charges"
@attribute [Authorize(Roles = "Administrator")]
@using BackOffice.Common.BaseComponents
@using BackOffice.Pages.AutoComplete
@using BackOffice.Pages.Payment.Components
@using CMSMicroservice.Protobuf.Protos
@using CMSMicroservice.Protobuf.Protos.UserWallet
@using DateTimeConverterCL
@using Google.Protobuf.WellKnownTypes
@inject UserWalletContract.UserWalletContractClient WalletClient
<BasePageComponent @ref="_basePage" OnSubmitClick="OnFilterSubmit" OnClearFilterClick="OnFilterCleared">
<Filters>
<MudItem xs="12">
<MudAlert Severity="Severity.Info" Variant="Variant.Text" Dense="true">
شارژ دستی کیف پول اصلی (Balance) — بدون درگاه. جدا از «پرداخت‌های دستی» عضویت باشگاه.
</MudAlert>
</MudItem>
<MudItem xs="12" sm="6" md="3">
<UserAutoComplete Label="جستجوی کاربر" @bind-SelectedUserId="_userIdFilter" />
</MudItem>
<MudItem xs="12" sm="6" md="3">
<MudTextField T="long?" Label="شناسه تراکنش" Variant="Variant.Outlined"
Margin="Margin.Dense" @bind-Value="_transactionIdFilter" />
</MudItem>
<MudItem xs="12" sm="6" md="3">
<MudTextField T="string" Label="RefId" Variant="Variant.Outlined"
Margin="Margin.Dense" @bind-Value="_refIdFilter" />
</MudItem>
<MudItem xs="12" sm="6" md="4">
<DateRangePicker @bind-From="_createdFrom" @bind-To="_createdTo" Label="بازه ثبت" />
</MudItem>
</Filters>
<Content>
<MudDataGrid @ref="_dataGrid" T="ManualCreditChargeModel"
ServerData="LoadData"
Hover="true" Dense="true"
Height="calc(100vh - 240px)">
<ToolBarContent>
<MudText Typo="Typo.subtitle1">شارژ دستی کیف پول اصلی</MudText>
<MudSpacer />
<MudButton Variant="Variant.Filled" Color="Color.Primary" Size="Size.Small"
StartIcon="@Icons.Material.Filled.Add" OnClick="OpenCreateDialog">
شارژ دستی جدید
</MudButton>
</ToolBarContent>
<Columns>
<PropertyColumn Property="x => x.TransactionId" Title="شناسه تراکنش" />
<PropertyColumn Property="x => x.UserId" Title="شناسه کاربر" />
<PropertyColumn Property="x => x.UserName" Title="نام کاربر" />
<PropertyColumn Property="x => x.Amount" Title="مبلغ">
<CellTemplate>
@context.Item.Amount.ToString("N0")
</CellTemplate>
</PropertyColumn>
<PropertyColumn Property="x => x.NewBalance" Title="موجودی بعد از شارژ">
<CellTemplate>
@context.Item.NewBalance.ToString("N0")
</CellTemplate>
</PropertyColumn>
<PropertyColumn Property="x => x.RefId" Title="RefId" />
<PropertyColumn Property="x => x.Description" Title="توضیحات" />
<TemplateColumn Title="تاریخ">
<CellTemplate>
@(context.Item.Created?.ToDateTime().ToLocalTime().MiladiToJalaliWithTime() ?? "-")
</CellTemplate>
</TemplateColumn>
</Columns>
<NoRecordsContent>
<MudAlert Severity="Severity.Info" Dense="true" Variant="Variant.Text" Class="my-4">موردی یافت نشد.</MudAlert>
</NoRecordsContent>
<PagerContent>
<MudDataGridPager T="ManualCreditChargeModel" PageSizeOptions="@(new int[] { 20, 50, 100 })"
InfoFormat="سطر {first_item} تا {last_item} از {all_items}"
RowsPerPageString="تعداد در صفحه" />
</PagerContent>
</MudDataGrid>
</Content>
</BasePageComponent>
@code {
private BasePageComponent? _basePage;
private MudDataGrid<ManualCreditChargeModel>? _dataGrid;
private long? _userIdFilter;
private long? _transactionIdFilter;
private string? _refIdFilter;
private DateTime? _createdFrom;
private DateTime? _createdTo;
private async Task<GridData<ManualCreditChargeModel>> LoadData(GridState<ManualCreditChargeModel> state)
{
var request = new GetManualCreditChargesRequest
{
PaginationState = new PaginationState
{
PageNumber = state.Page + 1,
PageSize = state.PageSize
},
Filter = new GetManualCreditChargesFilter()
};
if (_userIdFilter.HasValue)
request.Filter.UserId = _userIdFilter.Value;
if (_transactionIdFilter.HasValue)
request.Filter.TransactionId = _transactionIdFilter.Value;
if (!string.IsNullOrWhiteSpace(_refIdFilter))
request.Filter.RefId = _refIdFilter.Trim();
if (_createdFrom.HasValue)
request.Filter.CreatedFrom = Timestamp.FromDateTime(DateTime.SpecifyKind(_createdFrom.Value, DateTimeKind.Utc));
if (_createdTo.HasValue)
request.Filter.CreatedTo = Timestamp.FromDateTime(DateTime.SpecifyKind(_createdTo.Value, DateTimeKind.Utc));
var response = await WalletClient.GetManualCreditChargesAsync(request);
return new GridData<ManualCreditChargeModel>
{
Items = response.Models.ToList(),
TotalItems = (int)(response.MetaData?.TotalCount ?? 0)
};
}
private async Task OnFilterSubmit()
{
if (_dataGrid != null)
await _dataGrid.ReloadServerData();
}
private async Task OnFilterCleared()
{
_userIdFilter = null;
_transactionIdFilter = null;
_refIdFilter = null;
_createdFrom = null;
_createdTo = null;
if (_dataGrid != null)
await _dataGrid.ReloadServerData();
}
private async Task OpenCreateDialog()
{
var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Small, FullWidth = true };
var dialog = DialogService.Show<ManualCreditChargeDialog>("شارژ دستی کیف پول اصلی", options);
var result = await dialog.Result;
if (result is { Canceled: false } && _dataGrid != null)
await _dataGrid.ReloadServerData();
}
}
+6
View File
@@ -200,6 +200,12 @@
</MudNavLink> </MudNavLink>
} }
<MudNavLink Match="NavLinkMatch.Prefix"
Href="/payment/manual-credit-charges"
Icon="@Icons.Material.Filled.AddCard">
شارژ دستی کیف پول
</MudNavLink>
<MudNavLink Match="NavLinkMatch.Prefix" <MudNavLink Match="NavLinkMatch.Prefix"
Href="/payment/transactions" Href="/payment/transactions"
Icon="@Icons.Material.Filled.ReceiptLong"> Icon="@Icons.Material.Filled.ReceiptLong">