feat(commission): add per-package calculation support
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 15m3s

- SP sp_CalculateWeeklyBalances: added @PackageId, @InputMaxBalancesPerLeg, @InputMaxNetworkLevel params; filters by PackageId
- SP sp_CalculateWeeklyCommissionPool: added @PackageId param; all queries filter by PackageId/WeeklyPoolId for isolation
- ICommissionCalculationStrategy: added optional PackageId param to both methods
- StoredProcedureCommissionCalculationStrategy: loops per-package for both Balance and Pool methods; filters by packageId if provided
- OrmCommissionCalculationStrategy: signature updated to match interface
- TriggerWeeklyCalculationCommand: added PackageId optional field
- TriggerWeeklyCalculationCommandHandler: passes PackageId to strategy
- GetWeeklyCommissionPoolQuery: added optional PackageId filter
- GetWeeklyCommissionPoolQueryHandler: filters pool by PackageId if provided
- GetAllWeeklyPoolsQuery/Handler/DTO: added PackageId filter + PackageTitle in response
- Proto commission.proto: added package_id to TriggerWeeklyCalculationRequest, GetWeeklyCommissionPoolRequest, WeeklyCommissionPoolModel, GetWeeklyCommissionPoolResponse, GetAllWeeklyPoolsRequest
- CommissionProfile: added explicit mappings for TriggerWeeklyCalculation, GetWeeklyCommissionPool, GetAllWeeklyPools

Fixes: SP picks wrong pool when multiple packages per week
Fixes: SP ignores PackageId on ForceRecalculate (now uses WeeklyPoolId)
Fixes: Zero-balance pools not fully recorded (now sets TotalBalances=0, ValuePerBalance=0)
This commit is contained in:
masoodafar-web
2026-04-24 03:43:20 +03:30
parent a35e2eeb92
commit 4d6d77531d
14 changed files with 1548 additions and 40 deletions
@@ -21,4 +21,9 @@ public record TriggerWeeklyCalculationCommand : IRequest<TriggerWeeklyCalculatio
/// Skip pool calculation and payout processing (Step 2)
/// </summary>
public bool SkipPayouts { get; init; }
/// <summary>
/// شناسه پکیج — اگر مقدار داشته باشد فقط این پکیج محاسبه می‌شود، در غیر این صورت همه پکیج‌ها
/// </summary>
public long? PackageId { get; init; }
}
@@ -53,6 +53,7 @@ public class TriggerWeeklyCalculationCommandHandler : IRequestHandler<TriggerWee
var balancesCount = await strategy.CalculateWeeklyBalancesAsync(
request.WeekDefinitionId,
request.ForceRecalculate,
request.PackageId,
cancellationToken);
steps.Add($"محاسبه امتیازات هفتگی ({balancesCount} کاربر)");
@@ -64,6 +65,7 @@ public class TriggerWeeklyCalculationCommandHandler : IRequestHandler<TriggerWee
var poolId = await strategy.CalculateWeeklyCommissionPoolAsync(
request.WeekDefinitionId,
request.ForceRecalculate,
request.PackageId,
cancellationToken);
steps.Add($"محاسبه استخر و پرداخت کاربران (Pool: {poolId})");
@@ -20,6 +20,11 @@ public record GetAllWeeklyPoolsQuery : IRequest<GetAllWeeklyPoolsResponseDto>
/// </summary>
public bool? OnlyCalculated { get; init; }
/// <summary>
/// فیلتر بر اساس پکیج (اختیاری)
/// </summary>
public long? PackageId { get; init; }
/// <summary>
/// شماره صفحه
/// </summary>
@@ -17,6 +17,7 @@ public class GetAllWeeklyPoolsQueryHandler : IRequestHandler<GetAllWeeklyPoolsQu
{
var query = _context.WeeklyCommissionPools
.Include(i=> i.WeekDefinition)
.Include(i => i.Package)
.AsNoTracking();
// Apply filters
@@ -35,6 +36,11 @@ public class GetAllWeeklyPoolsQueryHandler : IRequestHandler<GetAllWeeklyPoolsQu
query = query.Where(x => x.IsCalculated);
}
if (request.PackageId.HasValue)
{
query = query.Where(x => x.PackageId == request.PackageId.Value);
}
// Order by week number descending (newest first)
query = query.OrderByDescending(x => x.WeekDefinitionId);
@@ -55,7 +61,9 @@ public class GetAllWeeklyPoolsQueryHandler : IRequestHandler<GetAllWeeklyPoolsQu
ValuePerBalance = x.ValuePerBalance,
IsCalculated = x.IsCalculated,
CalculatedAt = x.CalculatedAt,
Created = x.Created
Created = x.Created,
PackageId = x.PackageId,
PackageTitle = x.Package != null ? x.Package.Title : string.Empty
})
.ToListAsync(cancellationToken);
@@ -17,6 +17,8 @@ public record WeeklyCommissionPoolDto
public bool IsCalculated { get; init; }
public DateTime? CalculatedAt { get; init; }
public DateTime Created { get; init; }
public long PackageId { get; init; }
public string PackageTitle { get; init; } = string.Empty;
}
public record MetaDataDto
@@ -9,4 +9,9 @@ public record GetWeeklyCommissionPoolQuery : IRequest<WeeklyCommissionPoolDto?>
/// شماره هفته
/// </summary>
public long WeekDefinitionId { get; init; }
/// <summary>
/// شناسه پکیج — اختیاری
/// </summary>
public long? PackageId { get; init; }
}
@@ -15,29 +15,32 @@ public class GetWeeklyCommissionPoolQueryHandler : IRequestHandler<GetWeeklyComm
public async Task<WeeklyCommissionPoolDto?> Handle(GetWeeklyCommissionPoolQuery request, CancellationToken cancellationToken)
{
var pool = await _context.WeeklyCommissionPools
var query = _context.WeeklyCommissionPools
.Include(x => x.WeekDefinition)
.Include(x => x.Package)
.AsNoTracking()
.Where(x => x.WeekDefinitionId == request.WeekDefinitionId)
.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,
WeekDisplayName = x.WeekDefinition.DisplayName,
TotalPoolAmount = x.TotalPoolAmount,
TotalBalances = x.TotalBalances,
ValuePerBalance = x.ValuePerBalance,
IsCalculated = x.IsCalculated,
CalculatedAt = x.CalculatedAt,
Created = x.Created
Created = x.Created,
PackageId = x.PackageId,
PackageTitle = x.Package != null ? x.Package.Title : string.Empty
})
.FirstOrDefaultAsync(cancellationToken);
// if (pool != null)
// {
// pool.WeekDisplayName = _weekDefinitionRepository.GetDisplayNameByGregorianWeekNumber(pool.WeekNumber);
// }
return pool ?? new WeeklyCommissionPoolDto();
}
}
@@ -14,4 +14,6 @@ public class WeeklyCommissionPoolDto
public bool IsCalculated { get; set; }
public DateTime? CalculatedAt { get; set; }
public DateTimeOffset Created { get; set; }
public long PackageId { get; set; }
public string PackageTitle { get; set; } = string.Empty;
}