749 lines
32 KiB
C#
749 lines
32 KiB
C#
using LCU.API.Internal.Operations.IncentiveApi.Exceptions;
|
|
using LCU.API.Internal.Operations.IncentiveApi.Services.DuplicateDetection;
|
|
using LCU.API.Internal.Operations.IncentiveApi.Models.DataTransfer;
|
|
using LCU.API.Internal.Operations.IncentiveApiLib.Extensions;
|
|
using LCU.API.Internal.Operations.IncentiveApi.Models;
|
|
using LCU.API.Internal.Operations.IncentiveApi.Models.ExternalActivities;
|
|
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.Enums;
|
|
using LCU.Library.Internal.IncentiveApiModels.Common;
|
|
using LCU.API.Internal.Operations.IncentiveApi.Services.MicroApiServices;
|
|
using LCU.Library.Internal.IncentiveApiModels.Requests;
|
|
using LCU.Library.Internal.IncentiveApiModels.Results;
|
|
|
|
namespace LCU.API.Internal.Operations.IncentiveApi.Services;
|
|
|
|
public class ActivityService : IActivityService
|
|
{
|
|
private const string TransferredFromString = "Activity has been transferred from Activity ID: {0}";
|
|
private const string TransferredToString = "Activity has been transferred to Activity ID: {0}";
|
|
private readonly IActivityRepository _activityRepository;
|
|
private readonly IActivityChangeRepository _activityChangeRepository;
|
|
private readonly IActivityTransferActionRepository _activityTransferActionRepository;
|
|
private readonly IUserService _userService;
|
|
private readonly INotificationApiService _notificationService;
|
|
private readonly IUnitOfWork _unitOfWork;
|
|
private readonly IDuplicateDetectionService _duplicateDetectionService;
|
|
private readonly IAuthorizationService _authorizationService;
|
|
private readonly ProcessResult _incentiveProcessResult;
|
|
|
|
public ActivityService(
|
|
IActivityRepository activityRepository,
|
|
IActivityChangeRepository activityChangeRepository,
|
|
IActivityTransferActionRepository activityTransferActionRepository,
|
|
IUserService userService,
|
|
INotificationApiService notificationService,
|
|
IDuplicateDetectionService duplicateDetectionService,
|
|
IAuthorizationService authorizationService,
|
|
IUnitOfWork unitOfWork
|
|
)
|
|
{
|
|
_activityRepository = activityRepository;
|
|
_activityChangeRepository = activityChangeRepository;
|
|
_activityTransferActionRepository = activityTransferActionRepository;
|
|
_userService = userService;
|
|
_notificationService = notificationService;
|
|
_unitOfWork = unitOfWork;
|
|
_authorizationService = authorizationService;
|
|
_duplicateDetectionService = duplicateDetectionService;
|
|
_incentiveProcessResult = new ProcessResult();
|
|
}
|
|
|
|
public async Task<int> SaveActivityAsync(Activity activity)
|
|
{
|
|
if (LogGapInformation(activity))
|
|
{
|
|
Log.Information($"Activity {activity.Id} - {activity.Detail.MemberName} " +
|
|
$"GapEligible saved as : {activity.Detail.IsGapInsuranceEligible} {DateTime.UtcNow} ");
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(activity.EmployeeId))
|
|
{
|
|
throw new DeadActivityException(
|
|
$"Attempted to save activity missing EmployeeId.");
|
|
}
|
|
|
|
if (activity.DepartmentId < 0)
|
|
{
|
|
throw new DeadActivityException(
|
|
$"Attempted to save activity with invalid DepartmentId of {activity.DepartmentId}");
|
|
}
|
|
var appUser = await _userService.GetActiveUserByEmployeeIdAsync(activity.EmployeeId)
|
|
?? await _userService.GetUserByEmployeeIdAsync(activity.EmployeeId);
|
|
if (appUser == null)
|
|
{
|
|
throw new NotFoundException($"Employee Not Found For EmployeeId: {activity.EmployeeId}");
|
|
}
|
|
|
|
activity.AppUserId = appUser.Id;
|
|
activity.Accepted = activity.Accepted.Date.AddHours(12);
|
|
|
|
await _activityRepository.SaveActivityAsync(activity);
|
|
await _unitOfWork.CompleteAsync();
|
|
await MarkDuplicates(new List<Activity> { activity });
|
|
return activity.Id;
|
|
}
|
|
|
|
public static bool LogGapInformation(Activity activity)
|
|
{
|
|
return activity.Product == Product.Vehicle
|
|
&& activity.SourceSystem == SystemType.Dna
|
|
&& activity.Detail != null
|
|
&& activity.Detail.IsGapInsuranceEligible == false
|
|
&& activity.Detail.AccountBalance.HasValue
|
|
&& activity.Detail.AccountBalance.Value <= 100000
|
|
&& (!string.IsNullOrEmpty(activity.Detail?.MemberName))
|
|
&& (!activity.Detail.MemberName.ToLower().Contains(" inc") && !activity.Detail.MemberName.Contains(" llc"));
|
|
}
|
|
|
|
public async Task<Activity> CreateAndRetrieveActivity(Activity activity)
|
|
{
|
|
var newActivityId = await SaveActivityAsync(activity);
|
|
return await GetActivity(newActivityId);
|
|
}
|
|
|
|
public async Task MarkDuplicates(IReadOnlyCollection<Activity> activities)
|
|
{
|
|
foreach (var activity in activities)
|
|
{
|
|
var duplicates = await _duplicateDetectionService.TryFindDuplicates(activity);
|
|
if (duplicates == null || !duplicates.Any()) continue;
|
|
|
|
var change = new ActivityStateChange
|
|
{
|
|
ActivityId = activity.Id,
|
|
State = ActivityState.Rejected,
|
|
ActivityMatchId = duplicates.First().Id,
|
|
Note = $"Activity rejected, duplicate of {duplicates.First().Id}."
|
|
};
|
|
await ApplyStateChange(change);
|
|
}
|
|
}
|
|
|
|
public async Task SaveActivityRangeAsync(IReadOnlyCollection<Activity> activities)
|
|
{
|
|
await _activityRepository.SaveActivityRangeAsync(activities.ToList());
|
|
await _unitOfWork.CompleteAsync();
|
|
await MarkDuplicates(activities);
|
|
}
|
|
|
|
public async Task<ProcessResult> RunReferralStatusUpdates()
|
|
{
|
|
try
|
|
{
|
|
var activityUpdates = await MatchOrExpireReferrals();
|
|
var message = $"There were {activityUpdates.Count()} Referral Activity State Updates";
|
|
Log.Information(message);
|
|
_incentiveProcessResult.MessageStack.Add(message);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
var message = $"There was an issue with Activity State Updates Process. Exception Message: {exception.Message}.";
|
|
_incentiveProcessResult.MessageStack.Add(message);
|
|
_incentiveProcessResult.Success = false;
|
|
}
|
|
|
|
return _incentiveProcessResult;
|
|
}
|
|
|
|
public async Task<IReadOnlyCollection<ActivityStateChange>> MatchOrExpireReferrals()
|
|
{
|
|
var activeReferrals = await _activityRepository.FindUnmatchedReferrals();
|
|
var changes = new List<ActivityStateChange>();
|
|
foreach (var referral in activeReferrals)
|
|
{
|
|
var matchedSale = await _activityRepository.FindUnmatchedSaleForReferral(referral);
|
|
if (matchedSale != null)
|
|
{
|
|
var acceptedStateChange = await ApplyAcceptedStateChange(referral, matchedSale);
|
|
changes.Add(acceptedStateChange);
|
|
|
|
if (referral.EmployeeId == matchedSale.EmployeeId
|
|
&& referral.Product == Product.NewChecking
|
|
&& matchedSale.Product == Product.NewChecking)
|
|
{
|
|
ActivityStateChange rejectedStateChange = await ApplyRejectedStateChange(referral, matchedSale);
|
|
changes.Add(rejectedStateChange);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
var expireRange = referral.Product != Product.FirstMortgageReferral
|
|
? DateTime.Today.Subtract(TimeSpan.FromDays(60))
|
|
: DateTime.Today.Subtract(TimeSpan.FromDays(120));
|
|
|
|
if (referral.SourceCreated < expireRange)
|
|
{
|
|
var change = new ActivityStateChange
|
|
{
|
|
ActivityId = referral.Id,
|
|
State = ActivityState.Rejected,
|
|
Note = "Referral has expired."
|
|
};
|
|
await ApplyStateChange(change);
|
|
changes.Add(change);
|
|
}
|
|
}
|
|
|
|
return changes;
|
|
}
|
|
|
|
private async Task<ActivityStateChange> ApplyRejectedStateChange(Activity referral, Activity matchedSale)
|
|
{
|
|
var creditedEmployee = _userService.GetUserByEmployeeIdAsync(matchedSale.EmployeeId);
|
|
var rejectedStateChange = new ActivityStateChange
|
|
{
|
|
ActivityId = referral.Id,
|
|
State = ActivityState.Rejected,
|
|
ActivityMatchId = matchedSale.Id,
|
|
Note = $"Referral rejected as {creditedEmployee.Result.FullName} is also employee on {matchedSale.Id}."
|
|
};
|
|
await ApplyStateChange(rejectedStateChange);
|
|
return rejectedStateChange;
|
|
}
|
|
|
|
private async Task<ActivityStateChange> ApplyAcceptedStateChange(Activity referral, Activity matchedSale)
|
|
{
|
|
var acceptedStateChange = new ActivityStateChange
|
|
{
|
|
ActivityId = referral.Id,
|
|
State = ActivityState.Accepted,
|
|
ActivityMatchId = matchedSale.Id,
|
|
Note = $"Referral was matched to {matchedSale.Id}."
|
|
};
|
|
await ApplyStateChange(acceptedStateChange);
|
|
return acceptedStateChange;
|
|
}
|
|
|
|
public async Task<Activity> ApplyStateChange(ActivityStateChange activityStateChange, bool commitTransaction = true)
|
|
{
|
|
var existingActivity = await _activityRepository.GetActivityById(activityStateChange.ActivityId);
|
|
if (existingActivity == null)
|
|
{
|
|
throw new NotFoundException($"No activity with Id {activityStateChange.ActivityId} could be found");
|
|
}
|
|
|
|
if (activityStateChange.ActivityMatchId == activityStateChange.ActivityId)
|
|
{
|
|
throw new Exception("Activity cannot change a state against itself.");
|
|
}
|
|
|
|
if (existingActivity.State == activityStateChange.State &&
|
|
(activityStateChange.AcceptedDate == null || existingActivity.Accepted == activityStateChange.AcceptedDate))
|
|
{
|
|
await _activityChangeRepository.SaveActivityStateChange(activityStateChange);
|
|
await _unitOfWork.CompleteAsync();
|
|
return existingActivity;
|
|
}
|
|
|
|
existingActivity.State = activityStateChange.State;
|
|
|
|
if (activityStateChange.AcceptedDate != null)
|
|
{
|
|
existingActivity.Accepted = (DateTime)activityStateChange.AcceptedDate?.Date.AddHours(12);
|
|
}
|
|
|
|
if (activityStateChange.State == ActivityState.Accepted || activityStateChange.State == ActivityState.Rejected)
|
|
{
|
|
if (activityStateChange.AcceptedDate == null)
|
|
{
|
|
existingActivity.Accepted = DateTime.UtcNow.Date.AddHours(12);
|
|
}
|
|
|
|
if (activityStateChange.ActivityMatchId > 0)
|
|
{
|
|
var matchedActivity = await _activityRepository.GetActivityById(activityStateChange.ActivityMatchId);
|
|
if (matchedActivity == null)
|
|
{
|
|
throw new NotFoundException($"No matched activity with Id {activityStateChange.ActivityMatchId} could be found");
|
|
}
|
|
|
|
existingActivity.MatchingActivityId = activityStateChange.ActivityMatchId;
|
|
if (activityStateChange.State == ActivityState.Accepted)
|
|
{
|
|
matchedActivity.MatchingActivityId = activityStateChange.ActivityId;
|
|
}
|
|
}
|
|
}
|
|
|
|
await _activityChangeRepository.SaveActivityStateChange(activityStateChange);
|
|
if (commitTransaction)
|
|
{
|
|
await _unitOfWork.CompleteAsync();
|
|
}
|
|
|
|
return existingActivity;
|
|
}
|
|
|
|
public async Task<IReadOnlyCollection<Activity>> GetActivitiesAsync()
|
|
{
|
|
var activities = await _activityRepository.GetActivitiesAsync();
|
|
return activities ?? new List<Activity>();
|
|
}
|
|
|
|
public async Task<PagedResult<Activity>> ListByFilterAsync(ActivityFilter filter)
|
|
{
|
|
try
|
|
{
|
|
return await _activityRepository.GetPagedFilteredActivities(filter);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, $"Failed to retrieve filtered Actions limit: {filter.Limit}, page: {filter.Page}");
|
|
throw new ServerErrorException();
|
|
}
|
|
}
|
|
|
|
public async Task<IReadOnlyCollection<ActivityStateChange>> GetActivityStateChanges(int activityId)
|
|
{
|
|
return await _activityChangeRepository.GetActivityChangesByActivityId(activityId);
|
|
}
|
|
|
|
public async Task<IReadOnlyCollection<DepartmentProgress>> GetDepartmentActivityForMonthRangeAsync(
|
|
List<UserDepartment> userDepartments,
|
|
MonthRange monthRange
|
|
)
|
|
{
|
|
var deptProgress = new List<DepartmentProgress>();
|
|
foreach (var dept in userDepartments)
|
|
{
|
|
var isAssignedInMonthRange = dept.StartDate.Date < monthRange.EndTime.Date &&
|
|
(dept.EndDate == null ||
|
|
monthRange.StartTime.Date < dept.EndDate.Value.Date);
|
|
if (isAssignedInMonthRange)
|
|
{
|
|
var startDate = dept.StartDate.Date < monthRange.StartTime.Date
|
|
? monthRange.StartTime.Date
|
|
: dept.StartDate.Date;
|
|
var endDate = dept.EndDate == null ? monthRange.EndTime.Date
|
|
: dept.EndDate.Value.Date > monthRange.EndTime.Date ? monthRange.EndTime.Date
|
|
: dept.EndDate.Value.Date;
|
|
var monthRangeOfAssignment = new MonthRange
|
|
(
|
|
startDate.Year,
|
|
startDate.Month,
|
|
endDate.Year,
|
|
endDate.Month
|
|
);
|
|
|
|
var progress = new DepartmentProgress(dept.DepartmentId, monthRangeOfAssignment);
|
|
var departmentActivity = await _activityRepository.FindDepartmentActivityByMonthRange(
|
|
progress.DepartmentId, progress.MonthRange);
|
|
progress.ApplyActivity(departmentActivity);
|
|
deptProgress.Add(progress);
|
|
}
|
|
}
|
|
|
|
return deptProgress;
|
|
}
|
|
|
|
public async Task<Activity> GetActivity(int activityId)
|
|
{
|
|
var activity = await _activityRepository.GetActivityIncludingPayouts(activityId);
|
|
if (activity == null)
|
|
{
|
|
throw new NotFoundException($"No activity with Id {activityId} could be found");
|
|
}
|
|
|
|
return activity;
|
|
}
|
|
|
|
public async Task<Activity> GetActivityForStateUpdate(Activity queryActivity)
|
|
{
|
|
var activity = await _activityRepository.FindActivityForStateUpdate(queryActivity);
|
|
if (activity == null)
|
|
{
|
|
throw new NotFoundException(
|
|
$"No activity with matching query activity could be found. AccountNumber: {queryActivity.Detail.AccountNumber} MemberName:{queryActivity.Detail.MemberName}");
|
|
}
|
|
|
|
return activity;
|
|
}
|
|
|
|
public async Task<Activity> GetActivityForDetailUpdate(Activity queryActivity)
|
|
{
|
|
return await _activityRepository.FindActivityForDetailUpdate(queryActivity);
|
|
}
|
|
|
|
public async Task ValidateCanApplyActivityStateChange(AppUser appUser, ActivityStateChange activityStateChange)
|
|
{
|
|
var activity = await _activityRepository.GetActivityIncludingPayouts(activityStateChange.ActivityId);
|
|
if (activity == null)
|
|
{
|
|
throw new NotFoundException($"No activity with Id {activityStateChange.ActivityId} could be found");
|
|
}
|
|
|
|
if (activity.ActivityPayouts != null && activity.ActivityPayouts.Any())
|
|
{
|
|
throw new BadActivityStateChangeException("Cannot change the state of an activity that has a payout.");
|
|
}
|
|
|
|
if (activityStateChange.AcceptedDate != null)
|
|
{
|
|
var lastDateNotPaidOut = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1).AddMonths(-1);
|
|
if (activityStateChange.AcceptedDate < lastDateNotPaidOut || activityStateChange.AcceptedDate > DateTime.Today.AddMonths(1))
|
|
{
|
|
throw new BadActivityStateChangeException(
|
|
"Cannot change the accepted date of an activity to a date that's already paid out, or is greater than 1 month from now.");
|
|
}
|
|
}
|
|
|
|
var required = activity.RequiredToChangeState(activityStateChange.State);
|
|
switch (required)
|
|
{
|
|
case StateChangeRequirement.NotAllowed:
|
|
case StateChangeRequirement.SystemOnly:
|
|
throw new BadActivityStateChangeException("Activity can not be changed to target state.");
|
|
case StateChangeRequirement.AdminOnly when appUser.HasRoleAnywhere(Role.Admin):
|
|
case StateChangeRequirement.AdminOrPermission when appUser.HasRoleAnywhere(Role.Admin)
|
|
|| await _authorizationService.AppUserCanMakeStateChange(appUser, activity,
|
|
activityStateChange.State):
|
|
case StateChangeRequirement.Hierarchical
|
|
when await _authorizationService.ValidateHierarchicalDepartmentPermission(appUser, Role.DepartmentManagerRoles, activity.DepartmentId)
|
|
|| await _authorizationService.AppUserCanMakeStateChange(appUser, activity, activityStateChange.State):
|
|
return;
|
|
default:
|
|
throw new NotAuthorizedException("User does not have the permission to change the activity state.");
|
|
}
|
|
}
|
|
|
|
public async Task<ActivityTransferAction> RequestTransfer(ActivityTransferRequest activityTransferRequest)
|
|
{
|
|
var activity =
|
|
await _activityRepository.GetActivityIncludingPayouts(activityTransferRequest.RequestedActivityId);
|
|
if (activity == null)
|
|
{
|
|
throw new NotFoundException(
|
|
$"No activity with Id {activityTransferRequest.RequestedActivityId} could be found");
|
|
}
|
|
|
|
if (activity.ActivityPayouts.Any())
|
|
{
|
|
throw new BadActivityTransferActionException(
|
|
"Cannot request a transfer of an activity that has a payout.");
|
|
}
|
|
|
|
if (!ActivityTransferAction.IsEligibleForTransfer(activity.State))
|
|
{
|
|
throw new BadActivityTransferActionException(
|
|
"Cannot request a transfer that isn't pending or approved.");
|
|
}
|
|
|
|
var newAppUser = await _userService.GetActiveUserByEmployeeIdAsync(activityTransferRequest.NewEmployeeId);
|
|
|
|
var activityTransferAction = new ActivityTransferAction
|
|
{
|
|
Id = 0,
|
|
ActivityStateBeforeTransfer = activity.State,
|
|
RequestedActivityId = activity.Id,
|
|
State = ActivityTransferState.Requested,
|
|
NewAppUserId = newAppUser.Id,
|
|
NewActivityDepartmentId = activityTransferRequest.NewDepartmentId,
|
|
NewActivityEmployeeId = activityTransferRequest.NewEmployeeId
|
|
};
|
|
await _activityTransferActionRepository.SaveTransferAsync(activityTransferAction);
|
|
|
|
var stateChange = new ActivityStateChange
|
|
{
|
|
ActivityId = activityTransferRequest.RequestedActivityId,
|
|
Note = "",
|
|
State = ActivityState.PendingTransfer
|
|
};
|
|
await ApplyStateChange(stateChange);
|
|
|
|
var initiatingManager = await _userService
|
|
.FindDepartmentManagers(activityTransferAction.RequestedActivity.DepartmentId);
|
|
var destinationManager = await _userService
|
|
.FindDepartmentManagers(activityTransferAction.NewActivityDepartmentId);
|
|
|
|
var recipients = initiatingManager
|
|
.Concat(destinationManager)
|
|
.Select(manager => manager.AdEmail)
|
|
.Distinct()
|
|
.ToList();
|
|
|
|
await _notificationService.SendTransferRequestInitiatedNotification(activityTransferAction, recipients);
|
|
return activityTransferAction;
|
|
}
|
|
|
|
public async Task<ActivityTransferAction> TransferToOwnDepartment(
|
|
ActivityTransferRequest activityTransferRequest,
|
|
AppUser employee
|
|
)
|
|
{
|
|
var existingActivity =
|
|
await _activityRepository.GetActivityIncludingPayouts(activityTransferRequest.RequestedActivityId);
|
|
if (existingActivity == null)
|
|
{
|
|
throw new NotFoundException(
|
|
$"No activity with Id {activityTransferRequest.RequestedActivityId} could be found");
|
|
}
|
|
|
|
if (!string.Equals(existingActivity.EmployeeId, employee.EmployeeId) ||
|
|
!string.Equals(activityTransferRequest.NewEmployeeId, employee.EmployeeId))
|
|
{
|
|
throw new NotAuthorizedException(
|
|
"This activity does not belong to the requesting user and cannot be transferred.");
|
|
}
|
|
|
|
if (!employee.HasDepartment(activityTransferRequest.NewDepartmentId))
|
|
{
|
|
throw new BadActivityTransferActionException(
|
|
"Cannot request a transfer of an activity to a department that the user is not assigned to.");
|
|
}
|
|
|
|
if (existingActivity.ActivityPayouts.Any())
|
|
{
|
|
throw new BadActivityTransferActionException(
|
|
"Cannot request a transfer of an activity that has a payout.");
|
|
}
|
|
|
|
if (existingActivity.State != ActivityState.Accepted)
|
|
{
|
|
throw new BadActivityTransferActionException(
|
|
"Cannot request a transfer to another assigned department for an activity that isn't accepted.");
|
|
}
|
|
|
|
var newAppUser = await _userService.GetActiveUserByEmployeeIdAsync(activityTransferRequest.NewEmployeeId);
|
|
|
|
var activityTransferAction = new ActivityTransferAction
|
|
{
|
|
Id = 0,
|
|
ActivityStateBeforeTransfer = existingActivity.State,
|
|
RequestedActivityId = existingActivity.Id,
|
|
State = ActivityTransferState.Accepted,
|
|
NewAppUserId = newAppUser.Id,
|
|
NewActivityDepartmentId = activityTransferRequest.NewDepartmentId,
|
|
NewActivityEmployeeId = activityTransferRequest.NewEmployeeId
|
|
};
|
|
|
|
var newActivity = Activity.CopyActivityForTransfer(existingActivity, activityTransferAction, employee.AdEmail);
|
|
activityTransferAction.NewActivity = newActivity;
|
|
|
|
await _activityTransferActionRepository.SaveTransferAsync(activityTransferAction);
|
|
await _unitOfWork.CompleteAsync();
|
|
|
|
var newStateChange = new ActivityStateChange
|
|
{
|
|
ActivityId = newActivity.Id,
|
|
ActivityMatchId = existingActivity.Id,
|
|
Note = string.Format(TransferredFromString, existingActivity.Id),
|
|
State = activityTransferAction.ActivityStateBeforeTransfer
|
|
};
|
|
await ApplyStateChange(newStateChange);
|
|
|
|
var stateChange = new ActivityStateChange
|
|
{
|
|
ActivityId = existingActivity.Id,
|
|
ActivityMatchId = newActivity.Id,
|
|
Note = string.Format(TransferredToString, newActivity.Id),
|
|
State = ActivityState.Transferred
|
|
};
|
|
await ApplyStateChange(stateChange);
|
|
|
|
return activityTransferAction;
|
|
}
|
|
|
|
public async Task<ActivityTransferAction> ApproveTransfer(
|
|
AppUser appUser,
|
|
ActivityTransferApprovalRequest transferApproval
|
|
)
|
|
{
|
|
var activityTransferAction = await GetActivityTransferAction(transferApproval);
|
|
var activity = await _activityRepository.GetActivityIncludingPayouts(activityTransferAction.RequestedActivityId);
|
|
var targetStateChange = transferApproval.ApprovalState == ActivityTransferState.Accepted
|
|
? ActivityState.Accepted
|
|
: activityTransferAction.ActivityStateBeforeTransfer;
|
|
|
|
var canApproveTransferFrom = await _authorizationService.AppUserCanMakeStateChange(appUser, activity, targetStateChange);
|
|
var canApproveTransferTo =
|
|
await _authorizationService.AppUserCanMakeStateChange(appUser, activity, targetStateChange, activityTransferAction.NewActivityDepartmentId);
|
|
|
|
if (!canApproveTransferFrom && !canApproveTransferTo)
|
|
{
|
|
throw new NotAuthorizedException("Employee is not allowed to approve for either department");
|
|
}
|
|
|
|
if (transferApproval.ApprovalState == ActivityTransferState.Rejected)
|
|
{
|
|
return await RejectTransfer(activityTransferAction);
|
|
}
|
|
|
|
if (transferApproval.ApprovalState == ActivityTransferState.Accepted)
|
|
{
|
|
var isApprovedByRequestedDepartment =
|
|
!string.IsNullOrEmpty(activityTransferAction.RequestedDepartmentApprover);
|
|
var isApprovedByNewDepartment = !string.IsNullOrEmpty(activityTransferAction.NewDepartmentApprover);
|
|
|
|
if (canApproveTransferFrom && !isApprovedByRequestedDepartment)
|
|
{
|
|
activityTransferAction.RequestedDepartmentApprover = appUser.FullName;
|
|
activityTransferAction.RequestedDepartmentApproved = DateTime.Now;
|
|
isApprovedByRequestedDepartment = true;
|
|
}
|
|
|
|
if (canApproveTransferTo && !isApprovedByNewDepartment)
|
|
{
|
|
activityTransferAction.NewDepartmentApprover = appUser.FullName;
|
|
activityTransferAction.NewDepartmentApproved = DateTime.Now;
|
|
isApprovedByNewDepartment = true;
|
|
}
|
|
|
|
if (isApprovedByRequestedDepartment && isApprovedByNewDepartment)
|
|
{
|
|
var existingActivity = await _activityRepository
|
|
.GetActivityIncludingPayouts(activityTransferAction.RequestedActivityId);
|
|
var newActivityEmployee = await _userService
|
|
.GetUserByEmployeeIdAsync(activityTransferAction.NewActivityEmployeeId);
|
|
var newActivity = Activity.CopyActivityForTransfer(existingActivity, activityTransferAction,
|
|
newActivityEmployee.AdEmail);
|
|
|
|
activityTransferAction.NewActivity = newActivity;
|
|
activityTransferAction.State = ActivityTransferState.Accepted;
|
|
|
|
await _unitOfWork.CompleteAsync();
|
|
|
|
var newStateChange = new ActivityStateChange
|
|
{
|
|
ActivityId = newActivity.Id,
|
|
ActivityMatchId = existingActivity.Id,
|
|
Note = string.Format(TransferredFromString, existingActivity.Id),
|
|
State = activityTransferAction.ActivityStateBeforeTransfer
|
|
};
|
|
await ApplyStateChange(newStateChange);
|
|
|
|
var stateChange = new ActivityStateChange
|
|
{
|
|
ActivityId = existingActivity.Id,
|
|
ActivityMatchId = newActivity.Id,
|
|
Note = string.Format(TransferredToString, newActivity.Id),
|
|
State = ActivityState.Transferred
|
|
};
|
|
await ApplyStateChange(stateChange);
|
|
}
|
|
else
|
|
{
|
|
await _unitOfWork.CompleteAsync();
|
|
}
|
|
}
|
|
|
|
return activityTransferAction;
|
|
}
|
|
|
|
public async Task<ActivityTransferAction> GetRequestedTransfer(int activityId)
|
|
{
|
|
return await _activityTransferActionRepository.GetRequestedTransferActionByActivityId(activityId);
|
|
}
|
|
|
|
private async Task<ActivityTransferAction> GetActivityTransferAction(
|
|
ActivityTransferApprovalRequest transferApproval
|
|
)
|
|
{
|
|
var activityTransferAction = await _activityTransferActionRepository
|
|
.GetTransferActionById(transferApproval.Id);
|
|
if (activityTransferAction == null)
|
|
{
|
|
throw new NotFoundException(
|
|
$"No activity transfer action with Id {transferApproval.Id} could be found");
|
|
}
|
|
|
|
if (activityTransferAction.State != ActivityTransferState.Requested)
|
|
{
|
|
throw new BadActivityTransferActionException("Approval has already been decided for this activity");
|
|
}
|
|
|
|
return activityTransferAction;
|
|
}
|
|
|
|
private async Task<ActivityTransferAction> RejectTransfer(ActivityTransferAction activityTransferAction)
|
|
{
|
|
activityTransferAction.State = ActivityTransferState.Rejected;
|
|
|
|
var stateChange = new ActivityStateChange
|
|
{
|
|
ActivityId = activityTransferAction.RequestedActivityId,
|
|
Note = "Activity transfer has been rejected.",
|
|
State = activityTransferAction.ActivityStateBeforeTransfer
|
|
};
|
|
await ApplyStateChange(stateChange);
|
|
|
|
return activityTransferAction;
|
|
}
|
|
|
|
public async Task CancelPendingTransfersBetween(MonthRange range)
|
|
{
|
|
var transfers = await _activityTransferActionRepository.GetActiveTransfersForStateAsync(ActivityState.Accepted);
|
|
foreach (var transfer in transfers)
|
|
{
|
|
if (transfer.RequestedActivity.Accepted.Date >= range.StartTime.Date && transfer.RequestedActivity.Accepted.Date <= range.EndTime.Date)
|
|
{
|
|
await RejectTransfer(transfer);
|
|
}
|
|
}
|
|
}
|
|
|
|
public async Task<List<IExternalActivity>> FilterExternalActivitiesByExistingSourceId(
|
|
List<IExternalActivity> incomingEternalActivities
|
|
)
|
|
{
|
|
var existingActivities =
|
|
await _activityRepository.GetActivitiesBySourceId(incomingEternalActivities.Select(x => x.GetSourceId())
|
|
.ToList());
|
|
|
|
return incomingEternalActivities
|
|
.Where(newActivity => !existingActivities.Any(existingActivity =>
|
|
existingActivity.Product == newActivity.GetProductType() &&
|
|
existingActivity.Type == newActivity.GetActivityType() &&
|
|
existingActivity.SourceId == newActivity.GetSourceId()))
|
|
.ToList();
|
|
}
|
|
|
|
public async Task<AuthorizedActivityCommands> GetActivityAuthorizedCommands(AppUser appUser, int activityId)
|
|
{
|
|
var activity = await _activityRepository.GetActivityIncludingPayouts(activityId);
|
|
if (activity == null)
|
|
{
|
|
throw new NotFoundException($"No activity with Id {activityId} could be found");
|
|
}
|
|
|
|
var allowedChanges = new List<ActivityState>();
|
|
var canApproveTransferFrom = false;
|
|
var canApproveTransferTo = false;
|
|
var activityTransferAction = await GetRequestedTransfer(activityId);
|
|
var targetDepartmentId = activityTransferAction == null || activity.State != ActivityState.PendingTransfer ? 0 : activityTransferAction.NewActivityDepartmentId;
|
|
|
|
var hasHierarchicalPermission = await _authorizationService.ValidateHierarchicalDepartmentPermission(appUser,
|
|
Role.DepartmentManagerRoles, activity.DepartmentId);
|
|
foreach (var stateDetail in ActivityStateDetail.ActivityStateDetails)
|
|
{
|
|
var changeRequirement = activity.RequiredToChangeState(stateDetail.State);
|
|
switch (changeRequirement)
|
|
{
|
|
case StateChangeRequirement.AdminOnly when appUser.HasRoleAnywhere(Role.Admin):
|
|
case StateChangeRequirement.AdminOrPermission when appUser.HasRoleAnywhere(Role.Admin)
|
|
|| await _authorizationService.AppUserCanMakeStateChange(appUser, activity,
|
|
stateDetail.State, targetDepartmentId):
|
|
case StateChangeRequirement.Hierarchical when hasHierarchicalPermission
|
|
|| await _authorizationService.AppUserCanMakeStateChange(appUser, activity,
|
|
stateDetail.State, targetDepartmentId):
|
|
allowedChanges.Add(stateDetail.State);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (activityTransferAction != null && allowedChanges.Contains(ActivityState.Transferred))
|
|
{
|
|
canApproveTransferFrom = await _authorizationService.AppUserCanMakeStateChange(appUser, activity, ActivityState.Transferred);
|
|
canApproveTransferTo = await _authorizationService.AppUserCanMakeStateChange(appUser, activity, ActivityState.Transferred,
|
|
activityTransferAction.NewActivityDepartmentId);
|
|
}
|
|
|
|
allowedChanges = allowedChanges.Distinct().ToList();
|
|
|
|
return new AuthorizedActivityCommands(allowedChanges.ToArray(), canApproveTransferFrom, canApproveTransferTo);
|
|
}
|
|
}
|