From b53d9facc99cab99ecead3f8fe9fbe447b6f6a3f Mon Sep 17 00:00:00 2001 From: masoodafar-web Date: Fri, 26 Jun 2026 16:38:58 +0330 Subject: [PATCH] feat(transactions): add ledger transactions feature and update related components - Introduced new ledger transactions page in NavMenu.razor and AppBreadcrumb.razor. - Enhanced ReportLabels.cs with transaction status and type labels. - Updated ConfigureService.cs to include TransactionsContractClient for gRPC. - Bumped Foursat.CMSMicroservice.Protobuf version to 0.0.203 for compatibility with new features. - Added deployment notes for June 1405 regarding read-only reports and proto updates. --- docs/ADMIN-SERVICES.md | 17 +- src/BackOffice/BackOffice.csproj | 2 +- .../Common/Configure/ConfigureService.cs | 2 + .../Common/Utilities/ReportLabels.cs | 35 +++ .../Pages/Payment/LedgerTransactions.razor | 246 ++++++++++++++++++ src/BackOffice/Shared/AppBreadcrumb.razor | 1 + src/BackOffice/Shared/NavMenu.razor | 6 + 7 files changed, 307 insertions(+), 2 deletions(-) create mode 100644 src/BackOffice/Pages/Payment/LedgerTransactions.razor diff --git a/docs/ADMIN-SERVICES.md b/docs/ADMIN-SERVICES.md index ce55f9b..5ecef76 100644 --- a/docs/ADMIN-SERVICES.md +++ b/docs/ADMIN-SERVICES.md @@ -4,7 +4,7 @@ - **مسیر wrapper سرویس‌ها:** `BackOffice/src/BackOffice/Services/` - **ثبت gRPC:** `BackOffice/src/BackOffice/Common/Configure/ConfigureService.cs` -- **تاریخ:** ۱۴۰۵/۰۳/۱۷ +- **تاریخ:** ۱۴۰۵/۰۳/۱۷ (بروز: خرداد ۱۴۰۵ — گزارش‌های read-only + proto 0.0.201) --- @@ -388,3 +388,18 @@ flowchart LR | Wallet | My balances | All users wallet + history log | مرجع مشابه برای فرانت مشتری: [`FrontOffice/docs/CUSTOMER-SERVICES.md`](../FrontOffice/docs/CUSTOMER-SERVICES.md) + +--- + +## یادداشت deploy (خرداد ۱۴۰۵) + +| گام | جزئیات | +|-----|--------| +| Proto | `Foursat.CMSMicroservice.Protobuf` **0.0.201** | +| CMS | deploy WebApi (شامل CQRS + gRPC گزارش‌ها) | +| BackOffice | NuGet 0.0.201 یا ProjectReference در monorepo | +| FrontOffice | **redeploy لازم نیست** — روی 0.0.199 می‌ماند؛ RPCهای admin در FO register نشده | + +گزارش‌های read-only (بخش ۱۰ و ۱۵): فقط فیلتر + grid + summary — بدون CRUD، detail dialog، یا لینک cross-page. + +مستندات فنی کامل: [`totalDoc/technical/TECH-08-SESSION-LOG-202606.md`](../../totalDoc/technical/TECH-08-SESSION-LOG-202606.md) diff --git a/src/BackOffice/BackOffice.csproj b/src/BackOffice/BackOffice.csproj index 1b6aee7..78d7ffa 100644 --- a/src/BackOffice/BackOffice.csproj +++ b/src/BackOffice/BackOffice.csproj @@ -143,7 +143,7 @@ - + diff --git a/src/BackOffice/Common/Configure/ConfigureService.cs b/src/BackOffice/Common/Configure/ConfigureService.cs index 2f6835e..441c667 100644 --- a/src/BackOffice/Common/Configure/ConfigureService.cs +++ b/src/BackOffice/Common/Configure/ConfigureService.cs @@ -32,6 +32,7 @@ using CMSMicroservice.Protobuf.Protos.UserContract; using CMSMicroservice.Protobuf.Protos.UserWallet; using CMSMicroservice.Protobuf.Protos.UserWalletHistory; using CMSMicroservice.Protobuf.Protos.PaymentTransaction; +using CMSMicroservice.Protobuf.Protos.Transactions; using CMSMicroservice.Protobuf.Protos.UserPackagePurchase; using CMSMicroservice.Protobuf.Protos.ClubMembershipCycle; using CMSMicroservice.Protobuf.Protos.ClubMembershipCycleHistory; @@ -175,6 +176,7 @@ public static class ConfigureServices // Admin Report Services services.AddTransient(sp => new PaymentTransactionContract.PaymentTransactionContractClient(sp.GetRequiredService())); + services.AddTransient(sp => new TransactionsContract.TransactionsContractClient(sp.GetRequiredService())); services.AddTransient(sp => new UserPackagePurchaseContract.UserPackagePurchaseContractClient(sp.GetRequiredService())); services.AddTransient(sp => new ClubMembershipCycleContract.ClubMembershipCycleContractClient(sp.GetRequiredService())); services.AddTransient(sp => new ClubMembershipCycleHistoryContract.ClubMembershipCycleHistoryContractClient(sp.GetRequiredService())); diff --git a/src/BackOffice/Common/Utilities/ReportLabels.cs b/src/BackOffice/Common/Utilities/ReportLabels.cs index 41fd281..4c6c281 100644 --- a/src/BackOffice/Common/Utilities/ReportLabels.cs +++ b/src/BackOffice/Common/Utilities/ReportLabels.cs @@ -1,9 +1,44 @@ namespace BackOffice.Common.Utilities; +using CMSMicroservice.Protobuf.Protos; +using MudBlazor; + public static class ReportLabels { public static string GetPaymentStatusLabel(bool success) => success ? "موفق" : "در انتظار / ناموفق"; + public static string GetTransactionPaymentStatusLabel(PaymentStatus? status) => status switch + { + PaymentStatus.Success => "موفق", + PaymentStatus.Pending => "در انتظار", + PaymentStatus.Reject => "رد شده", + _ => "—" + }; + + public static Color GetTransactionPaymentStatusColor(PaymentStatus? status) => status switch + { + PaymentStatus.Success => Color.Success, + PaymentStatus.Pending => Color.Warning, + PaymentStatus.Reject => Color.Error, + _ => Color.Default + }; + + public static string GetTransactionTypeLabel(TransactionType? type) => type switch + { + TransactionType.Buy => "خرید", + TransactionType.DepositIpg => "واریز درگاه", + TransactionType.DepositExternal1 => "واریز خارجی", + TransactionType.Withdraw => "برداشت", + TransactionType.NetworkCommission => "کمیسیون شبکه", + TransactionType.ClubActivation => "فعال‌سازی باشگاه", + TransactionType.DiscountWalletCharge => "شارژ کیف تخفیف", + TransactionType.DiscountShopPurchase => "خرید فروشگاه تخفیف", + TransactionType.MagicWalletDeposit => "واریز کیف جادویی", + TransactionType.MagicWalletBonus => "بونوس کیف جادویی", + TransactionType.CreditWalletCharge => "شارژ کیف اصلی", + _ => type?.ToString() ?? "—" + }; + public static string GetPurchaseMethodLabel(int method) => method switch { 1 => "وام دایا", diff --git a/src/BackOffice/Pages/Payment/LedgerTransactions.razor b/src/BackOffice/Pages/Payment/LedgerTransactions.razor new file mode 100644 index 0000000..2ea08eb --- /dev/null +++ b/src/BackOffice/Pages/Payment/LedgerTransactions.razor @@ -0,0 +1,246 @@ +@page "/payment/ledger-transactions" +@using BackOffice.Common.BaseComponents +@using CMSMicroservice.Protobuf.Protos +@using CMSMicroservice.Protobuf.Protos.Transactions +@using Google.Protobuf.WellKnownTypes + +@inject TransactionsContract.TransactionsContractClient TransactionsClient + + + + + + گزارش read-only دفتر تراکنش‌های مالی (جدول Transactions). برای جزئیات درگاه پرداخت به «گزارش تراکنش درگاه» مراجعه کنید. + + + + + + + + + + + + + + موفق + در انتظار + رد شده + + + + + خرید + واریز درگاه + واریز خارجی + برداشت + کمیسیون شبکه + فعال‌سازی باشگاه + شارژ کیف تخفیف + خرید فروشگاه تخفیف + واریز کیف جادویی + بونوس کیف جادویی + شارژ کیف اصلی + + + + + + + + + + + تعداد + @_summaryTotalCount.ToString("N0") + + + + + جمع مبلغ (تومان) + @_summaryTotalAmount.ToString("N0") + + + + + موفق + @_summarySuccess.ToString("N0") + + + + + در انتظار + @_summaryPending.ToString("N0") + + + + + رد شده + @_summaryReject.ToString("N0") + + + + + + + دفتر تراکنش‌های مالی + + CSV + + + + + + + @ReportLabels.GetTransactionTypeLabel(context.Item.HasType ? context.Item.Type : null) + + + + + @context.Item.Amount.ToString("N0") + + + + + + @ReportLabels.GetTransactionPaymentStatusLabel(context.Item.HasPaymentStatus ? context.Item.PaymentStatus : null) + + + + + + + @(context.Item.PaymentDate != null + ? context.Item.PaymentDate.ToDateTime().ToLocalTime().MiladiToJalaliWithTime() + : "—") + + + + @context.Item.Created.ToDateTime().ToLocalTime().MiladiToJalaliWithTime() + + + + + با فیلتر فعلی موردی یافت نشد — بازه تاریخ یا فیلترها را تغییر دهید. + + + + + + + + + +@code { + private BasePageComponent? _basePage; + private MudDataGrid? _dataGrid; + private long? _id; + private string? _refId, _description; + private PaymentStatus? _paymentStatus; + private TransactionType? _type; + private DateTime? _createdFrom, _createdTo; + private long _summaryTotalCount, _summaryTotalAmount, _summarySuccess, _summaryPending, _summaryReject; + + private GetAllTransactionsByFilterFilter BuildFilter() + { + var filter = new GetAllTransactionsByFilterFilter + { + Id = _id > 0 ? _id : null, + RefId = string.IsNullOrWhiteSpace(_refId) ? null : _refId, + Description = string.IsNullOrWhiteSpace(_description) ? null : _description, + CreatedFrom = _createdFrom.HasValue ? Timestamp.FromDateTime(DateTime.SpecifyKind(_createdFrom.Value, DateTimeKind.Utc)) : null, + CreatedTo = _createdTo.HasValue ? Timestamp.FromDateTime(DateTime.SpecifyKind(_createdTo.Value.Date.AddDays(1).AddTicks(-1), DateTimeKind.Utc)) : null + }; + + if (_paymentStatus.HasValue) + filter.PaymentStatus = _paymentStatus.Value; + else + filter.ClearPaymentStatusItem(); + + if (_type.HasValue) + filter.Type = _type.Value; + else + filter.ClearTypeItem(); + + return filter; + } + + private async Task LoadSummary() + { + try + { + var s = await TransactionsClient.GetTransactionSummaryAsync(new GetTransactionSummaryRequest { Filter = BuildFilter() }); + _summaryTotalCount = s.TotalCount; + _summaryTotalAmount = s.TotalAmount; + _summarySuccess = s.SuccessCount; + _summaryPending = s.PendingCount; + _summaryReject = s.RejectCount; + } + catch { } + } + + private async Task> LoadData(GridState state) + { + try + { + await LoadSummary(); + var result = await TransactionsClient.GetAllTransactionsByFilterAsync(new GetAllTransactionsByFilterRequest + { + PaginationState = new PaginationState { PageNumber = state.Page + 1, PageSize = state.PageSize }, + SortBy = "-Created", + Filter = BuildFilter() + }); + return new GridData + { + Items = result?.Models?.ToList() ?? new(), + TotalItems = (int)(result?.MetaData?.TotalCount ?? 0) + }; + } + catch (Exception ex) + { + Snackbar.Add($"خطا: {ex.Message}", Severity.Error); + return new GridData(); + } + } + + private async Task OnFilterSubmit() { if (_dataGrid != null) await _dataGrid.ReloadServerData(); } + private async Task OnFilterCleared() + { + _id = null; _refId = _description = null; + _paymentStatus = null; _type = null; + _createdFrom = _createdTo = null; + if (_dataGrid != null) await _dataGrid.ReloadServerData(); + } + + private async Task ExportCsv() + { + try + { + var result = await TransactionsClient.GetAllTransactionsByFilterAsync(new GetAllTransactionsByFilterRequest + { + PaginationState = new PaginationState { PageNumber = 1, PageSize = 5000 }, + SortBy = "-Created", + Filter = BuildFilter() + }); + var sb = new System.Text.StringBuilder(); + sb.AppendLine("Id,Type,Amount,Description,Status,RefId,PaymentDate,Created"); + foreach (var r in result.Models) + { + var type = r.HasType ? ReportLabels.GetTransactionTypeLabel(r.Type) : ""; + var status = r.HasPaymentStatus ? ReportLabels.GetTransactionPaymentStatusLabel(r.PaymentStatus) : ""; + var paymentDate = r.PaymentDate != null ? r.PaymentDate.ToDateTime().ToString("yyyy-MM-dd HH:mm") : ""; + sb.AppendLine($"{r.Id},{type},{r.Amount},\"{r.Description}\",{status},{r.RefId},{paymentDate},{r.Created.ToDateTime():yyyy-MM-dd HH:mm}"); + } + await jsRuntime.InvokeVoidAsync("jsSaveAsFile", "ledger-transactions.csv", sb.ToString()); + } + catch (Exception ex) { Snackbar.Add($"خطا در export: {ex.Message}", Severity.Error); } + } +} diff --git a/src/BackOffice/Shared/AppBreadcrumb.razor b/src/BackOffice/Shared/AppBreadcrumb.razor index e6d7712..630a226 100644 --- a/src/BackOffice/Shared/AppBreadcrumb.razor +++ b/src/BackOffice/Shared/AppBreadcrumb.razor @@ -96,6 +96,7 @@ // Payment "payment" => "پرداخت", "transactions" => "تراکنش‌ها", + "ledger-transactions" => "دفتر تراکنش‌های مالی", "manual-payments" => "پرداخت دستی", // Network diff --git a/src/BackOffice/Shared/NavMenu.razor b/src/BackOffice/Shared/NavMenu.razor index b360d7d..77418c8 100644 --- a/src/BackOffice/Shared/NavMenu.razor +++ b/src/BackOffice/Shared/NavMenu.razor @@ -206,6 +206,12 @@ گزارش تراکنش درگاه + + دفتر تراکنش‌های مالی + +