229 lines
8.8 KiB
C#
229 lines
8.8 KiB
C#
using CMSMicroservice.Domain.Common;
|
|
using CMSMicroservice.Domain.Enums;
|
|
using CMSMicroservice.Domain.Events;
|
|
using CMSMicroservice.Domain.Entities.Order;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace CMSMicroservice.Application.UserOrderCQ.Commands.SubmitShopBuyOrder;
|
|
|
|
public class
|
|
SubmitShopBuyOrderCommandHandler : IRequestHandler<SubmitShopBuyOrderCommand, SubmitShopBuyOrderResponseDto>
|
|
{
|
|
private readonly IApplicationDbContext _context;
|
|
private readonly ILogger<SubmitShopBuyOrderCommandHandler> _logger;
|
|
private float _vatRate;
|
|
|
|
public SubmitShopBuyOrderCommandHandler(
|
|
IApplicationDbContext context,
|
|
ILogger<SubmitShopBuyOrderCommandHandler> logger)
|
|
{
|
|
_context = context;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<SubmitShopBuyOrderResponseDto> Handle(SubmitShopBuyOrderCommand request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var user = await _context.Users
|
|
.Include(i => i.UserAddresses)
|
|
.Include(i => i.UserWallets)
|
|
.ThenInclude(i => i.UserWalletChangeLogs)
|
|
.Include(i => i.UserCarts)
|
|
.ThenInclude(i => i.Product)
|
|
.FirstOrDefaultAsync(w => w.Id == request.UserId, cancellationToken: cancellationToken);
|
|
if (user.UserCarts.Count == 0)
|
|
throw new NotFoundException("UserCart", request.UserId);
|
|
|
|
// چک موجودی محصولات
|
|
var outOfStockProducts = new List<string>();
|
|
var insufficientStockProducts = new List<(string Title, int Requested, int Available)>();
|
|
|
|
foreach (var cartItem in user.UserCarts)
|
|
{
|
|
if (cartItem.Product.RemainingCount <= 0)
|
|
{
|
|
outOfStockProducts.Add(cartItem.Product.Title);
|
|
}
|
|
else if (cartItem.Product.RemainingCount < cartItem.Count)
|
|
{
|
|
insufficientStockProducts.Add((cartItem.Product.Title, cartItem.Count,
|
|
cartItem.Product.RemainingCount));
|
|
}
|
|
}
|
|
|
|
if (outOfStockProducts.Any())
|
|
{
|
|
throw new Exception($"محصولات زیر ناموجود شدهاند: {string.Join("، ", outOfStockProducts)}");
|
|
}
|
|
|
|
if (insufficientStockProducts.Any())
|
|
{
|
|
var messages = insufficientStockProducts.Select(p =>
|
|
$"«{p.Title}»: درخواست {p.Requested} عدد، موجودی {p.Available} عدد");
|
|
throw new Exception($"موجودی محصولات زیر کافی نیست: {string.Join(" | ", messages)}");
|
|
}
|
|
|
|
long finalAmount = 0;
|
|
|
|
// استفاده از SystemConstants برای VAT
|
|
if (SystemConstants.ShopVATEnabled)
|
|
{
|
|
_vatRate = (float)SystemConstants.ShopVAT;
|
|
finalAmount = AddVAT(user.UserCarts.Sum(s => s.Count * s.Product.Price));
|
|
_logger.LogInformation(
|
|
"Calculating final amount with VAT. Base Amount: {BaseAmount}, VAT Rate: {VATRate}, Final Amount: {FinalAmount}",
|
|
user.UserCarts.Sum(s => s.Count * s.Product.Price), _vatRate, finalAmount);
|
|
}
|
|
else
|
|
{
|
|
finalAmount = user.UserCarts.Sum(s => s.Count * s.Product.Price);
|
|
}
|
|
|
|
if (finalAmount != request.TotalAmount)
|
|
throw new Exception("مبلغ سفارش با مجموع سبد خرید مطابقت ندارد.");
|
|
|
|
|
|
var userWallet = user.UserWallets.FirstOrDefault();
|
|
if (userWallet == null)
|
|
throw new Exception("کیف پول کاربر یافت نشد.");
|
|
|
|
if (userWallet.Balance <= 0 || userWallet.Balance < request.TotalAmount)
|
|
throw new Exception("موجودی کیف پول کاربر برای انجام این تراکنش کافی نیست.");
|
|
|
|
var newTransaction = new Transaction()
|
|
{
|
|
Amount = request.TotalAmount,
|
|
Description = "خرید از فروشگاه",
|
|
PaymentStatus = PaymentStatus.Success,
|
|
PaymentDate = DateTime.Now,
|
|
Type = TransactionType.Buy,
|
|
RefId = "localwallet-" + Guid.NewGuid().ToString()
|
|
};
|
|
await _context.Transactions.AddAsync(newTransaction, cancellationToken);
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
|
|
var newWalletLog = new UserWalletChangeLog()
|
|
{
|
|
CurrentBalance = userWallet.Balance,
|
|
CurrentNetworkBalance = userWallet.NetworkBalance,
|
|
WalletId = userWallet.Id,
|
|
ChangeValue = -1 * request.TotalAmount,
|
|
IsIncrease = false,
|
|
RefrenceId = newTransaction.Id,
|
|
};
|
|
userWallet.Balance -= request.TotalAmount;
|
|
|
|
await _context.UserWalletChangeLogs.AddAsync(newWalletLog, cancellationToken);
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
|
|
var newOrder = new UserOrder()
|
|
{
|
|
Amount = request.TotalAmount,
|
|
PaymentStatus = PaymentStatus.Success,
|
|
PaymentMethod = PaymentMethod.Wallet,
|
|
PaymentDate = DateTime.Now,
|
|
UserId = request.UserId,
|
|
UserAddressId = user.UserAddresses.First(f => f.IsDefault).Id,
|
|
TransactionId = newTransaction.Id,
|
|
// سفارش فروشگاهی فیزیکی است، پس در ابتدا در انتظار ارسال است
|
|
DeliveryStatus = DeliveryStatus.Pending
|
|
};
|
|
await _context.UserOrders.AddAsync(newOrder, cancellationToken);
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
|
|
// محاسبه و ثبت VAT (اگر فعال باشد)
|
|
var vatCreated = await CalculateAndSaveVAT(newOrder.Id, request.TotalAmount, cancellationToken);
|
|
if (vatCreated)
|
|
{
|
|
newOrder.HasVAT = true;
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
}
|
|
|
|
var factorDetailsList = user.UserCarts.Select(s => new FactorDetails()
|
|
{
|
|
ProductId = s.ProductId,
|
|
Count = s.Count,
|
|
UnitPrice = SystemConstants.ShopVATEnabled ? AddVAT(s.Product.Price) : s.Product.Price,
|
|
OrderId = newOrder.Id
|
|
});
|
|
await _context.FactorDetails.AddRangeAsync(factorDetailsList, cancellationToken);
|
|
|
|
// کاهش موجودی محصولات و افزایش تعداد فروش
|
|
foreach (var cartItem in user.UserCarts)
|
|
{
|
|
cartItem.Product.RemainingCount -= cartItem.Count;
|
|
cartItem.Product.SaleCount += cartItem.Count;
|
|
}
|
|
|
|
user.UserCarts.Clear();
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
|
|
_logger.LogInformation("Order {OrderId} completed. Stock updated for {ProductCount} products.",
|
|
newOrder.Id, factorDetailsList.Count());
|
|
|
|
var finalResult = new SubmitShopBuyOrderResponseDto()
|
|
{
|
|
Id = newOrder.Id,
|
|
};
|
|
return finalResult;
|
|
}
|
|
|
|
public long CalculateVAT(long amount) => (long)(amount * _vatRate);
|
|
|
|
/// <summary>
|
|
/// محاسبه قیمت با احتساب مالیات
|
|
/// </summary>
|
|
public long AddVAT(long amount) => amount + CalculateVAT(amount);
|
|
|
|
private async Task<bool> CalculateAndSaveVAT(long orderId, long orderAmount, CancellationToken cancellationToken)
|
|
{
|
|
try
|
|
{
|
|
// بررسی فعال بودن VAT از SystemConstants
|
|
if (!SystemConstants.ShopVATEnabled)
|
|
{
|
|
_logger.LogInformation("VAT is disabled. Skipping VAT calculation for order {OrderId}", orderId);
|
|
return false;
|
|
}
|
|
|
|
// دریافت نرخ VAT از SystemConstants
|
|
var vatRate = SystemConstants.ShopVAT;
|
|
|
|
// محاسبه مالیات
|
|
var vatAmount = (long)(orderAmount * vatRate);
|
|
var totalAmount = orderAmount + vatAmount;
|
|
|
|
// ثبت VAT
|
|
var orderVAT = new OrderVAT
|
|
{
|
|
OrderId = orderId,
|
|
VATRate = vatRate,
|
|
BaseAmount = orderAmount,
|
|
VATAmount = vatAmount,
|
|
TotalAmount = totalAmount,
|
|
IsPaid = true,
|
|
PaidAt = DateTime.Now
|
|
};
|
|
|
|
await _context.OrderVATs.AddAsync(orderVAT, cancellationToken);
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
|
|
_logger.LogInformation(
|
|
"VAT calculated and saved for order {OrderId}. Rate: {Rate}%, Base: {Base}, VAT: {VAT}, Total: {Total}",
|
|
orderId,
|
|
vatRate * 100,
|
|
orderAmount,
|
|
vatAmount,
|
|
totalAmount
|
|
);
|
|
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error calculating VAT for order {OrderId}", orderId);
|
|
// عدم محاسبه VAT نباید مانع ثبت سفارش شود
|
|
return false;
|
|
}
|
|
}
|
|
} |