This commit is contained in:
Zeeshaun
2026-03-17 09:53:09 -05:00
parent cdcba54aa4
commit 1a0702a79a
590 changed files with 62058 additions and 0 deletions
@@ -0,0 +1,604 @@
using LCU.API.Internal.Operations.IncentiveApi.Exceptions;
using LCU.API.Internal.Operations.IncentiveApiLib.Persistence;
using LCU.API.Internal.Operations.IncentiveApi.Models.DataTransfer;
using LCU.API.Internal.Operations.IncentiveApi.Models.Dayforce;
using LCU.API.Internal.Operations.IncentiveApiLib.Repositories;
using LCU.Library.Internal.IncentiveApiModels.Entities;
using LCU.Library.Internal.IncentiveApiModels.Requests;
using LCU.Library.Internal.IncentiveApiModels.Common;
using LCU.Library.Internal.IncentiveApiModels.Results;
namespace LCU.API.Internal.Operations.IncentiveApiLib.Services;
public class UserService : IUserService
{
private const string AutoGeneratedHomeAssignment = "Auto Generated Home Assignment";
private readonly IApplicationDbIntegrityService _applicationDbIntegrityService;
private readonly IDepartmentRepository _departmentRepository;
private readonly IRoleMappingRepository _roleMappingRepository;
private readonly IUnitOfWork _unitOfWork;
private readonly IUserDepartmentHistoryRepository _userDepartmentHistoryRepository;
private readonly IUserPermissionHistoryRepository _userPermissionHistoryRepository;
private readonly IUserRepository _userRepository;
public UserService(
IApplicationDbIntegrityService applicationDbIntegrityService,
IDepartmentRepository departmentRepository,
IRoleMappingRepository roleMappingRepository,
IUnitOfWork unitOfWork,
IUserDepartmentHistoryRepository userDepartmentHistoryRepository,
IUserPermissionHistoryRepository userPermissionHistoryRepository,
IUserRepository userRepository
)
{
_applicationDbIntegrityService = applicationDbIntegrityService;
_departmentRepository = departmentRepository;
_roleMappingRepository = roleMappingRepository;
_unitOfWork = unitOfWork;
_userDepartmentHistoryRepository = userDepartmentHistoryRepository;
_userPermissionHistoryRepository = userPermissionHistoryRepository;
_userRepository = userRepository;
}
public async Task<IReadOnlyCollection<UserSummary>> GetAllUserSummariesAsync()
{
return await _userRepository.GetAllUserSummariesAsync();
}
public IReadOnlyCollection<UserSummary> GetTerminatedUsersAsync(MonthRange monthRange)
{
return _userRepository.GetTerminatedUsersAsync(monthRange);
}
public async Task<ICollection<AppUser>> GetAllUsersAsync(UserSearchRequest userSearchRequest)
{
if (string.IsNullOrWhiteSpace(userSearchRequest.Query) &&
string.IsNullOrWhiteSpace(userSearchRequest.Role) &&
string.IsNullOrWhiteSpace(userSearchRequest.FirstName) &&
string.IsNullOrWhiteSpace(userSearchRequest.LastName) &&
string.IsNullOrEmpty(userSearchRequest.DnaOrgNbr))
{
return await _userRepository.GetAllUsersAsync();
}
return await _userRepository.SearchUsersAsync(userSearchRequest);
}
public async Task<AppUser> GetUserByEmployeeIdAsync(string employeeId)
{
return await _userRepository.FindUserByEmployeeIdAsync(employeeId);
}
public async Task<AppUser> GetUserByAdEmailAsync(string adEmail)
{
return await _userRepository.FindUserByAdEmailAsync(adEmail);
}
public async Task<AppUser> GetActiveUserByEmployeeIdAsync(string employeeId)
{
return await _userRepository.FindActiveUserByEmployeeIdAsync(employeeId);
}
public async Task<AppUser> GetActiveUserByAdEmailAsync(string adEmail)
{
return await _userRepository.FindUserByAdEmailAsync(adEmail);
}
public async Task<AppUser> FindUserAsync(string employeeId, string adEmail)
{
return await _userRepository.FindUserAsync(employeeId, adEmail);
}
public async Task<AppUser> SaveUserAsync(AppUser newAppUser)
{
var user = await _userRepository.SaveUserAsync(newAppUser);
await _unitOfWork.CompleteAsync();
return user;
}
public async Task<ICollection<AppUser>> SaveUserRangeAsync(List<AppUser> newUsers)
{
var users = await _userRepository.SaveUserRangeAsync(newUsers);
await _unitOfWork.CompleteAsync();
return users;
}
private async Task<AppUser> SyncDayForceEmployeeExisting(AppUser existing, ActiveEmployeeResult activeEmployee)
{
var homeUserDepartment = existing.Departments.FirstOrDefault(x => x.IsHome && x.EndDate == null);
var salesforceDepartment = await LookupDepartment(activeEmployee);
if (homeUserDepartment == null && existing.Terminated != null)
{
homeUserDepartment = await GenerateNewEmployeeHomeDepartment(existing, activeEmployee);
existing.Departments.Add(homeUserDepartment);
existing.Terminated = null;
}
if (homeUserDepartment == null)
{
throw new Exception($"Existing user with no home department, {existing.EmployeeId}");
}
var homeDepartmentId = homeUserDepartment.Department == null
? homeUserDepartment.DepartmentId
: homeUserDepartment.Department.Id;
if (homeDepartmentId != salesforceDepartment.Id)
{
var monthBegin = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
var nextMonthBegin = monthBegin.AddMonths(1);
var existingAssignment = existing.Departments.FirstOrDefault(x =>
x.SafeGetDepartmentId() == salesforceDepartment.Id
&& x.EndDate == null || x.EndDate >= monthBegin);
if (existingAssignment != null)
{
existingAssignment.EndDate = null;
existingAssignment.IsHome = true;
existingAssignment.Role = _roleMappingRepository.FindMatchingRoleFromBusinessRole(activeEmployee.Role);
}
else
{
var assignment = new UserDepartment()
{
Employee = existing,
Note = AutoGeneratedHomeAssignment,
IsHome = true,
Department = salesforceDepartment,
StartDate = monthBegin,
SlidingPayout = 100,
Role = _roleMappingRepository.FindMatchingRoleFromBusinessRole(activeEmployee.Role)
};
existing.Departments.Add(assignment);
}
homeUserDepartment.IsHome = false;
homeUserDepartment.EndDate = nextMonthBegin.Subtract(TimeSpan.FromDays(1));
}
else
{
homeUserDepartment.Role = _roleMappingRepository.FindMatchingRoleFromBusinessRole(activeEmployee.Role);
}
existing.HomeDepartment = activeEmployee.OrgUnit;
existing.BaseRole = activeEmployee.Role;
existing.FullName = activeEmployee.DisplayName;
existing.ManagerKey = activeEmployee.ManagerKey;
existing.Role = _roleMappingRepository.FindMatchingRoleFromBusinessRole(activeEmployee.Role);
foreach (var userDepartment in existing.Departments?.Where(department =>
!department.IsHome && string.Equals(department.Note, AutoGeneratedHomeAssignment, StringComparison.InvariantCultureIgnoreCase)))
{
userDepartment.Note = "";
}
return existing;
}
private async Task<AppUser> SyncDayForceEmployeeNew(ActiveEmployeeResult activeEmployee)
{
var newEmployee = new AppUser
{
Role = _roleMappingRepository.FindMatchingRoleFromBusinessRole(activeEmployee.Role),
AdEmail = activeEmployee.LoginName,
EmployeeId = activeEmployee.EmployeeNumber,
HomeDepartment = activeEmployee.OrgUnit,
BaseRole = activeEmployee.Role,
FullName = activeEmployee.DisplayName,
ManagerKey = activeEmployee.ManagerKey,
Departments = new List<UserDepartment>()
};
newEmployee.Departments.Add(await GenerateNewEmployeeHomeDepartment(newEmployee, activeEmployee));
await SaveUserAsync(newEmployee);
return newEmployee;
}
private async Task<UserDepartment> GenerateNewEmployeeHomeDepartment(AppUser appUser, ActiveEmployeeResult activeEmployee)
{
var hireDate = activeEmployee.DetermineHireDate();
var assignment = new UserDepartment()
{
Employee = appUser,
Note = AutoGeneratedHomeAssignment,
IsHome = true,
Department = await LookupDepartment(activeEmployee),
StartDate = new DateTime(hireDate.Year, hireDate.Month, 1),
SlidingPayout = 100,
Role = _roleMappingRepository.FindMatchingRoleFromBusinessRole(activeEmployee.Role)
};
return assignment;
}
public async Task<AppUser> SyncDayForceEmployee(ActiveEmployeeResult activeEmployee)
{
var existing = await GetUserByEmployeeIdAsync(activeEmployee.EmployeeNumber);
if (existing != null)
{
existing = await SyncDayForceEmployeeExisting(existing, activeEmployee);
await _unitOfWork.CompleteAsync();
}
else
{
existing = await SyncDayForceEmployeeNew(activeEmployee);
}
return existing;
}
public async Task<AppUser> TerminateDayForceEmployee(TerminatedEmployeeResult terminatedEmployee)
{
var existingAppUser = await GetUserByEmployeeIdAsync(terminatedEmployee.EmployeeNumber);
if (!(existingAppUser is { Terminated: null })) return null;
var dataSyncSucceeded = await SyncUserIds(existingAppUser);
if (!dataSyncSucceeded)
{
throw new Exception($"Failed to sync AppUser data for AppUserId: {existingAppUser.Id}");
}
var monthBegin = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
var nextMonthBegin = monthBegin.AddMonths(1);
var termDeptDate = nextMonthBegin.Subtract(TimeSpan.FromDays(1));
existingAppUser.AdditionalPermissions.Clear();
existingAppUser.Terminated = terminatedEmployee.TerminationDate;
foreach (var department in existingAppUser.Departments)
{
if (department.EndDate == null || department.EndDate.Value.Date > termDeptDate.Date)
{
department.EndDate = termDeptDate;
}
}
await _unitOfWork.CompleteAsync();
return existingAppUser;
}
private async Task<bool> SyncUserIds(AppUser appUser)
{
return await _applicationDbIntegrityService.SyncIdsWithDb(appUser);
}
public async Task<List<UserDepartment>> AssignDepartments(
string employeeId,
DepartmentAssignmentRequest assignmentRequest
)
{
var employee = await GetUserByEmployeeIdAsync(employeeId);
if (employee == null)
{
throw new NotFoundException("Employee Not Found");
}
var newAssignments = new List<UserDepartment>();
foreach (var departmentId in assignmentRequest.DepartmentId)
{
var department = await _departmentRepository.FindDepartmentByIdAsync(departmentId);
if (department == null)
{
throw new NotFoundException("Department Not Found");
}
var role = Role.GetRolesList()
.FirstOrDefault(role => role.Name.Equals(assignmentRequest.Role));
if (role == null)
{
throw new BadDepartmentAssignmentException("Invalid Or Missing Role");
}
if (assignmentRequest.SlidingPayout < 0 || assignmentRequest.SlidingPayout > UserDepartment.PayoutMax)
{
throw new BadDepartmentAssignmentException(
$"Sliding Payout must be between 0 and {UserDepartment.PayoutMax}");
}
var newAssignment = new UserDepartment()
{
Employee = employee,
Department = department,
Note = assignmentRequest.Note,
StartDate = assignmentRequest.StartDate,
EndDate = assignmentRequest.EndDate,
Role = role,
SlidingPayout = assignmentRequest.SlidingPayout
};
foreach (var userDepartment in employee.Departments)
{
if (userDepartment.IsConflictingAssignment(newAssignment, out string message))
{
throw new BadDepartmentAssignmentException(message);
}
}
employee.Departments.Add(newAssignment);
newAssignments.Add(newAssignment);
}
await _unitOfWork.CompleteAsync();
return newAssignments;
}
public async Task<List<Permission>> UpdatePermissions(string employeeId, AdditionalPermissionRequest request)
{
await AssignPermissions(employeeId, request.AddPermCodes);
var additional = await RemovePermissions(employeeId, request.RemovePermCodes);
return additional;
}
public async Task<List<Permission>> AssignPermissions(string employeeId, List<string> permCodes)
{
var employee = await GetUserByEmployeeIdAsync(employeeId);
if (employee == null)
{
throw new NotFoundException("Employee Not Found");
}
var newPermissions = Permission.GetAllPermissions()
.Where(permission => permCodes.Contains(permission.Code) &&
!employee.UserHasPermission(permission))
.ToList();
var newUserPermissions = newPermissions.Select(perm => new UserPermission()
{
Permission = perm,
AppUser = employee
})
.ToList();
employee.AdditionalPermissions ??= new List<UserPermission>();
employee.AdditionalPermissions.AddRange(newUserPermissions);
await _userPermissionHistoryRepository.SaveUserPermissionsInsertion(newUserPermissions);
await _unitOfWork.CompleteAsync();
return employee.AdditionalPermissions.Select(x => x.Permission).ToList();
}
public async Task<List<Permission>> RemovePermissions(string employeeId, List<string> permCodes)
{
var employee = await GetUserByEmployeeIdAsync(employeeId);
if (employee == null)
{
throw new NotFoundException("Employee Not Found");
}
var permissionsToRemove = permCodes.Select(requestCode =>
{
return Permission.GetAllPermissions().SingleOrDefault(p => p.Code.Equals(requestCode));
});
var permissionsRemoved = employee.AdditionalPermissions.Where(existingPerm => permissionsToRemove.Contains(existingPerm.Permission)).ToList();
await _userPermissionHistoryRepository.SaveUserPermissionsDeletion(permissionsRemoved);
employee.AdditionalPermissions =
employee.AdditionalPermissions.Where(existingPerm => !permissionsToRemove.Contains(existingPerm.Permission)).ToList();
await _unitOfWork.CompleteAsync();
return employee.AdditionalPermissions.Select(x => x.Permission).ToList();
}
public async Task<UserDepartment> UpdateDepartmentAssignment(DepartmentUpdateRequest request)
{
var existing = await GetUserByEmployeeIdAsync(request.EmployeeId);
if (existing == null)
{
throw new NotFoundException("Employee not found.");
}
var assignment = existing.Departments.FirstOrDefault(x => x.Id == request.AssignmentId);
if (assignment == null)
{
throw new NotFoundException("Assignment not found.");
}
var role = Role.GetRolesList().FirstOrDefault(role => role.Name.Equals(request.Role));
if (role == null)
{
throw new BadDepartmentAssignmentException("Invalid Or Missing Role");
}
if (request.SlidingPayout is < 0 or > UserDepartment.PayoutMax)
{
throw new BadDepartmentAssignmentException($"Sliding Payout must be between 0 and {UserDepartment.PayoutMax}");
}
await _userDepartmentHistoryRepository.SaveUserDepartmentUpdate(assignment, request);
assignment.EndDate = request.EndDate;
assignment.Role = role;
assignment.SlidingPayout = request.SlidingPayout;
await _unitOfWork.CompleteAsync();
return assignment;
}
public async Task<List<AppUser>> FindDepartmentAssignments(DepartmentPersonnelSearchRequest searchRequest)
{
return await _departmentRepository.FindDepartmentAssignments(searchRequest);
}
private async Task<Department> LookupDepartment(ActiveEmployeeResult employee)
{
var department = await _departmentRepository.FindOrCreateDepartment(employee);
if (department == null)
{
throw new Exception("Couldn't get or create a department");
}
return department;
}
public async Task<AppUser> TryFindSingleUserByName(UserNameSearchRequest searchRequest)
{
var userSearchRequest = new UserSearchRequest()
{
FirstName = searchRequest.FirstName,
LastName = searchRequest.LastName,
MiddleInit = searchRequest.MiddleInit,
DnaOrgNbr = searchRequest.DnaOrgNbr
};
var foundUsers = await _userRepository.SearchUsersAsync(userSearchRequest);
if (foundUsers.Count < 1 && !string.IsNullOrWhiteSpace(userSearchRequest.MiddleInit))
{
foundUsers = await _userRepository.SearchUsersAsync(new UserSearchRequest()
{
FirstName = userSearchRequest.FirstName,
LastName = userSearchRequest.LastName,
DnaOrgNbr = userSearchRequest.DnaOrgNbr
});
}
if (foundUsers.Count < 1)
{
foundUsers = await _userRepository.SearchUsersAsync(new UserSearchRequest()
{
FirstName = userSearchRequest.FirstName,
LastName = userSearchRequest.LastName,
});
}
return foundUsers.Single();
}
public async Task<AppUser> FindManager(AppUser appUser)
{
return await GetUserByEmployeeIdAsync(appUser.ManagerKey);
}
public async Task<List<AppUser>> FindDepartmentManagers(int departmentId)
{
var searchRequest = new DepartmentPersonnelSearchRequest
{
DepartmentId = departmentId,
IncludeAllPersonnel = true,
Month = DateTime.Today.Month,
Year = DateTime.Today.Year
};
var usersInDepartment = await _departmentRepository.FindDepartmentAssignments(searchRequest);
var departmentManagers = usersInDepartment
.Where(user => user.Departments
.Any(deptAssignment => Role.DepartmentManagerRoles.Contains(deptAssignment.Role)
&& deptAssignment.DepartmentId == departmentId))
.ToList();
return departmentManagers;
}
public async Task<bool> UserHasAnyRequiredPermission(string appUserEmail, List<string> allowedPermissions)
{
var user = await GetUserByAdEmailAsync(appUserEmail);
if (user == null)
{
return false;
}
var hasPermission = false;
foreach (var userDepartment in user.Departments)
{
var userDeptPermissions = userDepartment.Role.Permissions.Select(x => x.Code).ToList();
if (allowedPermissions.Any(p => userDeptPermissions.Contains(p)))
{
hasPermission = true;
}
}
if (!hasPermission)
{
var userAdditionalPermissions = (user.AdditionalPermissions ?? new List<UserPermission>())
.Select(x => x.Permission.Code)
.ToList();
if (allowedPermissions.Any(p => userAdditionalPermissions.Contains(p)))
{
hasPermission = true;
}
}
return hasPermission;
}
public async Task<bool> SyncActiveUserIds()
{
var allUsersSucceeded = true;
try
{
var activeUsers = await _userRepository.GetAllUsersAsync();
foreach (var appUser in activeUsers.Where(x => x.Terminated == null))
{
var userSyncSucceeded = await _applicationDbIntegrityService.SyncIdsWithDb(appUser);
if (userSyncSucceeded)
{
continue;
}
allUsersSucceeded = false;
Log.Error($"Failed to sync Ids for AppUser: {appUser.Id}");
}
}
catch (Exception ex)
{
Log.Error(ex, ex.Message);
return false;
}
return allUsersSucceeded;
}
public static bool ValidateChangePermission(string currentEmployeeId, string targetEmployeeId) =>
currentEmployeeId == targetEmployeeId;
public async Task<EmployeeDetailsUpdateResult> UpdateEmployeeDetails(EmployeeDetailsUpdate employeeDetailsUpdate)
{
try
{
var existingAppUser = await GetUserByEmployeeIdAsync(employeeDetailsUpdate.OldEmployeeId);
if (existingAppUser != null)
{
existingAppUser = await SyncEmployeeDetails(existingAppUser, employeeDetailsUpdate);
await _unitOfWork.CompleteAsync();
return new EmployeeDetailsUpdateResult
{
AppUser = existingAppUser,
UpdateMessage = $"Employee Details Update Succeeded",
UpdatedSuccessfully = true
};
}
return new EmployeeDetailsUpdateResult
{
UpdateMessage = $"Employee Details Update Failed, No Existing Application User Found To Update"
};
}
catch (Exception ex)
{
Log.Error(ex, ex.Message);
return new EmployeeDetailsUpdateResult
{
UpdateMessage = $"Employee Details Update Failed, {ex.Message}"
};
}
}
public async Task<AppUser> SyncEmployeeDetails(AppUser appUser, EmployeeDetailsUpdate employeeDetailsUpdate)
{
appUser.AdEmail = employeeDetailsUpdate.NewEmail;
appUser.EmployeeId = employeeDetailsUpdate.NewEmployeeId;
appUser.FullName = employeeDetailsUpdate.NewFullName;
var updateSyncSucceeded = await _applicationDbIntegrityService.SyncAppUserUpdate(appUser);
if (!updateSyncSucceeded)
{
throw new Exception($"Failed to update AppUser details for AppUserId: {appUser.Id}");
}
return appUser;
}
}