175 lines
6.9 KiB
C#
175 lines
6.9 KiB
C#
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;
|
|
|
|
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);
|
|
|
|
if (user.UserCarts.Sum(s => s.Count * s.Product.Price) != 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 = s.Product.Price,
|
|
OrderId = newOrder.Id
|
|
});
|
|
await _context.FactorDetails.AddRangeAsync(factorDetailsList, cancellationToken);
|
|
user.UserCarts.Clear();
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
var finalResult = new SubmitShopBuyOrderResponseDto()
|
|
{
|
|
Id = newOrder.Id,
|
|
|
|
};
|
|
return finalResult;
|
|
}
|
|
|
|
private async Task<bool> CalculateAndSaveVAT(long orderId, long orderAmount, CancellationToken cancellationToken)
|
|
{
|
|
try
|
|
{
|
|
// بررسی فعال بودن VAT
|
|
var vatEnabledConfig = await _context.SystemConfigurations
|
|
.FirstOrDefaultAsync(x => x.Scope == ConfigurationScope.VAT && x.Key == "IsEnabled", cancellationToken);
|
|
|
|
if (vatEnabledConfig == null || !bool.TryParse(vatEnabledConfig.Value, out var isEnabled) || !isEnabled)
|
|
{
|
|
_logger.LogInformation("VAT is disabled. Skipping VAT calculation for order {OrderId}", orderId);
|
|
return false;
|
|
}
|
|
|
|
// دریافت نرخ VAT
|
|
var vatRateConfig = await _context.SystemConfigurations
|
|
.FirstOrDefaultAsync(x => x.Scope == ConfigurationScope.VAT && x.Key == "Rate", cancellationToken);
|
|
|
|
if (vatRateConfig == null || !decimal.TryParse(vatRateConfig.Value, out var vatRate))
|
|
{
|
|
_logger.LogWarning("VAT Rate configuration not found or invalid. Using default 0.09");
|
|
vatRate = 0.09m;
|
|
}
|
|
|
|
// محاسبه مالیات
|
|
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;
|
|
}
|
|
}
|
|
}
|