Adding
This commit is contained in:
+286
@@ -0,0 +1,286 @@
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
using LCU.Library.Internal.IncentiveApiModels.Entities;
|
||||
using LCU.API.Internal.Operations.IncentiveApi.Models;
|
||||
using LCU.API.Internal.Operations.IncentiveApi.Models.GoalDispersal;
|
||||
|
||||
namespace LCU.API.Internal.Operations.IncentiveApi.Services.DispersalStrategy;
|
||||
|
||||
public interface IDispersalStrategyService
|
||||
{
|
||||
public GoalDispersalReceipt ExtrapolateFromExistingGoals(ExtrapolationGoalDispersalData data);
|
||||
public GoalDispersalReceipt DefaultWeightedDispersal(DefaultWeightedGoalDispersalData data);
|
||||
public int CalculateDefaultWeightedValue(Role userRole, int goalValue, List<Role> allRolesInDept);
|
||||
public GoalDispersalReceipt ExtrapolateSingleDepartmentProduct(DepartmentGoalExtrapolationRequest request,
|
||||
PersonnelGoals departmentPersonnelGoals);
|
||||
|
||||
public GoalDispersalReceipt CustomWeightedDispersal(CustomWeightedDispersalRequest request,
|
||||
List<AppUser> personnel, int departmentGoal);
|
||||
}
|
||||
Reference in New Issue
Block a user