Files
monorepo/LCU.API.Operations.IncentiveAPI/LCU.API.Internal.Operations.IncentiveApi/Services/Payout/PayoutService.cs
T
2026-04-23 16:27:51 +00:00

243 lines
10 KiB
C#

using LCU.API.Internal.Operations.IncentiveApi.Models.Dayforce;
using LCU.API.Internal.Operations.IncentiveApi.Models.Payout;
using LCU.API.Internal.Operations.IncentiveApiLib.Repositories;
using LCU.Library.Internal.IncentiveApiModels.Entities;
using LCU.Library.Internal.IncentiveApiModels.Common;
using LCU.Library.Internal.IncentiveApiModels.Requests;
using LCU.Library.Internal.ApiResponse.Services;
using ProcessResult = LCU.Library.Internal.IncentiveApiModels.Results.ProcessResult;
namespace LCU.API.Internal.Operations.IncentiveApi.Services.Payout;
public class PayoutService : IPayoutService
{
private readonly IUserRepository _userRepository;
private readonly IPayoutRepository _payoutRepository;
private readonly IAccountingRepository _accountingRepository;
private readonly IUnitOfWork _unitOfWork;
private readonly IPayoutCalculateService _payoutCalculateService;
private readonly IActivityService _activityService;
private readonly ProcessResult _payoutIncentiveProcessResult;
private const int MonthsBack = -2;
public PayoutService(IUserRepository userRepository, IPayoutRepository payoutRepository, IUnitOfWork unitOfWork, IPayoutCalculateService payoutCalculateService,
IAccountingRepository accountingRepository, IActivityService activityService)
{
_userRepository = userRepository;
_payoutRepository = payoutRepository;
_unitOfWork = unitOfWork;
_payoutCalculateService = payoutCalculateService;
_accountingRepository = accountingRepository;
_activityService = activityService;
_payoutIncentiveProcessResult = new ProcessResult();
}
public async Task RunMonthlyIncentivePayouts(PayoutIncentiveRequest request)
{
try
{
Log.Information("Running Monthly Incentive Payout Process");
var result = await CreateMonthlyIncentivePayouts(request);
Log.Information("Successfully Processed Monthly Incentive Payouts.");
await QueueReturnResponse.QueueResponse(request, _payoutIncentiveProcessResult.Success, _payoutIncentiveProcessResult.MessageStack);
}
catch (Exception exception)
{
Log.Fatal(exception, "There was an Unhandled error with Monthly Incentive Payout Process");
var monthsBack = request?.MonthsBack.ToString() ?? "unknown";
var message = $"There was an Unhandled error with Monthly Incentive Payout Process from {monthsBack} months back from today's date. Exception Message: {exception.Message}.";
_payoutIncentiveProcessResult.MessageStack.Add(message);
_payoutIncentiveProcessResult.Success = false;
await QueueReturnResponse.QueueResponse(request, _payoutIncentiveProcessResult.Success, _payoutIncentiveProcessResult.MessageStack);
}
}
public async Task<IReadOnlyCollection<PayoutLineItem>> CreateMonthlyIncentivePayouts(PayoutIncentiveRequest request)
{
var period = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
period = request.MonthsBack == 0 ? period.AddMonths(MonthsBack) : period.AddMonths(request.MonthsBack);
var range = new MonthRange(period);
_activityService.CancelPendingTransfersBetween(range).GetAwaiter().GetResult();
var users = await _userRepository.GetAllUsersToPayoutAsync(range);
var allNewPayouts = new List<PayoutLineItem>();
IReadOnlyCollection<PayoutLineItem> combinedPayouts;
foreach (var incentiveUser in users)
{
try
{
combinedPayouts = await CreateCombinedDepartmentPayouts(incentiveUser, range);
if (!combinedPayouts.Any()) continue;
await _payoutRepository.SavePayoutLineItemRangeAsync(combinedPayouts);
allNewPayouts.AddRange(combinedPayouts);
}
catch (Exception ex)
{
Log.Fatal(ex, $"Employee {incentiveUser.EmployeeId} - {incentiveUser.FullName} experienced a failed payout calculation!");
}
}
await _unitOfWork.CompleteAsync();
return allNewPayouts;
}
public async Task<AccountingSummary> CreateMonthlyAccountingPayouts(MonthRange range)
{
var existing = await RetrieveMonthlyAccountingPayouts(range);
if (existing != null)
{
return existing;
}
var users = await _userRepository.GetAllUsersToPayoutAsync(range);
var allNewPayouts = new List<PayoutLineItem>();
var deptNames = new Dictionary<int, string>();
foreach (var incentiveUser in users)
{
try
{
var departments = incentiveUser.ActiveDepartmentsForRange(range);
foreach (var userDepartment in departments)
{
if (!deptNames.ContainsKey(userDepartment.Department.Id.Value))
{
deptNames.Add(userDepartment.Department.Id.Value, userDepartment.Department.DepartmentName);
}
var newPlan =
await _payoutCalculateService.BuildDepartmentPayoutView(incentiveUser, userDepartment,
range);
allNewPayouts.AddRange(newPlan.CommissionPayouts);
allNewPayouts.AddRange(newPlan.GoalPayouts);
}
}
catch (Exception ex)
{
Log.Fatal(ex, $"Employee {incentiveUser.EmployeeId} - {incentiveUser.FullName} experienced a failed accounting calculation!");
}
}
var summary = new AccountingSummary(range.StartTime.Date);
summary.Apply(allNewPayouts, deptNames);
await _accountingRepository.SaveAccountingSummaryAsync(summary);
await _unitOfWork.CompleteAsync();
return summary;
}
public async Task<AccountingSummary> RetrieveMonthlyAccountingPayouts(MonthRange range)
{
return await _accountingRepository.GetAccountingSummaryAsync(range.StartTime);
}
public async Task<IReadOnlyCollection<DepartmentPayout>> BuildUserDepartmentsPayoutView(AppUser employee,
MonthRange range)
{
var list = new List<DepartmentPayout>();
var departments = employee.ActiveDepartmentsForRange(range);
foreach (var userDepartment in departments)
{
var newPlan = await _payoutCalculateService.BuildDepartmentPayoutView(employee, userDepartment, range);
list.Add(newPlan);
}
return list;
}
public async Task<IReadOnlyCollection<PayoutEntry>> RetrieveMonthlyIncentivePayouts(DateTime period)
{
var payoutsRaw = await _payoutRepository.GetPayoutLineItemsForPeriodAsync(period);
var payouts = new Dictionary<string, PayoutEntry>();
foreach (var lineItem in payoutsRaw)
{
if (payouts.ContainsKey(lineItem.EmployeeId))
{
payouts[lineItem.EmployeeId].PayoutTotal += lineItem.PayoutTotal;
}
else
{
var employee = await _userRepository.FindUserByEmployeeIdAsync(lineItem.EmployeeId);
payouts[lineItem.EmployeeId] = new PayoutEntry()
{
Code = "Incentive",
EmployeeId = lineItem.EmployeeId,
PayoutTotal = lineItem.PayoutTotal,
EmployeeName = employee.FullName
};
}
}
return payouts.Values;
}
public async Task<IReadOnlyCollection<PayoutLineItem>> CreateCombinedDepartmentPayouts(AppUser incentiveAppUser, MonthRange range)
{
var combinedPayouts = new List<PayoutLineItem>();
var existing = await _payoutRepository.GetEmployeePayoutsForPeriod(incentiveAppUser, range.StartTime);
if (existing.Any()) return combinedPayouts;
var payouts = await BuildUserDepartmentsPayoutView(incentiveAppUser, range);
foreach (var departmentPayout in payouts)
{
combinedPayouts.AddRange(departmentPayout.CommissionPayouts);
combinedPayouts.AddRange(departmentPayout.GoalPayouts);
}
return combinedPayouts;
}
public async Task<IReadOnlyCollection<PayoutLineItem>> RetrieveCombinedDepartmentPayouts(AppUser incentiveAppUser, MonthRange range)
{
var existing = await _payoutRepository.GetEmployeePayoutsForPeriod(incentiveAppUser, range.StartTime);
if (existing.Any()) return existing;
return await CreateCombinedDepartmentPayouts(incentiveAppUser, range);
}
public async Task<PayoutEntry> RetrieveUserMonthlyIncentivePayout(AppUser appUser, DateTime period)
{
var payoutsRaw = await _payoutRepository.GetEmployeePayoutsForPeriod(appUser, period);
var payout = new PayoutEntry()
{
Code = "Incentive",
EmployeeId = appUser.EmployeeId,
PayoutTotal = 0,
EmployeeName = appUser.FullName
};
foreach (var lineItem in payoutsRaw)
{
payout.PayoutTotal += lineItem.PayoutTotal;
}
return payout;
}
public async Task<TerminatedEmployeeReceipt> CreateTerminatedEmployeePayouts(AppUser appUser, AppUser manager)
{
var firstOfThisMonth = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
var rangeThisMonth = new MonthRange(firstOfThisMonth);
var rangeLastMonth = new MonthRange(firstOfThisMonth.AddMonths(-1));
var payouts = await CreateCombinedDepartmentPayouts(appUser, rangeThisMonth);
if (payouts.Any())
{
await _payoutRepository.SavePayoutLineItemRangeAsync(payouts);
}
payouts = await CreateCombinedDepartmentPayouts(appUser, rangeLastMonth);
if (payouts.Any())
{
await _payoutRepository.SavePayoutLineItemRangeAsync(payouts);
}
await _unitOfWork.CompleteAsync();
var payoutThisMonth = await RetrieveUserMonthlyIncentivePayout(appUser, rangeThisMonth.StartTime);
var payoutLastMonth = await RetrieveUserMonthlyIncentivePayout(appUser, rangeLastMonth.StartTime);
return new TerminatedEmployeeReceipt(appUser, payoutLastMonth, payoutThisMonth, manager);
}
}