244 lines
10 KiB
C#
244 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.API.Internal.Operations.IncentiveApi.Services;
|
|
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 SafeQueueReturnResponse.TryQueueResponse(request, _payoutIncentiveProcessResult.Success, _payoutIncentiveProcessResult.MessageStack, nameof(PayoutService));
|
|
}
|
|
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 SafeQueueReturnResponse.TryQueueResponse(request, _payoutIncentiveProcessResult.Success, _payoutIncentiveProcessResult.MessageStack, nameof(PayoutService));
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|