Files
CMS/src/CMSMicroservice.Infrastructure/Persistence/Repositories/StockMovementRepository.cs
T
2026-01-02 00:45:37 +03:30

431 lines
14 KiB
C#

using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Application.Common.Interfaces.Repositories;
using CMSMicroservice.Domain.Entities;
using CMSMicroservice.Domain.Enums;
namespace CMSMicroservice.Infrastructure.Persistence.Repositories;
/// <summary>
/// Repository implementation برای مدیریت حرکات موجودی
/// </summary>
public class StockMovementRepository : IStockMovementRepository
{
private readonly IApplicationDbContext _context;
public StockMovementRepository(IApplicationDbContext context)
{
_context = context;
}
#region Read Operations
public async Task<StockMovement?> GetByIdAsync(long id, CancellationToken cancellationToken = default)
{
return await _context.StockMovements
.Include(m => m.InventoryItem)
.ThenInclude(i => i.Product)
.Include(m => m.InventoryItem)
.ThenInclude(i => i.DiscountProduct)
.FirstOrDefaultAsync(m => m.Id == id, cancellationToken);
}
public async Task<List<StockMovement>> GetByInventoryItemIdAsync(
long inventoryItemId,
StockMovementType? movementType = null,
DateTime? fromDate = null,
DateTime? toDate = null,
int skip = 0,
int take = 100,
CancellationToken cancellationToken = default)
{
var query = _context.StockMovements
.Include(m => m.InventoryItem)
.Where(m => m.InventoryItemId == inventoryItemId);
if (movementType.HasValue)
{
query = query.Where(m => m.MovementType == movementType.Value);
}
if (fromDate.HasValue)
{
query = query.Where(m => m.Created >= fromDate.Value);
}
if (toDate.HasValue)
{
query = query.Where(m => m.Created <= toDate.Value);
}
return await query
.OrderByDescending(m => m.Created)
.Skip(skip)
.Take(take)
.ToListAsync(cancellationToken);
}
public async Task<List<StockMovement>> GetByOrderIdAsync(long orderId, CancellationToken cancellationToken = default)
{
return await _context.StockMovements
.Include(m => m.InventoryItem)
.ThenInclude(i => i.Product)
.Include(m => m.InventoryItem)
.ThenInclude(i => i.DiscountProduct)
.Where(m => m.OrderId == orderId)
.OrderByDescending(m => m.Created)
.ToListAsync(cancellationToken);
}
public async Task<List<StockMovement>> GetByDiscountOrderIdAsync(long discountOrderId, CancellationToken cancellationToken = default)
{
return await _context.StockMovements
.Include(m => m.InventoryItem)
.ThenInclude(i => i.Product)
.Include(m => m.InventoryItem)
.ThenInclude(i => i.DiscountProduct)
.Where(m => m.DiscountOrderId == discountOrderId)
.OrderByDescending(m => m.Created)
.ToListAsync(cancellationToken);
}
public async Task<List<StockMovement>> GetByReferenceNumberAsync(string referenceNumber, CancellationToken cancellationToken = default)
{
return await _context.StockMovements
.Include(m => m.InventoryItem)
.ThenInclude(i => i.Product)
.Include(m => m.InventoryItem)
.ThenInclude(i => i.DiscountProduct)
.Where(m => m.ReferenceNumber == referenceNumber)
.OrderByDescending(m => m.Created)
.ToListAsync(cancellationToken);
}
public async Task<List<StockMovement>> GetByMovementTypeAsync(
StockMovementType movementType,
DateTime? fromDate = null,
DateTime? toDate = null,
int skip = 0,
int take = 100,
CancellationToken cancellationToken = default)
{
var query = _context.StockMovements
.Include(m => m.InventoryItem)
.ThenInclude(i => i.Product)
.Include(m => m.InventoryItem)
.ThenInclude(i => i.DiscountProduct)
.Where(m => m.MovementType == movementType);
if (fromDate.HasValue)
{
query = query.Where(m => m.Created >= fromDate.Value);
}
if (toDate.HasValue)
{
query = query.Where(m => m.Created <= toDate.Value);
}
return await query
.OrderByDescending(m => m.Created)
.Skip(skip)
.Take(take)
.ToListAsync(cancellationToken);
}
public async Task<List<StockMovement>> GetRecentMovementsAsync(
int count = 50,
StockMovementType? movementType = null,
CancellationToken cancellationToken = default)
{
var query = _context.StockMovements
.Include(m => m.InventoryItem)
.ThenInclude(i => i.Product)
.Include(m => m.InventoryItem)
.ThenInclude(i => i.DiscountProduct)
.AsQueryable();
if (movementType.HasValue)
{
query = query.Where(m => m.MovementType == movementType.Value);
}
return await query
.OrderByDescending(m => m.Created)
.Take(count)
.ToListAsync(cancellationToken);
}
public async 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)
{
var query = _context.StockMovements
.Include(m => m.InventoryItem)
.ThenInclude(i => i.Product)
.Include(m => m.InventoryItem)
.ThenInclude(i => i.DiscountProduct)
.AsQueryable();
if (inventoryItemId.HasValue)
{
query = query.Where(m => m.InventoryItemId == inventoryItemId.Value);
}
if (movementType.HasValue)
{
query = query.Where(m => m.MovementType == movementType.Value);
}
if (fromDate.HasValue)
{
query = query.Where(m => m.Created >= fromDate.Value);
}
if (toDate.HasValue)
{
query = query.Where(m => m.Created <= toDate.Value);
}
if (!string.IsNullOrWhiteSpace(referenceNumber))
{
query = query.Where(m => m.ReferenceNumber != null && m.ReferenceNumber.Contains(referenceNumber));
}
if (orderId.HasValue)
{
query = query.Where(m => m.OrderId == orderId.Value);
}
if (discountOrderId.HasValue)
{
query = query.Where(m => m.DiscountOrderId == discountOrderId.Value);
}
if (performedByUserId.HasValue)
{
query = query.Where(m => m.PerformedByUserId == performedByUserId.Value);
}
return await query
.OrderByDescending(m => m.Created)
.Skip(skip)
.Take(take)
.ToListAsync(cancellationToken);
}
public async 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)
{
var query = _context.StockMovements.AsQueryable();
if (inventoryItemId.HasValue)
{
query = query.Where(m => m.InventoryItemId == inventoryItemId.Value);
}
if (movementType.HasValue)
{
query = query.Where(m => m.MovementType == movementType.Value);
}
if (fromDate.HasValue)
{
query = query.Where(m => m.Created >= fromDate.Value);
}
if (toDate.HasValue)
{
query = query.Where(m => m.Created <= toDate.Value);
}
if (!string.IsNullOrWhiteSpace(referenceNumber))
{
query = query.Where(m => m.ReferenceNumber != null && m.ReferenceNumber.Contains(referenceNumber));
}
if (orderId.HasValue)
{
query = query.Where(m => m.OrderId == orderId.Value);
}
if (discountOrderId.HasValue)
{
query = query.Where(m => m.DiscountOrderId == discountOrderId.Value);
}
if (performedByUserId.HasValue)
{
query = query.Where(m => m.PerformedByUserId == performedByUserId.Value);
}
return await query.CountAsync(cancellationToken);
}
#endregion
#region Write Operations
public async Task<StockMovement> AddAsync(StockMovement stockMovement, CancellationToken cancellationToken = default)
{
_context.StockMovements.Add(stockMovement);
await _context.SaveChangesAsync(cancellationToken);
return stockMovement;
}
public async Task DeleteAsync(long id, CancellationToken cancellationToken = default)
{
var stockMovement = await _context.StockMovements.FindAsync(new object[] { id }, cancellationToken);
if (stockMovement != null)
{
_context.StockMovements.Remove(stockMovement);
await _context.SaveChangesAsync(cancellationToken);
}
}
public async Task<List<StockMovement>> BulkAddAsync(List<StockMovement> stockMovements, CancellationToken cancellationToken = default)
{
_context.StockMovements.AddRange(stockMovements);
await _context.SaveChangesAsync(cancellationToken);
return stockMovements;
}
#endregion
#region Analytics & Reports
public async Task<Dictionary<StockMovementType, int>> GetMovementSummaryAsync(
DateTime fromDate,
DateTime toDate,
long? inventoryItemId = null,
CancellationToken cancellationToken = default)
{
var query = _context.StockMovements
.Where(m => m.Created >= fromDate && m.Created <= toDate);
if (inventoryItemId.HasValue)
{
query = query.Where(m => m.InventoryItemId == inventoryItemId.Value);
}
var movements = await query
.GroupBy(m => m.MovementType)
.Select(g => new { MovementType = g.Key, TotalQuantity = g.Sum(m => m.Quantity) })
.ToListAsync(cancellationToken);
return movements.ToDictionary(x => x.MovementType, x => x.TotalQuantity);
}
public async Task<List<(DateTime Date, int InboundQuantity, int OutboundQuantity)>> GetDailyMovementVolumeAsync(
DateTime fromDate,
DateTime toDate,
long? inventoryItemId = null,
CancellationToken cancellationToken = default)
{
var query = _context.StockMovements
.Where(m => m.Created >= fromDate && m.Created <= toDate);
if (inventoryItemId.HasValue)
{
query = query.Where(m => m.InventoryItemId == inventoryItemId.Value);
}
var movements = await query.ToListAsync(cancellationToken);
// نوع‌های ورودی (افزایش موجودی)
var inboundTypes = new[]
{
StockMovementType.InitialStock,
StockMovementType.Restock,
StockMovementType.Return,
StockMovementType.TransferIn,
StockMovementType.AdjustmentPlus,
StockMovementType.Released
};
// نوع‌های خروجی (کاهش موجودی)
var outboundTypes = new[]
{
StockMovementType.Sale,
StockMovementType.Damaged,
StockMovementType.Lost,
StockMovementType.TransferOut,
StockMovementType.AdjustmentMinus,
StockMovementType.Reserved
};
var dailyVolumes = movements
.GroupBy(m => m.Created.Date)
.Select(g => (
Date: g.Key,
InboundQuantity: g.Where(m => inboundTypes.Contains(m.MovementType)).Sum(m => m.Quantity),
OutboundQuantity: g.Where(m => outboundTypes.Contains(m.MovementType)).Sum(m => m.Quantity)
))
.OrderBy(x => x.Date)
.ToList();
return dailyVolumes;
}
public async Task<List<(long InventoryItemId, string ProductName, int MovementCount, int TotalQuantityChange)>> GetTopMovingProductsAsync(
DateTime fromDate,
DateTime toDate,
int count = 10,
StockMovementType? movementType = null,
CancellationToken cancellationToken = default)
{
var query = _context.StockMovements
.Include(m => m.InventoryItem)
.ThenInclude(i => i.Product)
.Include(m => m.InventoryItem)
.ThenInclude(i => i.DiscountProduct)
.Where(m => m.Created >= fromDate && m.Created <= toDate);
if (movementType.HasValue)
{
query = query.Where(m => m.MovementType == movementType.Value);
}
var movements = await query.ToListAsync(cancellationToken);
var topProducts = movements
.GroupBy(m => m.InventoryItemId)
.Select(g =>
{
var firstItem = g.First().InventoryItem;
var productName = firstItem.Product?.Title ?? firstItem.DiscountProduct?.Title ?? "Unknown";
return (
InventoryItemId: g.Key,
ProductName: productName,
MovementCount: g.Count(),
TotalQuantityChange: g.Sum(m => m.Quantity)
);
})
.OrderByDescending(x => x.MovementCount)
.Take(count)
.ToList();
return topProducts;
}
#endregion
}