287 lines
12 KiB
C#
287 lines
12 KiB
C#
using LCU.API.Internal.Operations.IncentiveApi.Extensions;
|
|
using LCU.Library.Internal.IncentiveApiModels.Entities;
|
|
using LCU.Library.Internal.IncentiveApiModels.Enums;
|
|
using LCU.API.Internal.Operations.IncentiveApi.Models;
|
|
using LCU.API.Internal.Operations.IncentiveApi.Models.GoalDispersal;
|
|
using LCU.API.Internal.Operations.IncentiveApiLib.Services;
|
|
|
|
namespace LCU.API.Internal.Operations.IncentiveApi.Services.DispersalStrategy;
|
|
|
|
public class DispersalStrategyService : IDispersalStrategyService
|
|
{
|
|
private readonly IUserService _userService;
|
|
public DispersalStrategyService(IUserService userService)
|
|
{
|
|
_userService = userService;
|
|
}
|
|
|
|
public GoalDispersalReceipt ExtrapolateFromExistingGoals(ExtrapolationGoalDispersalData data)
|
|
{
|
|
var receipt = new GoalDispersalReceipt(data.DepartmentId, GoalDispersalStrategy.ExtrapolateFromExistingGoals);
|
|
foreach (var (employeeId, modelEmployeeProductGoals) in data.ModelPersonnelGoals.Goals)
|
|
{
|
|
var employee = data.DispersalMonthPersonnel.FirstOrDefault(x => x.EmployeeId.Equals(employeeId));
|
|
if (employee == null)
|
|
{
|
|
var missingEmployee =
|
|
data.ModelPersonnelGoals.Personnel.FirstOrDefault(x => x.EmployeeId == employeeId);
|
|
receipt.AddNewDispersalError(GoalDispersalError.UserNoLongerInDepartment, employeeId,
|
|
$"This employee is not assigned to dept: {data.DepartmentId} for the selected dispersal month", missingEmployee?.FullName);
|
|
continue;
|
|
}
|
|
foreach (var (goalProduct, dispersalMonthGoalValue) in data.DispersalMonthDepartmentGoals.ProductGoals)
|
|
{
|
|
ExtrapolateAndCreateEmployeeGoalChangesForProduct(employee,goalProduct, data, receipt);
|
|
}
|
|
}
|
|
|
|
BuildDispersalReceipt(data.DispersalMonthPersonnel, receipt);
|
|
CheckForAndLogDuplicates(receipt.GoalChanges, "Extrapolate");
|
|
|
|
return receipt;
|
|
}
|
|
|
|
private void ExtrapolateAndCreateEmployeeGoalChangesForProduct(AppUser employee, Product product,
|
|
ExtrapolationGoalDispersalData data,
|
|
GoalDispersalReceipt receipt)
|
|
{
|
|
var dispersalMonthGoalValue = data.DispersalMonthDepartmentGoals.ProductGoals[product];
|
|
var dispersalEmployeePerProductGoal = data.DispersalMonthPersonnelGoals.Goals[employee.EmployeeId];
|
|
var modelEmployeePerProductGoal = data.ModelPersonnelGoals.Goals[employee.EmployeeId];
|
|
|
|
try
|
|
{
|
|
double modelPersonnelValue = modelEmployeePerProductGoal.ProductGoals[product];
|
|
double dispersalPersonnelValue = dispersalEmployeePerProductGoal.ProductGoals[product];
|
|
double modelDeptGoalValue = data.ModelDepartmentGoals.ProductGoals[product];
|
|
int newValue;
|
|
|
|
if (modelDeptGoalValue == 0)
|
|
{
|
|
newValue = 0;
|
|
}
|
|
else
|
|
{
|
|
newValue = (int) Math.Ceiling(modelPersonnelValue / modelDeptGoalValue * dispersalMonthGoalValue);
|
|
}
|
|
|
|
if (newValue == (int) dispersalPersonnelValue)
|
|
{
|
|
return;
|
|
}
|
|
|
|
receipt.GoalChanges.Add(new GoalChange()
|
|
{
|
|
AppUserId = employee.Id,
|
|
DepartmentId = data.DepartmentId,
|
|
EndDate = data.DispersalMonthStartDate.EndOfMonth(),
|
|
PersonId = employee.EmployeeId,
|
|
Product = product,
|
|
StartDate = data.DispersalMonthStartDate,
|
|
Value = newValue
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex,
|
|
$"Failed to create goal change for employee {employee.EmployeeId} on product: {product} in department {data.DepartmentId}");
|
|
receipt.AddNewDispersalError(GoalDispersalError.ErrorsDuringDisperse, employee.EmployeeId,
|
|
$"Failed to create goal change for product: {product}. See Logs.");
|
|
}
|
|
}
|
|
|
|
private List<GoalDispersalReceiptItem> BuildDispersalReceipt(List<AppUser> dispersalMonthPersonnel, GoalDispersalReceipt receipt)
|
|
{
|
|
dispersalMonthPersonnel.ForEach(user =>
|
|
{
|
|
if (receipt.GoalChanges.Any(x => x.PersonId == user.EmployeeId && x.Value > 0))
|
|
{
|
|
if (!user.UserHasPermission(Permission.HaveSalesGoals))
|
|
{
|
|
receipt.AddNewDispersalError(GoalDispersalError.DispersedToUserWithoutPermission,
|
|
user.EmployeeId, "User received goals, but does not have HasSalesGoal permission. May require manual update.", user.FullName);
|
|
}
|
|
}
|
|
|
|
if(!receipt.GoalChanges.Any(x => x.PersonId == user.EmployeeId && x.Value > 0))
|
|
{
|
|
if (user.UserHasPermission(Permission.HaveSalesGoals))
|
|
{
|
|
receipt.AddNewDispersalError(GoalDispersalError.NewUserRequiresManualEntry, user.EmployeeId,
|
|
"User did not receive goals, but has HasSalesGoal permission. May require manual update.", user.FullName);
|
|
}
|
|
}
|
|
});
|
|
|
|
return receipt.GoalDispersalErrors.ToList();
|
|
}
|
|
|
|
public GoalDispersalReceipt DefaultWeightedDispersal(DefaultWeightedGoalDispersalData data)
|
|
{
|
|
var receipt = new GoalDispersalReceipt(data.DepartmentId, GoalDispersalStrategy.DefaultDisperseWithWeights);
|
|
var allRolesInDept = data.DispersalMonthPersonnel.Select(x => x.Departments.First(d => d.DepartmentId == data.DepartmentId).Role).ToList();
|
|
|
|
foreach (var employee in data.DispersalMonthPersonnel)
|
|
{
|
|
foreach (var (goalProduct, goalValue) in data.DispersalMonthDepartmentGoals.ProductGoals)
|
|
{
|
|
try
|
|
{
|
|
var userDept = employee.Departments.First(x => x.DepartmentId == data.DepartmentId);
|
|
receipt.GoalChanges.Add(new GoalChange()
|
|
{
|
|
AppUserId = employee.Id,
|
|
DepartmentId = data.DepartmentId,
|
|
EndDate = data.DispersalMonthStartDate.EndOfMonth(),
|
|
PersonId = employee.EmployeeId,
|
|
Product = goalProduct,
|
|
StartDate = data.DispersalMonthStartDate,
|
|
Value = CalculateDefaultWeightedValue(userDept.Role, goalValue, allRolesInDept)
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex,
|
|
$"Failed to create goal change for employee {employee.EmployeeId} on product: {goalProduct} in department {data.DepartmentId}");
|
|
receipt.AddNewDispersalError(GoalDispersalError.ErrorsDuringDisperse, employee.EmployeeId,
|
|
$"Failed to create goal change for product: {goalProduct}. See logs.");
|
|
}
|
|
}
|
|
}
|
|
|
|
CheckForAndLogDuplicates(receipt.GoalChanges, "DefaultWeight");
|
|
|
|
return receipt;
|
|
}
|
|
|
|
public int CalculateDefaultWeightedValue(Role role, int goalValue, List<Role> allRolesInDept)
|
|
{
|
|
if (role == Role.PFO)
|
|
{
|
|
return (int) Math.Ceiling(goalValue * 0.6 / allRolesInDept.Count(x => x.Equals(Role.PFO)));
|
|
}
|
|
|
|
if (role == Role.BM)
|
|
{
|
|
return (int) Math.Ceiling(goalValue * 0.25 / allRolesInDept.Count(x => x.Equals(Role.BM)));
|
|
}
|
|
|
|
if (role == Role.ABM)
|
|
{
|
|
return (int) Math.Ceiling(goalValue * 0.15 / allRolesInDept.Count(x => x.Equals(Role.ABM)));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public GoalDispersalReceipt ExtrapolateSingleDepartmentProduct(DepartmentGoalExtrapolationRequest request, PersonnelGoals departmentPersonnelGoals)
|
|
{
|
|
var receipt = new GoalDispersalReceipt(request.DepartmentId, GoalDispersalStrategy.ExtrapolateSingleDepartmentProduct);
|
|
|
|
foreach (var (employeeId, personnelPerProductGoals) in departmentPersonnelGoals.Goals)
|
|
{
|
|
try
|
|
{
|
|
var existingEmployeeGoal = personnelPerProductGoals.ProductGoals[request.Product];
|
|
int newValue;
|
|
if (request.OriginalDepartmentGoal == 0)
|
|
{
|
|
newValue = existingEmployeeGoal;
|
|
}
|
|
else
|
|
{
|
|
newValue = (int)Math.Ceiling((decimal)request.NewDepartmentGoal / request.OriginalDepartmentGoal *
|
|
existingEmployeeGoal);
|
|
}
|
|
|
|
if (newValue == existingEmployeeGoal)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var appUser = _userService.GetActiveUserByEmployeeIdAsync(employeeId).GetAwaiter().GetResult();
|
|
receipt.GoalChanges.Add(new GoalChange()
|
|
{
|
|
AppUserId = appUser.Id,
|
|
PersonId = employeeId,
|
|
DepartmentId = request.DepartmentId,
|
|
Value = newValue,
|
|
Product = request.Product,
|
|
StartDate = request.DepartmentGoalStartDate.StartOfMonth(),
|
|
EndDate = request.DepartmentGoalEndDate.EndOfMonth()
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex,
|
|
$"Failed to create goal change for employee {employeeId} on product: {request.Product} in department {request.DepartmentId}");
|
|
receipt.AddNewDispersalError(GoalDispersalError.ErrorsDuringDisperse, employeeId,
|
|
$"Failed to create goal change for product: {request.Product}. See logs.");
|
|
}
|
|
}
|
|
|
|
return receipt;
|
|
|
|
}
|
|
|
|
public GoalDispersalReceipt
|
|
CustomWeightedDispersal(CustomWeightedDispersalRequest request, List<AppUser> personnel, int departmentGoal)
|
|
{
|
|
var receipt = new GoalDispersalReceipt(request.DepartmentId, GoalDispersalStrategy.CustomWeightedDispersal);
|
|
var dispersalDate = new DateTime(request.year, request.month, 01);
|
|
var allRolesInDept = personnel
|
|
.Select(x => x.Departments.FirstOrDefault(d => d.DepartmentId == request.DepartmentId))
|
|
.Where(x => x != null)
|
|
.Select(x => x.Role)
|
|
.ToList();
|
|
|
|
foreach (var user in personnel)
|
|
{
|
|
var userDept = user.Departments.FirstOrDefault(x => x.DepartmentId == request.DepartmentId);
|
|
if(userDept == null){continue;}
|
|
if (request.DispersalWeightByRole.TryGetValue(userDept.Role.Name, out var percentage))
|
|
{
|
|
var newValue = (int)Math.Ceiling(departmentGoal * percentage /
|
|
allRolesInDept.Count(x =>
|
|
x.Name.ToLowerInvariant() == userDept.Role.Name.ToLowerInvariant()));
|
|
receipt.GoalChanges.Add(new GoalChange()
|
|
{
|
|
AppUserId = user.Id,
|
|
PersonId = user.EmployeeId,
|
|
DepartmentId = request.DepartmentId,
|
|
Value = newValue,
|
|
Product = request.DispersalProduct,
|
|
StartDate = dispersalDate.StartOfMonth(),
|
|
EndDate = dispersalDate.EndOfMonth()
|
|
});
|
|
}
|
|
}
|
|
|
|
CheckForAndLogDuplicates(receipt.GoalChanges, "CustomWeight");
|
|
|
|
return receipt;
|
|
}
|
|
|
|
|
|
private void CheckForAndLogDuplicates(List<GoalChange> goalChanges, string strategy)
|
|
{
|
|
if (goalChanges == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var duplicateGoals = goalChanges.GroupBy(g => new {
|
|
g.StartDate,
|
|
g.EndDate,
|
|
g.Product,
|
|
g.Value,
|
|
g.PersonId })
|
|
.Where(g => g.Skip(1).Any());
|
|
|
|
if (duplicateGoals.Any())
|
|
{
|
|
Log.Information($"Duplicate goals in department {goalChanges.Select(g => g.DepartmentId).FirstOrDefault()} during {strategy} strategy.");
|
|
}
|
|
}
|
|
}
|