- Added InventoryMainPage for managing inventory items with filtering options. - Created LowStockPage to display low stock items with filtering capabilities. - Developed MovementsPage to track inventory movements with detailed filtering. - Introduced WarehousesPage for managing warehouses, including creation and viewing inventory. - Enhanced backend integration with InventoryBFFContract for data retrieval. - Implemented user-friendly UI components using MudBlazor for better user experience. - Added necessary dialog components for adding stock, adjusting inventory, and viewing movements.
This commit is contained in:
@@ -0,0 +1,164 @@
|
|||||||
|
# فیچر فعالسازی (پرداخت) دستی باشگاه مشتریان
|
||||||
|
|
||||||
|
## خلاصه
|
||||||
|
این فیچر امکان فعالسازی دستی عضویت باشگاه مشتریان را برای ادمین فراهم میکند. ادمین میتواند کاربر را انتخاب کرده، تصویر فیش پرداخت را آپلود کند و عضویت را فعال کند.
|
||||||
|
|
||||||
|
## تاریخ: 2 ژانویه 2026
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## تغییرات انجام شده
|
||||||
|
|
||||||
|
### 1. CMS (Backend)
|
||||||
|
|
||||||
|
#### Entity - `ManualPayment.cs`
|
||||||
|
- اضافه شدن فیلد `ImageDocumentId` برای ذخیره شناسه سند در FMS
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public long? ImageDocumentId { get; set; }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Proto - `manualpayment.proto`
|
||||||
|
- اضافه شدن `image_document_id` به `CreateManualPaymentRequest` (فیلد 7)
|
||||||
|
- اضافه شدن `image_document_id` به `ManualPaymentModel` (فیلد 21)
|
||||||
|
|
||||||
|
#### Command - `CreateManualPaymentCommand.cs`
|
||||||
|
- اضافه شدن پراپرتی `ImageDocumentId`
|
||||||
|
|
||||||
|
#### Handler - `CreateManualPaymentCommandHandler.cs`
|
||||||
|
- ذخیره `ImageDocumentId` از request در entity
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. BFF (Backend For Frontend)
|
||||||
|
|
||||||
|
#### Proto - `manualpayment.proto`
|
||||||
|
- اضافه شدن `image_document_id` به `ManualPaymentModel` (فیلد 21)
|
||||||
|
- اضافه شدن `FileUploadModel` برای آپلود فایل
|
||||||
|
- اضافه شدن `image_file` به `CreateManualPaymentRequest`
|
||||||
|
|
||||||
|
#### Command - `CreateManualPaymentCommand.cs`
|
||||||
|
- اضافه شدن `FileUploadDto` برای دریافت فایل از فرانت
|
||||||
|
|
||||||
|
#### Handler - `CreateManualPaymentCommandHandler.cs`
|
||||||
|
- اتصال به FMS برای آپلود فایل
|
||||||
|
- استخراج `ImagePath` و `ImageDocumentId` از پاسخ FMS
|
||||||
|
- ارسال هر دو به CMS
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var fileInfo = await _context.FileInfos.CreateNewFileInfoAsync(new()
|
||||||
|
{
|
||||||
|
Directory = "Images/ManualPayments",
|
||||||
|
IsBase64 = false,
|
||||||
|
MIME = request.ImageFile.Mime,
|
||||||
|
FileName = request.ImageFile.FileName,
|
||||||
|
File = ByteString.CopyFrom(request.ImageFile.File)
|
||||||
|
}, cancellationToken: cancellationToken);
|
||||||
|
|
||||||
|
if (fileInfo != null)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(fileInfo.File))
|
||||||
|
grpcRequest.ImagePath = fileInfo.File;
|
||||||
|
if (fileInfo.Id > 0)
|
||||||
|
grpcRequest.ImageDocumentId = fileInfo.Id;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Query Handler - `GetManualPaymentsQueryHandler.cs`
|
||||||
|
- اضافه شدن mapping برای `ImagePath` و `ImageDocumentId`
|
||||||
|
|
||||||
|
#### DTO - `GetManualPaymentsResponseDto.cs`
|
||||||
|
- اضافه شدن فیلدهای:
|
||||||
|
```csharp
|
||||||
|
public string? ImagePath { get; set; }
|
||||||
|
public long? ImageDocumentId { get; set; }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Mapping - `ManualPaymentProfile.cs`
|
||||||
|
- اضافه شدن mapping برای `FileUploadModel -> FileUploadDto`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Frontend (Blazor)
|
||||||
|
|
||||||
|
#### `ManualPaymentDialog.razor`
|
||||||
|
- استفاده از `UserAutoComplete` برای انتخاب کاربر
|
||||||
|
- استفاده از `MudFileUpload` برای آپلود تصویر فیش
|
||||||
|
- استفاده از `MudSelect` برای انتخاب نوع پرداخت
|
||||||
|
- پیشنمایش تصویر قبل از ارسال
|
||||||
|
- تغییر عنوانها از "پرداخت دستی" به "فعالسازی دستی"
|
||||||
|
|
||||||
|
#### `ManualPaymentDialog.razor.cs`
|
||||||
|
- هندل کردن انتخاب فایل با `IBrowserFile`
|
||||||
|
- تبدیل فایل به Base64 برای پیشنمایش
|
||||||
|
- ایجاد `FileUploadModel` برای ارسال به BFF
|
||||||
|
- مقادیر پیشفرض:
|
||||||
|
- `Type = 1` (واریز نقدی)
|
||||||
|
- `Description = "عضویت دستی باشگاه مشتریان"`
|
||||||
|
|
||||||
|
#### `ManualPayments.razor`
|
||||||
|
- تغییر عنوان صفحه به "فعالسازی (پرداخت) دستی"
|
||||||
|
- تغییر دکمه به "ثبت فعالسازی دستی جدید"
|
||||||
|
|
||||||
|
#### `NavMenu.razor`
|
||||||
|
- حذف آیتم منوی "پرداخت دستی عضویت"
|
||||||
|
- تغییر نام "پرداختهای دستی" به "فعالسازی (پرداخت) دستی"
|
||||||
|
|
||||||
|
#### حذف شده
|
||||||
|
- `ManualMembershipPayment.razor` و `ManualMembershipPayment.razor.cs`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## مقادیر ثابت
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// مبلغ پایه پکیج: 56 میلیون ریال
|
||||||
|
SystemConstants.BasePackageAmount = 56_000_000;
|
||||||
|
|
||||||
|
// شارژ کیف پول:
|
||||||
|
// - مجموع شارژ: 56M ریال
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## فلو کامل
|
||||||
|
|
||||||
|
1. ادمین کاربر را با `UserAutoComplete` انتخاب میکند
|
||||||
|
2. نوع پرداخت را انتخاب میکند (پیشفرض: واریز نقدی)
|
||||||
|
3. توضیحات را وارد میکند (پیشفرض: عضویت دستی باشگاه مشتریان)
|
||||||
|
4. تصویر فیش را آپلود میکند (اختیاری)
|
||||||
|
5. دکمه ثبت را میزند
|
||||||
|
6. Frontend فایل را به BFF ارسال میکند
|
||||||
|
7. BFF فایل را به FMS آپلود میکند
|
||||||
|
8. FMS مسیر فایل (`File`) و شناسه سند (`Id`) را برمیگرداند
|
||||||
|
9. BFF هر دو را به CMS ارسال میکند
|
||||||
|
10. CMS تراکنش، پرداخت دستی و لاگ کیف پول را ثبت میکند
|
||||||
|
11. کیف پول کاربر شارژ میشود
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## فایلهای تغییر یافته
|
||||||
|
|
||||||
|
### CMS
|
||||||
|
- `CMSMicroservice.Domain/Entities/Payment/ManualPayment.cs`
|
||||||
|
- `CMSMicroservice.Application/ManualPaymentCQ/Commands/CreateManualPayment/CreateManualPaymentCommand.cs`
|
||||||
|
- `CMSMicroservice.Application/ManualPaymentCQ/Commands/CreateManualPayment/CreateManualPaymentCommandHandler.cs`
|
||||||
|
- `Protos/manualpayment.proto`
|
||||||
|
|
||||||
|
### BFF
|
||||||
|
- `BackOffice.BFF.Application/ManualPaymentCQ/Commands/CreateManualPayment/CreateManualPaymentCommand.cs`
|
||||||
|
- `BackOffice.BFF.Application/ManualPaymentCQ/Commands/CreateManualPayment/CreateManualPaymentCommandHandler.cs`
|
||||||
|
- `BackOffice.BFF.Application/ManualPaymentCQ/Queries/GetManualPayments/GetManualPaymentsQueryHandler.cs`
|
||||||
|
- `BackOffice.BFF.Application/ManualPaymentCQ/Queries/GetManualPayments/GetManualPaymentsResponseDto.cs`
|
||||||
|
- `BackOffice.BFF.Application/ManualPaymentCQ/ManualPaymentProfile.cs`
|
||||||
|
- `Protobufs/BackOffice.BFF.ManualPayment.Protobuf/Protos/manualpayment.proto`
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
- `BackOffice/Pages/Payment/Components/ManualPaymentDialog.razor`
|
||||||
|
- `BackOffice/Pages/Payment/Components/ManualPaymentDialog.razor.cs`
|
||||||
|
- `BackOffice/Pages/Payment/ManualPayments.razor`
|
||||||
|
- `BackOffice/Shared/NavMenu.razor`
|
||||||
|
|
||||||
|
### حذف شده
|
||||||
|
- `BackOffice.Main/Pages/Payment/ManualMembershipPayment.razor`
|
||||||
|
- `BackOffice.Main/Pages/Payment/ManualMembershipPayment.razor.cs`
|
||||||
+69
-3
@@ -1,12 +1,78 @@
|
|||||||
# کارهای باقیمانده - BackOffice
|
# کارهای باقیمانده - BackOffice
|
||||||
|
|
||||||
> آخرین بروزرسانی: December 20, 2025
|
> آخرین بروزرسانی: January 1, 2026
|
||||||
|
|
||||||
## وضعیت کلی
|
## وضعیت کلی
|
||||||
|
|
||||||
**Build Status**: ✅ SUCCESS (0 Errors)
|
**Build Status**: ✅ SUCCESS (0 Errors)
|
||||||
**Enabled Modules**: 10+ ماژول کامل
|
**Enabled Modules**: 12+ ماژول کامل
|
||||||
**Remaining Tasks**: فقط Backend Implementation
|
**System Status**: **PRODUCTION READY** 🚀
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ کارهای انجام شده - Session January 1, 2026
|
||||||
|
|
||||||
|
### فعالسازی ماژولهای فروشگاه تخفیفی (DiscountShop Frontend)
|
||||||
|
|
||||||
|
**وضعیت**: ✅ COMPLETED - همه چیز فعال و build موفق
|
||||||
|
|
||||||
|
**فایل اصلی تغییر یافته**:
|
||||||
|
- `BackOffice/Common/Configure/ConfigureService.cs`
|
||||||
|
|
||||||
|
**تغییرات**:
|
||||||
|
|
||||||
|
#### 1. Using Statements فعال شدند:
|
||||||
|
```csharp
|
||||||
|
// Discount Shop Proto Clients
|
||||||
|
using BackOffice.BFF.DiscountProduct.Protobuf.Protos.DiscountProduct;
|
||||||
|
using BackOffice.BFF.DiscountCategory.Protobuf.Protos.DiscountCategory;
|
||||||
|
using BackOffice.BFF.DiscountOrder.Protobuf.Protos.DiscountOrder;
|
||||||
|
using BackOffice.BFF.Tag.Protobuf.Protos.Tag;
|
||||||
|
using BackOffice.BFF.ProductTag.Protobuf.Protos.ProductTag;
|
||||||
|
using Foursat.BackOffice.BFF.PublicMessage.Protobuf;
|
||||||
|
|
||||||
|
// Application Services
|
||||||
|
using BackOffice.Services.DiscountProduct;
|
||||||
|
using BackOffice.Services.DiscountCategory;
|
||||||
|
using BackOffice.Services.DiscountOrder;
|
||||||
|
using BackOffice.Services.PublicMessage;
|
||||||
|
using BackOffice.Services.Tag;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. gRPC Clients فعال شدند:
|
||||||
|
```csharp
|
||||||
|
// Discount Shop Services
|
||||||
|
services.AddTransient(sp => new DiscountProductContract.DiscountProductContractClient(...));
|
||||||
|
services.AddTransient(sp => new DiscountCategoryContract.DiscountCategoryContractClient(...));
|
||||||
|
services.AddTransient(sp => new DiscountOrderContract.DiscountOrderContractClient(...));
|
||||||
|
|
||||||
|
// Public Message Service
|
||||||
|
services.AddTransient(sp => new PublicMessageContract.PublicMessageContractClient(...));
|
||||||
|
|
||||||
|
// Tag Management Services
|
||||||
|
services.AddTransient(sp => new TagContract.TagContractClient(...));
|
||||||
|
services.AddTransient(sp => new ProductTagContract.ProductTagContractClient(...));
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Application Services فعال شدند:
|
||||||
|
```csharp
|
||||||
|
services.AddScoped<IDiscountProductService, DiscountProductService>();
|
||||||
|
services.AddScoped<IDiscountCategoryService, DiscountCategoryService>();
|
||||||
|
services.AddScoped<IDiscountOrderService, DiscountOrderService>();
|
||||||
|
services.AddScoped<IPublicMessageService, PublicMessageService>();
|
||||||
|
services.AddScoped<ITagService, TagService>();
|
||||||
|
```
|
||||||
|
|
||||||
|
### صفحات فعال شده:
|
||||||
|
|
||||||
|
| صفحه | Route | توضیحات |
|
||||||
|
|------|-------|---------|
|
||||||
|
| مدیریت محصولات تخفیفی | `/discount-products` | CRUD + گالری تصاویر |
|
||||||
|
| مدیریت دستهبندیها | `/discount-categories` | CRUD + سلسلهمراتب |
|
||||||
|
| مدیریت سفارشات | `/discount-orders` | مشاهده + تغییر وضعیت |
|
||||||
|
| گزارش فروش | `/sales-reports` | آمار و نمودار |
|
||||||
|
| مدیریت تگها | `/tags` | CRUD تگها |
|
||||||
|
| پیامهای عمومی | `/public-messages` | CRUD + انتشار |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,131 +0,0 @@
|
|||||||
@page "/payment/membership"
|
|
||||||
@using BackOffice.BFF.ManualPayment.Protobuf
|
|
||||||
@using BackOffice.Main.Pages.AutoComplete
|
|
||||||
@attribute [Authorize(Roles = "Admin,SuperAdmin")]
|
|
||||||
|
|
||||||
<PageTitle>پرداخت دستی عضویت</PageTitle>
|
|
||||||
|
|
||||||
<MudContainer MaxWidth="MaxWidth.Large" Class="mt-4">
|
|
||||||
<MudCard>
|
|
||||||
<MudCardHeader>
|
|
||||||
<CardHeaderContent>
|
|
||||||
<MudText Typo="Typo.h5">
|
|
||||||
<MudIcon Icon="@Icons.Material.Filled.Payment" Class="ml-2" />
|
|
||||||
پرداخت دستی عضویت
|
|
||||||
</MudText>
|
|
||||||
</CardHeaderContent>
|
|
||||||
</MudCardHeader>
|
|
||||||
|
|
||||||
<MudCardContent>
|
|
||||||
<MudGrid>
|
|
||||||
<!-- انتخاب کاربر -->
|
|
||||||
<MudItem xs="12" md="6">
|
|
||||||
<UserAutoComplete
|
|
||||||
Label="انتخاب کاربر *"
|
|
||||||
@bind-SelectedUserId="_userId"
|
|
||||||
HelperText="جستجو با نام، موبایل یا شناسه" />
|
|
||||||
</MudItem>
|
|
||||||
|
|
||||||
<!-- مبلغ -->
|
|
||||||
<MudItem xs="12" md="6">
|
|
||||||
<MudNumericField
|
|
||||||
@bind-Value="_amount"
|
|
||||||
Label="مبلغ (ریال) *"
|
|
||||||
Min="0"
|
|
||||||
Step="10000"
|
|
||||||
Format="N0"
|
|
||||||
Variant="Variant.Outlined"
|
|
||||||
HelperText="مبلغ پرداختی به ریال" />
|
|
||||||
</MudItem>
|
|
||||||
|
|
||||||
<!-- شماره مرجع -->
|
|
||||||
<MudItem xs="12" md="6">
|
|
||||||
<MudTextField
|
|
||||||
@bind-Value="_referenceNumber"
|
|
||||||
Label="شماره مرجع *"
|
|
||||||
Variant="Variant.Outlined"
|
|
||||||
MaxLength="50"
|
|
||||||
HelperText="شماره مرجع تراکنش یا فیش" />
|
|
||||||
</MudItem>
|
|
||||||
|
|
||||||
<!-- توضیحات -->
|
|
||||||
<MudItem xs="12" md="6">
|
|
||||||
<MudTextField
|
|
||||||
@bind-Value="_description"
|
|
||||||
Label="توضیحات"
|
|
||||||
Variant="Variant.Outlined"
|
|
||||||
Lines="3"
|
|
||||||
MaxLength="500"
|
|
||||||
HelperText="توضیحات اختیاری" />
|
|
||||||
</MudItem>
|
|
||||||
</MudGrid>
|
|
||||||
|
|
||||||
@if (_showResult)
|
|
||||||
{
|
|
||||||
<MudAlert Severity="Severity.Success" Class="mt-4" ShowCloseIcon CloseIconClicked="@(() => _showResult = false)">
|
|
||||||
<MudText>@_resultMessage</MudText>
|
|
||||||
<MudText Typo="Typo.caption">شماره تراکنش: @_transactionId</MudText>
|
|
||||||
<MudText Typo="Typo.caption">شماره سفارش: @_orderId</MudText>
|
|
||||||
<MudText Typo="Typo.caption">موجودی جدید کیف پول: @_newWalletBalance.ToString("N0") ریال</MudText>
|
|
||||||
</MudAlert>
|
|
||||||
}
|
|
||||||
</MudCardContent>
|
|
||||||
|
|
||||||
<MudCardActions>
|
|
||||||
<MudButton
|
|
||||||
Variant="Variant.Filled"
|
|
||||||
Color="Color.Primary"
|
|
||||||
StartIcon="@Icons.Material.Filled.Check"
|
|
||||||
OnClick="ProcessPayment"
|
|
||||||
Disabled="@_isProcessing">
|
|
||||||
@if (_isProcessing)
|
|
||||||
{
|
|
||||||
<MudProgressCircular Class="ml-2" Size="Size.Small" Indeterminate="true" />
|
|
||||||
<MudText>در حال ثبت...</MudText>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<MudText>ثبت پرداخت</MudText>
|
|
||||||
}
|
|
||||||
</MudButton>
|
|
||||||
|
|
||||||
<MudButton
|
|
||||||
Variant="Variant.Outlined"
|
|
||||||
Color="Color.Default"
|
|
||||||
StartIcon="@Icons.Material.Filled.Clear"
|
|
||||||
OnClick="ResetForm"
|
|
||||||
Disabled="@_isProcessing">
|
|
||||||
پاک کردن فرم
|
|
||||||
</MudButton>
|
|
||||||
</MudCardActions>
|
|
||||||
</MudCard>
|
|
||||||
|
|
||||||
<!-- راهنما -->
|
|
||||||
<MudCard Class="mt-4">
|
|
||||||
<MudCardHeader>
|
|
||||||
<CardHeaderContent>
|
|
||||||
<MudText Typo="Typo.h6">
|
|
||||||
<MudIcon Icon="@Icons.Material.Filled.Info" Class="ml-2" />
|
|
||||||
راهنما
|
|
||||||
</MudText>
|
|
||||||
</CardHeaderContent>
|
|
||||||
</MudCardHeader>
|
|
||||||
<MudCardContent>
|
|
||||||
<MudText Typo="Typo.body2" Class="mb-2">این صفحه برای ثبت پرداختهای دستی عضویت استفاده میشود. پس از ثبت:</MudText>
|
|
||||||
<MudList Dense="true">
|
|
||||||
<MudListItem Icon="@Icons.Material.Filled.AccountBalance">
|
|
||||||
<MudText Typo="Typo.body2">مبلغ به کیف پول کاربر (Balance و DiscountBalance) اضافه میشود</MudText>
|
|
||||||
</MudListItem>
|
|
||||||
<MudListItem Icon="@Icons.Material.Filled.History">
|
|
||||||
<MudText Typo="Typo.body2">لاگ تغییرات کیف پول ثبت میشود</MudText>
|
|
||||||
</MudListItem>
|
|
||||||
<MudListItem Icon="@Icons.Material.Filled.Receipt">
|
|
||||||
<MudText Typo="Typo.body2">تراکنش با وضعیت موفق ثبت میشود</MudText>
|
|
||||||
</MudListItem>
|
|
||||||
<MudListItem Icon="@Icons.Material.Filled.ShoppingCart">
|
|
||||||
<MudText Typo="Typo.body2">سفارش خرید پکیج عضویت ثبت میشود</MudText>
|
|
||||||
</MudListItem>
|
|
||||||
</MudList>
|
|
||||||
</MudCardContent>
|
|
||||||
</MudCard>
|
|
||||||
</MudContainer>
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -117,25 +117,27 @@
|
|||||||
<PackageReference Include="Foursat.BackOffice.BFF.Commission.Protobuf" Version="0.0.15"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.Commission.Protobuf" Version="0.0.15"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.Common.Protobuf" Version="0.0.3"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.Common.Protobuf" Version="0.0.3"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.Configuration.Protobuf" Version="1.0.20"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.Configuration.Protobuf" Version="1.0.20"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.DiscountCategory.Protobuf" Version="0.0.3"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.DiscountCategory.Protobuf" Version="0.0.4"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.DiscountOrder.Protobuf" Version="0.0.3"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.DiscountOrder.Protobuf" Version="0.0.4"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.DiscountProduct.Protobuf" Version="0.0.3"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.DiscountProduct.Protobuf" Version="0.0.4"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.DiscountShoppingCart.Protobuf" Version="0.0.3"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.DiscountShoppingCart.Protobuf" Version="0.0.4"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.Health.Protobuf" Version="1.0.5"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.Health.Protobuf" Version="1.0.5"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.ManualPayment.Protobuf" Version="0.0.5"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.ManualPayment.Protobuf" Version="0.0.14"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.NetworkMembership.Protobuf" Version="0.0.11"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.NetworkMembership.Protobuf" Version="0.0.11"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.Otp.Protobuf" Version="0.0.113"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.Otp.Protobuf" Version="0.0.113"/>
|
||||||
<PackageReference Include="FourSat.BackOffice.BFF.Package.Protobuf" Version="0.0.113"/>
|
<PackageReference Include="FourSat.BackOffice.BFF.Package.Protobuf" Version="0.0.113"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.Products.Protobuf" Version="0.0.11"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.Products.Protobuf" Version="0.0.11"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.ProductTag.Protobuf" Version="0.0.3"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.ProductTag.Protobuf" Version="0.0.4"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.PublicMessage.Protobuf" Version="0.0.5"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.PublicMessage.Protobuf" Version="0.0.5"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.Role.Protobuf" Version="0.0.113"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.Role.Protobuf" Version="0.0.113"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.Tag.Protobuf" Version="0.0.4"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.Tag.Protobuf" Version="0.0.5"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.User.Protobuf" Version="0.0.114"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.User.Protobuf" Version="0.0.114"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.UserAddress.Protobuf" Version="0.0.113"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.UserAddress.Protobuf" Version="0.0.113"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.UserOrder.Protobuf" Version="0.0.116"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.UserOrder.Protobuf" Version="0.0.116"/>
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.UserRole.Protobuf" Version="0.0.113"/>
|
<PackageReference Include="Foursat.BackOffice.BFF.UserRole.Protobuf" Version="0.0.113"/>
|
||||||
|
<PackageReference Include="Foursat.BackOffice.BFF.Inventory.Protobuf" Version="0.0.1"/>
|
||||||
|
|
||||||
|
|
||||||
<!-- Proto Projects - Using ProjectReference for latest changes -->
|
<!-- Proto Projects - Using ProjectReference for latest changes -->
|
||||||
<!-- <ProjectReference Include="../../../BackOffice.BFF/src/Protobufs/BackOffice.BFF.Category.Protobuf/BackOffice.BFF.Category.Protobuf.csproj" />-->
|
<!-- <ProjectReference Include="../../../BackOffice.BFF/src/Protobufs/BackOffice.BFF.Category.Protobuf/BackOffice.BFF.Category.Protobuf.csproj" />-->
|
||||||
<!-- <ProjectReference Include="../../../BackOffice.BFF/src/Protobufs/BackOffice.BFF.ClubMembership.Protobuf/BackOffice.BFF.ClubMembership.Protobuf.csproj" />-->
|
<!-- <ProjectReference Include="../../../BackOffice.BFF/src/Protobufs/BackOffice.BFF.ClubMembership.Protobuf/BackOffice.BFF.ClubMembership.Protobuf.csproj" />-->
|
||||||
|
|||||||
@@ -14,19 +14,21 @@ using Foursat.BackOffice.BFF.NetworkMembership.Protos;
|
|||||||
using Foursat.BackOffice.BFF.Health.Protobuf;
|
using Foursat.BackOffice.BFF.Health.Protobuf;
|
||||||
using BackOffice.BFF.Configuration.Protobuf.Protos.AppVersion;
|
using BackOffice.BFF.Configuration.Protobuf.Protos.AppVersion;
|
||||||
|
|
||||||
// TODO: Create these proto projects - temporarily disabled
|
// Discount Shop Proto Clients
|
||||||
// using BackOffice.BFF.DiscountProduct.Protobuf.Protos.DiscountProduct;
|
using BackOffice.BFF.DiscountProduct.Protobuf.Protos.DiscountProduct;
|
||||||
// using BackOffice.BFF.DiscountCategory.Protobuf.Protos.DiscountCategory;
|
using BackOffice.BFF.DiscountCategory.Protobuf.Protos.DiscountCategory;
|
||||||
// using BackOffice.BFF.DiscountOrder.Protobuf.Protos.DiscountOrder;
|
using BackOffice.BFF.DiscountOrder.Protobuf.Protos.DiscountOrder;
|
||||||
// using BackOffice.BFF.Tag.Protobuf.Protos.Tag;
|
using BackOffice.BFF.Tag.Protobuf.Protos.Tag;
|
||||||
// using BackOffice.BFF.ProductTag.Protobuf.Protos.ProductTag;
|
using BackOffice.BFF.ProductTag.Protobuf.Protos.ProductTag;
|
||||||
// using BackOffice.BFF.PublicMessage.Protobuf.Protos.PublicMessage;
|
using BackOffice.BFF.ManualPayment.Protobuf;
|
||||||
|
using Foursat.BackOffice.BFF.PublicMessage.Protobuf;
|
||||||
|
using Foursat.BackOffice.BFF.Inventory.Protos;
|
||||||
using BackOffice.Common.Utilities;
|
using BackOffice.Common.Utilities;
|
||||||
// using BackOffice.Services.DiscountProduct;
|
using BackOffice.Services.DiscountProduct;
|
||||||
// using BackOffice.Services.DiscountCategory;
|
using BackOffice.Services.DiscountCategory;
|
||||||
// using BackOffice.Services.DiscountOrder;
|
using BackOffice.Services.DiscountOrder;
|
||||||
// using BackOffice.Services.PublicMessage;
|
using BackOffice.Services.PublicMessage;
|
||||||
// using BackOffice.Services.Tag;
|
using BackOffice.Services.Tag;
|
||||||
using Blazored.LocalStorage;
|
using Blazored.LocalStorage;
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using Grpc.Core.Interceptors;
|
using Grpc.Core.Interceptors;
|
||||||
@@ -37,9 +39,6 @@ using MudBlazor.Services;
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
using Foursat.BackOffice.BFF.Health.Protobuf;
|
|
||||||
using Foursat.BackOffice.BFF.NetworkMembership.Protos;
|
|
||||||
|
|
||||||
|
|
||||||
namespace Microsoft.Extensions.DependencyInjection;
|
namespace Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
@@ -70,12 +69,13 @@ public static class ConfigureServices
|
|||||||
// Application Services
|
// Application Services
|
||||||
services.AddScoped<BackOffice.Services.Authorization.IAuthorizationService, BackOffice.Services.Authorization.AuthorizationService>();
|
services.AddScoped<BackOffice.Services.Authorization.IAuthorizationService, BackOffice.Services.Authorization.AuthorizationService>();
|
||||||
services.AddScoped<BackOffice.Services.AppVersion.IAppVersionService, BackOffice.Services.AppVersion.AppVersionService>();
|
services.AddScoped<BackOffice.Services.AppVersion.IAppVersionService, BackOffice.Services.AppVersion.AppVersionService>();
|
||||||
// TODO: Re-enable when proto projects are created
|
|
||||||
// services.AddScoped<IDiscountProductService, DiscountProductService>();
|
// Discount Shop Services
|
||||||
// services.AddScoped<IDiscountCategoryService, DiscountCategoryService>();
|
services.AddScoped<IDiscountProductService, DiscountProductService>();
|
||||||
// services.AddScoped<IDiscountOrderService, DiscountOrderService>();
|
services.AddScoped<IDiscountCategoryService, DiscountCategoryService>();
|
||||||
// services.AddScoped<IPublicMessageService, PublicMessageService>();
|
services.AddScoped<IDiscountOrderService, DiscountOrderService>();
|
||||||
// services.AddScoped<ITagService, TagService>();
|
services.AddScoped<IPublicMessageService, PublicMessageService>();
|
||||||
|
services.AddScoped<ITagService, TagService>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
@@ -120,18 +120,23 @@ public static class ConfigureServices
|
|||||||
services.AddTransient(sp => new HealthContract.HealthContractClient(sp.GetRequiredService<CallInvoker>()));
|
services.AddTransient(sp => new HealthContract.HealthContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
services.AddTransient(sp => new AppVersionContract.AppVersionContractClient(sp.GetRequiredService<CallInvoker>()));
|
services.AddTransient(sp => new AppVersionContract.AppVersionContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
|
|
||||||
// TODO: Re-enable when proto projects are created
|
|
||||||
// Discount Shop Services
|
// Discount Shop Services
|
||||||
// services.AddTransient(sp => new DiscountProductsContract.DiscountProductsContractClient(sp.GetRequiredService<CallInvoker>()));
|
services.AddTransient(sp => new DiscountProductContract.DiscountProductContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
// services.AddTransient(sp => new DiscountCategoriesContract.DiscountCategoriesContractClient(sp.GetRequiredService<CallInvoker>()));
|
services.AddTransient(sp => new DiscountCategoryContract.DiscountCategoryContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
// services.AddTransient(sp => new DiscountOrdersContract.DiscountOrdersContractClient(sp.GetRequiredService<CallInvoker>()));
|
services.AddTransient(sp => new DiscountOrderContract.DiscountOrderContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
|
|
||||||
// Public Message Service
|
// Public Message Service
|
||||||
// services.AddTransient(sp => new PublicMessagesContract.PublicMessagesContractClient(sp.GetRequiredService<CallInvoker>()));
|
services.AddTransient(sp => new PublicMessageContract.PublicMessageContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
|
|
||||||
// Tag Management Services
|
// Tag Management Services
|
||||||
// services.AddTransient(sp => new TagContract.TagContractClient(sp.GetRequiredService<CallInvoker>()));
|
services.AddTransient(sp => new TagContract.TagContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
// services.AddTransient(sp => new ProductTagContract.ProductTagContractClient(sp.GetRequiredService<CallInvoker>()));
|
services.AddTransient(sp => new ProductTagContract.ProductTagContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
|
|
||||||
|
// Manual Payment Service
|
||||||
|
services.AddTransient(sp => new ManualPaymentContract.ManualPaymentContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
|
|
||||||
|
// Inventory Management Service
|
||||||
|
services.AddTransient(sp => new InventoryBFFContract.InventoryBFFContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,4 +17,11 @@ public static class RouteConstance
|
|||||||
public const string ProductCategories = "/ProductCategoriesPage/";
|
public const string ProductCategories = "/ProductCategoriesPage/";
|
||||||
public const string ProductsBulkEdit = "/ProductsBulkEditPage/";
|
public const string ProductsBulkEdit = "/ProductsBulkEditPage/";
|
||||||
public const string CategoryProducts = "/CategoryProductsPage/";
|
public const string CategoryProducts = "/CategoryProductsPage/";
|
||||||
|
|
||||||
|
// Inventory Management
|
||||||
|
public const string Inventory = "/InventoryPage/";
|
||||||
|
public const string InventoryLowStock = "/InventoryLowStockPage/";
|
||||||
|
public const string InventoryWarehouses = "/InventoryWarehousesPage/";
|
||||||
|
public const string InventoryAddStock = "/InventoryAddStockPage/";
|
||||||
|
public const string InventoryMovements = "/InventoryMovementsPage/";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,12 +23,12 @@
|
|||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
|
||||||
<MudPaper Class="pa-3" Elevation="0">
|
<MudPaper Class="pa-3" Elevation="0">
|
||||||
<MudText Typo="Typo.h6" Color="Color.Primary">جزئیات Payout</MudText>
|
<MudText Typo="Typo.h6" Color="Color.Primary">جزئیات پرداخت</MudText>
|
||||||
<MudDivider Class="my-2" />
|
<MudDivider Class="my-2" />
|
||||||
<MudSimpleTable Dense="true">
|
<MudSimpleTable Dense="true">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>شناسه Payout:</strong></td>
|
<td><strong>شناسه پرداخت:</strong></td>
|
||||||
<td>@Payout.Id</td>
|
<td>@Payout.Id</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -229,7 +229,7 @@
|
|||||||
Color="Color.Primary"
|
Color="Color.Primary"
|
||||||
StartIcon="@Icons.Material.Filled.Payments"
|
StartIcon="@Icons.Material.Filled.Payments"
|
||||||
Href="/commission/payouts">
|
Href="/commission/payouts">
|
||||||
مشاهده Payout ها
|
مشاهده پرداختها
|
||||||
</MudButton>
|
</MudButton>
|
||||||
<MudButton Variant="Variant.Filled"
|
<MudButton Variant="Variant.Filled"
|
||||||
Color="Color.Secondary"
|
Color="Color.Secondary"
|
||||||
|
|||||||
@@ -99,7 +99,7 @@
|
|||||||
<MudTd>@context.Id</MudTd>
|
<MudTd>@context.Id</MudTd>
|
||||||
<MudTd>@context.UserFullName (@context.UserNationalCode)</MudTd>
|
<MudTd>@context.UserFullName (@context.UserNationalCode)</MudTd>
|
||||||
<MudTd>@context.Amount.ToString("N0")</MudTd>
|
<MudTd>@context.Amount.ToString("N0")</MudTd>
|
||||||
<MudTd>@context.PaymentDate.ToDateTime().ToLocalTime().ToString("yyyy/MM/dd HH:mm")</MudTd>
|
<MudTd>@context.PaymentDate.ToDateTime().MiladiToJalali()</MudTd>
|
||||||
</RowTemplate>
|
</RowTemplate>
|
||||||
</MudTable>
|
</MudTable>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
@using Foursat.BackOffice.BFF.Inventory.Protos
|
||||||
|
@using Google.Protobuf.WellKnownTypes
|
||||||
|
@using InventoryProductType = Foursat.BackOffice.BFF.Inventory.Protos.ProductType
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<DialogContent>
|
||||||
|
<MudForm @ref="_form" @bind-IsValid="_formValid">
|
||||||
|
<MudGrid>
|
||||||
|
@if (ProductIdParam == null)
|
||||||
|
{
|
||||||
|
<MudItem xs="12">
|
||||||
|
<MudSelect T="InventoryProductType" @bind-Value="_productType"
|
||||||
|
Label="نوع محصول"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="نوع محصول الزامی است">
|
||||||
|
<MudSelectItem Value="InventoryProductType.Regular">محصول عادی</MudSelectItem>
|
||||||
|
<MudSelectItem Value="InventoryProductType.Discount">محصول تخفیفی</MudSelectItem>
|
||||||
|
</MudSelect>
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="12">
|
||||||
|
<MudNumericField T="long" @bind-Value="_productId"
|
||||||
|
Label="شناسه محصول"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="شناسه محصول الزامی است"
|
||||||
|
Min="1" />
|
||||||
|
</MudItem>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudItem xs="12">
|
||||||
|
<MudAlert Severity="Severity.Info">
|
||||||
|
محصول: <strong>@ProductName</strong>
|
||||||
|
</MudAlert>
|
||||||
|
</MudItem>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudItem xs="12">
|
||||||
|
<MudSelect T="long?" @bind-Value="_warehouseId"
|
||||||
|
Label="انبار"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="انبار الزامی است">
|
||||||
|
@foreach (var wh in Warehouses)
|
||||||
|
{
|
||||||
|
<MudSelectItem Value="@((long?)wh.Id)">@wh.Name</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
</MudItem>
|
||||||
|
|
||||||
|
<MudItem xs="12">
|
||||||
|
<MudNumericField T="int" @bind-Value="_quantity"
|
||||||
|
Label="تعداد"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="تعداد الزامی است"
|
||||||
|
Min="1" />
|
||||||
|
</MudItem>
|
||||||
|
|
||||||
|
<MudItem xs="12">
|
||||||
|
<MudTextField T="string" @bind-Value="_referenceNumber"
|
||||||
|
Label="شماره مرجع (فاکتور/رسید)"
|
||||||
|
Variant="Variant.Outlined" />
|
||||||
|
</MudItem>
|
||||||
|
|
||||||
|
<MudItem xs="12">
|
||||||
|
<MudTextField T="string" @bind-Value="_note"
|
||||||
|
Label="توضیحات"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Lines="3" />
|
||||||
|
</MudItem>
|
||||||
|
</MudGrid>
|
||||||
|
</MudForm>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton OnClick="Cancel">انصراف</MudButton>
|
||||||
|
<MudButton Color="Color.Primary"
|
||||||
|
Variant="Variant.Filled"
|
||||||
|
OnClick="Submit"
|
||||||
|
Disabled="@(!_formValid || _isSubmitting)">
|
||||||
|
@if (_isSubmitting)
|
||||||
|
{
|
||||||
|
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
|
||||||
|
}
|
||||||
|
ثبت موجودی
|
||||||
|
</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[CascadingParameter] IMudDialogInstance MudDialog { get; set; } = default!;
|
||||||
|
[Inject] public InventoryBFFContract.InventoryBFFContractClient InventoryContract { get; set; } = default!;
|
||||||
|
|
||||||
|
[Parameter] public List<WarehouseDto> Warehouses { get; set; } = new();
|
||||||
|
[Parameter] public long? ProductIdParam { get; set; }
|
||||||
|
[Parameter] public InventoryProductType? ProductTypeParam { get; set; }
|
||||||
|
[Parameter] public string? ProductName { get; set; }
|
||||||
|
[Parameter] public long? WarehouseIdParam { get; set; }
|
||||||
|
|
||||||
|
private MudForm? _form;
|
||||||
|
private bool _formValid;
|
||||||
|
private bool _isSubmitting;
|
||||||
|
|
||||||
|
private InventoryProductType _productType = InventoryProductType.Regular;
|
||||||
|
private long _productId;
|
||||||
|
private long? _warehouseId;
|
||||||
|
private int _quantity = 1;
|
||||||
|
private string _referenceNumber = string.Empty;
|
||||||
|
private string _note = string.Empty;
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
if (ProductIdParam.HasValue)
|
||||||
|
{
|
||||||
|
_productId = ProductIdParam.Value;
|
||||||
|
}
|
||||||
|
if (ProductTypeParam.HasValue)
|
||||||
|
{
|
||||||
|
_productType = ProductTypeParam.Value;
|
||||||
|
}
|
||||||
|
if (WarehouseIdParam.HasValue)
|
||||||
|
{
|
||||||
|
_warehouseId = WarehouseIdParam.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel() => MudDialog.Cancel();
|
||||||
|
|
||||||
|
private async Task Submit()
|
||||||
|
{
|
||||||
|
if (_form != null)
|
||||||
|
{
|
||||||
|
await _form.Validate();
|
||||||
|
if (!_formValid) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_warehouseId.HasValue)
|
||||||
|
{
|
||||||
|
Snackbar.Add("لطفاً انبار را انتخاب کنید", Severity.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isSubmitting = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new AddStockRequest
|
||||||
|
{
|
||||||
|
ProductId = ProductIdParam ?? _productId,
|
||||||
|
ProductType = ProductTypeParam ?? _productType,
|
||||||
|
Quantity = _quantity,
|
||||||
|
ReferenceNumber = _referenceNumber ?? string.Empty,
|
||||||
|
Note = _note ?? string.Empty,
|
||||||
|
WarehouseId = _warehouseId.Value
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await InventoryContract.AddStockAsync(request);
|
||||||
|
|
||||||
|
if (response.Success)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"موجودی جدید: {response.NewQuantity} عدد", 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
@using Foursat.BackOffice.BFF.Inventory.Protos
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<DialogContent>
|
||||||
|
<MudForm @ref="_form" @bind-IsValid="_formValid">
|
||||||
|
<MudStack Spacing="4">
|
||||||
|
<MudAlert Severity="Severity.Info">
|
||||||
|
<MudStack Spacing="1">
|
||||||
|
<MudText>محصول: <strong>@Item.ProductName</strong></MudText>
|
||||||
|
<MudText>موجودی فعلی: <strong>@Item.Quantity</strong> عدد</MudText>
|
||||||
|
<MudText>رزرو شده: <strong>@Item.ReservedQuantity</strong> عدد</MudText>
|
||||||
|
<MudText>قابل فروش: <strong>@Item.AvailableQuantity</strong> عدد</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudAlert>
|
||||||
|
|
||||||
|
<MudNumericField T="int" @bind-Value="_newQuantity"
|
||||||
|
Label="موجودی جدید"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="موجودی الزامی است"
|
||||||
|
Min="0"
|
||||||
|
HelperText="@GetDifferenceText()" />
|
||||||
|
|
||||||
|
<MudTextField T="string" @bind-Value="_note"
|
||||||
|
Label="توضیحات (دلیل تغییر)"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Lines="3"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="توضیحات الزامی است" />
|
||||||
|
|
||||||
|
@if (_newQuantity != Item.Quantity)
|
||||||
|
{
|
||||||
|
<MudAlert Severity="@(GetDifference() > 0 ? Severity.Success : Severity.Warning)">
|
||||||
|
@GetDifferenceAlertText()
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
</MudForm>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton OnClick="Cancel">انصراف</MudButton>
|
||||||
|
<MudButton Color="Color.Primary"
|
||||||
|
Variant="Variant.Filled"
|
||||||
|
OnClick="Submit"
|
||||||
|
Disabled="@(!_formValid || _isSubmitting || _newQuantity == Item.Quantity)">
|
||||||
|
@if (_isSubmitting)
|
||||||
|
{
|
||||||
|
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
|
||||||
|
}
|
||||||
|
ذخیره تغییرات
|
||||||
|
</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[CascadingParameter] IMudDialogInstance MudDialog { get; set; } = default!;
|
||||||
|
[Inject] public InventoryBFFContract.InventoryBFFContractClient InventoryContract { get; set; } = default!;
|
||||||
|
|
||||||
|
[Parameter] public InventoryItemDto Item { get; set; } = default!;
|
||||||
|
|
||||||
|
private MudForm? _form;
|
||||||
|
private bool _formValid;
|
||||||
|
private bool _isSubmitting;
|
||||||
|
|
||||||
|
private int _newQuantity;
|
||||||
|
private string _note = string.Empty;
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
_newQuantity = Item.Quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetDifference() => _newQuantity - Item.Quantity;
|
||||||
|
|
||||||
|
private string GetDifferenceText()
|
||||||
|
{
|
||||||
|
var diff = GetDifference();
|
||||||
|
if (diff == 0) return "بدون تغییر";
|
||||||
|
return diff > 0 ? $"افزایش {diff} عدد" : $"کاهش {Math.Abs(diff)} عدد";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetDifferenceAlertText()
|
||||||
|
{
|
||||||
|
var diff = GetDifference();
|
||||||
|
if (diff > 0)
|
||||||
|
return $"موجودی {diff} عدد افزایش مییابد (از {Item.Quantity} به {_newQuantity})";
|
||||||
|
return $"موجودی {Math.Abs(diff)} عدد کاهش مییابد (از {Item.Quantity} به {_newQuantity})";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel() => MudDialog.Cancel();
|
||||||
|
|
||||||
|
private async Task Submit()
|
||||||
|
{
|
||||||
|
if (_form != null)
|
||||||
|
{
|
||||||
|
await _form.Validate();
|
||||||
|
if (!_formValid) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_newQuantity == Item.Quantity)
|
||||||
|
{
|
||||||
|
Snackbar.Add("هیچ تغییری در موجودی انجام نشده است", Severity.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isSubmitting = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var productId = Item.ProductType == ProductType.Regular
|
||||||
|
? (Item.ProductId ?? 0)
|
||||||
|
: (Item.DiscountProductId ?? 0);
|
||||||
|
|
||||||
|
var request = new AdjustStockRequest
|
||||||
|
{
|
||||||
|
ProductId = productId,
|
||||||
|
ProductType = Item.ProductType,
|
||||||
|
NewQuantity = _newQuantity,
|
||||||
|
Note = _note ?? string.Empty
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await InventoryContract.AdjustStockAsync(request);
|
||||||
|
|
||||||
|
if (response.Success)
|
||||||
|
{
|
||||||
|
Snackbar.Add(response.Message ?? "موجودی با موفقیت تنظیم شد", 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
@using Foursat.BackOffice.BFF.Inventory.Protos
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<DialogContent>
|
||||||
|
<MudForm @ref="_form" @bind-IsValid="_formValid">
|
||||||
|
<MudStack Spacing="4">
|
||||||
|
<MudTextField T="string" @bind-Value="_name"
|
||||||
|
Label="نام انبار"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="نام انبار الزامی است"
|
||||||
|
MaxLength="100" />
|
||||||
|
|
||||||
|
<MudTextField T="string" @bind-Value="_code"
|
||||||
|
Label="کد انبار"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="کد انبار الزامی است"
|
||||||
|
MaxLength="20"
|
||||||
|
HelperText="کد منحصر به فرد برای شناسایی انبار (مثال: WH-001)" />
|
||||||
|
|
||||||
|
<MudTextField T="string" @bind-Value="_address"
|
||||||
|
Label="آدرس"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Lines="2"
|
||||||
|
MaxLength="500" />
|
||||||
|
|
||||||
|
<MudSwitch T="bool" @bind-Value="_isDefault"
|
||||||
|
Color="Color.Primary"
|
||||||
|
Label="انبار پیشفرض"
|
||||||
|
LabelPosition="LabelPosition.Start" />
|
||||||
|
|
||||||
|
@if (_isDefault)
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Info">
|
||||||
|
این انبار به عنوان انبار پیشفرض برای ورود کالا تنظیم میشود.
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
</MudForm>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton OnClick="Cancel">انصراف</MudButton>
|
||||||
|
<MudButton Color="Color.Primary"
|
||||||
|
Variant="Variant.Filled"
|
||||||
|
OnClick="Submit"
|
||||||
|
Disabled="@(!_formValid || _isSubmitting)">
|
||||||
|
@if (_isSubmitting)
|
||||||
|
{
|
||||||
|
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
|
||||||
|
}
|
||||||
|
ایجاد انبار
|
||||||
|
</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[CascadingParameter] IMudDialogInstance MudDialog { get; set; } = default!;
|
||||||
|
[Inject] public InventoryBFFContract.InventoryBFFContractClient InventoryContract { get; set; } = default!;
|
||||||
|
|
||||||
|
private MudForm? _form;
|
||||||
|
private bool _formValid;
|
||||||
|
private bool _isSubmitting;
|
||||||
|
|
||||||
|
private string _name = string.Empty;
|
||||||
|
private string _code = string.Empty;
|
||||||
|
private string _address = string.Empty;
|
||||||
|
private bool _isDefault = false;
|
||||||
|
|
||||||
|
private void Cancel() => MudDialog.Cancel();
|
||||||
|
|
||||||
|
private async Task Submit()
|
||||||
|
{
|
||||||
|
if (_form != null)
|
||||||
|
{
|
||||||
|
await _form.Validate();
|
||||||
|
if (!_formValid) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isSubmitting = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new CreateWarehouseRequest
|
||||||
|
{
|
||||||
|
Name = _name,
|
||||||
|
Code = _code,
|
||||||
|
Address = _address ?? string.Empty,
|
||||||
|
IsDefault = _isDefault
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await InventoryContract.CreateWarehouseAsync(request);
|
||||||
|
|
||||||
|
if (response.Id > 0)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"انبار '{_name}' با شناسه {response.Id} ایجاد شد", Severity.Success);
|
||||||
|
MudDialog.Close(DialogResult.Ok(response.Id));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Snackbar.Add("خطا در ایجاد انبار", Severity.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"خطا: {ex.Message}", Severity.Error);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isSubmitting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
@using Foursat.BackOffice.BFF.Inventory.Protos
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<DialogContent>
|
||||||
|
<MudForm @ref="_form" @bind-IsValid="_formValid">
|
||||||
|
<MudStack Spacing="4">
|
||||||
|
<MudAlert Severity="Severity.Info">
|
||||||
|
<MudStack Spacing="1">
|
||||||
|
<MudText>محصول: <strong>@Item.ProductName</strong></MudText>
|
||||||
|
<MudText>انبار: <strong>@Item.WarehouseName</strong></MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudAlert>
|
||||||
|
|
||||||
|
<MudNumericField T="int" @bind-Value="_lowStockThreshold"
|
||||||
|
Label="حد هشدار کمموجودی"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="این فیلد الزامی است"
|
||||||
|
Min="0"
|
||||||
|
HelperText="وقتی موجودی کمتر از این عدد شود، هشدار داده میشود" />
|
||||||
|
|
||||||
|
<MudNumericField T="int" @bind-Value="_reorderPoint"
|
||||||
|
Label="نقطه سفارش مجدد"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="این فیلد الزامی است"
|
||||||
|
Min="0"
|
||||||
|
HelperText="وقتی موجودی به این عدد برسد، باید سفارش داده شود" />
|
||||||
|
|
||||||
|
<MudNumericField T="int" @bind-Value="_maxStockLevel"
|
||||||
|
Label="حداکثر موجودی مجاز"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="این فیلد الزامی است"
|
||||||
|
Min="1"
|
||||||
|
HelperText="حداکثر تعدادی که در انبار نگهداری میشود" />
|
||||||
|
|
||||||
|
<MudDivider />
|
||||||
|
|
||||||
|
<MudText Typo="Typo.caption" Color="Color.Secondary">
|
||||||
|
مقادیر فعلی:
|
||||||
|
حد هشدار: @Item.LowStockThreshold |
|
||||||
|
نقطه سفارش: @Item.ReorderPoint |
|
||||||
|
حداکثر: @Item.MaxStockLevel
|
||||||
|
</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudForm>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton OnClick="Cancel">انصراف</MudButton>
|
||||||
|
<MudButton Color="Color.Primary"
|
||||||
|
Variant="Variant.Filled"
|
||||||
|
OnClick="Submit"
|
||||||
|
Disabled="@(!_formValid || _isSubmitting)">
|
||||||
|
@if (_isSubmitting)
|
||||||
|
{
|
||||||
|
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
|
||||||
|
}
|
||||||
|
ذخیره تنظیمات
|
||||||
|
</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[CascadingParameter] IMudDialogInstance MudDialog { get; set; } = default!;
|
||||||
|
[Inject] public InventoryBFFContract.InventoryBFFContractClient InventoryContract { get; set; } = default!;
|
||||||
|
|
||||||
|
[Parameter] public InventoryItemDto Item { get; set; } = default!;
|
||||||
|
|
||||||
|
private MudForm? _form;
|
||||||
|
private bool _formValid;
|
||||||
|
private bool _isSubmitting;
|
||||||
|
|
||||||
|
private int _lowStockThreshold;
|
||||||
|
private int _reorderPoint;
|
||||||
|
private int _maxStockLevel;
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
_lowStockThreshold = Item.LowStockThreshold;
|
||||||
|
_reorderPoint = Item.ReorderPoint;
|
||||||
|
_maxStockLevel = Item.MaxStockLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel() => MudDialog.Cancel();
|
||||||
|
|
||||||
|
private async Task Submit()
|
||||||
|
{
|
||||||
|
if (_form != null)
|
||||||
|
{
|
||||||
|
await _form.Validate();
|
||||||
|
if (!_formValid) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
if (_lowStockThreshold > _reorderPoint)
|
||||||
|
{
|
||||||
|
Snackbar.Add("حد هشدار نمیتواند از نقطه سفارش بیشتر باشد", Severity.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_reorderPoint > _maxStockLevel)
|
||||||
|
{
|
||||||
|
Snackbar.Add("نقطه سفارش نمیتواند از حداکثر موجودی بیشتر باشد", Severity.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isSubmitting = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new UpdateInventorySettingsRequest
|
||||||
|
{
|
||||||
|
InventoryItemId = Item.Id,
|
||||||
|
LowStockThreshold = _lowStockThreshold,
|
||||||
|
ReorderPoint = _reorderPoint,
|
||||||
|
MaxStockLevel = _maxStockLevel
|
||||||
|
};
|
||||||
|
|
||||||
|
await InventoryContract.UpdateInventorySettingsAsync(request);
|
||||||
|
|
||||||
|
Snackbar.Add("تنظیمات با موفقیت ذخیره شد", Severity.Success);
|
||||||
|
MudDialog.Close(DialogResult.Ok(true));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"خطا: {ex.Message}", Severity.Error);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isSubmitting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
@using Foursat.BackOffice.BFF.Inventory.Protos
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<DialogContent>
|
||||||
|
<MudForm @ref="_form" @bind-IsValid="_formValid">
|
||||||
|
<MudStack Spacing="4">
|
||||||
|
<MudAlert Severity="Severity.Warning">
|
||||||
|
<MudStack Spacing="1">
|
||||||
|
<MudText>محصول: <strong>@Item.ProductName</strong></MudText>
|
||||||
|
<MudText>موجودی فعلی: <strong>@Item.Quantity</strong> عدد</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudAlert>
|
||||||
|
|
||||||
|
<MudNumericField T="int" @bind-Value="_quantity"
|
||||||
|
Label="تعداد خسارت دیده"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="تعداد الزامی است"
|
||||||
|
Min="1"
|
||||||
|
Max="@Item.Quantity"
|
||||||
|
HelperText="@($"حداکثر: {Item.Quantity} عدد")" />
|
||||||
|
|
||||||
|
<MudTextField T="string" @bind-Value="_reason"
|
||||||
|
Label="دلیل خسارت"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Lines="2"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="دلیل خسارت الزامی است"
|
||||||
|
Placeholder="مثال: آسیب در حمل و نقل، منقضی شده، ..." />
|
||||||
|
|
||||||
|
<MudTextField T="string" @bind-Value="_referenceNumber"
|
||||||
|
Label="شماره مرجع"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
HelperText="شماره گزارش یا سند داخلی" />
|
||||||
|
|
||||||
|
@if (_quantity > 0 && _quantity <= Item.Quantity)
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Error">
|
||||||
|
<MudText>
|
||||||
|
<strong>@_quantity</strong> عدد از موجودی کسر خواهد شد.
|
||||||
|
موجودی جدید: <strong>@(Item.Quantity - _quantity)</strong> عدد
|
||||||
|
</MudText>
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
</MudForm>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton OnClick="Cancel">انصراف</MudButton>
|
||||||
|
<MudButton Color="Color.Error"
|
||||||
|
Variant="Variant.Filled"
|
||||||
|
OnClick="Submit"
|
||||||
|
Disabled="@(!_formValid || _isSubmitting)">
|
||||||
|
@if (_isSubmitting)
|
||||||
|
{
|
||||||
|
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
|
||||||
|
}
|
||||||
|
ثبت خسارت
|
||||||
|
</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[CascadingParameter] IMudDialogInstance MudDialog { get; set; } = default!;
|
||||||
|
[Inject] public InventoryBFFContract.InventoryBFFContractClient InventoryContract { get; set; } = default!;
|
||||||
|
|
||||||
|
[Parameter] public InventoryItemDto Item { get; set; } = default!;
|
||||||
|
|
||||||
|
private MudForm? _form;
|
||||||
|
private bool _formValid;
|
||||||
|
private bool _isSubmitting;
|
||||||
|
|
||||||
|
private int _quantity = 1;
|
||||||
|
private string _reason = string.Empty;
|
||||||
|
private string _referenceNumber = string.Empty;
|
||||||
|
|
||||||
|
private void Cancel() => MudDialog.Cancel();
|
||||||
|
|
||||||
|
private async Task Submit()
|
||||||
|
{
|
||||||
|
if (_form != null)
|
||||||
|
{
|
||||||
|
await _form.Validate();
|
||||||
|
if (!_formValid) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_quantity > Item.Quantity)
|
||||||
|
{
|
||||||
|
Snackbar.Add("تعداد خسارت نمیتواند از موجودی فعلی بیشتر باشد", Severity.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isSubmitting = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var productId = Item.ProductType == ProductType.Regular
|
||||||
|
? (Item.ProductId ?? 0)
|
||||||
|
: (Item.DiscountProductId ?? 0);
|
||||||
|
|
||||||
|
var request = new RecordLossRequest
|
||||||
|
{
|
||||||
|
ProductId = productId,
|
||||||
|
ProductType = Item.ProductType,
|
||||||
|
Quantity = _quantity,
|
||||||
|
Reason = _reason ?? string.Empty,
|
||||||
|
ReferenceNumber = _referenceNumber ?? string.Empty
|
||||||
|
};
|
||||||
|
|
||||||
|
await InventoryContract.RecordLossAsync(request);
|
||||||
|
|
||||||
|
Snackbar.Add("خسارت با موفقیت ثبت شد", Severity.Success);
|
||||||
|
MudDialog.Close(DialogResult.Ok(true));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"خطا: {ex.Message}", Severity.Error);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isSubmitting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,216 @@
|
|||||||
|
@attribute [Route(RouteConstance.Inventory)]
|
||||||
|
@attribute [Authorize]
|
||||||
|
|
||||||
|
@using Foursat.BackOffice.BFF.Inventory.Protos
|
||||||
|
@using BackOffice.Pages.Inventory.Components
|
||||||
|
|
||||||
|
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="mt-4">
|
||||||
|
<MudText Typo="Typo.h4" Class="mb-4">مدیریت انبار</MudText>
|
||||||
|
|
||||||
|
@* Filters Section *@
|
||||||
|
<MudPaper Class="pa-4 mb-4" Elevation="2">
|
||||||
|
<MudGrid>
|
||||||
|
<MudItem xs="12" sm="6" md="3">
|
||||||
|
<MudSelect T="long?" @bind-Value="_filterWarehouseId"
|
||||||
|
Label="انبار"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense"
|
||||||
|
Clearable="true">
|
||||||
|
@foreach (var wh in _warehouses)
|
||||||
|
{
|
||||||
|
<MudSelectItem Value="@((long?)wh.Id)">@wh.Name</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="12" sm="6" md="3">
|
||||||
|
<MudSelect T="ProductType" @bind-Value="_filterProductType"
|
||||||
|
Label="نوع محصول"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense">
|
||||||
|
<MudSelectItem Value="ProductType.Unspecified">همه</MudSelectItem>
|
||||||
|
<MudSelectItem Value="ProductType.Regular">محصول عادی</MudSelectItem>
|
||||||
|
<MudSelectItem Value="ProductType.Discount">محصول تخفیفی</MudSelectItem>
|
||||||
|
</MudSelect>
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="12" sm="6" md="3">
|
||||||
|
<MudTextField T="string" @bind-Value="_searchTerm"
|
||||||
|
Label="جستجو"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense"
|
||||||
|
Adornment="Adornment.End"
|
||||||
|
AdornmentIcon="@Icons.Material.Filled.Search"
|
||||||
|
Clearable="true" />
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="12" sm="6" md="3" Class="d-flex align-end">
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
OnClick="ApplyFilter"
|
||||||
|
StartIcon="@Icons.Material.Filled.FilterAlt"
|
||||||
|
Class="mr-2">
|
||||||
|
اعمال فیلتر
|
||||||
|
</MudButton>
|
||||||
|
<MudButton Variant="Variant.Outlined"
|
||||||
|
Color="Color.Secondary"
|
||||||
|
OnClick="ClearFilter"
|
||||||
|
StartIcon="@Icons.Material.Filled.Clear">
|
||||||
|
پاک کردن
|
||||||
|
</MudButton>
|
||||||
|
</MudItem>
|
||||||
|
</MudGrid>
|
||||||
|
</MudPaper>
|
||||||
|
|
||||||
|
@* Stats Cards *@
|
||||||
|
<MudGrid Class="mb-4">
|
||||||
|
<MudItem xs="12" sm="6" md="3">
|
||||||
|
<MudCard>
|
||||||
|
<MudCardContent>
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
|
||||||
|
<MudStack Spacing="0">
|
||||||
|
<MudText Typo="Typo.caption">کل اقلام انبار</MudText>
|
||||||
|
<MudText Typo="Typo.h5">@_totalCount</MudText>
|
||||||
|
</MudStack>
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Inventory" Color="Color.Primary" Size="Size.Large" />
|
||||||
|
</MudStack>
|
||||||
|
</MudCardContent>
|
||||||
|
</MudCard>
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="12" sm="6" md="3">
|
||||||
|
<MudCard>
|
||||||
|
<MudCardContent>
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
|
||||||
|
<MudStack Spacing="0">
|
||||||
|
<MudText Typo="Typo.caption">محصولات کمموجود</MudText>
|
||||||
|
<MudText Typo="Typo.h5" Color="Color.Warning">@_lowStockCount</MudText>
|
||||||
|
</MudStack>
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Warning" Color="Color.Warning" Size="Size.Large" />
|
||||||
|
</MudStack>
|
||||||
|
</MudCardContent>
|
||||||
|
</MudCard>
|
||||||
|
</MudItem>
|
||||||
|
</MudGrid>
|
||||||
|
|
||||||
|
@* Data Grid *@
|
||||||
|
<MudDataGrid T="InventoryItemDto"
|
||||||
|
ServerData="@(new Func<GridState<InventoryItemDto>, Task<GridData<InventoryItemDto>>>(ServerReload))"
|
||||||
|
Hover="true"
|
||||||
|
@ref="_gridData"
|
||||||
|
Height="60vh">
|
||||||
|
<ToolBarContent>
|
||||||
|
<MudText Typo="Typo.h6">لیست موجودی انبار</MudText>
|
||||||
|
<MudSpacer />
|
||||||
|
<MudStack Row="true" Spacing="2">
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
OnClick="OpenAddStockDialog"
|
||||||
|
StartIcon="@Icons.Material.Filled.Add">
|
||||||
|
ورود کالا
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
</ToolBarContent>
|
||||||
|
<Columns>
|
||||||
|
<PropertyColumn Property="x => x.Id" Title="شناسه" />
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.ProductName" Title="نام محصول">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudStack Spacing="0">
|
||||||
|
<MudText Typo="Typo.body2"><strong>@context.Item.ProductName</strong></MudText>
|
||||||
|
<MudText Typo="Typo.caption" Color="Color.Secondary">
|
||||||
|
@GetProductTypeLabel(context.Item.ProductType)
|
||||||
|
</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.WarehouseName" Title="انبار">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudChip T="string" Color="Color.Info" Size="Size.Small">
|
||||||
|
@context.Item.WarehouseName
|
||||||
|
</MudChip>
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.Quantity" Title="موجودی">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudText Color="@(context.Item.IsLowStock ? Color.Error : Color.Default)">
|
||||||
|
<strong>@context.Item.Quantity</strong>
|
||||||
|
</MudText>
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.ReservedQuantity" Title="رزرو شده">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudText Color="Color.Warning">@context.Item.ReservedQuantity</MudText>
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.AvailableQuantity" Title="قابل فروش">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudText Color="Color.Success"><strong>@context.Item.AvailableQuantity</strong></MudText>
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.LowStockThreshold" Title="حد هشدار" />
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.IsLowStock" Title="وضعیت">
|
||||||
|
<CellTemplate>
|
||||||
|
@if (context.Item.IsLowStock)
|
||||||
|
{
|
||||||
|
<MudChip T="string" Color="Color.Error" Size="Size.Small">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Warning" Size="Size.Small" Class="mr-1" />
|
||||||
|
کمموجود
|
||||||
|
</MudChip>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudChip T="string" Color="Color.Success" Size="Size.Small">
|
||||||
|
موجود
|
||||||
|
</MudChip>
|
||||||
|
}
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
|
||||||
|
<TemplateColumn Title="عملیات" Sortable="false">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudStack Row="true" Spacing="1">
|
||||||
|
<MudTooltip Text="ورود کالا">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.AddCircle"
|
||||||
|
Size="Size.Small"
|
||||||
|
Color="Color.Success"
|
||||||
|
OnClick="@(() => OpenAddStockDialogForItem(context.Item))" />
|
||||||
|
</MudTooltip>
|
||||||
|
<MudTooltip Text="تنظیم موجودی">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Edit"
|
||||||
|
Size="Size.Small"
|
||||||
|
Color="Color.Primary"
|
||||||
|
OnClick="@(() => OpenAdjustStockDialog(context.Item))" />
|
||||||
|
</MudTooltip>
|
||||||
|
<MudTooltip Text="ثبت خسارت">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.RemoveCircle"
|
||||||
|
Size="Size.Small"
|
||||||
|
Color="Color.Error"
|
||||||
|
OnClick="@(() => OpenRecordLossDialog(context.Item))" />
|
||||||
|
</MudTooltip>
|
||||||
|
<MudTooltip Text="تنظیمات">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Settings"
|
||||||
|
Size="Size.Small"
|
||||||
|
Color="Color.Secondary"
|
||||||
|
OnClick="@(() => OpenSettingsDialog(context.Item))" />
|
||||||
|
</MudTooltip>
|
||||||
|
<MudTooltip Text="تاریخچه تغییرات">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.History"
|
||||||
|
Size="Size.Small"
|
||||||
|
Color="Color.Info"
|
||||||
|
OnClick="@(() => ViewMovements(context.Item))" />
|
||||||
|
</MudTooltip>
|
||||||
|
</MudStack>
|
||||||
|
</CellTemplate>
|
||||||
|
</TemplateColumn>
|
||||||
|
</Columns>
|
||||||
|
<PagerContent>
|
||||||
|
<MudDataGridPager T="InventoryItemDto"
|
||||||
|
PageSizeOptions="@(new int[] { 20, 50, 100 })"
|
||||||
|
InfoFormat="سطر {first_item} تا {last_item} از {all_items}"
|
||||||
|
RowsPerPageString="تعداد سطرهای صفحه" />
|
||||||
|
</PagerContent>
|
||||||
|
</MudDataGrid>
|
||||||
|
</MudContainer>
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using MudBlazor;
|
||||||
|
using Google.Protobuf.WellKnownTypes;
|
||||||
|
using Foursat.BackOffice.BFF.Inventory.Protos;
|
||||||
|
using BackOffice.Pages.Inventory.Components;
|
||||||
|
using BackOffice.Common.Utilities;
|
||||||
|
|
||||||
|
namespace BackOffice.Pages.Inventory;
|
||||||
|
|
||||||
|
public partial class InventoryMainPage
|
||||||
|
{
|
||||||
|
[Inject] public InventoryBFFContract.InventoryBFFContractClient InventoryContract { get; set; } = default!;
|
||||||
|
|
||||||
|
private MudDataGrid<InventoryItemDto>? _gridData;
|
||||||
|
private List<WarehouseDto> _warehouses = new();
|
||||||
|
|
||||||
|
// Filter fields
|
||||||
|
private long? _filterWarehouseId;
|
||||||
|
private ProductType _filterProductType = ProductType.Unspecified;
|
||||||
|
private string _searchTerm = string.Empty;
|
||||||
|
|
||||||
|
// Stats
|
||||||
|
private int _totalCount;
|
||||||
|
private int _lowStockCount;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
await LoadWarehouses();
|
||||||
|
await LoadLowStockCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadWarehouses()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await InventoryContract.GetAllWarehousesAsync(new GetAllWarehousesRequest
|
||||||
|
{
|
||||||
|
ActiveOnly = true
|
||||||
|
});
|
||||||
|
_warehouses = response.Warehouses.ToList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"خطا در بارگذاری انبارها: {ex.Message}", Severity.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadLowStockCount()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await InventoryContract.GetLowStockItemsAsync(new GetLowStockItemsRequest
|
||||||
|
{
|
||||||
|
Count = 100 // Get up to 100 low stock items to count
|
||||||
|
});
|
||||||
|
_lowStockCount = response.TotalCount;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_lowStockCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<GridData<InventoryItemDto>> ServerReload(GridState<InventoryItemDto> state)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new GetAllInventoryItemsRequest
|
||||||
|
{
|
||||||
|
PageIndex = state.Page + 1,
|
||||||
|
PageSize = state.PageSize,
|
||||||
|
ProductType = _filterProductType,
|
||||||
|
SearchTerm = _searchTerm ?? string.Empty
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_filterWarehouseId.HasValue)
|
||||||
|
{
|
||||||
|
request.WarehouseId = _filterWarehouseId.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await InventoryContract.GetAllInventoryItemsAsync(request);
|
||||||
|
|
||||||
|
if (result?.Items != null)
|
||||||
|
{
|
||||||
|
_totalCount = result.TotalCount;
|
||||||
|
return new GridData<InventoryItemDto>
|
||||||
|
{
|
||||||
|
Items = result.Items.ToList(),
|
||||||
|
TotalItems = result.TotalCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GridData<InventoryItemDto>();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"خطا در بارگذاری دادهها: {ex.Message}", Severity.Error);
|
||||||
|
return new GridData<InventoryItemDto>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetProductTypeLabel(ProductType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
ProductType.Regular => "محصول عادی",
|
||||||
|
ProductType.Discount => "محصول تخفیفی",
|
||||||
|
_ => "نامشخص"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ApplyFilter()
|
||||||
|
{
|
||||||
|
if (_gridData != null)
|
||||||
|
{
|
||||||
|
await _gridData.ReloadServerData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ClearFilter()
|
||||||
|
{
|
||||||
|
_filterWarehouseId = null;
|
||||||
|
_filterProductType = ProductType.Unspecified;
|
||||||
|
_searchTerm = string.Empty;
|
||||||
|
await ApplyFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenAddStockDialog()
|
||||||
|
{
|
||||||
|
var parameters = new DialogParameters
|
||||||
|
{
|
||||||
|
["Warehouses"] = _warehouses
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialog = await DialogService.ShowAsync<AddStockDialog>("ورود کالا به انبار", parameters, new DialogOptions
|
||||||
|
{
|
||||||
|
CloseButton = true,
|
||||||
|
MaxWidth = MaxWidth.Medium,
|
||||||
|
FullWidth = true
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result!.Canceled)
|
||||||
|
{
|
||||||
|
Snackbar.Add("موجودی با موفقیت اضافه شد", Severity.Success);
|
||||||
|
await ApplyFilter();
|
||||||
|
await LoadLowStockCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenAddStockDialogForItem(InventoryItemDto item)
|
||||||
|
{
|
||||||
|
var parameters = new DialogParameters
|
||||||
|
{
|
||||||
|
["Warehouses"] = _warehouses,
|
||||||
|
["ProductIdParam"] = item.ProductType == ProductType.Regular ? item.ProductId : item.DiscountProductId,
|
||||||
|
["ProductTypeParam"] = item.ProductType,
|
||||||
|
["ProductName"] = item.ProductName,
|
||||||
|
["WarehouseIdParam"] = item.WarehouseId
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialog = await DialogService.ShowAsync<AddStockDialog>($"ورود کالا - {item.ProductName}", parameters, new DialogOptions
|
||||||
|
{
|
||||||
|
CloseButton = true,
|
||||||
|
MaxWidth = MaxWidth.Medium,
|
||||||
|
FullWidth = true
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result!.Canceled)
|
||||||
|
{
|
||||||
|
Snackbar.Add("موجودی با موفقیت اضافه شد", Severity.Success);
|
||||||
|
await ApplyFilter();
|
||||||
|
await LoadLowStockCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenAdjustStockDialog(InventoryItemDto item)
|
||||||
|
{
|
||||||
|
var parameters = new DialogParameters
|
||||||
|
{
|
||||||
|
["Item"] = item
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialog = await DialogService.ShowAsync<AdjustStockDialog>($"تنظیم موجودی - {item.ProductName}", parameters, new DialogOptions
|
||||||
|
{
|
||||||
|
CloseButton = true,
|
||||||
|
MaxWidth = MaxWidth.Small,
|
||||||
|
FullWidth = true
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result!.Canceled)
|
||||||
|
{
|
||||||
|
Snackbar.Add("موجودی با موفقیت تنظیم شد", Severity.Success);
|
||||||
|
await ApplyFilter();
|
||||||
|
await LoadLowStockCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenRecordLossDialog(InventoryItemDto item)
|
||||||
|
{
|
||||||
|
var parameters = new DialogParameters
|
||||||
|
{
|
||||||
|
["Item"] = item
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialog = await DialogService.ShowAsync<RecordLossDialog>($"ثبت خسارت - {item.ProductName}", parameters, new DialogOptions
|
||||||
|
{
|
||||||
|
CloseButton = true,
|
||||||
|
MaxWidth = MaxWidth.Small,
|
||||||
|
FullWidth = true
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result!.Canceled)
|
||||||
|
{
|
||||||
|
Snackbar.Add("خسارت با موفقیت ثبت شد", Severity.Warning);
|
||||||
|
await ApplyFilter();
|
||||||
|
await LoadLowStockCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenSettingsDialog(InventoryItemDto item)
|
||||||
|
{
|
||||||
|
var parameters = new DialogParameters
|
||||||
|
{
|
||||||
|
["Item"] = item
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialog = await DialogService.ShowAsync<InventorySettingsDialog>($"تنظیمات - {item.ProductName}", parameters, new DialogOptions
|
||||||
|
{
|
||||||
|
CloseButton = true,
|
||||||
|
MaxWidth = MaxWidth.Small,
|
||||||
|
FullWidth = true
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result!.Canceled)
|
||||||
|
{
|
||||||
|
Snackbar.Add("تنظیمات با موفقیت ذخیره شد", Severity.Success);
|
||||||
|
await ApplyFilter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewMovements(InventoryItemDto item)
|
||||||
|
{
|
||||||
|
Navigation.NavigateTo($"{RouteConstance.InventoryMovements}?itemId={item.Id}&productName={item.ProductName}");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
@attribute [Route(RouteConstance.InventoryLowStock)]
|
||||||
|
@attribute [Authorize]
|
||||||
|
|
||||||
|
@using Foursat.BackOffice.BFF.Inventory.Protos
|
||||||
|
|
||||||
|
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="mt-4">
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center" Class="mb-4">
|
||||||
|
<MudStack Spacing="0">
|
||||||
|
<MudText Typo="Typo.h4">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Warning" Color="Color.Warning" Class="mr-2" />
|
||||||
|
محصولات کمموجود
|
||||||
|
</MudText>
|
||||||
|
<MudText Typo="Typo.subtitle1" Color="Color.Secondary">
|
||||||
|
لیست محصولاتی که موجودی آنها کمتر از حد هشدار است
|
||||||
|
</MudText>
|
||||||
|
</MudStack>
|
||||||
|
<MudButton Variant="Variant.Outlined"
|
||||||
|
Color="Color.Secondary"
|
||||||
|
StartIcon="@Icons.Material.Filled.ArrowBack"
|
||||||
|
OnClick="GoBack">
|
||||||
|
بازگشت به انبار
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
@* Filters Section *@
|
||||||
|
<MudPaper Class="pa-4 mb-4" Elevation="2">
|
||||||
|
<MudGrid>
|
||||||
|
<MudItem xs="12" sm="6" md="4">
|
||||||
|
<MudSelect T="long?" @bind-Value="_filterWarehouseId"
|
||||||
|
Label="انبار"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense"
|
||||||
|
Clearable="true">
|
||||||
|
@foreach (var wh in _warehouses)
|
||||||
|
{
|
||||||
|
<MudSelectItem Value="@((long?)wh.Id)">@wh.Name</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="12" sm="6" md="4">
|
||||||
|
<MudSelect T="ProductType" @bind-Value="_filterProductType"
|
||||||
|
Label="نوع محصول"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense">
|
||||||
|
<MudSelectItem Value="ProductType.Unspecified">همه</MudSelectItem>
|
||||||
|
<MudSelectItem Value="ProductType.Regular">محصول عادی</MudSelectItem>
|
||||||
|
<MudSelectItem Value="ProductType.Discount">محصول تخفیفی</MudSelectItem>
|
||||||
|
</MudSelect>
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="12" sm="6" md="4" Class="d-flex align-end">
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
OnClick="LoadData"
|
||||||
|
StartIcon="@Icons.Material.Filled.Refresh"
|
||||||
|
Class="mr-2">
|
||||||
|
بارگذاری
|
||||||
|
</MudButton>
|
||||||
|
</MudItem>
|
||||||
|
</MudGrid>
|
||||||
|
</MudPaper>
|
||||||
|
|
||||||
|
@* Stats *@
|
||||||
|
<MudAlert Severity="Severity.Warning" Class="mb-4">
|
||||||
|
<MudText>
|
||||||
|
<strong>@_items.Count</strong> محصول کمموجود شناسایی شده است.
|
||||||
|
لطفاً برای جلوگیری از کمبود موجودی، اقدام به سفارش مجدد کنید.
|
||||||
|
</MudText>
|
||||||
|
</MudAlert>
|
||||||
|
|
||||||
|
@if (_isLoading)
|
||||||
|
{
|
||||||
|
<MudProgressLinear Indeterminate="true" Color="Color.Primary" />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudDataGrid T="LowStockItemDto" Items="_items" Hover="true" Height="60vh">
|
||||||
|
<Columns>
|
||||||
|
<PropertyColumn Property="x => x.Id" Title="شناسه" />
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.ProductName" Title="نام محصول">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudStack Spacing="0">
|
||||||
|
<MudText Typo="Typo.body2"><strong>@context.Item.ProductName</strong></MudText>
|
||||||
|
<MudText Typo="Typo.caption" Color="Color.Secondary">
|
||||||
|
@GetProductTypeLabel(context.Item.ProductType)
|
||||||
|
</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.WarehouseName" Title="انبار">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudChip T="string" Color="Color.Info" Size="Size.Small">
|
||||||
|
@context.Item.WarehouseName
|
||||||
|
</MudChip>
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.Quantity" Title="موجودی فعلی">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudText Color="Color.Error"><strong>@context.Item.Quantity</strong></MudText>
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.AvailableQuantity" Title="قابل فروش">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudText Color="Color.Warning">@context.Item.AvailableQuantity</MudText>
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.LowStockThreshold" Title="حد هشدار" />
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.ReorderPoint" Title="نقطه سفارش" />
|
||||||
|
|
||||||
|
<TemplateColumn Title="کمبود">
|
||||||
|
<CellTemplate>
|
||||||
|
@{
|
||||||
|
var shortage = context.Item.LowStockThreshold - context.Item.Quantity;
|
||||||
|
}
|
||||||
|
<MudChip T="string" Color="Color.Error" Size="Size.Small">
|
||||||
|
@shortage عدد
|
||||||
|
</MudChip>
|
||||||
|
</CellTemplate>
|
||||||
|
</TemplateColumn>
|
||||||
|
|
||||||
|
<TemplateColumn Title="عملیات" Sortable="false">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Success"
|
||||||
|
Size="Size.Small"
|
||||||
|
StartIcon="@Icons.Material.Filled.Add"
|
||||||
|
OnClick="@(() => OpenAddStockDialog(context.Item))">
|
||||||
|
ورود کالا
|
||||||
|
</MudButton>
|
||||||
|
</CellTemplate>
|
||||||
|
</TemplateColumn>
|
||||||
|
</Columns>
|
||||||
|
<PagerContent>
|
||||||
|
<MudDataGridPager T="LowStockItemDto"
|
||||||
|
PageSizeOptions="@(new int[] { 20, 50, 100 })"
|
||||||
|
InfoFormat="سطر {first_item} تا {last_item} از {all_items}"
|
||||||
|
RowsPerPageString="تعداد سطرهای صفحه" />
|
||||||
|
</PagerContent>
|
||||||
|
</MudDataGrid>
|
||||||
|
}
|
||||||
|
</MudContainer>
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using MudBlazor;
|
||||||
|
using Foursat.BackOffice.BFF.Inventory.Protos;
|
||||||
|
using BackOffice.Pages.Inventory.Components;
|
||||||
|
using BackOffice.Common.Utilities;
|
||||||
|
|
||||||
|
namespace BackOffice.Pages.Inventory;
|
||||||
|
|
||||||
|
public partial class LowStockPage
|
||||||
|
{
|
||||||
|
[Inject] public InventoryBFFContract.InventoryBFFContractClient InventoryContract { get; set; } = default!;
|
||||||
|
|
||||||
|
private List<LowStockItemDto> _items = new();
|
||||||
|
private List<WarehouseDto> _warehouses = new();
|
||||||
|
private bool _isLoading = true;
|
||||||
|
|
||||||
|
// Filter fields
|
||||||
|
private long? _filterWarehouseId;
|
||||||
|
private ProductType _filterProductType = ProductType.Unspecified;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
await LoadWarehouses();
|
||||||
|
await LoadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadWarehouses()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await InventoryContract.GetAllWarehousesAsync(new GetAllWarehousesRequest
|
||||||
|
{
|
||||||
|
ActiveOnly = true
|
||||||
|
});
|
||||||
|
_warehouses = response.Warehouses.ToList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"خطا در بارگذاری انبارها: {ex.Message}", Severity.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadData()
|
||||||
|
{
|
||||||
|
_isLoading = true;
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new GetLowStockItemsRequest
|
||||||
|
{
|
||||||
|
Count = 200, // Get up to 200 low stock items
|
||||||
|
ProductType = _filterProductType
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_filterWarehouseId.HasValue)
|
||||||
|
{
|
||||||
|
request.WarehouseId = _filterWarehouseId.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = await InventoryContract.GetLowStockItemsAsync(request);
|
||||||
|
_items = response.Items.ToList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"خطا در بارگذاری دادهها: {ex.Message}", Severity.Error);
|
||||||
|
_items = new();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isLoading = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetProductTypeLabel(ProductType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
ProductType.Regular => "محصول عادی",
|
||||||
|
ProductType.Discount => "محصول تخفیفی",
|
||||||
|
_ => "نامشخص"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenAddStockDialog(LowStockItemDto item)
|
||||||
|
{
|
||||||
|
var productId = item.ProductType == ProductType.Regular
|
||||||
|
? (item.ProductId ?? 0)
|
||||||
|
: (item.DiscountProductId ?? 0);
|
||||||
|
|
||||||
|
var parameters = new DialogParameters
|
||||||
|
{
|
||||||
|
["Warehouses"] = _warehouses,
|
||||||
|
["ProductIdParam"] = productId,
|
||||||
|
["ProductTypeParam"] = item.ProductType,
|
||||||
|
["ProductName"] = item.ProductName,
|
||||||
|
["WarehouseIdParam"] = item.WarehouseId
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialog = await DialogService.ShowAsync<AddStockDialog>($"ورود کالا - {item.ProductName}", parameters, new DialogOptions
|
||||||
|
{
|
||||||
|
CloseButton = true,
|
||||||
|
MaxWidth = MaxWidth.Medium,
|
||||||
|
FullWidth = true
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result!.Canceled)
|
||||||
|
{
|
||||||
|
Snackbar.Add("موجودی با موفقیت اضافه شد", Severity.Success);
|
||||||
|
await LoadData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GoBack()
|
||||||
|
{
|
||||||
|
Navigation.NavigateTo(RouteConstance.Inventory);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
@attribute [Route(RouteConstance.InventoryMovements)]
|
||||||
|
@attribute [Authorize]
|
||||||
|
|
||||||
|
@using Foursat.BackOffice.BFF.Inventory.Protos
|
||||||
|
@using Google.Protobuf.WellKnownTypes
|
||||||
|
|
||||||
|
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="mt-4">
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center" Class="mb-4">
|
||||||
|
<MudStack Spacing="0">
|
||||||
|
<MudText Typo="Typo.h4">تاریخچه تغییرات موجودی</MudText>
|
||||||
|
@if (!string.IsNullOrEmpty(_productName))
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.subtitle1" Color="Color.Secondary">محصول: @_productName</MudText>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
<MudButton Variant="Variant.Outlined"
|
||||||
|
Color="Color.Secondary"
|
||||||
|
StartIcon="@Icons.Material.Filled.ArrowBack"
|
||||||
|
OnClick="GoBack">
|
||||||
|
بازگشت به انبار
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
@* Filters Section *@
|
||||||
|
<MudPaper Class="pa-4 mb-4" Elevation="2">
|
||||||
|
<MudGrid>
|
||||||
|
<MudItem xs="12" sm="6" md="3">
|
||||||
|
<MudSelect T="StockMovementType" @bind-Value="_filterMovementType"
|
||||||
|
Label="نوع تغییر"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense">
|
||||||
|
<MudSelectItem Value="StockMovementType.Unspecified">همه</MudSelectItem>
|
||||||
|
<MudSelectItem Value="StockMovementType.InitialStock">موجودی اولیه</MudSelectItem>
|
||||||
|
<MudSelectItem Value="StockMovementType.Restock">ورود کالا</MudSelectItem>
|
||||||
|
<MudSelectItem Value="StockMovementType.Return">برگشت از مشتری</MudSelectItem>
|
||||||
|
<MudSelectItem Value="StockMovementType.Sale">فروش</MudSelectItem>
|
||||||
|
<MudSelectItem Value="StockMovementType.AdjustmentIncrease">افزایش موجودی</MudSelectItem>
|
||||||
|
<MudSelectItem Value="StockMovementType.AdjustmentDecrease">کاهش موجودی</MudSelectItem>
|
||||||
|
<MudSelectItem Value="StockMovementType.Reserved">رزرو</MudSelectItem>
|
||||||
|
<MudSelectItem Value="StockMovementType.Released">آزادسازی رزرو</MudSelectItem>
|
||||||
|
<MudSelectItem Value="StockMovementType.Loss">خسارت</MudSelectItem>
|
||||||
|
<MudSelectItem Value="StockMovementType.Damaged">آسیبدیده</MudSelectItem>
|
||||||
|
<MudSelectItem Value="StockMovementType.Expired">منقضی</MudSelectItem>
|
||||||
|
<MudSelectItem Value="StockMovementType.TransferOut">انتقال به بیرون</MudSelectItem>
|
||||||
|
<MudSelectItem Value="StockMovementType.TransferIn">انتقال به داخل</MudSelectItem>
|
||||||
|
</MudSelect>
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="12" sm="6" md="3">
|
||||||
|
<MudSelect T="ProductType" @bind-Value="_filterProductType"
|
||||||
|
Label="نوع محصول"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense">
|
||||||
|
<MudSelectItem Value="ProductType.Unspecified">همه</MudSelectItem>
|
||||||
|
<MudSelectItem Value="ProductType.Regular">محصول عادی</MudSelectItem>
|
||||||
|
<MudSelectItem Value="ProductType.Discount">محصول تخفیفی</MudSelectItem>
|
||||||
|
</MudSelect>
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="12" sm="6" md="3">
|
||||||
|
<MudDatePicker @bind-Date="_filterFromDate"
|
||||||
|
Label="از تاریخ"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense"
|
||||||
|
Clearable="true" />
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="12" sm="6" md="3">
|
||||||
|
<MudDatePicker @bind-Date="_filterToDate"
|
||||||
|
Label="تا تاریخ"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense"
|
||||||
|
Clearable="true" />
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="12" Class="d-flex justify-end">
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
OnClick="ApplyFilter"
|
||||||
|
StartIcon="@Icons.Material.Filled.FilterAlt"
|
||||||
|
Class="mr-2">
|
||||||
|
اعمال فیلتر
|
||||||
|
</MudButton>
|
||||||
|
<MudButton Variant="Variant.Outlined"
|
||||||
|
Color="Color.Secondary"
|
||||||
|
OnClick="ClearFilter"
|
||||||
|
StartIcon="@Icons.Material.Filled.Clear">
|
||||||
|
پاک کردن
|
||||||
|
</MudButton>
|
||||||
|
</MudItem>
|
||||||
|
</MudGrid>
|
||||||
|
</MudPaper>
|
||||||
|
|
||||||
|
@* Data Grid *@
|
||||||
|
<MudDataGrid T="StockMovementDto"
|
||||||
|
ServerData="@(new Func<GridState<StockMovementDto>, Task<GridData<StockMovementDto>>>(ServerReload))"
|
||||||
|
Hover="true"
|
||||||
|
@ref="_gridData"
|
||||||
|
Height="65vh">
|
||||||
|
<ToolBarContent>
|
||||||
|
<MudText Typo="Typo.h6">تغییرات موجودی</MudText>
|
||||||
|
<MudSpacer />
|
||||||
|
<MudText Typo="Typo.body2" Color="Color.Secondary">
|
||||||
|
کل: @_totalCount ردیف
|
||||||
|
</MudText>
|
||||||
|
</ToolBarContent>
|
||||||
|
<Columns>
|
||||||
|
<PropertyColumn Property="x => x.Id" Title="شناسه" />
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.ProductName" Title="محصول" />
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.MovementTypeName" Title="نوع تغییر">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudChip T="string"
|
||||||
|
Color="@GetMovementColor(context.Item.MovementType)"
|
||||||
|
Size="Size.Small">
|
||||||
|
@context.Item.MovementTypeName
|
||||||
|
</MudChip>
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.Quantity" Title="تعداد">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudText Color="@(IsIncreaseMovement(context.Item.MovementType) ? Color.Success : Color.Error)">
|
||||||
|
@(IsIncreaseMovement(context.Item.MovementType) ? "+" : "-")@Math.Abs(context.Item.Quantity)
|
||||||
|
</MudText>
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.QuantityBefore" Title="قبل" />
|
||||||
|
<PropertyColumn Property="x => x.QuantityAfter" Title="بعد" />
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.ReferenceNumber" Title="شماره مرجع">
|
||||||
|
<CellTemplate>
|
||||||
|
@if (!string.IsNullOrEmpty(context.Item.ReferenceNumber))
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.caption">@context.Item.ReferenceNumber</MudText>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.caption" Color="Color.Secondary">-</MudText>
|
||||||
|
}
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.PerformedByUserName" Title="کاربر">
|
||||||
|
<CellTemplate>
|
||||||
|
@if (!string.IsNullOrEmpty(context.Item.PerformedByUserName))
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.caption">@context.Item.PerformedByUserName</MudText>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.caption" Color="Color.Secondary">سیستم</MudText>
|
||||||
|
}
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.CreatedAt" Title="تاریخ">
|
||||||
|
<CellTemplate>
|
||||||
|
@if (context.Item.CreatedAt != null)
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.caption">
|
||||||
|
@context.Item.CreatedAt.ToDateTime().ToLocalTime().ToString("yyyy/MM/dd HH:mm")
|
||||||
|
</MudText>
|
||||||
|
}
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.Note" Title="توضیحات">
|
||||||
|
<CellTemplate>
|
||||||
|
@if (!string.IsNullOrEmpty(context.Item.Note))
|
||||||
|
{
|
||||||
|
<MudTooltip Text="@context.Item.Note">
|
||||||
|
<MudText Typo="Typo.caption" Style="max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
|
||||||
|
@context.Item.Note
|
||||||
|
</MudText>
|
||||||
|
</MudTooltip>
|
||||||
|
}
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
</Columns>
|
||||||
|
<PagerContent>
|
||||||
|
<MudDataGridPager T="StockMovementDto"
|
||||||
|
PageSizeOptions="@(new int[] { 20, 50, 100 })"
|
||||||
|
InfoFormat="سطر {first_item} تا {last_item} از {all_items}"
|
||||||
|
RowsPerPageString="تعداد سطرهای صفحه" />
|
||||||
|
</PagerContent>
|
||||||
|
</MudDataGrid>
|
||||||
|
</MudContainer>
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using MudBlazor;
|
||||||
|
using Google.Protobuf.WellKnownTypes;
|
||||||
|
using Foursat.BackOffice.BFF.Inventory.Protos;
|
||||||
|
using BackOffice.Common.Utilities;
|
||||||
|
|
||||||
|
namespace BackOffice.Pages.Inventory;
|
||||||
|
|
||||||
|
public partial class MovementsPage
|
||||||
|
{
|
||||||
|
[Inject] public InventoryBFFContract.InventoryBFFContractClient InventoryContract { get; set; } = default!;
|
||||||
|
|
||||||
|
[SupplyParameterFromQuery(Name = "itemId")]
|
||||||
|
public long? ItemId { get; set; }
|
||||||
|
|
||||||
|
[SupplyParameterFromQuery(Name = "productName")]
|
||||||
|
public string? _productName { get; set; }
|
||||||
|
|
||||||
|
private MudDataGrid<StockMovementDto>? _gridData;
|
||||||
|
private int _totalCount;
|
||||||
|
|
||||||
|
// Filter fields
|
||||||
|
private StockMovementType _filterMovementType = StockMovementType.Unspecified;
|
||||||
|
private ProductType _filterProductType = ProductType.Unspecified;
|
||||||
|
private DateTime? _filterFromDate;
|
||||||
|
private DateTime? _filterToDate;
|
||||||
|
|
||||||
|
private async Task<GridData<StockMovementDto>> ServerReload(GridState<StockMovementDto> state)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new GetStockMovementsRequest
|
||||||
|
{
|
||||||
|
PageIndex = state.Page + 1,
|
||||||
|
PageSize = state.PageSize,
|
||||||
|
MovementType = _filterMovementType,
|
||||||
|
ProductType = _filterProductType
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ItemId.HasValue)
|
||||||
|
{
|
||||||
|
request.InventoryItemId = ItemId.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_filterFromDate.HasValue)
|
||||||
|
{
|
||||||
|
request.FromDate = Timestamp.FromDateTime(_filterFromDate.Value.ToUniversalTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_filterToDate.HasValue)
|
||||||
|
{
|
||||||
|
request.ToDate = Timestamp.FromDateTime(_filterToDate.Value.ToUniversalTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await InventoryContract.GetStockMovementsAsync(request);
|
||||||
|
|
||||||
|
if (result?.Movements != null)
|
||||||
|
{
|
||||||
|
_totalCount = result.TotalCount;
|
||||||
|
return new GridData<StockMovementDto>
|
||||||
|
{
|
||||||
|
Items = result.Movements.ToList(),
|
||||||
|
TotalItems = result.TotalCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GridData<StockMovementDto>();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"خطا در بارگذاری دادهها: {ex.Message}", Severity.Error);
|
||||||
|
return new GridData<StockMovementDto>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color GetMovementColor(StockMovementType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
StockMovementType.InitialStock => Color.Info,
|
||||||
|
StockMovementType.Restock => Color.Success,
|
||||||
|
StockMovementType.Return => Color.Success,
|
||||||
|
StockMovementType.Sale => Color.Primary,
|
||||||
|
StockMovementType.AdjustmentIncrease => Color.Success,
|
||||||
|
StockMovementType.AdjustmentDecrease => Color.Warning,
|
||||||
|
StockMovementType.Reserved => Color.Secondary,
|
||||||
|
StockMovementType.Released => Color.Tertiary,
|
||||||
|
StockMovementType.Loss => Color.Error,
|
||||||
|
StockMovementType.Damaged => Color.Error,
|
||||||
|
StockMovementType.Expired => Color.Error,
|
||||||
|
StockMovementType.TransferOut => Color.Warning,
|
||||||
|
StockMovementType.TransferIn => Color.Info,
|
||||||
|
_ => Color.Default
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsIncreaseMovement(StockMovementType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
StockMovementType.InitialStock => true,
|
||||||
|
StockMovementType.Restock => true,
|
||||||
|
StockMovementType.Return => true,
|
||||||
|
StockMovementType.AdjustmentIncrease => true,
|
||||||
|
StockMovementType.Released => true,
|
||||||
|
StockMovementType.TransferIn => true,
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ApplyFilter()
|
||||||
|
{
|
||||||
|
if (_gridData != null)
|
||||||
|
{
|
||||||
|
await _gridData.ReloadServerData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ClearFilter()
|
||||||
|
{
|
||||||
|
_filterMovementType = StockMovementType.Unspecified;
|
||||||
|
_filterProductType = ProductType.Unspecified;
|
||||||
|
_filterFromDate = null;
|
||||||
|
_filterToDate = null;
|
||||||
|
await ApplyFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GoBack()
|
||||||
|
{
|
||||||
|
Navigation.NavigateTo(RouteConstance.Inventory);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
@attribute [Route(RouteConstance.InventoryWarehouses)]
|
||||||
|
@attribute [Authorize]
|
||||||
|
|
||||||
|
@using Foursat.BackOffice.BFF.Inventory.Protos
|
||||||
|
@using BackOffice.Pages.Inventory.Components
|
||||||
|
|
||||||
|
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="mt-4">
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center" Class="mb-4">
|
||||||
|
<MudStack Spacing="0">
|
||||||
|
<MudText Typo="Typo.h4">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Warehouse" Color="Color.Primary" Class="mr-2" />
|
||||||
|
مدیریت انبارها
|
||||||
|
</MudText>
|
||||||
|
<MudText Typo="Typo.subtitle1" Color="Color.Secondary">
|
||||||
|
تعریف و مدیریت انبارهای سیستم
|
||||||
|
</MudText>
|
||||||
|
</MudStack>
|
||||||
|
<MudStack Row="true" Spacing="2">
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
StartIcon="@Icons.Material.Filled.Add"
|
||||||
|
OnClick="OpenCreateWarehouseDialog">
|
||||||
|
انبار جدید
|
||||||
|
</MudButton>
|
||||||
|
<MudButton Variant="Variant.Outlined"
|
||||||
|
Color="Color.Secondary"
|
||||||
|
StartIcon="@Icons.Material.Filled.ArrowBack"
|
||||||
|
OnClick="GoBack">
|
||||||
|
بازگشت به انبار
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
@* Filter Toggle *@
|
||||||
|
<MudPaper Class="pa-4 mb-4" Elevation="2">
|
||||||
|
<MudSwitch T="bool" @bind-Value="_showActiveOnly"
|
||||||
|
Color="Color.Primary"
|
||||||
|
Label="فقط انبارهای فعال"
|
||||||
|
LabelPosition="LabelPosition.Start" />
|
||||||
|
<MudButton Variant="Variant.Text"
|
||||||
|
Color="Color.Primary"
|
||||||
|
OnClick="LoadWarehouses"
|
||||||
|
StartIcon="@Icons.Material.Filled.Refresh"
|
||||||
|
Class="ml-4">
|
||||||
|
بارگذاری مجدد
|
||||||
|
</MudButton>
|
||||||
|
</MudPaper>
|
||||||
|
|
||||||
|
@if (_isLoading)
|
||||||
|
{
|
||||||
|
<MudProgressLinear Indeterminate="true" Color="Color.Primary" />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudGrid>
|
||||||
|
@foreach (var warehouse in _warehouses)
|
||||||
|
{
|
||||||
|
<MudItem xs="12" sm="6" md="4">
|
||||||
|
<MudCard Elevation="3" Class="@(warehouse.IsDefault ? "border-2 border-solid mud-border-primary" : "")">
|
||||||
|
<MudCardHeader>
|
||||||
|
<CardHeaderAvatar>
|
||||||
|
<MudAvatar Color="@(warehouse.IsActive ? Color.Success : Color.Default)" Size="Size.Large">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Warehouse" />
|
||||||
|
</MudAvatar>
|
||||||
|
</CardHeaderAvatar>
|
||||||
|
<CardHeaderContent>
|
||||||
|
<MudText Typo="Typo.h6">
|
||||||
|
@warehouse.Name
|
||||||
|
@if (warehouse.IsDefault)
|
||||||
|
{
|
||||||
|
<MudChip T="string" Color="Color.Primary" Size="Size.Small" Class="ml-2">پیشفرض</MudChip>
|
||||||
|
}
|
||||||
|
</MudText>
|
||||||
|
<MudText Typo="Typo.caption" Color="Color.Secondary">
|
||||||
|
کد: @warehouse.Code
|
||||||
|
</MudText>
|
||||||
|
</CardHeaderContent>
|
||||||
|
<CardHeaderActions>
|
||||||
|
<MudChip T="string" Color="@(warehouse.IsActive ? Color.Success : Color.Error)" Size="Size.Small">
|
||||||
|
@(warehouse.IsActive ? "فعال" : "غیرفعال")
|
||||||
|
</MudChip>
|
||||||
|
</CardHeaderActions>
|
||||||
|
</MudCardHeader>
|
||||||
|
<MudCardContent>
|
||||||
|
<MudStack Spacing="2">
|
||||||
|
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.LocationOn" Size="Size.Small" Color="Color.Secondary" />
|
||||||
|
<MudText Typo="Typo.body2">
|
||||||
|
@(string.IsNullOrEmpty(warehouse.Address) ? "آدرس ثبت نشده" : warehouse.Address)
|
||||||
|
</MudText>
|
||||||
|
</MudStack>
|
||||||
|
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.CalendarToday" Size="Size.Small" Color="Color.Secondary" />
|
||||||
|
<MudText Typo="Typo.caption" Color="Color.Secondary">
|
||||||
|
ایجاد: @(warehouse.Created?.ToDateTime().ToLocalTime().ToString("yyyy/MM/dd") ?? "-")
|
||||||
|
</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudStack>
|
||||||
|
</MudCardContent>
|
||||||
|
<MudCardActions>
|
||||||
|
<MudButton Variant="Variant.Text"
|
||||||
|
Color="Color.Primary"
|
||||||
|
Size="Size.Small"
|
||||||
|
StartIcon="@Icons.Material.Filled.Visibility"
|
||||||
|
OnClick="@(() => ViewWarehouseInventory(warehouse))">
|
||||||
|
مشاهده موجودی
|
||||||
|
</MudButton>
|
||||||
|
</MudCardActions>
|
||||||
|
</MudCard>
|
||||||
|
</MudItem>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (_warehouses.Count == 0)
|
||||||
|
{
|
||||||
|
<MudItem xs="12">
|
||||||
|
<MudAlert Severity="Severity.Info">
|
||||||
|
هیچ انباری یافت نشد. برای شروع، یک انبار جدید ایجاد کنید.
|
||||||
|
</MudAlert>
|
||||||
|
</MudItem>
|
||||||
|
}
|
||||||
|
</MudGrid>
|
||||||
|
}
|
||||||
|
</MudContainer>
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using MudBlazor;
|
||||||
|
using Foursat.BackOffice.BFF.Inventory.Protos;
|
||||||
|
using BackOffice.Pages.Inventory.Components;
|
||||||
|
using BackOffice.Common.Utilities;
|
||||||
|
|
||||||
|
namespace BackOffice.Pages.Inventory;
|
||||||
|
|
||||||
|
public partial class WarehousesPage
|
||||||
|
{
|
||||||
|
[Inject] public InventoryBFFContract.InventoryBFFContractClient InventoryContract { get; set; } = default!;
|
||||||
|
|
||||||
|
private List<WarehouseDto> _warehouses = new();
|
||||||
|
private bool _isLoading = true;
|
||||||
|
private bool _showActiveOnly = true;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
await LoadWarehouses();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadWarehouses()
|
||||||
|
{
|
||||||
|
_isLoading = true;
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await InventoryContract.GetAllWarehousesAsync(new GetAllWarehousesRequest
|
||||||
|
{
|
||||||
|
ActiveOnly = _showActiveOnly
|
||||||
|
});
|
||||||
|
_warehouses = response.Warehouses.ToList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"خطا در بارگذاری انبارها: {ex.Message}", Severity.Error);
|
||||||
|
_warehouses = new();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isLoading = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenCreateWarehouseDialog()
|
||||||
|
{
|
||||||
|
var dialog = await DialogService.ShowAsync<CreateWarehouseDialog>("ایجاد انبار جدید", new DialogOptions
|
||||||
|
{
|
||||||
|
CloseButton = true,
|
||||||
|
MaxWidth = MaxWidth.Small,
|
||||||
|
FullWidth = true
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result!.Canceled)
|
||||||
|
{
|
||||||
|
Snackbar.Add("انبار با موفقیت ایجاد شد", Severity.Success);
|
||||||
|
await LoadWarehouses();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewWarehouseInventory(WarehouseDto warehouse)
|
||||||
|
{
|
||||||
|
Navigation.NavigateTo($"{RouteConstance.Inventory}?warehouseId={warehouse.Id}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GoBack()
|
||||||
|
{
|
||||||
|
Navigation.NavigateTo(RouteConstance.Inventory);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -366,7 +366,7 @@
|
|||||||
Color="Color.Info"
|
Color="Color.Info"
|
||||||
StartIcon="@Icons.Material.Filled.Money"
|
StartIcon="@Icons.Material.Filled.Money"
|
||||||
OnClick="@(() => NavigationManager.NavigateTo($"/commission/payouts?userId={UserId}"))">
|
OnClick="@(() => NavigationManager.NavigateTo($"/commission/payouts?userId={UserId}"))">
|
||||||
Payout های کاربر
|
پرداختهای کاربر
|
||||||
</MudButton>
|
</MudButton>
|
||||||
<MudButton Variant="Variant.Outlined"
|
<MudButton Variant="Variant.Outlined"
|
||||||
Color="Color.Secondary"
|
Color="Color.Secondary"
|
||||||
|
|||||||
@@ -1,46 +1,86 @@
|
|||||||
@using BackOffice.BFF.ManualPayment.Protobuf
|
@using BackOffice.BFF.ManualPayment.Protobuf
|
||||||
@using BackOffice.Pages.Payment.Components
|
@using BackOffice.Pages.Payment.Components
|
||||||
|
@using BackOffice.Pages.AutoComplete
|
||||||
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
|
|
||||||
<MudDialog>
|
<MudDialog>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
@if (Mode == ManualPaymentDialogMode.Create)
|
@if (Mode == ManualPaymentDialogMode.Create)
|
||||||
{
|
{
|
||||||
<MudStack Spacing="2">
|
<MudStack Spacing="2">
|
||||||
<MudText Typo="Typo.h6">ثبت پرداخت دستی جدید</MudText>
|
<MudText Typo="Typo.h6">ثبت فعالسازی دستی جدید</MudText>
|
||||||
|
|
||||||
<MudNumericField T="long"
|
<UserAutoComplete Label="انتخاب کاربر"
|
||||||
Label="شناسه کاربر"
|
@bind-SelectedUserId="_selectedUserId" />
|
||||||
Variant="Variant.Outlined"
|
|
||||||
@bind-Value="_createModel.UserId" />
|
|
||||||
|
|
||||||
<MudNumericField T="long"
|
<MudNumericField T="long"
|
||||||
Label="مبلغ"
|
Label="مبلغ"
|
||||||
Variant="Variant.Outlined"
|
Variant="Variant.Outlined"
|
||||||
Adornment="Adornment.End"
|
Adornment="Adornment.End"
|
||||||
AdornmentText="ریال"
|
AdornmentText="تومان"
|
||||||
|
Disabled="true"
|
||||||
@bind-Value="_createModel.Amount" />
|
@bind-Value="_createModel.Amount" />
|
||||||
|
|
||||||
<MudTextField T="string"
|
<MudSelect T="int"
|
||||||
Label="نوع / توضیح نوع"
|
Label="نوع پرداخت"
|
||||||
Variant="Variant.Outlined"
|
Variant="Variant.Outlined"
|
||||||
@bind-Value="_createModel.TypeDisplay" />
|
@bind-Value="_selectedType">
|
||||||
|
<MudSelectItem Value="1">واریز نقدی</MudSelectItem>
|
||||||
|
<MudSelectItem Value="2">شارژ کیف پول تخفیف</MudSelectItem>
|
||||||
|
<MudSelectItem Value="3">شارژ کیف پول شبکه</MudSelectItem>
|
||||||
|
<MudSelectItem Value="4">تسویه حساب</MudSelectItem>
|
||||||
|
<MudSelectItem Value="5">اصلاح خطا</MudSelectItem>
|
||||||
|
<MudSelectItem Value="6">بازپرداخت</MudSelectItem>
|
||||||
|
<MudSelectItem Value="99">سایر</MudSelectItem>
|
||||||
|
</MudSelect>
|
||||||
|
|
||||||
<MudTextField T="string"
|
<MudTextField T="string"
|
||||||
Label="توضیحات"
|
Label="توضیحات"
|
||||||
Variant="Variant.Outlined"
|
Variant="Variant.Outlined"
|
||||||
Lines="3"
|
Lines="3"
|
||||||
@bind-Value="_createModel.Description" />
|
Required="true"
|
||||||
|
RequiredError="توضیحات الزامی است"
|
||||||
|
@bind-Value="_description" />
|
||||||
|
|
||||||
<MudTextField T="string"
|
<MudTextField T="string"
|
||||||
Label="شماره مرجع (اختیاری)"
|
Label="شماره مرجع (اختیاری)"
|
||||||
Variant="Variant.Outlined"
|
Variant="Variant.Outlined"
|
||||||
@bind-Value="_createModel.ReferenceNumber" />
|
@bind-Value="_createModel.ReferenceNumber" />
|
||||||
|
|
||||||
|
<MudText Typo="Typo.subtitle2">تصویر فیش پرداخت (اختیاری)</MudText>
|
||||||
|
<MudFileUpload T="IBrowserFile"
|
||||||
|
FilesChanged="OnImageFileSelected"
|
||||||
|
Accept=".jpg,.jpeg,.png,.gif,.webp"
|
||||||
|
MaximumFileCount="1">
|
||||||
|
<ActivatorContent>
|
||||||
|
<MudButton Variant="Variant.Outlined"
|
||||||
|
Color="Color.Primary"
|
||||||
|
StartIcon="@Icons.Material.Filled.CloudUpload">
|
||||||
|
انتخاب تصویر
|
||||||
|
</MudButton>
|
||||||
|
</ActivatorContent>
|
||||||
|
</MudFileUpload>
|
||||||
|
|
||||||
|
@if (!string.IsNullOrWhiteSpace(_imagePreview))
|
||||||
|
{
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
||||||
|
<MudImage Src="@_imagePreview"
|
||||||
|
Alt="پیشنمایش فیش"
|
||||||
|
Width="150"
|
||||||
|
Height="150"
|
||||||
|
ObjectFit="ObjectFit.Cover"
|
||||||
|
Class="rounded" />
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Delete"
|
||||||
|
Color="Color.Error"
|
||||||
|
OnClick="RemoveImage" />
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
</MudStack>
|
</MudStack>
|
||||||
}
|
}
|
||||||
else if (Model is not null)
|
else if (Model is not null)
|
||||||
{
|
{
|
||||||
<MudStack Spacing="2">
|
<MudStack Spacing="2">
|
||||||
<MudText Typo="Typo.h6">جزئیات پرداخت دستی</MudText>
|
<MudText Typo="Typo.h6">جزئیات فعالسازی دستی</MudText>
|
||||||
<MudText Typo="Typo.body2">شناسه: @Model.Id</MudText>
|
<MudText Typo="Typo.body2">شناسه: @Model.Id</MudText>
|
||||||
<MudText Typo="Typo.body2">کاربر: @Model.UserFullName (@Model.UserId)</MudText>
|
<MudText Typo="Typo.body2">کاربر: @Model.UserFullName (@Model.UserId)</MudText>
|
||||||
<MudText Typo="Typo.body2">مبلغ: @Model.Amount.ToString("N0") ریال</MudText>
|
<MudText Typo="Typo.body2">مبلغ: @Model.Amount.ToString("N0") ریال</MudText>
|
||||||
@@ -52,6 +92,16 @@
|
|||||||
<MudText Typo="Typo.body2">شماره مرجع: @Model.ReferenceNumber</MudText>
|
<MudText Typo="Typo.body2">شماره مرجع: @Model.ReferenceNumber</MudText>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if (!string.IsNullOrWhiteSpace(Model.ImagePath))
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.body2">تصویر فیش:</MudText>
|
||||||
|
<MudImage Src="@Model.ImagePath"
|
||||||
|
Alt="تصویر فیش"
|
||||||
|
Width="200"
|
||||||
|
ObjectFit="ObjectFit.Contain"
|
||||||
|
Class="rounded" />
|
||||||
|
}
|
||||||
|
|
||||||
<MudText Typo="Typo.body2">توضیحات: @Model.Description</MudText>
|
<MudText Typo="Typo.body2">توضیحات: @Model.Description</MudText>
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(Model.RejectionReason))
|
@if (!string.IsNullOrWhiteSpace(Model.RejectionReason))
|
||||||
@@ -79,7 +129,11 @@
|
|||||||
|
|
||||||
@if (Mode == ManualPaymentDialogMode.Create)
|
@if (Mode == ManualPaymentDialogMode.Create)
|
||||||
{
|
{
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="CreateAsync">
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="CreateAsync" Disabled="@_isSubmitting">
|
||||||
|
@if (_isSubmitting)
|
||||||
|
{
|
||||||
|
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="me-2" />
|
||||||
|
}
|
||||||
ثبت
|
ثبت
|
||||||
</MudButton>
|
</MudButton>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using BackOffice.BFF.ManualPayment.Protobuf;
|
using BackOffice.BFF.ManualPayment.Protobuf;
|
||||||
|
using Google.Protobuf;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
using MudBlazor;
|
using MudBlazor;
|
||||||
|
|
||||||
namespace BackOffice.Pages.Payment.Components;
|
namespace BackOffice.Pages.Payment.Components;
|
||||||
@@ -27,19 +29,81 @@ public partial class ManualPaymentDialog
|
|||||||
[Parameter] public ManualPaymentDialogMode Mode { get; set; }
|
[Parameter] public ManualPaymentDialogMode Mode { get; set; }
|
||||||
[Parameter] public ManualPaymentModel? Model { get; set; }
|
[Parameter] public ManualPaymentModel? Model { get; set; }
|
||||||
|
|
||||||
private ManualPaymentModel _createModel = new();
|
private ManualPaymentModel _createModel = new(){
|
||||||
|
Amount = 56000000,
|
||||||
|
};
|
||||||
private string? _adminNote;
|
private string? _adminNote;
|
||||||
|
|
||||||
|
// User selection
|
||||||
|
private long? _selectedUserId;
|
||||||
|
|
||||||
|
// Type and Description
|
||||||
|
private int _selectedType = 1; // Default: CashDeposit
|
||||||
|
private string _description = "عضویت دستی باشگاه مشتریان";
|
||||||
|
|
||||||
|
// Image upload
|
||||||
|
private IBrowserFile? _imageFile;
|
||||||
|
private string? _imagePreview;
|
||||||
|
private const long MaxFileSize = 10 * 1024 * 1024; // 10MB
|
||||||
|
|
||||||
|
private bool _isSubmitting;
|
||||||
|
|
||||||
|
private async Task OnImageFileSelected(IBrowserFile? file)
|
||||||
|
{
|
||||||
|
if (file == null)
|
||||||
|
{
|
||||||
|
RemoveImage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_imageFile = file;
|
||||||
|
|
||||||
|
// Create preview
|
||||||
|
var buffer = new byte[file.Size];
|
||||||
|
await file.OpenReadStream(MaxFileSize).ReadAsync(buffer);
|
||||||
|
_imagePreview = $"data:{file.ContentType};base64,{Convert.ToBase64String(buffer)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveImage()
|
||||||
|
{
|
||||||
|
_imageFile = null;
|
||||||
|
_imagePreview = null;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task CreateAsync()
|
private async Task CreateAsync()
|
||||||
{
|
{
|
||||||
|
if (_isSubmitting) return;
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
if (!_selectedUserId.HasValue || _selectedUserId.Value <= 0)
|
||||||
|
{
|
||||||
|
Snackbar.Add("لطفاً کاربر را انتخاب کنید.", Severity.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_selectedType <= 0)
|
||||||
|
{
|
||||||
|
Snackbar.Add("لطفاً نوع پرداخت را انتخاب کنید.", Severity.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(_description))
|
||||||
|
{
|
||||||
|
Snackbar.Add("لطفاً توضیحات را وارد کنید.", Severity.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isSubmitting = true;
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var request = new CreateManualPaymentRequest
|
var request = new CreateManualPaymentRequest
|
||||||
{
|
{
|
||||||
UserId = _createModel.UserId,
|
UserId = _selectedUserId.Value,
|
||||||
Amount = _createModel.Amount,
|
Amount = _createModel.Amount,
|
||||||
Type = _createModel.Type,
|
Type = _selectedType,
|
||||||
Description = _createModel.Description
|
Description = _description
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(_createModel.ReferenceNumber))
|
if (!string.IsNullOrWhiteSpace(_createModel.ReferenceNumber))
|
||||||
@@ -47,6 +111,20 @@ public partial class ManualPaymentDialog
|
|||||||
request.ReferenceNumber = _createModel.ReferenceNumber;
|
request.ReferenceNumber = _createModel.ReferenceNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Upload image file
|
||||||
|
if (_imageFile != null)
|
||||||
|
{
|
||||||
|
var buffer = new byte[_imageFile.Size];
|
||||||
|
await _imageFile.OpenReadStream(MaxFileSize).ReadAsync(buffer);
|
||||||
|
|
||||||
|
request.ImageFile = new FileUploadModel
|
||||||
|
{
|
||||||
|
File = ByteString.CopyFrom(buffer),
|
||||||
|
FileName = _imageFile.Name,
|
||||||
|
Mime = _imageFile.ContentType
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
await ManualPaymentClient.CreateManualPaymentAsync(request);
|
await ManualPaymentClient.CreateManualPaymentAsync(request);
|
||||||
Snackbar.Add("پرداخت دستی با موفقیت ثبت شد.", Severity.Success);
|
Snackbar.Add("پرداخت دستی با موفقیت ثبت شد.", Severity.Success);
|
||||||
MudDialog.Close(DialogResult.Ok(true));
|
MudDialog.Close(DialogResult.Ok(true));
|
||||||
@@ -55,6 +133,11 @@ public partial class ManualPaymentDialog
|
|||||||
{
|
{
|
||||||
Snackbar.Add($"خطا در ثبت پرداخت دستی: {ex.Message}", Severity.Error);
|
Snackbar.Add($"خطا در ثبت پرداخت دستی: {ex.Message}", Severity.Error);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isSubmitting = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ApproveAsync()
|
private async Task ApproveAsync()
|
||||||
|
|||||||
@@ -45,11 +45,11 @@
|
|||||||
<Content>
|
<Content>
|
||||||
<MudPaper Class="pa-2 mb-2">
|
<MudPaper Class="pa-2 mb-2">
|
||||||
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
|
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
|
||||||
<MudText Typo="Typo.subtitle1">پرداختهای دستی</MudText>
|
<MudText Typo="Typo.subtitle1">فعالسازی (پرداخت) دستی</MudText>
|
||||||
<MudButton Variant="Variant.Outlined"
|
<MudButton Variant="Variant.Outlined"
|
||||||
Color="Color.Primary"
|
Color="Color.Primary"
|
||||||
OnClick="OpenCreateDialog">
|
OnClick="OpenCreateDialog">
|
||||||
ثبت پرداخت دستی جدید
|
ثبت فعالسازی دستی جدید
|
||||||
</MudButton>
|
</MudButton>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
</Filters>
|
</Filters>
|
||||||
<Content>
|
<Content>
|
||||||
<MudGrid Class="mb-2">
|
<MudGrid Class="mb-2">
|
||||||
<MudItem xs="12" md="4">
|
@* <MudItem xs="12" md="4">
|
||||||
<MudCard>
|
<MudCard>
|
||||||
<MudCardContent>
|
<MudCardContent>
|
||||||
<MudText Typo="Typo.subtitle2">جمع سفارشها در بازه فعلی</MudText>
|
<MudText Typo="Typo.subtitle2">جمع سفارشها در بازه فعلی</MudText>
|
||||||
@@ -107,7 +107,7 @@
|
|||||||
}
|
}
|
||||||
</MudCardContent>
|
</MudCardContent>
|
||||||
</MudCard>
|
</MudCard>
|
||||||
</MudItem>
|
</MudItem> *@
|
||||||
</MudGrid>
|
</MudGrid>
|
||||||
|
|
||||||
<MudDataGrid T="DataModel"
|
<MudDataGrid T="DataModel"
|
||||||
@@ -127,7 +127,7 @@
|
|||||||
<Columns>
|
<Columns>
|
||||||
<PropertyColumn Property="x => x.Id" Title="شناسه"/>
|
<PropertyColumn Property="x => x.Id" Title="شناسه"/>
|
||||||
<PropertyColumn Property="x => x.Amount" Title="مبلغ"/>
|
<PropertyColumn Property="x => x.Amount" Title="مبلغ"/>
|
||||||
<PropertyColumn Property="x => x.VatAmount" Title="مبلغ VAT">
|
@* <PropertyColumn Property="x => x.VatAmount" Title="مبلغ VAT">
|
||||||
<CellTemplate>
|
<CellTemplate>
|
||||||
@(context.Item.VatAmount > 0 ? context.Item.VatAmount.ToString("N0") : "-")
|
@(context.Item.VatAmount > 0 ? context.Item.VatAmount.ToString("N0") : "-")
|
||||||
</CellTemplate>
|
</CellTemplate>
|
||||||
@@ -136,7 +136,7 @@
|
|||||||
<CellTemplate>
|
<CellTemplate>
|
||||||
@(context.Item.VatPercentage > 0 ? $"{context.Item.VatPercentage:0}٪" : "-")
|
@(context.Item.VatPercentage > 0 ? $"{context.Item.VatPercentage:0}٪" : "-")
|
||||||
</CellTemplate>
|
</CellTemplate>
|
||||||
</PropertyColumn>
|
</PropertyColumn> *@
|
||||||
<PropertyColumn Property="x => x.UserFullName" Title="نام کاربر"/>
|
<PropertyColumn Property="x => x.UserFullName" Title="نام کاربر"/>
|
||||||
<PropertyColumn Property="x => x.UserNationalCode" Title="کدملی"/>
|
<PropertyColumn Property="x => x.UserNationalCode" Title="کدملی"/>
|
||||||
<TemplateColumn Title="روش پرداخت">
|
<TemplateColumn Title="روش پرداخت">
|
||||||
|
|||||||
@@ -81,7 +81,16 @@
|
|||||||
آمار باشگاه
|
آمار باشگاه
|
||||||
</MudNavLink>
|
</MudNavLink>
|
||||||
</MudNavGroup>
|
</MudNavGroup>
|
||||||
|
@if (CanManagePayments)
|
||||||
|
{
|
||||||
|
<MudNavGroup Title="پرداختها و فعالسازیها" Icon="@Icons.Material.Filled.Payment" Expanded="false">
|
||||||
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
|
Href="/payment/manual-payments"
|
||||||
|
Icon="@Icons.Material.Filled.Payments">
|
||||||
|
فعالسازی (پرداخت) دستی
|
||||||
|
</MudNavLink>
|
||||||
|
</MudNavGroup>
|
||||||
|
}
|
||||||
<MudDivider Class="my-2" />
|
<MudDivider Class="my-2" />
|
||||||
<MudText Class="nav-menu__title" Typo="Typo.subtitle2">مدیریت</MudText>
|
<MudText Class="nav-menu__title" Typo="Typo.subtitle2">مدیریت</MudText>
|
||||||
|
|
||||||
@@ -106,7 +115,7 @@
|
|||||||
</MudNavLink>
|
</MudNavLink>
|
||||||
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
Href="/products/bulk-edit"
|
Href="@(RouteConstance.ProductsBulkEdit)"
|
||||||
Icon="@Icons.Material.Filled.EditNote">
|
Icon="@Icons.Material.Filled.EditNote">
|
||||||
ویرایش دستهجمعی
|
ویرایش دستهجمعی
|
||||||
</MudNavLink>
|
</MudNavLink>
|
||||||
@@ -129,6 +138,29 @@
|
|||||||
مدیریت تگها
|
مدیریت تگها
|
||||||
</MudNavLink>
|
</MudNavLink>
|
||||||
</MudNavGroup>
|
</MudNavGroup>
|
||||||
|
|
||||||
|
<MudNavGroup Title="انبارداری" Icon="@Icons.Material.Filled.Warehouse" Expanded="false">
|
||||||
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
|
Href="@(RouteConstance.Inventory)"
|
||||||
|
Icon="@Icons.Material.Filled.Inventory">
|
||||||
|
موجودی انبار
|
||||||
|
</MudNavLink>
|
||||||
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
|
Href="@(RouteConstance.InventoryLowStock)"
|
||||||
|
Icon="@Icons.Material.Filled.Warning">
|
||||||
|
محصولات کمموجود
|
||||||
|
</MudNavLink>
|
||||||
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
|
Href="@(RouteConstance.InventoryWarehouses)"
|
||||||
|
Icon="@Icons.Material.Filled.Store">
|
||||||
|
مدیریت انبارها
|
||||||
|
</MudNavLink>
|
||||||
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
|
Href="@(RouteConstance.InventoryMovements)"
|
||||||
|
Icon="@Icons.Material.Filled.History">
|
||||||
|
تاریخچه تغییرات
|
||||||
|
</MudNavLink>
|
||||||
|
</MudNavGroup>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (CanViewOrders)
|
@if (CanViewOrders)
|
||||||
@@ -158,21 +190,7 @@
|
|||||||
</MudNavLink>
|
</MudNavLink>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (CanManagePayments)
|
|
||||||
{
|
|
||||||
<MudNavGroup Title="پرداختها" Icon="@Icons.Material.Filled.Payment" Expanded="false">
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
|
||||||
Href="/payment/manual-payments"
|
|
||||||
Icon="@Icons.Material.Filled.Payments">
|
|
||||||
پرداختهای دستی
|
|
||||||
</MudNavLink>
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
|
||||||
Href="/payment/membership"
|
|
||||||
Icon="@Icons.Material.Filled.CardMembership">
|
|
||||||
پرداخت دستی عضویت
|
|
||||||
</MudNavLink>
|
|
||||||
</MudNavGroup>
|
|
||||||
}
|
|
||||||
</Authorized>
|
</Authorized>
|
||||||
</AuthorizeView>
|
</AuthorizeView>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user