Files
monorepo/LCU.API.Operations.IncentiveAPI/LCU.API.Internal.Operations.IncentiveApi/Services/Dayforce/DayForceSyncService.cs
T
2026-04-27 15:26:58 +00:00

293 lines
12 KiB
C#

using LCU.API.Internal.Operations.IncentiveApi.Services.Payout;
using LCU.API.Internal.Operations.IncentiveApi.Models.Dayforce;
using LCU.API.Internal.Operations.IncentiveApiLib.Services;
using LCU.API.Internal.Operations.IncentiveApiLib.Repositories;
using LCU.Library.Internal.IncentiveApiModels.Entities;
using LCU.Library.Internal.IncentiveApiModels.Results;
using LCU.API.Internal.Operations.IncentiveApi.Services;
using LCU.API.Internal.Operations.IncentiveApi.Services.MicroApiServices;
using LCU.API.Internal.Operations.IncentiveApi.Configuration;
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.Dayforce;
public class DayForceSyncService : IDayForceSyncService
{
private readonly IUnitOfWork _unitOfWork;
private readonly IUserService _userService;
private readonly INotificationApiService _notificationService;
private readonly IPayoutService _payoutService;
private readonly IDayForceClientService _dayForceClientService;
private readonly ISettingsService _settingsService;
private readonly DayForceConfiguration _dayForceConfig;
private readonly ProcessResult _dayforceSyncProcessResult;
public DayForceSyncService(
IUnitOfWork unitOfWork,
IUserService userService,
INotificationApiService notificationService,
IPayoutService payoutService,
IDayForceClientService dayForceClientService,
ISettingsService settingsService,
DayForceConfiguration dayForceConfig
)
{
_unitOfWork = unitOfWork;
_userService = userService;
_notificationService = notificationService;
_payoutService = payoutService;
_dayForceClientService = dayForceClientService;
_settingsService = settingsService;
_dayForceConfig = dayForceConfig;
_dayforceSyncProcessResult = new ProcessResult();
}
public async Task SyncDayForceUsers(DayforceProcessRequest request)
{
try
{
Log.Information("Starting Dayforce Sync Process for Active Users.");
if (request.MissingRoleNotification == null)
{
request.MissingRoleNotification = _dayForceConfig.DayForceManualReportRecipients;
}
if (request.TerminatedEmployeeNotification == null)
{
request.TerminatedEmployeeNotification = _dayForceConfig.DayForceManualReportRecipients;
}
var activeUsersResult = await SyncActiveDayForceUsers(request.MissingRoleNotification);
Log.Information("Completed Dayforce Sync Process for Active Users.");
_dayforceSyncProcessResult.MessageStack.Add($"Dayforce Sync Process for Active Users complete with {activeUsersResult.Count()}");
Log.Information("Starting Dayforce Sync Process for Terminated Users.");
var terminatedUsersResult = await SyncTerminatedDayForceUsers(request.TerminatedEmployeeNotification);
Log.Information("Completed Dayforce Sync Process for Terminated Users.");
_dayforceSyncProcessResult.MessageStack.Add($"Dayforce Sync Process for Terminated Users complete with {terminatedUsersResult.Count()}");
await SafeQueueReturnResponse.TryQueueResponse(request, _dayforceSyncProcessResult.Success, _dayforceSyncProcessResult.MessageStack, nameof(DayForceSyncService));
}
catch ( Exception exception )
{
Log.Fatal(exception, "There was an Unhandled error Executing Dayforce Sync Process");
var message = $"There was an issue with handling the Dayforce Sync Service Process Exception Message: {exception.Message}.";
_dayforceSyncProcessResult.MessageStack.Add(message);
_dayforceSyncProcessResult.Success = false;
await SafeQueueReturnResponse.TryQueueResponse(request, _dayforceSyncProcessResult.Success, _dayforceSyncProcessResult.MessageStack, nameof(DayForceSyncService));
}
}
public async Task<IReadOnlyCollection<ActiveEmployeeReceipt>> SyncActiveDayForceUsers(List<string> recipients)
{
var report = await _dayForceClientService.GetReportData(_dayForceConfig.DayForceActiveUserReport);
var results = new List<ActiveEmployeeReceipt>();
if (report.Rows == null)
{
throw new Exception("Dayforce returned invalid active report");
}
foreach (var reportRow in report.Rows)
{
var receipt = new ActiveEmployeeReceipt(reportRow);
results.Add(receipt);
try
{
var employee = new ActiveEmployeeResult(reportRow);
receipt.EmployeeRow = employee;
if (!employee.CanProcess())
{
Log.Error("Cannot process dayforce employee. {reportRow}", reportRow.Values);
receipt.Status = ActiveEmployeeReceiptStatus.MISSING_DATA;
continue;
}
var appUser = await _userService.SyncDayForceEmployee(employee);
receipt.AppUser = appUser;
receipt.Status = ActiveEmployeeReceiptStatus.SUCCESS;
if (appUser.Departments.Any(x => x.Role == Role.MISSING_ROLE))
{
receipt.Status = ActiveEmployeeReceiptStatus.MISSING_ROLE;
}
}
catch (Exception ex)
{
Log.Error(ex, "Failed to process dayforce employee. {reportRow}", reportRow.Values);
receipt.Status = ActiveEmployeeReceiptStatus.EXCEPTION;
}
}
var nonExistentRoleAssignments = results
.Where(x => x.Status == ActiveEmployeeReceiptStatus.MISSING_ROLE)
.Select(x => x.AppUser)
.ToList();
if (nonExistentRoleAssignments.Any())
{
var newRoleMappings = nonExistentRoleAssignments.Select(x => x.BaseRole).ToList().Distinct();
await _notificationService.SendMissingRoleNotification(nonExistentRoleAssignments, recipients);
await _settingsService.SaveNewUnknownRoleMappingRangeAsync(newRoleMappings);
}
return results;
}
public async Task<IReadOnlyCollection<TerminatedEmployeeReceipt>> SyncTerminatedDayForceUsers(List<string> recipients)
{
var report = await _dayForceClientService.GetReportData(_dayForceConfig.DayForceTerminatedUserReport);
var terminated = new List<TerminatedEmployeeReceipt>();
if (report.Rows == null)
{
throw new Exception("Dayforce returned invalid termination report");
}
foreach (var reportRow in report.Rows)
{
try
{
var employee = new TerminatedEmployeeResult(reportRow);
var user = await _userService.TerminateDayForceEmployee(employee);
if (user != null)
{
var manager = await _userService.FindManager(user);
var receipt = await _payoutService.CreateTerminatedEmployeePayouts(user, manager);
terminated.Add(receipt);
}
}
catch (Exception ex)
{
Log.Error(ex, "Failed to term dayforce employee. {reportRow}", reportRow.Values);
}
}
if (terminated.Any())
{
await _notificationService.SendTerminatedEmployeeNotification(terminated, recipients);
}
return terminated;
}
public async Task<EmployeeDetailsUpdateResult> UpdateEmployeeDetails(EmployeeDetailsUpdate employeeDetailsUpdate)
{
var currentAppUser = await _userService.GetUserByAdEmailAsync(employeeDetailsUpdate.OldEmail);
if (currentAppUser == null)
{
return new EmployeeDetailsUpdateResult
{
UpdateMessage = $"Employee Details Update Failed, No Current Users Returned From Database"
};
}
if (!string.IsNullOrWhiteSpace(employeeDetailsUpdate.InvalidUpdateData()))
{
return new EmployeeDetailsUpdateResult
{
AppUser = currentAppUser,
UpdateMessage = $"Employee Details Update Failed, {employeeDetailsUpdate.InvalidUpdateData()}"
};
}
var existingUserForNewEmail = await _userService.GetActiveUserByAdEmailAsync(employeeDetailsUpdate.NewEmail);
if (existingUserForNewEmail != null
&& !existingUserForNewEmail.AdEmail.Equals(currentAppUser.AdEmail, StringComparison.InvariantCultureIgnoreCase))
{
return new EmployeeDetailsUpdateResult
{
AppUser = currentAppUser,
UpdateMessage = $"Employee Details Update Failed, Another Active User Already Exists With Email: {employeeDetailsUpdate.NewEmail}"
};
}
var existingUserForNewEmployeeId = await _userService.GetActiveUserByEmployeeIdAsync(employeeDetailsUpdate.NewEmployeeId);
if (existingUserForNewEmployeeId != null
&& !existingUserForNewEmployeeId.EmployeeId.Equals(currentAppUser.EmployeeId, StringComparison.InvariantCultureIgnoreCase))
{
return new EmployeeDetailsUpdateResult
{
AppUser = currentAppUser,
UpdateMessage = $"Employee Details Update Failed, Another Active User Already Exists With EmployeeId: {employeeDetailsUpdate.NewEmployeeId}"
};
}
var hcmUsersMatched = await MatchingHcmUserCount(employeeDetailsUpdate);
if (hcmUsersMatched != 1)
{
return new EmployeeDetailsUpdateResult
{
AppUser = currentAppUser,
UpdateMessage = $"Employee Details Update Failed, {hcmUsersMatched} Users Returned From HCM System Matching New Details"
};
}
return await _userService.UpdateEmployeeDetails(employeeDetailsUpdate);
}
public async Task<IReadOnlyCollection<ActiveEmployeeResult>> GetMatchingHcmUsers(string email = "", string employeeId = "", string fullName = "")
{
if (
string.IsNullOrWhiteSpace(email)
&& string.IsNullOrWhiteSpace(employeeId)
&& string.IsNullOrWhiteSpace(fullName)
)
{
return new List<ActiveEmployeeResult>();
}
var dayForceUsers = await GetAllHcmUsers();
return dayForceUsers.Where(x =>
x.EmployeeNumber.ToString().Equals(employeeId, StringComparison.InvariantCultureIgnoreCase)
|| x.DisplayName.ToString().Equals(fullName, StringComparison.InvariantCultureIgnoreCase)
|| x.LoginName.ToString().Equals(email, StringComparison.InvariantCultureIgnoreCase)
)
.ToList();
}
private async Task<IReadOnlyCollection<ActiveEmployeeResult>> GetAllHcmUsers()
{
var report = await _dayForceClientService.GetReportData(_dayForceConfig.DayForceActiveUserReport);
var results = new List<ActiveEmployeeResult>();
if (report.Rows == null)
{
throw new Exception("Dayforce returned invalid active report");
}
foreach (var reportRow in report.Rows)
{
try
{
var employee = new ActiveEmployeeResult(reportRow);
results.Add(employee);
}
catch (Exception ex)
{
Log.Error(ex, "Failed to parse dayforce employee. {reportRow}", reportRow.Values);
}
}
return results.Where(x => x.CanProcess()).OrderBy(x => x.DisplayName).ToList();
}
private async Task<int> MatchingHcmUserCount(EmployeeDetailsUpdate employeeDetailsUpdate)
{
var dayForceUsers = await GetAllHcmUsers();
var matchingDayforceUsers = dayForceUsers.Where(x =>
x.EmployeeNumber.ToString().Equals(employeeDetailsUpdate.NewEmployeeId, StringComparison.InvariantCultureIgnoreCase)
&& x.DisplayName.ToString().Equals(employeeDetailsUpdate.NewFullName, StringComparison.InvariantCultureIgnoreCase)
&& x.LoginName.ToString().Equals(employeeDetailsUpdate.NewEmail, StringComparison.InvariantCultureIgnoreCase)
)
.ToList();
return matchingDayforceUsers.Count;
}
}