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 { private readonly IApplicationDbContext _context; private readonly ILogger _logger; private float _vatRate; public SubmitShopBuyOrderCommandHandler( IApplicationDbContext context, ILogger logger) { _context = context; _logger = logger; } public async Task 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(); 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); /// /// محاسبه قیمت با احتساب مالیات /// public long AddVAT(long amount) => amount + CalculateVAT(amount); private async Task 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; } } }