8518c7b3ec
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m9s
- Updated AcceptClubMembershipContractCommandHandler to set PackagePurchasedAt to the contract-signing time, ensuring accurate pool charging. - Modified ActivateClubMembershipCommand to allow specifying a PackageId for forced activation, defaulting to the base package if not provided. - Improved ActivateClubMembershipCommandHandler to handle new and existing memberships differently regarding pool charging. - Added validation logic in GetWeeklyCommissionPoolQueryHandler to compare TotalPoolAmount against actual activations. - Introduced validation fields in WeeklyCommissionPoolDto for tracking discrepancies and expected amounts. - Updated stored procedures to ensure accurate calculations based on package purchases and to handle empty pools. - Enhanced protobuf definitions to include validation fields for commission pools and network tree nodes. - Added mapping for new fields in CommissionProfile and NetworkMembershipProfile. - Created DiscountProductProfile for mapping discount product requests and responses.
102 lines
4.7 KiB
C#
102 lines
4.7 KiB
C#
namespace CMSMicroservice.Application.CommissionCQ.Queries.GetWeeklyCommissionPool;
|
|
|
|
public class GetWeeklyCommissionPoolQueryHandler : IRequestHandler<GetWeeklyCommissionPoolQuery, WeeklyCommissionPoolDto?>
|
|
{
|
|
private readonly IApplicationDbContext _context;
|
|
private readonly IWeekDefinitionRepository _weekDefinitionRepository;
|
|
|
|
public GetWeeklyCommissionPoolQueryHandler(
|
|
IApplicationDbContext context,
|
|
IWeekDefinitionRepository weekDefinitionRepository)
|
|
{
|
|
_context = context;
|
|
_weekDefinitionRepository = weekDefinitionRepository;
|
|
}
|
|
|
|
public async Task<WeeklyCommissionPoolDto?> Handle(GetWeeklyCommissionPoolQuery request, CancellationToken cancellationToken)
|
|
{
|
|
var query = _context.WeeklyCommissionPools
|
|
.Include(x => x.WeekDefinition)
|
|
.Include(x => x.Package)
|
|
.AsNoTracking()
|
|
.Where(x => x.WeekDefinitionId == request.WeekDefinitionId);
|
|
|
|
if (request.PackageId.HasValue)
|
|
query = query.Where(x => x.PackageId == request.PackageId.Value);
|
|
|
|
var pool = await query
|
|
.Select(x => new WeeklyCommissionPoolDto
|
|
{
|
|
Id = x.Id,
|
|
WeekDefinitionId = x.WeekDefinitionId,
|
|
WeekDisplayName = x.WeekDefinition.DisplayName,
|
|
TotalPoolAmount = x.TotalPoolAmount,
|
|
TotalBalances = x.TotalBalances,
|
|
ValuePerBalance = x.ValuePerBalance,
|
|
IsCalculated = x.IsCalculated,
|
|
CalculatedAt = x.CalculatedAt,
|
|
Created = x.Created,
|
|
PackageId = x.PackageId,
|
|
PackageTitle = x.Package != null ? x.Package.Title : string.Empty
|
|
})
|
|
.FirstOrDefaultAsync(cancellationToken);
|
|
|
|
pool ??= new WeeklyCommissionPoolDto();
|
|
|
|
// ── Q29: اعتبارسنجی TotalPoolAmount در برابر فعالسازیهای واقعی ──
|
|
await ValidatePoolAmountAsync(pool, request, cancellationToken);
|
|
|
|
return pool;
|
|
}
|
|
|
|
/// <summary>
|
|
/// TotalPoolAmount استخر را با تعداد فعالسازیهای ثبتشده (AcceptContract + ActivateClubMembership) مقایسه میکند.
|
|
/// نتیجه در فیلدهای Validation... ذخیره میشود.
|
|
/// </summary>
|
|
private async Task ValidatePoolAmountAsync(WeeklyCommissionPoolDto pool, GetWeeklyCommissionPoolQuery request, CancellationToken cancellationToken)
|
|
{
|
|
var packageId = pool.PackageId > 0 ? pool.PackageId : request.PackageId;
|
|
if (packageId is null or 0)
|
|
return;
|
|
|
|
var weekDef = _weekDefinitionRepository.GetWeekById(request.WeekDefinitionId);
|
|
if (weekDef is null)
|
|
return;
|
|
|
|
var activationFee = await _context.Packages
|
|
.AsNoTracking()
|
|
.Where(p => p.Id == packageId.Value)
|
|
.Select(p => p.ActivationFee)
|
|
.FirstOrDefaultAsync(cancellationToken);
|
|
|
|
if (activationFee <= 0)
|
|
return;
|
|
|
|
// شمارش Cycleهایی که Pool آنها واقعاً شارژ شده:
|
|
// - عضو جدید: Pool توسط AcceptContract شارژ میشود → UserContract وجود دارد
|
|
// - خرید مجدد: Pool در ActivateClubMembership شارژ میشود → IsCurrentCycle نبوده (قبلاً active بوده)
|
|
// - نباید از IsActive استفاده شود: وضعیت تاریخی نیست (عضو ممکن است بعداً غیرفعال شده باشد)
|
|
//
|
|
// قانون: Cycle شارژ شده = یا قرارداد دارد (عضو جدید) یا IsActive قبلاً true بوده (خرید مجدد)
|
|
// سادهترین معادل: Cycle هایی که ClubMembership.ActivatedAt در بازه هفته است (زمان واقعی شارژ Pool)
|
|
var totalActivations = await _context.ClubMembershipCycles
|
|
.AsNoTracking()
|
|
.Where(c => c.PackageId == packageId.Value
|
|
&& c.PackagePurchasedAt >= weekDef.StartDate
|
|
&& c.PackagePurchasedAt < weekDef.EndDate
|
|
&& _context.UserContracts.Any(uc =>
|
|
uc.UserId == c.UserId
|
|
&& _context.Contracts.Any(con =>
|
|
con.Id == uc.ContractId
|
|
&& con.Type == ContractType.ClubMembership)))
|
|
.CountAsync(cancellationToken);
|
|
|
|
var expectedAmount = (long)totalActivations * activationFee;
|
|
|
|
pool.ValidationActivationCount = totalActivations;
|
|
pool.ValidationExpectedAmount = expectedAmount;
|
|
pool.ValidationDiscrepancy = pool.TotalPoolAmount - expectedAmount;
|
|
pool.ValidationIsValid = pool.TotalPoolAmount == expectedAmount;
|
|
}
|
|
}
|