using LCU.API.Internal.Operations.IncentiveApi.Services.DispersalStrategy; using LCU.API.Internal.Operations.IncentiveApi.Models.GoalDispersal; using LCU.API.Internal.Operations.IncentiveApiLib.Services; using LCU.API.Internal.Operations.IncentiveApiLib.Repositories; using LCU.Library.Internal.IncentiveApiModels.Requests; using LCU.API.Internal.Operations.IncentiveApi.Services.MicroApiServices; using LCU.Library.Internal.ApiResponse.Services; using LCU.Library.Internal.Exceptions; using LCU.Library.Internal.IncentiveApiModels.Results; namespace LCU.API.Internal.Operations.IncentiveApi.Services; public class GoalDispersalService : IGoalDispersalService { private readonly IUserService _userService; private readonly IGoalService _goalService; private readonly IDepartmentRepository _departmentRepository; private readonly INotificationApiService _notificationService; private readonly IDispersalStrategyService _dispersalStrategyService; private readonly ProcessResult _goalDispersalProcessResult; public GoalDispersalService( IUserService userService, IGoalService goalService, IDepartmentRepository departmentRepository, INotificationApiService notificationService, IDispersalStrategyService dispersalStrategyService ) { _userService = userService; _goalService = goalService; _departmentRepository = departmentRepository; _notificationService = notificationService; _dispersalStrategyService = dispersalStrategyService; _goalDispersalProcessResult = new ProcessResult(); } public async Task DisperseGoalsByExtrapolatingLastMonth(int departmentId, int dispersalMonth, int dispersalYear) { try { var lastMonth = dispersalMonth == 1 ? 12 : dispersalMonth - 1; var lastMonthCorrectedYear = dispersalMonth == 1 ? dispersalYear - 1 : dispersalYear; var dispersalMonthStartDateTime = DateTime.Parse($"{dispersalYear}-{dispersalMonth}-01"); var departmentGoals = await _goalService.GetAnnualDepartmentGoalsAsync(departmentId, dispersalYear); var dispersalMonthDepartmentGoals = departmentGoals.MonthlyGoals[dispersalMonth - 1]; var dispersalMonthPersonnel = await _userService.FindDepartmentAssignments( new DepartmentPersonnelSearchRequest() { DepartmentId = departmentId, Month = dispersalMonth, Year = dispersalYear, IncludeAllPersonnel = true } ); var dispersalMonthPersonnelGoals = await _goalService.GetMonthlyPersonnelGoalsAsync( new DepartmentPersonnelSearchRequest() { DepartmentId = departmentId, Month = dispersalMonth, Year = dispersalYear, IncludeAllPersonnel = true } ); var modelMonthPersonnelGoals = await _goalService.GetMonthlyPersonnelGoalsAsync( new DepartmentPersonnelSearchRequest() { DepartmentId = departmentId, Month = lastMonth, Year = lastMonthCorrectedYear, IncludeAllPersonnel = true } ); if (modelMonthPersonnelGoals.GoalAudit.All(x => x.Value < 1)) { var weightedDispersalRequest = new DefaultWeightedGoalDispersalData() { DepartmentId = departmentId, DispersalMonthStartDate = dispersalMonthStartDateTime, DispersalMonthPersonnel = dispersalMonthPersonnel, DispersalMonthDepartmentGoals = dispersalMonthDepartmentGoals }; var weightedReceipt = _dispersalStrategyService.DefaultWeightedDispersal(weightedDispersalRequest); await _goalService.SaveGoalRangeAsync(weightedReceipt.GoalChanges); return weightedReceipt; } departmentGoals = await _goalService.GetAnnualDepartmentGoalsAsync(departmentId, lastMonthCorrectedYear); var lastMonthDepartmentGoals = departmentGoals.MonthlyGoals[lastMonth - 1]; var extrapolateGoalsDispersalRequest = new ExtrapolationGoalDispersalData() { DepartmentId = departmentId, DispersalMonthStartDate = dispersalMonthStartDateTime, DispersalMonthDepartmentGoals = dispersalMonthDepartmentGoals, DispersalMonthPersonnelGoals = dispersalMonthPersonnelGoals, ModelDepartmentGoals = lastMonthDepartmentGoals, ModelPersonnelGoals = modelMonthPersonnelGoals, DispersalMonthPersonnel = dispersalMonthPersonnel }; var receipt = _dispersalStrategyService.ExtrapolateFromExistingGoals(extrapolateGoalsDispersalRequest); await _goalService.SaveGoalRangeAsync(receipt.GoalChanges); return receipt; } catch (Exception ex) { Log.Error(ex, $"Failed to DisperseGoalsForNextMonth for dept:{departmentId} month:{dispersalMonth} dispersalYear: {dispersalYear}"); throw new Exceptions.ServerErrorException(); } } public async Task> DisperseAllDepartmentGoalsForMonth(int month, int year) { var results = new Dictionary(); var departments = await _departmentRepository.GetAllDepartmentsAsync(); foreach (var department in departments) { try { if (department.Id == null) { throw new Exception($"Department Has No Id. Skipping Dispersal: {department.DepartmentName}"); } var departmentKey = $"{department.Id} - {department.DepartmentName}"; Log.Debug($"Dispersing Goals for Department: {departmentKey}"); var receipt = await DisperseGoalsByExtrapolatingLastMonth((int)department.Id, month, year); results.Add(departmentKey, receipt); Log.Debug($"Completed Dispersal for: {departmentKey}"); } catch (Exception ex) { var departmentKey = $"{department.Id} - {department.DepartmentName}"; Log.Error(ex, $"Failed Month Start Auto-Disperse of Personnel Goals for Department: {departmentKey}"); results.Add(departmentKey, null); } } return results; } public async Task ExecuteMonthStartDispersal(GoalDispersalRequest request) { try { Log.Information("Executing Monthly Goal Dispersal"); var result = await DisperseAllDepartmentGoalsForMonth(request.StartDate.Month, request.StartDate.Year); Log.Information("Finished Monthly Goal Dispersal. Sending Report Email."); await _notificationService.SendMonthStartGoalDispersalReceipt(result, request.GoalDispersalNotification); _goalDispersalProcessResult.MessageStack.Add($"Goal Dispersal Service Processed for {request.StartDate.Month}{request.StartDate.Year} records."); Log.Information("Successfully Sent Report Email."); await QueueReturnResponse.QueueResponse(request, _goalDispersalProcessResult.Success, _goalDispersalProcessResult.MessageStack); } catch (Exception exception) { Log.Fatal(exception, "There was an Unhandled error Executing Month Start Dispersal"); var message = $"There was an issue with handling the Goal Dispersal Service Process {request.StartDate.Month}{request.StartDate.Year}. Exception Message: {exception.Message}."; _goalDispersalProcessResult.MessageStack.Add(message); _goalDispersalProcessResult.Success = false; await QueueReturnResponse.QueueResponse(request, _goalDispersalProcessResult.Success, _goalDispersalProcessResult.MessageStack); throw new ServiceException(message); } } public async Task DepartmentGoalChangeReDispersal(DepartmentGoalExtrapolationRequest request) { var personnelGoals = await _goalService.GetMonthlyPersonnelGoalsAsync(new DepartmentPersonnelSearchRequest() { DepartmentId = request.DepartmentId, IncludeAllPersonnel = true, Year = request.DepartmentGoalStartDate.Year, Month = request.DepartmentGoalStartDate.Month, }); var receipt = _dispersalStrategyService.ExtrapolateSingleDepartmentProduct(request, personnelGoals); return receipt; } public async Task CustomWeightedDispersal(CustomWeightedDispersalRequest request) { try { var departmentGoals = await _goalService.GetAnnualDepartmentGoalsAsync(request.DepartmentId, request.year); var productDepartmentGoal = departmentGoals.MonthlyGoals[request.month - 1].ProductGoals[request.DispersalProduct]; var departmentPersonnel = await _userService.FindDepartmentAssignments( new DepartmentPersonnelSearchRequest() { DepartmentId = request.DepartmentId, Month = request.month, Year = request.year, IncludeAllPersonnel = true } ); var receipt = _dispersalStrategyService.CustomWeightedDispersal(request, departmentPersonnel, productDepartmentGoal); await _goalService.SaveGoalRangeAsync(receipt.GoalChanges); return receipt; } catch (Exception ex) { Log.Error(ex, "Custom Weighted Dispersal Failed"); throw new Exceptions.ServerErrorException(); } } }