Implement Inventory Management Service with CRUD operations for warehouses and inventory items, stock operations, and bulk processing capabilities.
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 1m48s
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 1m48s
This commit is contained in:
@@ -61,6 +61,11 @@ public interface IApplicationDbContext
|
||||
DbSet<State> States { get; }
|
||||
DbSet<City> Cities { get; }
|
||||
|
||||
// ============= Inventory Management =============
|
||||
DbSet<Warehouse> Warehouses { get; }
|
||||
DbSet<InventoryItem> InventoryItems { get; }
|
||||
DbSet<StockMovement> StockMovements { get; }
|
||||
|
||||
/// <summary>
|
||||
/// دسترسی به DatabaseFacade برای اجرای raw SQL و Stored Procedures
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
|
||||
namespace CMSMicroservice.Application.Common.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// سرویس مدیریت موجودی - لایه بالاتر برای عملیات business
|
||||
/// این سرویس مسئول همگامسازی موجودی بین InventoryItem و Product.RemainingCount است
|
||||
/// </summary>
|
||||
public interface IInventoryService
|
||||
{
|
||||
#region Initialization
|
||||
|
||||
/// <summary>
|
||||
/// ایجاد رکورد موجودی برای محصول جدید
|
||||
/// این متد باید در CreateProductCommandHandler و CreateDiscountProductCommandHandler فراخوانی شود
|
||||
/// </summary>
|
||||
/// <param name="productId">شناسه محصول (Product.Id یا DiscountProduct.Id)</param>
|
||||
/// <param name="productType">نوع محصول (RegularProduct یا DiscountProduct)</param>
|
||||
/// <param name="initialQuantity">موجودی اولیه</param>
|
||||
/// <param name="warehouseId">شناسه انبار (پیشفرض: انبار اصلی)</param>
|
||||
/// <param name="lowStockThreshold">آستانه هشدار کمموجودی</param>
|
||||
/// <param name="ct">CancellationToken</param>
|
||||
/// <returns>شناسه InventoryItem ایجاد شده</returns>
|
||||
Task<long> InitializeInventoryAsync(
|
||||
long productId,
|
||||
ProductType productType,
|
||||
int initialQuantity,
|
||||
long? warehouseId = null,
|
||||
int lowStockThreshold = 10,
|
||||
CancellationToken ct = default);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Query Operations
|
||||
|
||||
/// <summary>
|
||||
/// دریافت موجودی یک محصول
|
||||
/// </summary>
|
||||
Task<InventoryItem?> GetInventoryAsync(
|
||||
long productId,
|
||||
ProductType productType,
|
||||
long? warehouseId = null,
|
||||
CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت موجودی قابل فروش (Quantity - ReservedQuantity)
|
||||
/// </summary>
|
||||
Task<int> GetAvailableQuantityAsync(
|
||||
long productId,
|
||||
ProductType productType,
|
||||
long? warehouseId = null,
|
||||
CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// بررسی اینکه آیا موجودی کافی برای فروش وجود دارد
|
||||
/// </summary>
|
||||
Task<bool> CheckAvailabilityAsync(
|
||||
long productId,
|
||||
ProductType productType,
|
||||
int requiredQuantity,
|
||||
long? warehouseId = null,
|
||||
CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت لیست محصولات کمموجود
|
||||
/// </summary>
|
||||
Task<List<InventoryItem>> GetLowStockItemsAsync(
|
||||
ProductType? productType = null,
|
||||
long? warehouseId = null,
|
||||
int count = 50,
|
||||
CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت تاریخچه حرکات موجودی
|
||||
/// </summary>
|
||||
Task<List<StockMovement>> GetStockMovementsAsync(
|
||||
long productId,
|
||||
ProductType productType,
|
||||
DateTime? fromDate = null,
|
||||
DateTime? toDate = null,
|
||||
CancellationToken ct = default);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Order Flow Operations
|
||||
|
||||
/// <summary>
|
||||
/// رزرو موجودی برای سفارش pending
|
||||
/// این متد در PlaceOrderCommandHandler فراخوانی میشود
|
||||
/// فقط ReservedQuantity را افزایش میدهد، Quantity تغییر نمیکند
|
||||
/// </summary>
|
||||
/// <param name="productId">شناسه محصول</param>
|
||||
/// <param name="productType">نوع محصول</param>
|
||||
/// <param name="quantity">تعداد رزرو</param>
|
||||
/// <param name="orderId">شناسه سفارش (Order.Id یا DiscountOrder.Id)</param>
|
||||
/// <param name="ct">CancellationToken</param>
|
||||
/// <returns>true اگر رزرو موفق بود</returns>
|
||||
Task<bool> ReserveStockAsync(
|
||||
long productId,
|
||||
ProductType productType,
|
||||
int quantity,
|
||||
long? orderId = null,
|
||||
CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// آزادسازی رزرو (لغو سفارش یا timeout)
|
||||
/// این متد در CancelOrderCommandHandler فراخوانی میشود
|
||||
/// </summary>
|
||||
Task<bool> ReleaseReservationAsync(
|
||||
long productId,
|
||||
ProductType productType,
|
||||
int quantity,
|
||||
long? orderId = null,
|
||||
CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// تایید فروش - کسر واقعی موجودی
|
||||
/// این متد در CompleteOrderPaymentCommandHandler فراخوانی میشود
|
||||
/// ReservedQuantity کاهش مییابد، Quantity کاهش مییابد، Product.RemainingCount sync میشود
|
||||
/// </summary>
|
||||
Task<bool> ConfirmSaleAsync(
|
||||
long productId,
|
||||
ProductType productType,
|
||||
int quantity,
|
||||
long? orderId = null,
|
||||
CancellationToken ct = default);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stock Management Operations
|
||||
|
||||
/// <summary>
|
||||
/// ورود کالا به انبار (Restock)
|
||||
/// </summary>
|
||||
/// <param name="productId">شناسه محصول</param>
|
||||
/// <param name="productType">نوع محصول</param>
|
||||
/// <param name="quantity">تعداد ورودی</param>
|
||||
/// <param name="referenceNumber">شماره مرجع (مثل شماره فاکتور خرید)</param>
|
||||
/// <param name="note">یادداشت</param>
|
||||
/// <param name="performedByUserId">شناسه کاربر انجامدهنده</param>
|
||||
/// <param name="ct">CancellationToken</param>
|
||||
Task<bool> AddStockAsync(
|
||||
long productId,
|
||||
ProductType productType,
|
||||
int quantity,
|
||||
string? referenceNumber = null,
|
||||
string? note = null,
|
||||
long? performedByUserId = null,
|
||||
CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// تعدیل موجودی (تنظیم به مقدار جدید)
|
||||
/// </summary>
|
||||
Task<bool> AdjustStockAsync(
|
||||
long productId,
|
||||
ProductType productType,
|
||||
int newQuantity,
|
||||
string? note = null,
|
||||
long? performedByUserId = null,
|
||||
CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// ثبت برگشت کالا از مشتری
|
||||
/// </summary>
|
||||
Task<bool> ProcessReturnAsync(
|
||||
long productId,
|
||||
ProductType productType,
|
||||
int quantity,
|
||||
long? orderId = null,
|
||||
string? note = null,
|
||||
long? performedByUserId = null,
|
||||
CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// ثبت ضایعات/مفقودی
|
||||
/// </summary>
|
||||
Task<bool> RecordLossAsync(
|
||||
long productId,
|
||||
ProductType productType,
|
||||
int quantity,
|
||||
StockMovementType lossType, // Damaged or Lost
|
||||
string? note = null,
|
||||
long? performedByUserId = null,
|
||||
CancellationToken ct = default);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Bulk Operations
|
||||
|
||||
/// <summary>
|
||||
/// رزرو موجودی برای چند آیتم (یک سفارش با چند محصول)
|
||||
/// </summary>
|
||||
Task<bool> BulkReserveStockAsync(
|
||||
IEnumerable<(long ProductId, ProductType ProductType, int Quantity)> items,
|
||||
long? orderId = null,
|
||||
CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// آزادسازی رزرو برای چند آیتم
|
||||
/// </summary>
|
||||
Task<bool> BulkReleaseReservationAsync(
|
||||
IEnumerable<(long ProductId, ProductType ProductType, int Quantity)> items,
|
||||
long? orderId = null,
|
||||
CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// تایید فروش برای چند آیتم
|
||||
/// </summary>
|
||||
Task<bool> BulkConfirmSaleAsync(
|
||||
IEnumerable<(long ProductId, ProductType ProductType, int Quantity)> items,
|
||||
long? orderId = null,
|
||||
CancellationToken ct = default);
|
||||
|
||||
#endregion
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
|
||||
namespace CMSMicroservice.Application.Common.Interfaces.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Repository interface برای مدیریت موجودی محصولات
|
||||
/// </summary>
|
||||
public interface IInventoryItemRepository
|
||||
{
|
||||
#region Read Operations
|
||||
|
||||
/// <summary>
|
||||
/// دریافت آیتم موجودی بر اساس شناسه
|
||||
/// </summary>
|
||||
Task<InventoryItem?> GetByIdAsync(long id, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت آیتم موجودی بر اساس محصول معمولی
|
||||
/// </summary>
|
||||
Task<InventoryItem?> GetByProductIdAsync(long productId, long warehouseId = 1, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت آیتم موجودی بر اساس محصول تخفیفی
|
||||
/// </summary>
|
||||
Task<InventoryItem?> GetByDiscountProductIdAsync(long discountProductId, long warehouseId = 1, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت تمام آیتمهای موجودی یک انبار
|
||||
/// </summary>
|
||||
Task<List<InventoryItem>> GetByWarehouseIdAsync(long warehouseId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت محصولات کمموجود
|
||||
/// </summary>
|
||||
Task<List<InventoryItem>> GetLowStockItemsAsync(ProductType? productType = null, long warehouseId = 1, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت محصولات با موجودی صفر
|
||||
/// </summary>
|
||||
Task<List<InventoryItem>> GetOutOfStockItemsAsync(ProductType? productType = null, long warehouseId = 1, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// جستجوی آیتمهای موجودی با فیلتر
|
||||
/// </summary>
|
||||
Task<List<InventoryItem>> SearchAsync(
|
||||
string? searchTerm = null,
|
||||
ProductType? productType = null,
|
||||
long? warehouseId = null,
|
||||
int? minQuantity = null,
|
||||
int? maxQuantity = null,
|
||||
int skip = 0,
|
||||
int take = 50,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// شمارش کل آیتمهای موجودی با فیلتر
|
||||
/// </summary>
|
||||
Task<int> CountAsync(
|
||||
string? searchTerm = null,
|
||||
ProductType? productType = null,
|
||||
long? warehouseId = null,
|
||||
int? minQuantity = null,
|
||||
int? maxQuantity = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Write Operations
|
||||
|
||||
/// <summary>
|
||||
/// افزودن آیتم موجودی جدید
|
||||
/// </summary>
|
||||
Task<InventoryItem> AddAsync(InventoryItem inventoryItem, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// بروزرسانی آیتم موجودی
|
||||
/// </summary>
|
||||
Task UpdateAsync(InventoryItem inventoryItem, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// حذف آیتم موجودی
|
||||
/// </summary>
|
||||
Task DeleteAsync(long id, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// بروزرسانی موجودی (با ثبت حرکت)
|
||||
/// </summary>
|
||||
Task<bool> UpdateQuantityAsync(
|
||||
long inventoryItemId,
|
||||
int quantityChange,
|
||||
StockMovementType movementType,
|
||||
string? note = null,
|
||||
string? referenceNumber = null,
|
||||
long? orderId = null,
|
||||
long? discountOrderId = null,
|
||||
long? performedByUserId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// رزرو موجودی
|
||||
/// </summary>
|
||||
Task<bool> ReserveQuantityAsync(
|
||||
long inventoryItemId,
|
||||
int quantity,
|
||||
string? note = null,
|
||||
string? referenceNumber = null,
|
||||
long? orderId = null,
|
||||
long? discountOrderId = null,
|
||||
long? performedByUserId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// آزاد کردن موجودی رزرو شده
|
||||
/// </summary>
|
||||
Task<bool> ReleaseReservedQuantityAsync(
|
||||
long inventoryItemId,
|
||||
int quantity,
|
||||
string? note = null,
|
||||
string? referenceNumber = null,
|
||||
long? orderId = null,
|
||||
long? discountOrderId = null,
|
||||
long? performedByUserId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Bulk Operations
|
||||
|
||||
/// <summary>
|
||||
/// بروزرسانی انبوه موجودی چندین محصول
|
||||
/// </summary>
|
||||
Task<bool> BulkUpdateQuantityAsync(
|
||||
List<(long InventoryItemId, int QuantityChange, string? Note)> updates,
|
||||
StockMovementType movementType,
|
||||
string? referenceNumber = null,
|
||||
long? performedByUserId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// رزرو انبوه موجودی چندین محصول
|
||||
/// </summary>
|
||||
Task<bool> BulkReserveQuantityAsync(
|
||||
List<(long InventoryItemId, int Quantity, string? Note)> reservations,
|
||||
string? referenceNumber = null,
|
||||
long? orderId = null,
|
||||
long? discountOrderId = null,
|
||||
long? performedByUserId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
|
||||
namespace CMSMicroservice.Application.Common.Interfaces.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Repository interface برای مدیریت حرکات موجودی
|
||||
/// </summary>
|
||||
public interface IStockMovementRepository
|
||||
{
|
||||
#region Read Operations
|
||||
|
||||
/// <summary>
|
||||
/// دریافت حرکت موجودی بر اساس شناسه
|
||||
/// </summary>
|
||||
Task<StockMovement?> GetByIdAsync(long id, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت تاریخچه حرکات یک آیتم موجودی
|
||||
/// </summary>
|
||||
Task<List<StockMovement>> GetByInventoryItemIdAsync(
|
||||
long inventoryItemId,
|
||||
StockMovementType? movementType = null,
|
||||
DateTime? fromDate = null,
|
||||
DateTime? toDate = null,
|
||||
int skip = 0,
|
||||
int take = 100,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت حرکات مربوط به یک سفارش
|
||||
/// </summary>
|
||||
Task<List<StockMovement>> GetByOrderIdAsync(long orderId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت حرکات مربوط به یک سفارش تخفیفی
|
||||
/// </summary>
|
||||
Task<List<StockMovement>> GetByDiscountOrderIdAsync(long discountOrderId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت حرکات بر اساس شماره مرجع
|
||||
/// </summary>
|
||||
Task<List<StockMovement>> GetByReferenceNumberAsync(string referenceNumber, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت حرکات بر اساس نوع حرکت
|
||||
/// </summary>
|
||||
Task<List<StockMovement>> GetByMovementTypeAsync(
|
||||
StockMovementType movementType,
|
||||
DateTime? fromDate = null,
|
||||
DateTime? toDate = null,
|
||||
int skip = 0,
|
||||
int take = 100,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت آخرین حرکات موجودی
|
||||
/// </summary>
|
||||
Task<List<StockMovement>> GetRecentMovementsAsync(
|
||||
int count = 50,
|
||||
StockMovementType? movementType = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// جستجوی حرکات موجودی با فیلتر پیشرفته
|
||||
/// </summary>
|
||||
Task<List<StockMovement>> SearchAsync(
|
||||
long? inventoryItemId = null,
|
||||
StockMovementType? movementType = null,
|
||||
DateTime? fromDate = null,
|
||||
DateTime? toDate = null,
|
||||
string? referenceNumber = null,
|
||||
long? orderId = null,
|
||||
long? discountOrderId = null,
|
||||
long? performedByUserId = null,
|
||||
int skip = 0,
|
||||
int take = 100,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// شمارش حرکات موجودی با فیلتر
|
||||
/// </summary>
|
||||
Task<int> CountAsync(
|
||||
long? inventoryItemId = null,
|
||||
StockMovementType? movementType = null,
|
||||
DateTime? fromDate = null,
|
||||
DateTime? toDate = null,
|
||||
string? referenceNumber = null,
|
||||
long? orderId = null,
|
||||
long? discountOrderId = null,
|
||||
long? performedByUserId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Write Operations
|
||||
|
||||
/// <summary>
|
||||
/// افزودن حرکت موجودی جدید
|
||||
/// </summary>
|
||||
Task<StockMovement> AddAsync(StockMovement stockMovement, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// حذف حرکت موجودی (نرمافزاری)
|
||||
/// </summary>
|
||||
Task DeleteAsync(long id, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// افزودن انبوه حرکات موجودی
|
||||
/// </summary>
|
||||
Task<List<StockMovement>> BulkAddAsync(List<StockMovement> stockMovements, CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Analytics & Reports
|
||||
|
||||
/// <summary>
|
||||
/// گزارش خلاصه حرکات در بازه زمانی
|
||||
/// </summary>
|
||||
Task<Dictionary<StockMovementType, int>> GetMovementSummaryAsync(
|
||||
DateTime fromDate,
|
||||
DateTime toDate,
|
||||
long? inventoryItemId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// گزارش حجم ورود و خروج روزانه
|
||||
/// </summary>
|
||||
Task<List<(DateTime Date, int InboundQuantity, int OutboundQuantity)>> GetDailyMovementVolumeAsync(
|
||||
DateTime fromDate,
|
||||
DateTime toDate,
|
||||
long? inventoryItemId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// گزارش بیشترین حرکات محصولات
|
||||
/// </summary>
|
||||
Task<List<(long InventoryItemId, string ProductName, int MovementCount, int TotalQuantityChange)>> GetTopMovingProductsAsync(
|
||||
DateTime fromDate,
|
||||
DateTime toDate,
|
||||
int count = 10,
|
||||
StockMovementType? movementType = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
|
||||
namespace CMSMicroservice.Application.Common.Interfaces.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Repository interface برای مدیریت انبارها
|
||||
/// </summary>
|
||||
public interface IWarehouseRepository
|
||||
{
|
||||
#region Read Operations
|
||||
|
||||
/// <summary>
|
||||
/// دریافت انبار بر اساس شناسه
|
||||
/// </summary>
|
||||
Task<Warehouse?> GetByIdAsync(long id, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت انبار بر اساس کد
|
||||
/// </summary>
|
||||
Task<Warehouse?> GetByCodeAsync(string code, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت انبار پیشفرض
|
||||
/// </summary>
|
||||
Task<Warehouse?> GetDefaultWarehouseAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت تمام انبارهای فعال
|
||||
/// </summary>
|
||||
Task<List<Warehouse>> GetActiveWarehousesAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت تمام انبارها
|
||||
/// </summary>
|
||||
Task<List<Warehouse>> GetAllAsync(bool includeInactive = false, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// جستجوی انبارها
|
||||
/// </summary>
|
||||
Task<List<Warehouse>> SearchAsync(
|
||||
string? searchTerm = null,
|
||||
bool? isActive = null,
|
||||
int skip = 0,
|
||||
int take = 50,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// شمارش انبارها
|
||||
/// </summary>
|
||||
Task<int> CountAsync(
|
||||
string? searchTerm = null,
|
||||
bool? isActive = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// بررسی وجود انبار با کد مشخص
|
||||
/// </summary>
|
||||
Task<bool> ExistsByCodeAsync(string code, long? excludeId = null, CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Write Operations
|
||||
|
||||
/// <summary>
|
||||
/// افزودن انبار جدید
|
||||
/// </summary>
|
||||
Task<Warehouse> AddAsync(Warehouse warehouse, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// بروزرسانی انبار
|
||||
/// </summary>
|
||||
Task UpdateAsync(Warehouse warehouse, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// حذف انبار (نرمافزاری)
|
||||
/// </summary>
|
||||
Task DeleteAsync(long id, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// فعال/غیرفعال کردن انبار
|
||||
/// </summary>
|
||||
Task SetActiveStatusAsync(long id, bool isActive, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// تنظیم انبار پیشفرض
|
||||
/// </summary>
|
||||
Task SetAsDefaultAsync(long id, CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Analytics
|
||||
|
||||
/// <summary>
|
||||
/// گزارش آمار کلی انبار
|
||||
/// </summary>
|
||||
Task<(int TotalProducts, int LowStockProducts, int OutOfStockProducts, decimal TotalValue)> GetWarehouseStatisticsAsync(
|
||||
long warehouseId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// گزارش محصولات پرفروش انبار
|
||||
/// </summary>
|
||||
Task<List<(long ProductId, string ProductName, int TotalSold, int CurrentStock)>> GetTopSellingProductsAsync(
|
||||
long warehouseId,
|
||||
DateTime fromDate,
|
||||
DateTime toDate,
|
||||
int count = 10,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion
|
||||
}
|
||||
+26
-6
@@ -8,10 +8,14 @@ namespace CMSMicroservice.Application.DiscountShopCQ.Commands.CompleteOrderPayme
|
||||
public class CompleteOrderPaymentCommandHandler : IRequestHandler<CompleteOrderPaymentCommand, CompleteOrderPaymentResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IInventoryService _inventoryService;
|
||||
|
||||
public CompleteOrderPaymentCommandHandler(IApplicationDbContext context)
|
||||
public CompleteOrderPaymentCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
IInventoryService inventoryService)
|
||||
{
|
||||
_context = context;
|
||||
_inventoryService = inventoryService;
|
||||
}
|
||||
|
||||
public async Task<CompleteOrderPaymentResponseDto> Handle(CompleteOrderPaymentCommand request, CancellationToken cancellationToken)
|
||||
@@ -63,12 +67,18 @@ public class CompleteOrderPaymentCommandHandler : IRequestHandler<CompleteOrderP
|
||||
userWallet.DiscountBalance -= order.DiscountBalanceUsed;
|
||||
}
|
||||
|
||||
// Update product stock and sale count
|
||||
// تایید فروش و کسر موجودی از طریق InventoryService
|
||||
foreach (var orderDetail in order.OrderDetails)
|
||||
{
|
||||
var product = orderDetail.Product;
|
||||
product.RemainingCount -= orderDetail.Count;
|
||||
product.SaleCount += orderDetail.Count;
|
||||
await _inventoryService.ConfirmSaleAsync(
|
||||
orderDetail.ProductId,
|
||||
ProductType.DiscountProduct,
|
||||
orderDetail.Count,
|
||||
order.Id,
|
||||
cancellationToken);
|
||||
|
||||
// افزایش تعداد فروش
|
||||
orderDetail.Product.SaleCount += orderDetail.Count;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
@@ -82,7 +92,17 @@ public class CompleteOrderPaymentCommandHandler : IRequestHandler<CompleteOrderP
|
||||
}
|
||||
else
|
||||
{
|
||||
// Payment failed
|
||||
// Payment failed - آزادسازی رزرو
|
||||
foreach (var orderDetail in order.OrderDetails)
|
||||
{
|
||||
await _inventoryService.ReleaseReservationAsync(
|
||||
orderDetail.ProductId,
|
||||
ProductType.DiscountProduct,
|
||||
orderDetail.Count,
|
||||
order.Id,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
transaction.PaymentStatus = PaymentStatus.Reject;
|
||||
order.PaymentStatus = PaymentStatus.Reject;
|
||||
|
||||
|
||||
+13
-1
@@ -1,5 +1,6 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Entities.DiscountShop;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@@ -8,10 +9,14 @@ namespace CMSMicroservice.Application.DiscountShopCQ.Commands.CreateDiscountProd
|
||||
public class CreateDiscountProductCommandHandler : IRequestHandler<CreateDiscountProductCommand, long>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IInventoryService _inventoryService;
|
||||
|
||||
public CreateDiscountProductCommandHandler(IApplicationDbContext context)
|
||||
public CreateDiscountProductCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
IInventoryService inventoryService)
|
||||
{
|
||||
_context = context;
|
||||
_inventoryService = inventoryService;
|
||||
}
|
||||
|
||||
public async Task<long> Handle(CreateDiscountProductCommand request, CancellationToken cancellationToken)
|
||||
@@ -35,6 +40,13 @@ public class CreateDiscountProductCommandHandler : IRequestHandler<CreateDiscoun
|
||||
_context.DiscountProducts.Add(product);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// ایجاد رکورد موجودی در سیستم انبارداری
|
||||
await _inventoryService.InitializeInventoryAsync(
|
||||
product.Id,
|
||||
ProductType.DiscountProduct,
|
||||
request.RemainingCount,
|
||||
ct: cancellationToken);
|
||||
|
||||
// Add product categories
|
||||
if (request.CategoryIds.Any())
|
||||
{
|
||||
|
||||
+16
-1
@@ -11,10 +11,14 @@ namespace CMSMicroservice.Application.DiscountShopCQ.Commands.PlaceOrder;
|
||||
public class PlaceOrderCommandHandler : IRequestHandler<PlaceOrderCommand, PlaceOrderResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IInventoryService _inventoryService;
|
||||
|
||||
public PlaceOrderCommandHandler(IApplicationDbContext context)
|
||||
public PlaceOrderCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
IInventoryService inventoryService)
|
||||
{
|
||||
_context = context;
|
||||
_inventoryService = inventoryService;
|
||||
}
|
||||
|
||||
public async Task<PlaceOrderResponseDto> Handle(PlaceOrderCommand request, CancellationToken cancellationToken)
|
||||
@@ -152,6 +156,17 @@ public class PlaceOrderCommandHandler : IRequestHandler<PlaceOrderCommand, Place
|
||||
|
||||
_context.DiscountOrderDetails.AddRange(orderDetails);
|
||||
|
||||
// رزرو موجودی برای سفارش pending
|
||||
foreach (var cartItem in cartItems)
|
||||
{
|
||||
await _inventoryService.ReserveStockAsync(
|
||||
cartItem.ProductId,
|
||||
ProductType.DiscountProduct,
|
||||
cartItem.Count,
|
||||
order.Id,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
// Clear cart
|
||||
_context.DiscountShoppingCarts.RemoveRange(cartItems);
|
||||
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.Features.InventoryItems.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Command برای ایجاد آیتم موجودی جدید
|
||||
/// </summary>
|
||||
public record CreateInventoryItemCommand : IRequest<long>
|
||||
{
|
||||
public long? ProductId { get; init; }
|
||||
public long? DiscountProductId { get; init; }
|
||||
public long WarehouseId { get; init; }
|
||||
public int Quantity { get; init; }
|
||||
public int MinQuantity { get; init; }
|
||||
public int MaxQuantity { get; init; }
|
||||
public bool IsActive { get; init; } = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command برای آپدیت آیتم موجودی
|
||||
/// </summary>
|
||||
public record UpdateInventoryItemCommand : IRequest<bool>
|
||||
{
|
||||
public long Id { get; init; }
|
||||
public int? Quantity { get; init; }
|
||||
public int? MinQuantity { get; init; }
|
||||
public int? MaxQuantity { get; init; }
|
||||
public long? WarehouseId { get; init; }
|
||||
public bool? IsActive { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command برای آپدیت کردن موجودی یک آیتم
|
||||
/// </summary>
|
||||
public record UpdateInventoryQuantityCommand : IRequest<bool>
|
||||
{
|
||||
public long Id { get; init; }
|
||||
public int NewQuantity { get; init; }
|
||||
public string? ReferenceNumber { get; init; }
|
||||
public long? PerformedByUserId { get; init; }
|
||||
public string? Note { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command برای رزرو کردن موجودی
|
||||
/// </summary>
|
||||
public record ReserveInventoryCommand : IRequest<bool>
|
||||
{
|
||||
public long Id { get; init; }
|
||||
public int Quantity { get; init; }
|
||||
public long? OrderId { get; init; }
|
||||
public long? DiscountOrderId { get; init; }
|
||||
public string? ReferenceNumber { get; init; }
|
||||
public long? PerformedByUserId { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command برای آزاد کردن موجودی رزرو شده
|
||||
/// </summary>
|
||||
public record ReleaseReservedInventoryCommand : IRequest<bool>
|
||||
{
|
||||
public long Id { get; init; }
|
||||
public int Quantity { get; init; }
|
||||
public long? OrderId { get; init; }
|
||||
public long? DiscountOrderId { get; init; }
|
||||
public string? ReferenceNumber { get; init; }
|
||||
public long? PerformedByUserId { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command برای کم کردن موجودی (فروش)
|
||||
/// </summary>
|
||||
public record ReduceInventoryCommand : IRequest<bool>
|
||||
{
|
||||
public long Id { get; init; }
|
||||
public int Quantity { get; init; }
|
||||
public long? OrderId { get; init; }
|
||||
public long? DiscountOrderId { get; init; }
|
||||
public string? ReferenceNumber { get; init; }
|
||||
public long? PerformedByUserId { get; init; }
|
||||
public bool FromReserved { get; init; } = true; // آیا از موجودی رزرو شده کم شود؟
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command برای اضافه کردن موجودی (خرید)
|
||||
/// </summary>
|
||||
public record IncreaseInventoryCommand : IRequest<bool>
|
||||
{
|
||||
public long Id { get; init; }
|
||||
public int Quantity { get; init; }
|
||||
public string? ReferenceNumber { get; init; }
|
||||
public long? PerformedByUserId { get; init; }
|
||||
public string? Note { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command برای حذف آیتم موجودی
|
||||
/// </summary>
|
||||
public record DeleteInventoryItemCommand : IRequest<bool>
|
||||
{
|
||||
public long Id { get; init; }
|
||||
}
|
||||
+289
@@ -0,0 +1,289 @@
|
||||
using MediatR;
|
||||
using CMSMicroservice.Application.Common.Interfaces.Repositories;
|
||||
using CMSMicroservice.Application.Features.InventoryItems.Commands;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
|
||||
namespace CMSMicroservice.Application.Features.InventoryItems.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای ایجاد آیتم موجودی جدید
|
||||
/// </summary>
|
||||
public class CreateInventoryItemCommandHandler : IRequestHandler<CreateInventoryItemCommand, long>
|
||||
{
|
||||
private readonly IInventoryItemRepository _repository;
|
||||
private readonly IStockMovementRepository _stockMovementRepository;
|
||||
|
||||
public CreateInventoryItemCommandHandler(
|
||||
IInventoryItemRepository repository,
|
||||
IStockMovementRepository stockMovementRepository)
|
||||
{
|
||||
_repository = repository;
|
||||
_stockMovementRepository = stockMovementRepository;
|
||||
}
|
||||
|
||||
public async Task<long> Handle(CreateInventoryItemCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// بررسی اینکه حداقل یکی از Product یا DiscountProduct تعریف شده باشد
|
||||
if (request.ProductId == null && request.DiscountProductId == null)
|
||||
{
|
||||
throw new ArgumentException("Either ProductId or DiscountProductId must be provided");
|
||||
}
|
||||
|
||||
// بررسی اینکه هر دو ProductId و DiscountProductId تعریف نشده باشند
|
||||
if (request.ProductId != null && request.DiscountProductId != null)
|
||||
{
|
||||
throw new ArgumentException("Only one of ProductId or DiscountProductId can be provided");
|
||||
}
|
||||
|
||||
// بررسی وجود آیتم موجودی قبلی برای همین محصول در همین انبار
|
||||
InventoryItem? existingItem = null;
|
||||
if (request.ProductId.HasValue)
|
||||
{
|
||||
existingItem = await _repository.GetByProductIdAsync(request.ProductId.Value, request.WarehouseId, cancellationToken);
|
||||
}
|
||||
else if (request.DiscountProductId.HasValue)
|
||||
{
|
||||
existingItem = await _repository.GetByDiscountProductIdAsync(request.DiscountProductId.Value, request.WarehouseId, cancellationToken);
|
||||
}
|
||||
|
||||
if (existingItem != null)
|
||||
{
|
||||
throw new InvalidOperationException("Inventory item already exists for this product in this warehouse");
|
||||
}
|
||||
|
||||
var productType = request.ProductId.HasValue ? ProductType.RegularProduct : ProductType.DiscountProduct;
|
||||
|
||||
var inventoryItem = new InventoryItem
|
||||
{
|
||||
ProductId = request.ProductId,
|
||||
DiscountProductId = request.DiscountProductId,
|
||||
ProductType = productType,
|
||||
WarehouseId = request.WarehouseId,
|
||||
Quantity = request.Quantity,
|
||||
LowStockThreshold = request.MinQuantity,
|
||||
MaxStockLevel = request.MaxQuantity,
|
||||
ReservedQuantity = 0
|
||||
};
|
||||
|
||||
var createdItem = await _repository.AddAsync(inventoryItem, cancellationToken);
|
||||
|
||||
// ثبت حرکت موجودی اولیه اگر موجودی اولیه بیشتر از صفر باشد
|
||||
if (request.Quantity > 0)
|
||||
{
|
||||
var stockMovement = new StockMovement
|
||||
{
|
||||
InventoryItemId = createdItem.Id,
|
||||
MovementType = StockMovementType.InitialStock,
|
||||
Quantity = request.Quantity,
|
||||
Note = "Initial stock creation",
|
||||
ReferenceNumber = $"INIT-{createdItem.Id}"
|
||||
};
|
||||
|
||||
await _stockMovementRepository.AddAsync(stockMovement, cancellationToken);
|
||||
}
|
||||
|
||||
return createdItem.Id;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای آپدیت آیتم موجودی
|
||||
/// </summary>
|
||||
public class UpdateInventoryItemCommandHandler : IRequestHandler<UpdateInventoryItemCommand, bool>
|
||||
{
|
||||
private readonly IInventoryItemRepository _repository;
|
||||
|
||||
public UpdateInventoryItemCommandHandler(IInventoryItemRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(UpdateInventoryItemCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var inventoryItem = await _repository.GetByIdAsync(request.Id, cancellationToken);
|
||||
if (inventoryItem == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (request.WarehouseId.HasValue && request.WarehouseId.Value != inventoryItem.WarehouseId)
|
||||
{
|
||||
inventoryItem.WarehouseId = request.WarehouseId.Value;
|
||||
}
|
||||
|
||||
if (request.Quantity.HasValue)
|
||||
{
|
||||
inventoryItem.Quantity = request.Quantity.Value;
|
||||
}
|
||||
|
||||
if (request.MinQuantity.HasValue)
|
||||
{
|
||||
inventoryItem.LowStockThreshold = request.MinQuantity.Value;
|
||||
}
|
||||
|
||||
if (request.MaxQuantity.HasValue)
|
||||
{
|
||||
inventoryItem.MaxStockLevel = request.MaxQuantity.Value;
|
||||
}
|
||||
|
||||
await _repository.UpdateAsync(inventoryItem, cancellationToken);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای آپدیت موجودی
|
||||
/// </summary>
|
||||
public class UpdateInventoryQuantityCommandHandler : IRequestHandler<UpdateInventoryQuantityCommand, bool>
|
||||
{
|
||||
private readonly IInventoryItemRepository _repository;
|
||||
|
||||
public UpdateInventoryQuantityCommandHandler(IInventoryItemRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(UpdateInventoryQuantityCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var inventoryItem = await _repository.GetByIdAsync(request.Id, cancellationToken);
|
||||
if (inventoryItem == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var quantityChange = request.NewQuantity - inventoryItem.Quantity;
|
||||
var movementType = quantityChange >= 0 ? StockMovementType.AdjustmentPlus : StockMovementType.AdjustmentMinus;
|
||||
|
||||
var result = await _repository.UpdateQuantityAsync(
|
||||
request.Id,
|
||||
quantityChange,
|
||||
movementType,
|
||||
note: request.Note,
|
||||
referenceNumber: request.ReferenceNumber,
|
||||
performedByUserId: request.PerformedByUserId,
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای رزرو موجودی
|
||||
/// </summary>
|
||||
public class ReserveInventoryCommandHandler : IRequestHandler<ReserveInventoryCommand, bool>
|
||||
{
|
||||
private readonly IInventoryItemRepository _repository;
|
||||
|
||||
public ReserveInventoryCommandHandler(IInventoryItemRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(ReserveInventoryCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.ReserveQuantityAsync(
|
||||
request.Id,
|
||||
request.Quantity,
|
||||
referenceNumber: request.ReferenceNumber,
|
||||
orderId: request.OrderId,
|
||||
discountOrderId: request.DiscountOrderId,
|
||||
performedByUserId: request.PerformedByUserId,
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای آزاد کردن موجودی رزرو شده
|
||||
/// </summary>
|
||||
public class ReleaseReservedInventoryCommandHandler : IRequestHandler<ReleaseReservedInventoryCommand, bool>
|
||||
{
|
||||
private readonly IInventoryItemRepository _repository;
|
||||
|
||||
public ReleaseReservedInventoryCommandHandler(IInventoryItemRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(ReleaseReservedInventoryCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.ReleaseReservedQuantityAsync(
|
||||
request.Id,
|
||||
request.Quantity,
|
||||
referenceNumber: request.ReferenceNumber,
|
||||
orderId: request.OrderId,
|
||||
discountOrderId: request.DiscountOrderId,
|
||||
performedByUserId: request.PerformedByUserId,
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای کم کردن موجودی
|
||||
/// </summary>
|
||||
public class ReduceInventoryCommandHandler : IRequestHandler<ReduceInventoryCommand, bool>
|
||||
{
|
||||
private readonly IInventoryItemRepository _repository;
|
||||
|
||||
public ReduceInventoryCommandHandler(IInventoryItemRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(ReduceInventoryCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.UpdateQuantityAsync(
|
||||
request.Id,
|
||||
-request.Quantity,
|
||||
StockMovementType.Sale,
|
||||
referenceNumber: request.ReferenceNumber,
|
||||
orderId: request.OrderId,
|
||||
discountOrderId: request.DiscountOrderId,
|
||||
performedByUserId: request.PerformedByUserId,
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای اضافه کردن موجودی
|
||||
/// </summary>
|
||||
public class IncreaseInventoryCommandHandler : IRequestHandler<IncreaseInventoryCommand, bool>
|
||||
{
|
||||
private readonly IInventoryItemRepository _repository;
|
||||
|
||||
public IncreaseInventoryCommandHandler(IInventoryItemRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(IncreaseInventoryCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.UpdateQuantityAsync(
|
||||
request.Id,
|
||||
request.Quantity,
|
||||
StockMovementType.Restock,
|
||||
note: request.Note,
|
||||
referenceNumber: request.ReferenceNumber,
|
||||
performedByUserId: request.PerformedByUserId,
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای حذف آیتم موجودی
|
||||
/// </summary>
|
||||
public class DeleteInventoryItemCommandHandler : IRequestHandler<DeleteInventoryItemCommand, bool>
|
||||
{
|
||||
private readonly IInventoryItemRepository _repository;
|
||||
|
||||
public DeleteInventoryItemCommandHandler(IInventoryItemRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(DeleteInventoryItemCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
await _repository.DeleteAsync(request.Id, cancellationToken);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
using MediatR;
|
||||
using CMSMicroservice.Application.Common.Interfaces.Repositories;
|
||||
using CMSMicroservice.Application.Features.InventoryItems.Queries;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
|
||||
namespace CMSMicroservice.Application.Features.InventoryItems.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت آیتم موجودی به ID
|
||||
/// </summary>
|
||||
public class GetInventoryItemByIdQueryHandler : IRequestHandler<GetInventoryItemByIdQuery, InventoryItem?>
|
||||
{
|
||||
private readonly IInventoryItemRepository _repository;
|
||||
|
||||
public GetInventoryItemByIdQueryHandler(IInventoryItemRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<InventoryItem?> Handle(GetInventoryItemByIdQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetByIdAsync(request.Id, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت آیتم موجودی به Product ID
|
||||
/// </summary>
|
||||
public class GetInventoryItemByProductIdQueryHandler : IRequestHandler<GetInventoryItemByProductIdQuery, InventoryItem?>
|
||||
{
|
||||
private readonly IInventoryItemRepository _repository;
|
||||
|
||||
public GetInventoryItemByProductIdQueryHandler(IInventoryItemRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<InventoryItem?> Handle(GetInventoryItemByProductIdQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetByProductIdAsync(request.ProductId, request.WarehouseId ?? 1, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت آیتم موجودی به DiscountProduct ID
|
||||
/// </summary>
|
||||
public class GetInventoryItemByDiscountProductIdQueryHandler : IRequestHandler<GetInventoryItemByDiscountProductIdQuery, InventoryItem?>
|
||||
{
|
||||
private readonly IInventoryItemRepository _repository;
|
||||
|
||||
public GetInventoryItemByDiscountProductIdQueryHandler(IInventoryItemRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<InventoryItem?> Handle(GetInventoryItemByDiscountProductIdQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetByDiscountProductIdAsync(request.DiscountProductId, request.WarehouseId ?? 1, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای جستجوی آیتم های موجودی
|
||||
/// </summary>
|
||||
public class SearchInventoryItemsQueryHandler : IRequestHandler<SearchInventoryItemsQuery, List<InventoryItem>>
|
||||
{
|
||||
private readonly IInventoryItemRepository _repository;
|
||||
|
||||
public SearchInventoryItemsQueryHandler(IInventoryItemRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<List<InventoryItem>> Handle(SearchInventoryItemsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.SearchAsync(
|
||||
searchTerm: request.ProductName,
|
||||
warehouseId: request.WarehouseId,
|
||||
skip: request.Skip,
|
||||
take: request.Take,
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای شمارش آیتم های موجودی
|
||||
/// </summary>
|
||||
public class GetInventoryItemsCountQueryHandler : IRequestHandler<GetInventoryItemsCountQuery, int>
|
||||
{
|
||||
private readonly IInventoryItemRepository _repository;
|
||||
|
||||
public GetInventoryItemsCountQueryHandler(IInventoryItemRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<int> Handle(GetInventoryItemsCountQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.CountAsync(
|
||||
searchTerm: request.ProductName,
|
||||
warehouseId: request.WarehouseId,
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت آیتم های کم موجود
|
||||
/// </summary>
|
||||
public class GetLowStockItemsQueryHandler : IRequestHandler<GetLowStockItemsQuery, List<InventoryItem>>
|
||||
{
|
||||
private readonly IInventoryItemRepository _repository;
|
||||
|
||||
public GetLowStockItemsQueryHandler(IInventoryItemRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<List<InventoryItem>> Handle(GetLowStockItemsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetLowStockItemsAsync(warehouseId: request.WarehouseId ?? 1, cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت آیتم های ناموجود
|
||||
/// </summary>
|
||||
public class GetOutOfStockItemsQueryHandler : IRequestHandler<GetOutOfStockItemsQuery, List<InventoryItem>>
|
||||
{
|
||||
private readonly IInventoryItemRepository _repository;
|
||||
|
||||
public GetOutOfStockItemsQueryHandler(IInventoryItemRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<List<InventoryItem>> Handle(GetOutOfStockItemsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetOutOfStockItemsAsync(warehouseId: request.WarehouseId ?? 1, cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای چک کردن دسترسی موجودی
|
||||
/// </summary>
|
||||
public class CheckInventoryAvailabilityQueryHandler : IRequestHandler<CheckInventoryAvailabilityQuery, bool>
|
||||
{
|
||||
private readonly IInventoryItemRepository _repository;
|
||||
|
||||
public CheckInventoryAvailabilityQueryHandler(IInventoryItemRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(CheckInventoryAvailabilityQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var item = await _repository.GetByIdAsync(request.InventoryItemId, cancellationToken);
|
||||
if (item == null) return false;
|
||||
return item.AvailableQuantity >= request.RequiredQuantity;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت موجودی قابل دسترس
|
||||
/// </summary>
|
||||
public class GetAvailableQuantityQueryHandler : IRequestHandler<GetAvailableQuantityQuery, int>
|
||||
{
|
||||
private readonly IInventoryItemRepository _repository;
|
||||
|
||||
public GetAvailableQuantityQueryHandler(IInventoryItemRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<int> Handle(GetAvailableQuantityQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var item = await _repository.GetByIdAsync(request.InventoryItemId, cancellationToken);
|
||||
return item?.AvailableQuantity ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت آیتم های موجودی در انبار
|
||||
/// </summary>
|
||||
public class GetWarehouseInventoryItemsQueryHandler : IRequestHandler<GetWarehouseInventoryItemsQuery, List<InventoryItem>>
|
||||
{
|
||||
private readonly IInventoryItemRepository _repository;
|
||||
|
||||
public GetWarehouseInventoryItemsQueryHandler(IInventoryItemRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<List<InventoryItem>> Handle(GetWarehouseInventoryItemsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetByWarehouseIdAsync(request.WarehouseId, cancellationToken);
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
using MediatR;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
|
||||
namespace CMSMicroservice.Application.Features.InventoryItems.Queries;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت آیتم موجودی به ID
|
||||
/// </summary>
|
||||
public record GetInventoryItemByIdQuery(long Id) : IRequest<InventoryItem?>;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت آیتم موجودی به Product ID
|
||||
/// </summary>
|
||||
public record GetInventoryItemByProductIdQuery(long ProductId, long? WarehouseId = null) : IRequest<InventoryItem?>;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت آیتم موجودی به DiscountProduct ID
|
||||
/// </summary>
|
||||
public record GetInventoryItemByDiscountProductIdQuery(long DiscountProductId, long? WarehouseId = null) : IRequest<InventoryItem?>;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای جستجوی آیتم های موجودی
|
||||
/// </summary>
|
||||
public record SearchInventoryItemsQuery : IRequest<List<InventoryItem>>
|
||||
{
|
||||
public long? WarehouseId { get; init; }
|
||||
public long? ProductId { get; init; }
|
||||
public long? DiscountProductId { get; init; }
|
||||
public string? ProductName { get; init; }
|
||||
public bool? IsActive { get; init; }
|
||||
public bool? IsLowStock { get; init; }
|
||||
public bool? IsOutOfStock { get; init; }
|
||||
public int Skip { get; init; } = 0;
|
||||
public int Take { get; init; } = 100;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت تعداد آیتم های موجودی
|
||||
/// </summary>
|
||||
public record GetInventoryItemsCountQuery : IRequest<int>
|
||||
{
|
||||
public long? WarehouseId { get; init; }
|
||||
public long? ProductId { get; init; }
|
||||
public long? DiscountProductId { get; init; }
|
||||
public string? ProductName { get; init; }
|
||||
public bool? IsActive { get; init; }
|
||||
public bool? IsLowStock { get; init; }
|
||||
public bool? IsOutOfStock { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت آیتم های کم موجود
|
||||
/// </summary>
|
||||
public record GetLowStockItemsQuery : IRequest<List<InventoryItem>>
|
||||
{
|
||||
public long? WarehouseId { get; init; }
|
||||
public int Count { get; init; } = 50;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت آیتم های ناموجود
|
||||
/// </summary>
|
||||
public record GetOutOfStockItemsQuery : IRequest<List<InventoryItem>>
|
||||
{
|
||||
public long? WarehouseId { get; init; }
|
||||
public int Count { get; init; } = 50;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query برای چک کردن دسترسی موجودی
|
||||
/// </summary>
|
||||
public record CheckInventoryAvailabilityQuery : IRequest<bool>
|
||||
{
|
||||
public long InventoryItemId { get; init; }
|
||||
public int RequiredQuantity { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت موجودی قابل دسترس
|
||||
/// </summary>
|
||||
public record GetAvailableQuantityQuery(long InventoryItemId) : IRequest<int>;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت آیتم های موجودی در انبار
|
||||
/// </summary>
|
||||
public record GetWarehouseInventoryItemsQuery : IRequest<List<InventoryItem>>
|
||||
{
|
||||
public long WarehouseId { get; init; }
|
||||
public bool? IsActive { get; init; }
|
||||
public bool? IsLowStock { get; init; }
|
||||
public int Skip { get; init; } = 0;
|
||||
public int Take { get; init; } = 100;
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
using MediatR;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
|
||||
namespace CMSMicroservice.Application.Features.StockMovements.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Command برای ثبت حرکت موجودی
|
||||
/// </summary>
|
||||
public record CreateStockMovementCommand : IRequest<long>
|
||||
{
|
||||
public long InventoryItemId { get; init; }
|
||||
public StockMovementType MovementType { get; init; }
|
||||
public int Quantity { get; init; }
|
||||
public long? OrderId { get; init; }
|
||||
public long? DiscountOrderId { get; init; }
|
||||
public string? ReferenceNumber { get; init; }
|
||||
public string? Note { get; init; }
|
||||
public long? PerformedByUserId { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command برای ثبت چندین حرکت موجودی به صورت bulk
|
||||
/// </summary>
|
||||
public record BulkCreateStockMovementCommand : IRequest<List<long>>
|
||||
{
|
||||
public List<StockMovementItem> Movements { get; init; } = new();
|
||||
|
||||
public record StockMovementItem
|
||||
{
|
||||
public long InventoryItemId { get; init; }
|
||||
public StockMovementType MovementType { get; init; }
|
||||
public int Quantity { get; init; }
|
||||
public long? OrderId { get; init; }
|
||||
public long? DiscountOrderId { get; init; }
|
||||
public string? ReferenceNumber { get; init; }
|
||||
public string? Note { get; init; }
|
||||
public long? PerformedByUserId { get; init; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command برای حذف حرکت موجودی
|
||||
/// </summary>
|
||||
public record DeleteStockMovementCommand(long Id) : IRequest<bool>;
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
using MediatR;
|
||||
using CMSMicroservice.Application.Common.Interfaces.Repositories;
|
||||
using CMSMicroservice.Application.Features.StockMovements.Commands;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
|
||||
namespace CMSMicroservice.Application.Features.StockMovements.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای ثبت حرکت موجودی
|
||||
/// </summary>
|
||||
public class CreateStockMovementCommandHandler : IRequestHandler<CreateStockMovementCommand, long>
|
||||
{
|
||||
private readonly IStockMovementRepository _repository;
|
||||
private readonly IInventoryItemRepository _inventoryRepository;
|
||||
|
||||
public CreateStockMovementCommandHandler(
|
||||
IStockMovementRepository repository,
|
||||
IInventoryItemRepository inventoryRepository)
|
||||
{
|
||||
_repository = repository;
|
||||
_inventoryRepository = inventoryRepository;
|
||||
}
|
||||
|
||||
public async Task<long> Handle(CreateStockMovementCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// بررسی وجود آیتم موجودی
|
||||
var inventoryItem = await _inventoryRepository.GetByIdAsync(request.InventoryItemId, cancellationToken);
|
||||
if (inventoryItem == null)
|
||||
{
|
||||
throw new ArgumentException("Inventory item not found");
|
||||
}
|
||||
|
||||
var stockMovement = new StockMovement
|
||||
{
|
||||
InventoryItemId = request.InventoryItemId,
|
||||
MovementType = request.MovementType,
|
||||
Quantity = request.Quantity,
|
||||
OrderId = request.OrderId,
|
||||
DiscountOrderId = request.DiscountOrderId,
|
||||
ReferenceNumber = request.ReferenceNumber,
|
||||
Note = request.Note,
|
||||
PerformedByUserId = request.PerformedByUserId
|
||||
};
|
||||
|
||||
var createdMovement = await _repository.AddAsync(stockMovement, cancellationToken);
|
||||
return createdMovement.Id;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای ثبت چندین حرکت موجودی bulk
|
||||
/// </summary>
|
||||
public class BulkCreateStockMovementCommandHandler : IRequestHandler<BulkCreateStockMovementCommand, List<long>>
|
||||
{
|
||||
private readonly IStockMovementRepository _repository;
|
||||
private readonly IInventoryItemRepository _inventoryRepository;
|
||||
|
||||
public BulkCreateStockMovementCommandHandler(
|
||||
IStockMovementRepository repository,
|
||||
IInventoryItemRepository inventoryRepository)
|
||||
{
|
||||
_repository = repository;
|
||||
_inventoryRepository = inventoryRepository;
|
||||
}
|
||||
|
||||
public async Task<List<long>> Handle(BulkCreateStockMovementCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var stockMovements = new List<StockMovement>();
|
||||
|
||||
// بررسی وجود تمام آیتم های موجودی
|
||||
var inventoryItemIds = request.Movements.Select(m => m.InventoryItemId).Distinct().ToList();
|
||||
foreach (var inventoryItemId in inventoryItemIds)
|
||||
{
|
||||
var item = await _inventoryRepository.GetByIdAsync(inventoryItemId, cancellationToken);
|
||||
if (item == null)
|
||||
{
|
||||
throw new ArgumentException($"Inventory item with ID {inventoryItemId} not found");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var movement in request.Movements)
|
||||
{
|
||||
var stockMovement = new StockMovement
|
||||
{
|
||||
InventoryItemId = movement.InventoryItemId,
|
||||
MovementType = movement.MovementType,
|
||||
Quantity = movement.Quantity,
|
||||
OrderId = movement.OrderId,
|
||||
DiscountOrderId = movement.DiscountOrderId,
|
||||
ReferenceNumber = movement.ReferenceNumber,
|
||||
Note = movement.Note,
|
||||
PerformedByUserId = movement.PerformedByUserId
|
||||
};
|
||||
stockMovements.Add(stockMovement);
|
||||
}
|
||||
|
||||
var createdMovements = await _repository.BulkAddAsync(stockMovements, cancellationToken);
|
||||
return createdMovements.Select(m => m.Id).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای حذف حرکت موجودی
|
||||
/// </summary>
|
||||
public class DeleteStockMovementCommandHandler : IRequestHandler<DeleteStockMovementCommand, bool>
|
||||
{
|
||||
private readonly IStockMovementRepository _repository;
|
||||
|
||||
public DeleteStockMovementCommandHandler(IStockMovementRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(DeleteStockMovementCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
await _repository.DeleteAsync(request.Id, cancellationToken);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
using MediatR;
|
||||
using CMSMicroservice.Application.Common.Interfaces.Repositories;
|
||||
using CMSMicroservice.Application.Features.StockMovements.Queries;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
|
||||
namespace CMSMicroservice.Application.Features.StockMovements.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت حرکت موجودی به ID
|
||||
/// </summary>
|
||||
public class GetStockMovementByIdQueryHandler : IRequestHandler<GetStockMovementByIdQuery, StockMovement?>
|
||||
{
|
||||
private readonly IStockMovementRepository _repository;
|
||||
|
||||
public GetStockMovementByIdQueryHandler(IStockMovementRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<StockMovement?> Handle(GetStockMovementByIdQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetByIdAsync(request.Id, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت تاریخچه حرکت موجودی یک آیتم
|
||||
/// </summary>
|
||||
public class GetInventoryItemMovementHistoryQueryHandler : IRequestHandler<GetInventoryItemMovementHistoryQuery, List<StockMovement>>
|
||||
{
|
||||
private readonly IStockMovementRepository _repository;
|
||||
|
||||
public GetInventoryItemMovementHistoryQueryHandler(IStockMovementRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<List<StockMovement>> Handle(GetInventoryItemMovementHistoryQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetByInventoryItemIdAsync(
|
||||
request.InventoryItemId,
|
||||
request.MovementType,
|
||||
request.FromDate,
|
||||
request.ToDate,
|
||||
request.Skip,
|
||||
request.Take,
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت حرکات موجودی بر اساس سفارش
|
||||
/// </summary>
|
||||
public class GetStockMovementsByOrderQueryHandler : IRequestHandler<GetStockMovementsByOrderQuery, List<StockMovement>>
|
||||
{
|
||||
private readonly IStockMovementRepository _repository;
|
||||
|
||||
public GetStockMovementsByOrderQueryHandler(IStockMovementRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<List<StockMovement>> Handle(GetStockMovementsByOrderQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetByOrderIdAsync(request.OrderId, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت حرکات موجودی بر اساس سفارش تخفیف
|
||||
/// </summary>
|
||||
public class GetStockMovementsByDiscountOrderQueryHandler : IRequestHandler<GetStockMovementsByDiscountOrderQuery, List<StockMovement>>
|
||||
{
|
||||
private readonly IStockMovementRepository _repository;
|
||||
|
||||
public GetStockMovementsByDiscountOrderQueryHandler(IStockMovementRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<List<StockMovement>> Handle(GetStockMovementsByDiscountOrderQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetByDiscountOrderIdAsync(request.DiscountOrderId, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت حرکات موجودی بر اساس شماره مرجع
|
||||
/// </summary>
|
||||
public class GetStockMovementsByReferenceQueryHandler : IRequestHandler<GetStockMovementsByReferenceQuery, List<StockMovement>>
|
||||
{
|
||||
private readonly IStockMovementRepository _repository;
|
||||
|
||||
public GetStockMovementsByReferenceQueryHandler(IStockMovementRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<List<StockMovement>> Handle(GetStockMovementsByReferenceQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetByReferenceNumberAsync(request.ReferenceNumber, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت حرکات موجودی بر اساس نوع
|
||||
/// </summary>
|
||||
public class GetStockMovementsByTypeQueryHandler : IRequestHandler<GetStockMovementsByTypeQuery, List<StockMovement>>
|
||||
{
|
||||
private readonly IStockMovementRepository _repository;
|
||||
|
||||
public GetStockMovementsByTypeQueryHandler(IStockMovementRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<List<StockMovement>> Handle(GetStockMovementsByTypeQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetByMovementTypeAsync(
|
||||
request.MovementType,
|
||||
request.FromDate,
|
||||
request.ToDate,
|
||||
request.Skip,
|
||||
request.Take,
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت آخرین حرکات موجودی
|
||||
/// </summary>
|
||||
public class GetRecentStockMovementsQueryHandler : IRequestHandler<GetRecentStockMovementsQuery, List<StockMovement>>
|
||||
{
|
||||
private readonly IStockMovementRepository _repository;
|
||||
|
||||
public GetRecentStockMovementsQueryHandler(IStockMovementRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<List<StockMovement>> Handle(GetRecentStockMovementsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetRecentMovementsAsync(request.Count, request.MovementType, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای جستجوی حرکات موجودی
|
||||
/// </summary>
|
||||
public class SearchStockMovementsQueryHandler : IRequestHandler<SearchStockMovementsQuery, List<StockMovement>>
|
||||
{
|
||||
private readonly IStockMovementRepository _repository;
|
||||
|
||||
public SearchStockMovementsQueryHandler(IStockMovementRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<List<StockMovement>> Handle(SearchStockMovementsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.SearchAsync(
|
||||
request.InventoryItemId,
|
||||
request.MovementType,
|
||||
request.FromDate,
|
||||
request.ToDate,
|
||||
request.ReferenceNumber,
|
||||
request.OrderId,
|
||||
request.DiscountOrderId,
|
||||
request.PerformedByUserId,
|
||||
request.Skip,
|
||||
request.Take,
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای شمارش حرکات موجودی
|
||||
/// </summary>
|
||||
public class GetStockMovementsCountQueryHandler : IRequestHandler<GetStockMovementsCountQuery, int>
|
||||
{
|
||||
private readonly IStockMovementRepository _repository;
|
||||
|
||||
public GetStockMovementsCountQueryHandler(IStockMovementRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<int> Handle(GetStockMovementsCountQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.CountAsync(
|
||||
request.InventoryItemId,
|
||||
request.MovementType,
|
||||
request.FromDate,
|
||||
request.ToDate,
|
||||
request.ReferenceNumber,
|
||||
request.OrderId,
|
||||
request.DiscountOrderId,
|
||||
request.PerformedByUserId,
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت خلاصه حرکات موجودی
|
||||
/// </summary>
|
||||
public class GetMovementSummaryQueryHandler : IRequestHandler<GetMovementSummaryQuery, Dictionary<StockMovementType, int>>
|
||||
{
|
||||
private readonly IStockMovementRepository _repository;
|
||||
|
||||
public GetMovementSummaryQueryHandler(IStockMovementRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<Dictionary<StockMovementType, int>> Handle(GetMovementSummaryQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetMovementSummaryAsync(
|
||||
request.FromDate,
|
||||
request.ToDate,
|
||||
request.InventoryItemId,
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت حجم حرکات روزانه
|
||||
/// </summary>
|
||||
public class GetDailyMovementVolumeQueryHandler : IRequestHandler<GetDailyMovementVolumeQuery, List<(DateTime Date, int InboundQuantity, int OutboundQuantity)>>
|
||||
{
|
||||
private readonly IStockMovementRepository _repository;
|
||||
|
||||
public GetDailyMovementVolumeQueryHandler(IStockMovementRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<List<(DateTime Date, int InboundQuantity, int OutboundQuantity)>> Handle(GetDailyMovementVolumeQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetDailyMovementVolumeAsync(
|
||||
request.FromDate,
|
||||
request.ToDate,
|
||||
request.InventoryItemId,
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت محصولات پر حرکت
|
||||
/// </summary>
|
||||
public class GetTopMovingProductsQueryHandler : IRequestHandler<GetTopMovingProductsQuery, List<(long InventoryItemId, string ProductName, int MovementCount, int TotalQuantityChange)>>
|
||||
{
|
||||
private readonly IStockMovementRepository _repository;
|
||||
|
||||
public GetTopMovingProductsQueryHandler(IStockMovementRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<List<(long InventoryItemId, string ProductName, int MovementCount, int TotalQuantityChange)>> Handle(GetTopMovingProductsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetTopMovingProductsAsync(
|
||||
request.FromDate,
|
||||
request.ToDate,
|
||||
request.Count,
|
||||
request.MovementType,
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
using MediatR;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
|
||||
namespace CMSMicroservice.Application.Features.StockMovements.Queries;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت حرکت موجودی به ID
|
||||
/// </summary>
|
||||
public record GetStockMovementByIdQuery(long Id) : IRequest<StockMovement?>;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت تاریخچه حرکت موجودی یک آیتم
|
||||
/// </summary>
|
||||
public record GetInventoryItemMovementHistoryQuery : IRequest<List<StockMovement>>
|
||||
{
|
||||
public long InventoryItemId { get; init; }
|
||||
public StockMovementType? MovementType { get; init; }
|
||||
public DateTime? FromDate { get; init; }
|
||||
public DateTime? ToDate { get; init; }
|
||||
public int Skip { get; init; } = 0;
|
||||
public int Take { get; init; } = 100;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت حرکات موجودی بر اساس سفارش
|
||||
/// </summary>
|
||||
public record GetStockMovementsByOrderQuery(long OrderId) : IRequest<List<StockMovement>>;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت حرکات موجودی بر اساس سفارش تخفیف
|
||||
/// </summary>
|
||||
public record GetStockMovementsByDiscountOrderQuery(long DiscountOrderId) : IRequest<List<StockMovement>>;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت حرکات موجودی بر اساس شماره مرجع
|
||||
/// </summary>
|
||||
public record GetStockMovementsByReferenceQuery(string ReferenceNumber) : IRequest<List<StockMovement>>;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت حرکات موجودی بر اساس نوع
|
||||
/// </summary>
|
||||
public record GetStockMovementsByTypeQuery : IRequest<List<StockMovement>>
|
||||
{
|
||||
public StockMovementType MovementType { get; init; }
|
||||
public DateTime? FromDate { get; init; }
|
||||
public DateTime? ToDate { get; init; }
|
||||
public int Skip { get; init; } = 0;
|
||||
public int Take { get; init; } = 100;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت آخرین حرکات موجودی
|
||||
/// </summary>
|
||||
public record GetRecentStockMovementsQuery : IRequest<List<StockMovement>>
|
||||
{
|
||||
public int Count { get; init; } = 50;
|
||||
public StockMovementType? MovementType { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query برای جستجوی حرکات موجودی
|
||||
/// </summary>
|
||||
public record SearchStockMovementsQuery : IRequest<List<StockMovement>>
|
||||
{
|
||||
public long? InventoryItemId { get; init; }
|
||||
public StockMovementType? MovementType { get; init; }
|
||||
public DateTime? FromDate { get; init; }
|
||||
public DateTime? ToDate { get; init; }
|
||||
public string? ReferenceNumber { get; init; }
|
||||
public long? OrderId { get; init; }
|
||||
public long? DiscountOrderId { get; init; }
|
||||
public long? PerformedByUserId { get; init; }
|
||||
public int Skip { get; init; } = 0;
|
||||
public int Take { get; init; } = 100;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query برای شمارش حرکات موجودی
|
||||
/// </summary>
|
||||
public record GetStockMovementsCountQuery : IRequest<int>
|
||||
{
|
||||
public long? InventoryItemId { get; init; }
|
||||
public StockMovementType? MovementType { get; init; }
|
||||
public DateTime? FromDate { get; init; }
|
||||
public DateTime? ToDate { get; init; }
|
||||
public string? ReferenceNumber { get; init; }
|
||||
public long? OrderId { get; init; }
|
||||
public long? DiscountOrderId { get; init; }
|
||||
public long? PerformedByUserId { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت خلاصه حرکات موجودی
|
||||
/// </summary>
|
||||
public record GetMovementSummaryQuery : IRequest<Dictionary<StockMovementType, int>>
|
||||
{
|
||||
public DateTime FromDate { get; init; }
|
||||
public DateTime ToDate { get; init; }
|
||||
public long? InventoryItemId { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت حجم حرکات روزانه
|
||||
/// </summary>
|
||||
public record GetDailyMovementVolumeQuery : IRequest<List<(DateTime Date, int InboundQuantity, int OutboundQuantity)>>
|
||||
{
|
||||
public DateTime FromDate { get; init; }
|
||||
public DateTime ToDate { get; init; }
|
||||
public long? InventoryItemId { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت محصولات پر حرکت
|
||||
/// </summary>
|
||||
public record GetTopMovingProductsQuery : IRequest<List<(long InventoryItemId, string ProductName, int MovementCount, int TotalQuantityChange)>>
|
||||
{
|
||||
public DateTime FromDate { get; init; }
|
||||
public DateTime ToDate { get; init; }
|
||||
public int Count { get; init; } = 10;
|
||||
public StockMovementType? MovementType { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.Features.Warehouses.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Command برای ایجاد انبار جدید
|
||||
/// </summary>
|
||||
public record CreateWarehouseCommand : IRequest<long>
|
||||
{
|
||||
public string Name { get; init; } = string.Empty;
|
||||
public string Code { get; init; } = string.Empty;
|
||||
public string? Description { get; init; }
|
||||
public string? Address { get; init; }
|
||||
public string? CityName { get; init; }
|
||||
public bool IsActive { get; init; } = true;
|
||||
public bool IsDefault { get; init; } = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command برای آپدیت انبار
|
||||
/// </summary>
|
||||
public record UpdateWarehouseCommand : IRequest<bool>
|
||||
{
|
||||
public long Id { get; init; }
|
||||
public string? Name { get; init; }
|
||||
public string? Code { get; init; }
|
||||
public string? Description { get; init; }
|
||||
public string? Address { get; init; }
|
||||
public string? CityName { get; init; }
|
||||
public bool? IsActive { get; init; }
|
||||
public bool? IsDefault { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command برای حذف انبار
|
||||
/// </summary>
|
||||
public record DeleteWarehouseCommand(long Id) : IRequest<bool>;
|
||||
|
||||
/// <summary>
|
||||
/// Command برای تعیین انبار پیشفرض
|
||||
/// </summary>
|
||||
public record SetDefaultWarehouseCommand(long Id) : IRequest<bool>;
|
||||
|
||||
/// <summary>
|
||||
/// Command برای فعال/غیرفعال کردن انبار
|
||||
/// </summary>
|
||||
public record ActivateWarehouseCommand(long Id, bool IsActive) : IRequest<bool>;
|
||||
|
||||
/// <summary>
|
||||
/// Command برای ایجاد چندین انبار به صورت bulk
|
||||
/// </summary>
|
||||
public record BulkCreateWarehousesCommand : IRequest<List<long>>
|
||||
{
|
||||
public List<WarehouseItem> Warehouses { get; init; } = new();
|
||||
|
||||
public record WarehouseItem
|
||||
{
|
||||
public string Name { get; init; } = string.Empty;
|
||||
public string Code { get; init; } = string.Empty;
|
||||
public string? Description { get; init; }
|
||||
public string? Address { get; init; }
|
||||
public string? CityName { get; init; }
|
||||
public bool IsActive { get; init; } = true;
|
||||
public bool IsDefault { get; init; } = false;
|
||||
}
|
||||
}
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
using MediatR;
|
||||
using CMSMicroservice.Application.Common.Interfaces.Repositories;
|
||||
using CMSMicroservice.Application.Features.Warehouses.Commands;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
|
||||
namespace CMSMicroservice.Application.Features.Warehouses.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای ایجاد انبار جدید
|
||||
/// </summary>
|
||||
public class CreateWarehouseCommandHandler : IRequestHandler<CreateWarehouseCommand, long>
|
||||
{
|
||||
private readonly IWarehouseRepository _repository;
|
||||
|
||||
public CreateWarehouseCommandHandler(IWarehouseRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<long> Handle(CreateWarehouseCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// بررسی تکراری نبودن کد
|
||||
var codeExists = await _repository.ExistsByCodeAsync(request.Code, cancellationToken: cancellationToken);
|
||||
if (codeExists)
|
||||
{
|
||||
throw new InvalidOperationException($"Warehouse with code '{request.Code}' already exists");
|
||||
}
|
||||
|
||||
var warehouse = new Warehouse
|
||||
{
|
||||
Name = request.Name,
|
||||
Code = request.Code,
|
||||
Address = request.Address,
|
||||
IsActive = request.IsActive,
|
||||
IsDefault = request.IsDefault
|
||||
};
|
||||
|
||||
var createdWarehouse = await _repository.AddAsync(warehouse, cancellationToken);
|
||||
return createdWarehouse.Id;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای آپدیت انبار
|
||||
/// </summary>
|
||||
public class UpdateWarehouseCommandHandler : IRequestHandler<UpdateWarehouseCommand, bool>
|
||||
{
|
||||
private readonly IWarehouseRepository _repository;
|
||||
|
||||
public UpdateWarehouseCommandHandler(IWarehouseRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(UpdateWarehouseCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var warehouse = await _repository.GetByIdAsync(request.Id, cancellationToken);
|
||||
if (warehouse == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// بررسی تکراری نبودن کد جدید
|
||||
if (!string.IsNullOrEmpty(request.Code) && request.Code != warehouse.Code)
|
||||
{
|
||||
var codeExists = await _repository.ExistsByCodeAsync(request.Code, request.Id, cancellationToken);
|
||||
if (codeExists)
|
||||
{
|
||||
throw new InvalidOperationException($"Warehouse with code '{request.Code}' already exists");
|
||||
}
|
||||
warehouse.Code = request.Code;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(request.Name))
|
||||
{
|
||||
warehouse.Name = request.Name;
|
||||
}
|
||||
|
||||
if (request.Address != null)
|
||||
{
|
||||
warehouse.Address = request.Address;
|
||||
}
|
||||
|
||||
if (request.IsActive.HasValue)
|
||||
{
|
||||
warehouse.IsActive = request.IsActive.Value;
|
||||
}
|
||||
|
||||
if (request.IsDefault.HasValue)
|
||||
{
|
||||
warehouse.IsDefault = request.IsDefault.Value;
|
||||
}
|
||||
|
||||
await _repository.UpdateAsync(warehouse, cancellationToken);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای حذف انبار
|
||||
/// </summary>
|
||||
public class DeleteWarehouseCommandHandler : IRequestHandler<DeleteWarehouseCommand, bool>
|
||||
{
|
||||
private readonly IWarehouseRepository _repository;
|
||||
|
||||
public DeleteWarehouseCommandHandler(IWarehouseRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(DeleteWarehouseCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _repository.DeleteAsync(request.Id, cancellationToken);
|
||||
return true;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// انبار دارای موجودی است
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای تعیین انبار پیشفرض
|
||||
/// </summary>
|
||||
public class SetDefaultWarehouseCommandHandler : IRequestHandler<SetDefaultWarehouseCommand, bool>
|
||||
{
|
||||
private readonly IWarehouseRepository _repository;
|
||||
|
||||
public SetDefaultWarehouseCommandHandler(IWarehouseRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(SetDefaultWarehouseCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
await _repository.SetAsDefaultAsync(request.Id, cancellationToken);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای فعال/غیرفعال کردن انبار
|
||||
/// </summary>
|
||||
public class ActivateWarehouseCommandHandler : IRequestHandler<ActivateWarehouseCommand, bool>
|
||||
{
|
||||
private readonly IWarehouseRepository _repository;
|
||||
|
||||
public ActivateWarehouseCommandHandler(IWarehouseRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(ActivateWarehouseCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
await _repository.SetActiveStatusAsync(request.Id, request.IsActive, cancellationToken);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای ایجاد چندین انبار bulk
|
||||
/// </summary>
|
||||
public class BulkCreateWarehousesCommandHandler : IRequestHandler<BulkCreateWarehousesCommand, List<long>>
|
||||
{
|
||||
private readonly IWarehouseRepository _repository;
|
||||
|
||||
public BulkCreateWarehousesCommandHandler(IWarehouseRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<List<long>> Handle(BulkCreateWarehousesCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var ids = new List<long>();
|
||||
|
||||
// بررسی تکراری نبودن کدها
|
||||
var codes = request.Warehouses.Select(w => w.Code).ToList();
|
||||
foreach (var code in codes.Distinct())
|
||||
{
|
||||
var codeExists = await _repository.ExistsByCodeAsync(code, cancellationToken: cancellationToken);
|
||||
if (codeExists)
|
||||
{
|
||||
throw new InvalidOperationException($"Warehouse with code '{code}' already exists");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var warehouseItem in request.Warehouses)
|
||||
{
|
||||
var warehouse = new Warehouse
|
||||
{
|
||||
Name = warehouseItem.Name,
|
||||
Code = warehouseItem.Code,
|
||||
Address = warehouseItem.Address,
|
||||
IsActive = warehouseItem.IsActive,
|
||||
IsDefault = warehouseItem.IsDefault
|
||||
};
|
||||
|
||||
var created = await _repository.AddAsync(warehouse, cancellationToken);
|
||||
ids.Add(created.Id);
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
using MediatR;
|
||||
using CMSMicroservice.Application.Common.Interfaces.Repositories;
|
||||
using CMSMicroservice.Application.Features.Warehouses.Queries;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
|
||||
namespace CMSMicroservice.Application.Features.Warehouses.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت انبار به ID
|
||||
/// </summary>
|
||||
public class GetWarehouseByIdQueryHandler : IRequestHandler<GetWarehouseByIdQuery, Warehouse?>
|
||||
{
|
||||
private readonly IWarehouseRepository _repository;
|
||||
|
||||
public GetWarehouseByIdQueryHandler(IWarehouseRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<Warehouse?> Handle(GetWarehouseByIdQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetByIdAsync(request.Id, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت انبار به کد
|
||||
/// </summary>
|
||||
public class GetWarehouseByCodeQueryHandler : IRequestHandler<GetWarehouseByCodeQuery, Warehouse?>
|
||||
{
|
||||
private readonly IWarehouseRepository _repository;
|
||||
|
||||
public GetWarehouseByCodeQueryHandler(IWarehouseRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<Warehouse?> Handle(GetWarehouseByCodeQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetByCodeAsync(request.Code, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت انبار پیشفرض
|
||||
/// </summary>
|
||||
public class GetDefaultWarehouseQueryHandler : IRequestHandler<GetDefaultWarehouseQuery, Warehouse?>
|
||||
{
|
||||
private readonly IWarehouseRepository _repository;
|
||||
|
||||
public GetDefaultWarehouseQueryHandler(IWarehouseRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<Warehouse?> Handle(GetDefaultWarehouseQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetDefaultWarehouseAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت انبارهای فعال
|
||||
/// </summary>
|
||||
public class GetActiveWarehousesQueryHandler : IRequestHandler<GetActiveWarehousesQuery, List<Warehouse>>
|
||||
{
|
||||
private readonly IWarehouseRepository _repository;
|
||||
|
||||
public GetActiveWarehousesQueryHandler(IWarehouseRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<List<Warehouse>> Handle(GetActiveWarehousesQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetActiveWarehousesAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت تمام انبارها
|
||||
/// </summary>
|
||||
public class GetAllWarehousesQueryHandler : IRequestHandler<GetAllWarehousesQuery, List<Warehouse>>
|
||||
{
|
||||
private readonly IWarehouseRepository _repository;
|
||||
|
||||
public GetAllWarehousesQueryHandler(IWarehouseRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<List<Warehouse>> Handle(GetAllWarehousesQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.GetAllAsync(includeInactive: true, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای جستجوی انبارها
|
||||
/// </summary>
|
||||
public class SearchWarehousesQueryHandler : IRequestHandler<SearchWarehousesQuery, List<Warehouse>>
|
||||
{
|
||||
private readonly IWarehouseRepository _repository;
|
||||
|
||||
public SearchWarehousesQueryHandler(IWarehouseRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<List<Warehouse>> Handle(SearchWarehousesQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.SearchAsync(
|
||||
request.SearchTerm,
|
||||
request.IsActive,
|
||||
request.Skip,
|
||||
request.Take,
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای شمارش انبارها
|
||||
/// </summary>
|
||||
public class GetWarehousesCountQueryHandler : IRequestHandler<GetWarehousesCountQuery, int>
|
||||
{
|
||||
private readonly IWarehouseRepository _repository;
|
||||
|
||||
public GetWarehousesCountQueryHandler(IWarehouseRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<int> Handle(GetWarehousesCountQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.CountAsync(
|
||||
request.SearchTerm,
|
||||
request.IsActive,
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای بررسی وجود انبار
|
||||
/// </summary>
|
||||
public class WarehouseExistsQueryHandler : IRequestHandler<WarehouseExistsQuery, bool>
|
||||
{
|
||||
private readonly IWarehouseRepository _repository;
|
||||
|
||||
public WarehouseExistsQueryHandler(IWarehouseRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(WarehouseExistsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var warehouse = await _repository.GetByIdAsync(request.Id, cancellationToken);
|
||||
return warehouse != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای بررسی وجود انبار با کد
|
||||
/// </summary>
|
||||
public class WarehouseExistsByCodeQueryHandler : IRequestHandler<WarehouseExistsByCodeQuery, bool>
|
||||
{
|
||||
private readonly IWarehouseRepository _repository;
|
||||
|
||||
public WarehouseExistsByCodeQueryHandler(IWarehouseRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(WarehouseExistsByCodeQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _repository.ExistsByCodeAsync(request.Code, request.ExcludeId, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت آمار انبار
|
||||
/// </summary>
|
||||
public class GetWarehouseStatisticsQueryHandler : IRequestHandler<GetWarehouseStatisticsQuery, Dictionary<string, object>>
|
||||
{
|
||||
private readonly IWarehouseRepository _repository;
|
||||
|
||||
public GetWarehouseStatisticsQueryHandler(IWarehouseRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, object>> Handle(GetWarehouseStatisticsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var stats = await _repository.GetWarehouseStatisticsAsync(request.Id, cancellationToken);
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
["TotalProducts"] = stats.TotalProducts,
|
||||
["LowStockProducts"] = stats.LowStockProducts,
|
||||
["OutOfStockProducts"] = stats.OutOfStockProducts,
|
||||
["TotalValue"] = stats.TotalValue
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using MediatR;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
|
||||
namespace CMSMicroservice.Application.Features.Warehouses.Queries;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت انبار به ID
|
||||
/// </summary>
|
||||
public record GetWarehouseByIdQuery(long Id) : IRequest<Warehouse?>;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت انبار به کد
|
||||
/// </summary>
|
||||
public record GetWarehouseByCodeQuery(string Code) : IRequest<Warehouse?>;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت انبار پیشفرض
|
||||
/// </summary>
|
||||
public record GetDefaultWarehouseQuery : IRequest<Warehouse?>;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت انبارهای فعال
|
||||
/// </summary>
|
||||
public record GetActiveWarehousesQuery : IRequest<List<Warehouse>>;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت تمام انبارها
|
||||
/// </summary>
|
||||
public record GetAllWarehousesQuery : IRequest<List<Warehouse>>;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای جستجوی انبارها
|
||||
/// </summary>
|
||||
public record SearchWarehousesQuery : IRequest<List<Warehouse>>
|
||||
{
|
||||
public string? SearchTerm { get; init; }
|
||||
public bool? IsActive { get; init; }
|
||||
public int Skip { get; init; } = 0;
|
||||
public int Take { get; init; } = 100;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query برای شمارش انبارها
|
||||
/// </summary>
|
||||
public record GetWarehousesCountQuery : IRequest<int>
|
||||
{
|
||||
public string? SearchTerm { get; init; }
|
||||
public bool? IsActive { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query برای بررسی وجود انبار
|
||||
/// </summary>
|
||||
public record WarehouseExistsQuery(long Id) : IRequest<bool>;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای بررسی وجود انبار با کد
|
||||
/// </summary>
|
||||
public record WarehouseExistsByCodeQuery : IRequest<bool>
|
||||
{
|
||||
public string Code { get; init; } = string.Empty;
|
||||
public long? ExcludeId { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت آمار انبار
|
||||
/// </summary>
|
||||
public record GetWarehouseStatisticsQuery(long Id) : IRequest<Dictionary<string, object>>;
|
||||
+5
@@ -32,4 +32,9 @@ public class CreateManualPaymentCommand : IRequest<long>
|
||||
/// شماره مرجع یا شماره فیش (اختیاری)
|
||||
/// </summary>
|
||||
public string? ReferenceNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// مسیر تصویر فیش واریزی (اختیاری)
|
||||
/// </summary>
|
||||
public string? ImagePath { get; set; }
|
||||
}
|
||||
|
||||
+86
-19
@@ -1,5 +1,6 @@
|
||||
using CMSMicroservice.Application.Common.Exceptions;
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Common;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using CMSMicroservice.Domain.Entities.Payment;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
@@ -32,13 +33,24 @@ public class CreateManualPaymentCommandHandler : IRequestHandler<CreateManualPay
|
||||
try
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"Creating manual payment for UserId: {UserId}, Amount: {Amount}, Type: {Type}",
|
||||
"Creating manual membership payment for UserId: {UserId}, Type: {Type}",
|
||||
request.UserId,
|
||||
request.Amount,
|
||||
request.Type
|
||||
);
|
||||
|
||||
// 1. بررسی وجود کاربر
|
||||
// 1. بررسی Admin فعلی
|
||||
var currentUserId = _currentUser.UserId;
|
||||
if (string.IsNullOrEmpty(currentUserId))
|
||||
{
|
||||
throw new UnauthorizedAccessException("کاربر احراز هویت نشده است");
|
||||
}
|
||||
|
||||
if (!long.TryParse(currentUserId, out var adminUserId))
|
||||
{
|
||||
throw new UnauthorizedAccessException("شناسه کاربر نامعتبر است");
|
||||
}
|
||||
|
||||
// 2. بررسی وجود کاربر
|
||||
var user = await _context.Users
|
||||
.FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken);
|
||||
|
||||
@@ -48,47 +60,102 @@ public class CreateManualPaymentCommandHandler : IRequestHandler<CreateManualPay
|
||||
throw new NotFoundException(nameof(User), request.UserId);
|
||||
}
|
||||
|
||||
// 2. بررسی Admin فعلی
|
||||
var currentUserId = _currentUser.UserId;
|
||||
if (string.IsNullOrEmpty(currentUserId))
|
||||
// 3. پیدا کردن کیف پول
|
||||
var wallet = await _context.UserWallets
|
||||
.FirstOrDefaultAsync(w => w.UserId == request.UserId, cancellationToken);
|
||||
|
||||
if (wallet == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException("کاربر احراز هویت نشده است");
|
||||
_logger.LogError("Wallet not found for UserId: {UserId}", request.UserId);
|
||||
throw new NotFoundException($"کیف پول کاربر {request.UserId} یافت نشد");
|
||||
}
|
||||
|
||||
if (!long.TryParse(currentUserId, out var requestedById))
|
||||
{
|
||||
throw new UnauthorizedAccessException("شناسه کاربر نامعتبر است");
|
||||
}
|
||||
// 4. محاسبه مبالغ
|
||||
var balanceAmount = SystemConstants.BasePackageAmount; // 56M
|
||||
var discountBalanceAmount = SystemConstants.BasePackageAmount * 2; // 112M
|
||||
var totalAmount = balanceAmount + discountBalanceAmount; // 168M
|
||||
|
||||
// 3. ایجاد ManualPayment
|
||||
// 5. ثبت تراکنش
|
||||
var transaction = new Transaction
|
||||
{
|
||||
Amount = totalAmount,
|
||||
Description = $"عضویت دستی باشگاه مشتریان - {request.Description} - مرجع: {request.ReferenceNumber}",
|
||||
PaymentStatus = PaymentStatus.Success,
|
||||
PaymentDate = DateTime.Now,
|
||||
RefId = request.ReferenceNumber,
|
||||
Type = TransactionType.DepositExternal1
|
||||
};
|
||||
|
||||
_context.Transactions.Add(transaction);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 6. ایجاد ManualPayment با وضعیت Approved (بدون نیاز به تایید دو مرحلهای)
|
||||
var manualPayment = new ManualPayment
|
||||
{
|
||||
UserId = request.UserId,
|
||||
Amount = request.Amount,
|
||||
Amount = totalAmount,
|
||||
Type = request.Type,
|
||||
Description = request.Description,
|
||||
ReferenceNumber = request.ReferenceNumber,
|
||||
Status = ManualPaymentStatus.Pending,
|
||||
RequestedBy = requestedById
|
||||
ImagePath = request.ImagePath,
|
||||
Status = ManualPaymentStatus.Approved,
|
||||
RequestedBy = adminUserId,
|
||||
ApprovedBy = adminUserId,
|
||||
ApprovedAt = DateTime.Now,
|
||||
TransactionId = transaction.Id
|
||||
};
|
||||
|
||||
_context.ManualPayments.Add(manualPayment);
|
||||
|
||||
// 7. اعمال تغییرات بر کیف پول
|
||||
var oldBalance = wallet.Balance;
|
||||
var oldDiscountBalance = wallet.DiscountBalance;
|
||||
|
||||
wallet.Balance += balanceAmount; // +56M
|
||||
wallet.DiscountBalance += discountBalanceAmount; // +112M
|
||||
|
||||
// 8. ثبت لاگ کیف پول
|
||||
var walletLog = new UserWalletChangeLog
|
||||
{
|
||||
WalletId = wallet.Id,
|
||||
CurrentBalance = wallet.Balance,
|
||||
ChangeValue = balanceAmount,
|
||||
CurrentNetworkBalance = wallet.NetworkBalance,
|
||||
ChangeNerworkValue = 0,
|
||||
CurrentDiscountBalance = wallet.DiscountBalance,
|
||||
ChangeDiscountValue = discountBalanceAmount,
|
||||
IsIncrease = true,
|
||||
RefrenceId = transaction.Id
|
||||
};
|
||||
|
||||
await _context.UserWalletChangeLogs.AddAsync(walletLog, cancellationToken);
|
||||
|
||||
// 9. تنظیم روش خرید پکیج
|
||||
user.PackagePurchaseMethod = PackagePurchaseMethod.DirectPurchase;
|
||||
|
||||
// 10. ذخیره همه تغییرات
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Manual payment created successfully. Id: {Id}, UserId: {UserId}, RequestedBy: {RequestedBy}",
|
||||
"Manual membership payment created successfully. " +
|
||||
"ManualPaymentId: {Id}, UserId: {UserId}, TransactionId: {TransactionId}, " +
|
||||
"Balance: {OldBalance} -> {NewBalance}, DiscountBalance: {OldDiscount} -> {NewDiscount}",
|
||||
manualPayment.Id,
|
||||
request.UserId,
|
||||
requestedById
|
||||
transaction.Id,
|
||||
oldBalance,
|
||||
wallet.Balance,
|
||||
oldDiscountBalance,
|
||||
wallet.DiscountBalance
|
||||
);
|
||||
|
||||
return manualPayment.Id;
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception ex) when (ex is not NotFoundException && ex is not UnauthorizedAccessException)
|
||||
{
|
||||
_logger.LogError(
|
||||
ex,
|
||||
"Error creating manual payment for UserId: {UserId}",
|
||||
"Error creating manual membership payment for UserId: {UserId}",
|
||||
request.UserId
|
||||
);
|
||||
throw;
|
||||
|
||||
+3
-42
@@ -118,58 +118,19 @@ public class ProcessManualMembershipPaymentCommandHandler : IRequestHandler<Proc
|
||||
};
|
||||
await _context.UserWalletChangeLogs.AddAsync(balanceLog, cancellationToken);
|
||||
|
||||
|
||||
user.PackagePurchaseMethod = PackagePurchaseMethod.DirectPurchase;
|
||||
// 10. بهروزرسانی ManualPayment با TransactionId
|
||||
manualPayment.TransactionId = transaction.Id;
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 11. پیدا کردن یا ایجاد آدرس پیشفرض کاربر
|
||||
var userAddress = await _context.UserAddresses
|
||||
.Where(a => a.UserId == request.UserId)
|
||||
.OrderByDescending(a => a.IsDefault)
|
||||
.ThenBy(a => a.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (userAddress == null)
|
||||
{
|
||||
userAddress = new UserAddress
|
||||
{
|
||||
UserId = request.UserId,
|
||||
Title = "آدرس پیشفرض",
|
||||
Address = "پرداخت دستی عضویت - آدرس موقت",
|
||||
PostalCode = "0000000000",
|
||||
IsDefault = true,
|
||||
CityId = 1
|
||||
};
|
||||
await _context.UserAddresses.AddAsync(userAddress, cancellationToken);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
// 12. ثبت سفارش
|
||||
var order = new UserOrder
|
||||
{
|
||||
UserId = request.UserId,
|
||||
Amount = request.Amount,
|
||||
TransactionId = transaction.Id,
|
||||
PaymentStatus = PaymentStatus.Success,
|
||||
PaymentDate = DateTime.Now,
|
||||
PaymentMethod = PaymentMethod.Deposit,
|
||||
DeliveryStatus = DeliveryStatus.None,
|
||||
UserAddressId = userAddress.Id,
|
||||
DeliveryDescription = $"پرداخت دستی عضویت - مرجع: {request.ReferenceNumber}"
|
||||
};
|
||||
|
||||
_context.UserOrders.Add(order);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Manual membership payment processed successfully. UserId: {UserId}, Amount: {Amount}, ManualPaymentId: {ManualPaymentId}, TransactionId: {TransactionId}, OrderId: {OrderId}, AdminUserId: {AdminUserId}",
|
||||
request.UserId, request.Amount, manualPayment.Id, transaction.Id, order.Id, adminUserId);
|
||||
"Manual membership payment processed successfully. UserId: {UserId}, Amount: {Amount}, ManualPaymentId: {ManualPaymentId}, TransactionId: {TransactionId}, AdminUserId: {AdminUserId}",
|
||||
request.UserId, request.Amount, manualPayment.Id, transaction.Id, adminUserId);
|
||||
|
||||
return new ProcessManualMembershipPaymentResponseDto
|
||||
{
|
||||
TransactionId = transaction.Id,
|
||||
OrderId = order.Id,
|
||||
NewWalletBalance = wallet.Balance,
|
||||
Message = "پرداخت دستی با موفقیت ثبت شد"
|
||||
};
|
||||
|
||||
+16
-1
@@ -1,13 +1,21 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
using CMSMicroservice.Domain.Events;
|
||||
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.CreateNewProducts;
|
||||
|
||||
public class CreateNewProductsCommandHandler : IRequestHandler<CreateNewProductsCommand, CreateNewProductsResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IInventoryService _inventoryService;
|
||||
|
||||
public CreateNewProductsCommandHandler(IApplicationDbContext context)
|
||||
public CreateNewProductsCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
IInventoryService inventoryService)
|
||||
{
|
||||
_context = context;
|
||||
_inventoryService = inventoryService;
|
||||
}
|
||||
|
||||
public async Task<CreateNewProductsResponseDto> Handle(CreateNewProductsCommand request,
|
||||
@@ -17,6 +25,13 @@ public class CreateNewProductsCommandHandler : IRequestHandler<CreateNewProducts
|
||||
await _context.Products.AddAsync(entity, cancellationToken);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// ایجاد رکورد موجودی در سیستم انبارداری
|
||||
await _inventoryService.InitializeInventoryAsync(
|
||||
entity.Id,
|
||||
ProductType.RegularProduct,
|
||||
request.RemainingCount,
|
||||
ct: cancellationToken);
|
||||
|
||||
// ثبت دستهبندیهای محصول (در صورت ارسال)
|
||||
if (request.CategoryIds is { Count: > 0 })
|
||||
{
|
||||
|
||||
+21
-2
@@ -1,3 +1,4 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Events;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
|
||||
@@ -6,17 +7,22 @@ namespace CMSMicroservice.Application.UserOrderCQ.Commands.CancelOrder;
|
||||
public class CancelOrderCommandHandler : IRequestHandler<CancelOrderCommand, CancelOrderResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IInventoryService _inventoryService;
|
||||
|
||||
public CancelOrderCommandHandler(IApplicationDbContext context)
|
||||
public CancelOrderCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
IInventoryService inventoryService)
|
||||
{
|
||||
_context = context;
|
||||
_inventoryService = inventoryService;
|
||||
}
|
||||
|
||||
public async Task<CancelOrderResponseDto> Handle(CancelOrderCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// پیدا کردن سفارش
|
||||
// پیدا کردن سفارش با جزئیات
|
||||
var order = await _context.UserOrders
|
||||
.Include(o => o.Transaction)
|
||||
.Include(o => o.FactorDetails)
|
||||
.FirstOrDefaultAsync(o => o.Id == request.OrderId, cancellationToken);
|
||||
|
||||
if (order == null)
|
||||
@@ -35,6 +41,19 @@ public class CancelOrderCommandHandler : IRequestHandler<CancelOrderCommand, Can
|
||||
throw new InvalidOperationException("این سفارش قبلاً لغو شده است");
|
||||
}
|
||||
|
||||
// برگشت موجودی محصولات به انبار
|
||||
foreach (var factorDetail in order.FactorDetails)
|
||||
{
|
||||
await _inventoryService.ProcessReturnAsync(
|
||||
factorDetail.ProductId,
|
||||
ProductType.RegularProduct,
|
||||
factorDetail.Count,
|
||||
order.Id,
|
||||
$"لغو سفارش: {request.CancelReason}",
|
||||
null,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
// تغییر وضعیت سفارش
|
||||
order.DeliveryStatus = DeliveryStatus.Cancelled;
|
||||
order.DeliveryDescription = $"لغو شده: {request.CancelReason}";
|
||||
|
||||
+14
-2
@@ -1,3 +1,4 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Common;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
using CMSMicroservice.Domain.Events;
|
||||
@@ -10,14 +11,17 @@ public class
|
||||
SubmitShopBuyOrderCommandHandler : IRequestHandler<SubmitShopBuyOrderCommand, SubmitShopBuyOrderResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IInventoryService _inventoryService;
|
||||
private readonly ILogger<SubmitShopBuyOrderCommandHandler> _logger;
|
||||
private float _vatRate;
|
||||
|
||||
public SubmitShopBuyOrderCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
IInventoryService inventoryService,
|
||||
ILogger<SubmitShopBuyOrderCommandHandler> logger)
|
||||
{
|
||||
_context = context;
|
||||
_inventoryService = inventoryService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -148,10 +152,18 @@ public class
|
||||
});
|
||||
await _context.FactorDetails.AddRangeAsync(factorDetailsList, cancellationToken);
|
||||
|
||||
// کاهش موجودی محصولات و افزایش تعداد فروش
|
||||
// کاهش موجودی محصولات و افزایش تعداد فروش از طریق InventoryService
|
||||
foreach (var cartItem in user.UserCarts)
|
||||
{
|
||||
cartItem.Product.RemainingCount -= cartItem.Count;
|
||||
// استفاده از سرویس انبارداری برای کسر موجودی
|
||||
await _inventoryService.ConfirmSaleAsync(
|
||||
cartItem.ProductId,
|
||||
ProductType.RegularProduct,
|
||||
cartItem.Count,
|
||||
newOrder.Id,
|
||||
cancellationToken);
|
||||
|
||||
// افزایش تعداد فروش
|
||||
cartItem.Product.SaleCount += cartItem.Count;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user