feat: Add discount balance and change value to wallet change log
- Added CurrentDiscountBalance and ChangeDiscountValue properties to GetCustomerWalletChangeLogResponseDto. - Updated userwallet.proto to include current_discount_balance and change_discount_value fields. - Enhanced CommissionProfile mapping to support GetMyCommissionPayouts requests and responses. - Implemented GetMyCommissionPayouts query and handler to retrieve user commission payouts. - Added validation for GetMyCommissionPayouts query. - Updated UserOrderService to handle user orders, including VAT calculations and wallet transactions. - Enhanced UserWalletService to include new properties in wallet change log responses.
This commit is contained in:
@@ -2,23 +2,33 @@ using CMSMicroservice.Protobuf.Protos.UserOrder;
|
||||
using CMSMicroservice.Application.UserOrderCQ.Queries.GetCustomerOrders;
|
||||
using CMSMicroservice.Application.UserOrderCQ.Queries.GetCustomerOrder;
|
||||
using CMSMicroservice.Application.UserOrderCQ.Queries.GetCustomerOrderHistory;
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using CMSMicroservice.Domain.Entities.Order;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
using AppModels = CMSMicroservice.Application.Common.Models;
|
||||
using Grpc.Core;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CMSMicroservice.Protobuf.Protos;
|
||||
using MediatR;
|
||||
using Mapster;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.WebApi.Services;
|
||||
|
||||
public class UserOrderService : UserOrderContract.UserOrderContractBase
|
||||
{
|
||||
private readonly ISender _sender;
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly ICurrentUserService _currentUserService;
|
||||
|
||||
public UserOrderService(ISender sender)
|
||||
public UserOrderService(ISender sender, IApplicationDbContext context, ICurrentUserService currentUserService)
|
||||
{
|
||||
_sender = sender;
|
||||
_context = context;
|
||||
_currentUserService = currentUserService;
|
||||
}
|
||||
public override async Task<CreateNewUserOrderResponse> CreateNewUserOrder(CreateNewUserOrderRequest request, ServerCallContext context)
|
||||
{
|
||||
@@ -37,17 +47,299 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
|
||||
|
||||
public override async Task<GetUserOrderResponse> GetUserOrder(GetUserOrderRequest request, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
|
||||
var query = new GetCustomerOrderQuery
|
||||
{
|
||||
OrderId = request.Id,
|
||||
UserId = 0 // از JWT دریافت میشود
|
||||
};
|
||||
|
||||
var result = await _sender.Send(query, context.CancellationToken);
|
||||
|
||||
var response = new GetUserOrderResponse
|
||||
{
|
||||
Id = result.Id,
|
||||
Amount = result.Amount,
|
||||
PackageId = result.PackageId ?? 0,
|
||||
TransactionId = result.TransactionId,
|
||||
UserId = result.UserId,
|
||||
UserAddressId = result.UserAddressId,
|
||||
UserAddressText = result.UserAddressText,
|
||||
TrackingCode = result.TrackingCode,
|
||||
DeliveryDescription = result.DeliveryDescription,
|
||||
UserFullName = result.UserFullName,
|
||||
UserNationalCode = result.UserNationalCode
|
||||
};
|
||||
|
||||
// VAT Info
|
||||
if (result.VatAmount > 0)
|
||||
{
|
||||
response.VatInfo = new OrderVATInfo
|
||||
{
|
||||
VatRate = result.VatPercentage / 100,
|
||||
BaseAmount = result.Amount - result.VatAmount,
|
||||
VatAmount = result.VatAmount,
|
||||
TotalAmount = result.Amount,
|
||||
IsPaid = result.PaymentStatus == Domain.Enums.PaymentStatus.Success
|
||||
};
|
||||
}
|
||||
|
||||
response.PaymentStatus = (CMSMicroservice.Protobuf.Protos.PaymentStatus)result.PaymentStatus;
|
||||
if (result.PaymentDate.HasValue)
|
||||
response.PaymentDate = Timestamp.FromDateTime(DateTime.SpecifyKind(result.PaymentDate.Value, DateTimeKind.Utc));
|
||||
|
||||
if (result.PaymentMethod.HasValue)
|
||||
response.PaymentMethod = (CMSMicroservice.Protobuf.Protos.PaymentMethod)result.PaymentMethod.Value;
|
||||
|
||||
response.DeliveryStatus = (CMSMicroservice.Protobuf.Protos.DeliveryStatus)result.DeliveryStatus;
|
||||
|
||||
foreach (var fd in result.FactorDetails)
|
||||
{
|
||||
response.FactorDetails.Add(new GetUserOrderResponseFactorDetail
|
||||
{
|
||||
ProductId = fd.ProductId,
|
||||
ProductTitle = fd.ProductTitle,
|
||||
ProductThumbnailPath = fd.ProductThumbnailPath,
|
||||
UnitPrice = fd.UnitPrice,
|
||||
Count = fd.Count,
|
||||
UnitDiscountPrice = fd.UnitDiscountPrice
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public override async Task<GetAllUserOrderByFilterResponse> GetAllUserOrderByFilter(GetAllUserOrderByFilterRequest request, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
|
||||
// Admin API - can view all orders or filter by specific user
|
||||
var query = new GetCustomerOrdersQuery
|
||||
{
|
||||
UserId = request.Filter?.UserId ?? 0, // 0 means all users (admin view)
|
||||
PaginationState = request.PaginationState?.Adapt<AppModels.PaginationState>(),
|
||||
PaymentStatusFilter = request.Filter?.PaymentStatus != null
|
||||
? (int?)request.Filter.PaymentStatus
|
||||
: null,
|
||||
DeliveryStatusFilter = request.Filter?.DeliveryStatus != null
|
||||
? (int?)request.Filter.DeliveryStatus
|
||||
: null,
|
||||
FromDate = request.Filter?.PaymentDate?.ToDateTime(),
|
||||
ToDate = null
|
||||
};
|
||||
|
||||
var result = await _sender.Send(query, context.CancellationToken);
|
||||
|
||||
var response = new GetAllUserOrderByFilterResponse
|
||||
{
|
||||
MetaData = result.MetaData.Adapt<MetaData>()
|
||||
};
|
||||
|
||||
foreach (var model in result.Models)
|
||||
{
|
||||
var orderModel = new GetAllUserOrderByFilterResponseModel
|
||||
{
|
||||
Id = model.Id,
|
||||
Amount = model.Amount,
|
||||
PackageId = model.PackageId ?? 0,
|
||||
TransactionId = model.TransactionId,
|
||||
UserId = model.UserId,
|
||||
UserAddressId = model.UserAddressId,
|
||||
UserAddressText = model.UserAddressText,
|
||||
TrackingCode = model.TrackingCode,
|
||||
DeliveryDescription = model.DeliveryDescription,
|
||||
UserFullName = model.UserFullName,
|
||||
UserNationalCode = model.UserNationalCode,
|
||||
VatAmount = model.VatAmount,
|
||||
VatPercentage = model.VatPercentage
|
||||
};
|
||||
|
||||
orderModel.PaymentStatus = (CMSMicroservice.Protobuf.Protos.PaymentStatus)model.PaymentStatus;
|
||||
if (model.PaymentDate.HasValue)
|
||||
orderModel.PaymentDate = Timestamp.FromDateTime(DateTime.SpecifyKind(model.PaymentDate.Value, DateTimeKind.Utc));
|
||||
|
||||
if (model.PaymentMethod.HasValue)
|
||||
orderModel.PaymentMethod = (CMSMicroservice.Protobuf.Protos.PaymentMethod)model.PaymentMethod.Value;
|
||||
|
||||
orderModel.DeliveryStatus = (CMSMicroservice.Protobuf.Protos.DeliveryStatus)model.DeliveryStatus;
|
||||
|
||||
foreach (var fd in model.FactorDetails)
|
||||
{
|
||||
orderModel.FactorDetails.Add(new GetAllUserOrderByFilterResponseModelFactorDetail
|
||||
{
|
||||
ProductId = fd.ProductId,
|
||||
ProductTitle = fd.ProductTitle,
|
||||
ProductThumbnailPath = fd.ProductThumbnailPath,
|
||||
UnitPrice = fd.UnitPrice,
|
||||
Count = fd.Count,
|
||||
UnitDiscountPrice = fd.UnitDiscountPrice
|
||||
});
|
||||
}
|
||||
|
||||
response.Models.Add(orderModel);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public override async Task<SubmitShopBuyOrderResponse> SubmitShopBuyOrder(SubmitShopBuyOrderRequest request, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, "Method not implemented yet"));
|
||||
// Get UserId from JWT or request
|
||||
var userId = !string.IsNullOrEmpty(_currentUserService.UserId)
|
||||
? long.Parse(_currentUserService.UserId)
|
||||
: request.UserId;
|
||||
|
||||
if (userId == 0)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unauthenticated, "User not authenticated"));
|
||||
}
|
||||
|
||||
// Get user's cart items with product details
|
||||
var cartItems = await _context.UserCarts
|
||||
.Include(c => c.Product)
|
||||
.Where(c => c.UserId == userId && !c.IsDeleted)
|
||||
.ToListAsync(context.CancellationToken);
|
||||
|
||||
if (!cartItems.Any())
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.FailedPrecondition, "سبد خرید خالی است"));
|
||||
}
|
||||
|
||||
// Get user's default address
|
||||
var defaultAddress = await _context.UserAddresses
|
||||
.Where(a => a.UserId == userId && a.IsDefault && !a.IsDeleted)
|
||||
.FirstOrDefaultAsync(context.CancellationToken);
|
||||
|
||||
if (defaultAddress == null)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.FailedPrecondition, "آدرس پیشفرض یافت نشد"));
|
||||
}
|
||||
|
||||
// Calculate amounts
|
||||
const decimal vatRate = 0.09m;
|
||||
long baseAmount = cartItems.Sum(c => c.Product.Price * c.Count);
|
||||
long vatAmount = (long)(baseAmount * vatRate);
|
||||
long totalAmount = baseAmount + vatAmount;
|
||||
|
||||
// Validate total amount
|
||||
if (request.TotalAmount > 0 && Math.Abs(totalAmount - request.TotalAmount) > 100)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.InvalidArgument,
|
||||
$"مبلغ نامعتبر است. محاسبه شده: {totalAmount}, دریافتی: {request.TotalAmount}"));
|
||||
}
|
||||
|
||||
// Get user's wallet
|
||||
var wallet = await _context.UserWallets
|
||||
.Where(w => w.UserId == userId)
|
||||
.FirstOrDefaultAsync(context.CancellationToken);
|
||||
|
||||
if (wallet == null)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.FailedPrecondition, "کیف پول یافت نشد"));
|
||||
}
|
||||
|
||||
// Check wallet balance
|
||||
if (wallet.Balance < totalAmount)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.FailedPrecondition,
|
||||
$"موجودی کیف پول کافی نیست. موجودی: {wallet.Balance:N0} تومان، مورد نیاز: {totalAmount:N0} تومان"));
|
||||
}
|
||||
|
||||
// Create transaction
|
||||
var transaction = new Transaction
|
||||
{
|
||||
Amount = totalAmount,
|
||||
Description = $"خرید محصولات - سفارش شماره در حال ایجاد",
|
||||
PaymentStatus = CMSMicroservice.Domain.Enums.PaymentStatus.Success,
|
||||
PaymentDate = DateTime.UtcNow,
|
||||
Type = CMSMicroservice.Domain.Enums.TransactionType.Buy,
|
||||
RefId = $"SHOP_{DateTime.UtcNow.Ticks}"
|
||||
};
|
||||
|
||||
_context.Transactions.Add(transaction);
|
||||
await _context.SaveChangesAsync(context.CancellationToken);
|
||||
|
||||
// Deduct from wallet
|
||||
var oldBalance = wallet.Balance;
|
||||
wallet.Balance -= totalAmount;
|
||||
|
||||
// Create wallet change log
|
||||
var walletLog = new UserWalletChangeLog
|
||||
{
|
||||
WalletId = wallet.Id,
|
||||
CurrentBalance = wallet.Balance,
|
||||
ChangeValue = -totalAmount,
|
||||
CurrentNetworkBalance = wallet.NetworkBalance,
|
||||
ChangeNerworkValue = 0,
|
||||
CurrentDiscountBalance = wallet.DiscountBalance,
|
||||
ChangeDiscountValue = 0,
|
||||
IsIncrease = false,
|
||||
RefrenceId = transaction.Id
|
||||
};
|
||||
|
||||
_context.UserWalletChangeLogs.Add(walletLog);
|
||||
|
||||
// Create order
|
||||
var order = new UserOrder
|
||||
{
|
||||
UserId = userId,
|
||||
UserAddressId = defaultAddress.Id,
|
||||
Amount = totalAmount,
|
||||
TransactionId = transaction.Id,
|
||||
PaymentStatus = CMSMicroservice.Domain.Enums.PaymentStatus.Success,
|
||||
PaymentDate = DateTime.UtcNow,
|
||||
PaymentMethod = CMSMicroservice.Domain.Enums.PaymentMethod.Wallet,
|
||||
DeliveryStatus = CMSMicroservice.Domain.Enums.DeliveryStatus.Pending,
|
||||
HasVAT = true
|
||||
};
|
||||
|
||||
_context.UserOrders.Add(order);
|
||||
await _context.SaveChangesAsync(context.CancellationToken);
|
||||
|
||||
// Update transaction description with order ID
|
||||
transaction.Description = $"خرید محصولات - سفارش #{order.Id}";
|
||||
await _context.SaveChangesAsync(context.CancellationToken);
|
||||
|
||||
// Create order VAT record
|
||||
var orderVat = new OrderVAT
|
||||
{
|
||||
OrderId = order.Id,
|
||||
VATRate = vatRate,
|
||||
BaseAmount = baseAmount,
|
||||
VATAmount = vatAmount,
|
||||
TotalAmount = totalAmount
|
||||
};
|
||||
|
||||
_context.OrderVATs.Add(orderVat);
|
||||
|
||||
// Create factor details for each cart item
|
||||
foreach (var cartItem in cartItems)
|
||||
{
|
||||
var factorDetail = new FactorDetails
|
||||
{
|
||||
OrderId = order.Id,
|
||||
ProductId = cartItem.ProductId,
|
||||
Count = cartItem.Count,
|
||||
UnitPrice = cartItem.Product.Price,
|
||||
UnitDiscount = 0,
|
||||
UnitDiscountPrice = cartItem.Product.Price,
|
||||
IsChangePrice = false
|
||||
};
|
||||
|
||||
_context.FactorDetails.Add(factorDetail);
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync(context.CancellationToken);
|
||||
|
||||
// Clear user's cart
|
||||
foreach (var cartItem in cartItems)
|
||||
{
|
||||
cartItem.IsDeleted = true;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync(context.CancellationToken);
|
||||
|
||||
return new SubmitShopBuyOrderResponse
|
||||
{
|
||||
Id = order.Id
|
||||
};
|
||||
}
|
||||
|
||||
public override async Task<CancelOrderResponse> CancelOrder(CancelOrderRequest request, ServerCallContext context)
|
||||
@@ -131,14 +423,14 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
|
||||
VatPercentage = model.VatPercentage
|
||||
};
|
||||
|
||||
orderModel.PaymentStatus = (PaymentStatus)model.PaymentStatus;
|
||||
orderModel.PaymentStatus = (CMSMicroservice.Protobuf.Protos.PaymentStatus)model.PaymentStatus;
|
||||
if (model.PaymentDate.HasValue)
|
||||
orderModel.PaymentDate = Timestamp.FromDateTime(DateTime.SpecifyKind(model.PaymentDate.Value, DateTimeKind.Utc));
|
||||
|
||||
if (model.PaymentMethod.HasValue)
|
||||
orderModel.PaymentMethod = (PaymentMethod)model.PaymentMethod.Value;
|
||||
orderModel.PaymentMethod = (CMSMicroservice.Protobuf.Protos.PaymentMethod)model.PaymentMethod.Value;
|
||||
|
||||
orderModel.DeliveryStatus = (DeliveryStatus)model.DeliveryStatus;
|
||||
orderModel.DeliveryStatus = (CMSMicroservice.Protobuf.Protos.DeliveryStatus)model.DeliveryStatus;
|
||||
|
||||
foreach (var fd in model.FactorDetails)
|
||||
{
|
||||
@@ -197,14 +489,14 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
|
||||
};
|
||||
}
|
||||
|
||||
response.PaymentStatus = (PaymentStatus)result.PaymentStatus;
|
||||
response.PaymentStatus = (CMSMicroservice.Protobuf.Protos.PaymentStatus)result.PaymentStatus;
|
||||
if (result.PaymentDate.HasValue)
|
||||
response.PaymentDate = Timestamp.FromDateTime(DateTime.SpecifyKind(result.PaymentDate.Value, DateTimeKind.Utc));
|
||||
|
||||
if (result.PaymentMethod.HasValue)
|
||||
response.PaymentMethod = (PaymentMethod)result.PaymentMethod.Value;
|
||||
response.PaymentMethod = (CMSMicroservice.Protobuf.Protos.PaymentMethod)result.PaymentMethod.Value;
|
||||
|
||||
response.DeliveryStatus = (DeliveryStatus)result.DeliveryStatus;
|
||||
response.DeliveryStatus = (CMSMicroservice.Protobuf.Protos.DeliveryStatus)result.DeliveryStatus;
|
||||
|
||||
foreach (var fd in result.FactorDetails)
|
||||
{
|
||||
@@ -384,4 +676,15 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
|
||||
TotalAmount = totalAmount
|
||||
};
|
||||
}
|
||||
|
||||
public override Task<GetVATRateResponse> GetVATRate(Google.Protobuf.WellKnownTypes.Empty request, ServerCallContext context)
|
||||
{
|
||||
// VAT Rate for Iran: 9% (نرخ مالیات بر ارزش افزوده ایران)
|
||||
return Task.FromResult(new GetVATRateResponse
|
||||
{
|
||||
VatRate = 0.09,
|
||||
VatPercentage = 9,
|
||||
IsEnabled = true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user