Adding
This commit is contained in:
+413
@@ -0,0 +1,413 @@
|
||||
using LCU.API.Internal.Operations.IncentiveApi.Exceptions;
|
||||
using LCU.API.Internal.Operations.IncentiveApi.Extensions;
|
||||
using LCU.Library.Internal.IncentiveApiModels.Entities;
|
||||
using LCU.Library.Internal.IncentiveApiModels.Enums;
|
||||
using LCU.Library.Internal.IncentiveApiModels.Common;
|
||||
using LCU.Library.Internal.IncentiveApiModels.Requests;
|
||||
using LCU.Library.Internal.IncentiveApiModels.Results;
|
||||
using LCU.Library.Internal.ApiResponse.Services;
|
||||
using LCU.Library.Internal.Exceptions;
|
||||
|
||||
namespace LCU.API.Internal.Operations.IncentiveApi.Services.AutoDecision;
|
||||
|
||||
public class ActivityDecisionService : IActivityDecisionService
|
||||
{
|
||||
private const int PageItemLimit = 1000;
|
||||
private readonly IEnumerable<IDecisionStrategy> _strategies;
|
||||
private readonly IActivityService _activityService;
|
||||
private readonly IActivityDetailService _activityDetailService;
|
||||
private readonly DebtProtectionAccessoryService _debtProtectionAccessoryService;
|
||||
private readonly IDnaService _dnaService;
|
||||
private static readonly List<string> TransactionCodes = new List<string> { "XDEP" };
|
||||
private readonly ProcessResult _incentiveProcessResult;
|
||||
|
||||
public ActivityDecisionService(
|
||||
IEnumerable<IDecisionStrategy> strategies,
|
||||
IActivityService activityService,
|
||||
IActivityDetailService activityDetailService,
|
||||
DebtProtectionAccessoryService debtProtectionAccessoryService,
|
||||
IDnaService dnaService
|
||||
)
|
||||
{
|
||||
_strategies = strategies;
|
||||
_activityService = activityService;
|
||||
_activityDetailService = activityDetailService;
|
||||
_debtProtectionAccessoryService = debtProtectionAccessoryService;
|
||||
_dnaService = dnaService;
|
||||
_incentiveProcessResult = new ProcessResult();
|
||||
}
|
||||
|
||||
public async Task RunNightlyDecisionProcess(NightlyIncentiveProcessRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var decisionedCreated = await DecisionActivityByState(ActivityState.Created);
|
||||
var createdStateResults = $"Auto-decisioned {decisionedCreated.Count} activities in Created state.";
|
||||
Log.Information(createdStateResults);
|
||||
_incentiveProcessResult.MessageStack.Add(createdStateResults);
|
||||
|
||||
var decisionedManualReview = await DecisionActivityByState(ActivityState.ManualReview);
|
||||
var manualStateActivityResults = $"Auto-decisioned {decisionedManualReview.Count} activities in Manual Review state.";
|
||||
Log.Information(manualStateActivityResults);
|
||||
_incentiveProcessResult.MessageStack.Add(manualStateActivityResults);
|
||||
|
||||
var decisionManualReviewUpdated = await UpdateAndDecideManualReviewActivities();
|
||||
var manualReviewUpdates = $"Updated and Auto-Decisioned {decisionManualReviewUpdated.Count} Activities.";
|
||||
Log.Information(manualReviewUpdates);
|
||||
_incentiveProcessResult.MessageStack.Add(manualReviewUpdates);
|
||||
|
||||
var decisionUpdateDpEligible = await UpdateAndDecideDpEligibleLoanActivities();
|
||||
var dpEligibleDecisionResults = $"Updated and Auto-Decisioned {decisionUpdateDpEligible.Count} Debt Protection Eligible Loan Activities.";
|
||||
Log.Information(dpEligibleDecisionResults);
|
||||
_incentiveProcessResult.MessageStack.Add(dpEligibleDecisionResults);
|
||||
|
||||
await QueueReturnResponse.QueueResponse(request, _incentiveProcessResult.Success, _incentiveProcessResult.MessageStack);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var message = $"There was an issue with handling the Daily Decision Process Exception Message: {exception.Message}.";
|
||||
_incentiveProcessResult.MessageStack.Add(message);
|
||||
_incentiveProcessResult.Success = false;
|
||||
await QueueReturnResponse.QueueResponse(request, _incentiveProcessResult.Success, _incentiveProcessResult.MessageStack);
|
||||
throw new ServiceException(message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ActivityStateChange> TryDecisionActivity(Activity activity)
|
||||
{
|
||||
var strategy = _strategies.FirstOrDefault(x => x.CanDecideActivity(activity));
|
||||
|
||||
if (strategy != null)
|
||||
{
|
||||
return await strategy.DecideActivity(activity);
|
||||
}
|
||||
|
||||
throw new NoAvailableDecisionStrategy(
|
||||
$"No decision strategy available, {activity.Id} - {activity.Product}:{activity.Type}");
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<ActivityStateChange>> DecisionActivityByState(ActivityState state)
|
||||
{
|
||||
var changes = new List<ActivityStateChange>();
|
||||
var allItemsProcessed = false;
|
||||
var page = 0;
|
||||
var processedItemCount = 0;
|
||||
|
||||
while (!allItemsProcessed)
|
||||
{
|
||||
try
|
||||
{
|
||||
var activityPage = await _activityService.ListByFilterAsync(new ActivityFilter
|
||||
{
|
||||
Limit = PageItemLimit,
|
||||
Page = page += 1,
|
||||
State = new List<ActivityState> { state }
|
||||
});
|
||||
|
||||
if (activityPage?.Items == null)
|
||||
{
|
||||
return new List<ActivityStateChange>();
|
||||
}
|
||||
|
||||
var activities = activityPage.Items.ToList();
|
||||
changes.AddRange(await AutoDecideActivities(activities));
|
||||
processedItemCount += activities.Count;
|
||||
allItemsProcessed = processedItemCount == activityPage.Total;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failed to process all updates for activities in State:{state}");
|
||||
allItemsProcessed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<ActivityStateChange>> UpdateAndDecideManualReviewActivities()
|
||||
{
|
||||
var changes = new List<ActivityStateChange>();
|
||||
var allItemsProcessed = false;
|
||||
var page = 0;
|
||||
var processedItemCount = 0;
|
||||
|
||||
while (!allItemsProcessed)
|
||||
{
|
||||
try
|
||||
{
|
||||
var activityPage = await _activityService.ListByFilterAsync(new ActivityFilter
|
||||
{
|
||||
Limit = PageItemLimit,
|
||||
Page = page += 1,
|
||||
State = new List<ActivityState> { ActivityState.ManualReview }
|
||||
});
|
||||
|
||||
if (activityPage?.Items == null)
|
||||
{
|
||||
return new List<ActivityStateChange>();
|
||||
}
|
||||
|
||||
var activities = activityPage.Items.ToList();
|
||||
var updatedActivities = await _activityDetailService.TryUpdateActivityAccountDetails(activities);
|
||||
|
||||
changes.AddRange(await AutoDecideActivities(updatedActivities));
|
||||
processedItemCount += activities.Count;
|
||||
allItemsProcessed = processedItemCount == activityPage.Total;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failed to process all Manual Review updates");
|
||||
allItemsProcessed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<ActivityStateChange>> UpdateAndDecideDpEligibleLoanActivities()
|
||||
{
|
||||
var dateLow = DateTime.Now.StartOfMonth().AddMonths(-2).AddDays(-1);
|
||||
var dpEligibleProducts = new List<Product> { Product.DebtProtectionEligibleLoan, Product.Mortgage, Product.Vehicle };
|
||||
var changes = new List<ActivityStateChange>();
|
||||
var allItemsProcessed = false;
|
||||
var page = 0;
|
||||
var processedItemCount = 0;
|
||||
|
||||
while (!allItemsProcessed)
|
||||
{
|
||||
try
|
||||
{
|
||||
var activityResults = new PagedResult<Activity>();
|
||||
activityResults = await _activityService.ListByFilterAsync(new ActivityFilter
|
||||
{
|
||||
Limit = PageItemLimit,
|
||||
Page = page += 1,
|
||||
DateLow = dateLow,
|
||||
State = new List<ActivityState> { ActivityState.Accepted },
|
||||
Product = dpEligibleProducts
|
||||
});
|
||||
|
||||
if (activityResults.Items != null)
|
||||
{
|
||||
var activities = activityResults.Items.ToList();
|
||||
var updatedDpActivities = await _debtProtectionAccessoryService.ProcessDpAccessoryUpdates(activities);
|
||||
var updatedActivities = await _activityDetailService.TryUpdateDpEligibleLoanDetail(activities);
|
||||
updatedActivities.AddRange(await _debtProtectionAccessoryService.ProcessDpAccessoryUpdates(updatedActivities));
|
||||
updatedActivities.AddRange(updatedDpActivities);
|
||||
changes.AddRange(await AutoDecideActivities(updatedActivities));
|
||||
processedItemCount += activities.Count;
|
||||
}
|
||||
|
||||
allItemsProcessed = processedItemCount == activityResults.Total;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failed to process all Debt Protection updates UpdateAndDecideDpEligibleLoanActivities");
|
||||
allItemsProcessed = true;
|
||||
}
|
||||
}
|
||||
|
||||
changes.AddRange(await RejectDpOnRejectedDpEligibleLoanActivities(dateLow, dpEligibleProducts));
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyCollection<ActivityStateChange>> RejectDpOnRejectedDpEligibleLoanActivities(DateTime dateLow, List<Product> dpEligibleProducts)
|
||||
{
|
||||
var changes = new List<ActivityStateChange>();
|
||||
var allItemsProcessed = false;
|
||||
var page = 0;
|
||||
var processedItemCount = 0;
|
||||
|
||||
while (!allItemsProcessed)
|
||||
{
|
||||
try
|
||||
{
|
||||
var activityPage = await _activityService.ListByFilterAsync(new ActivityFilter
|
||||
{
|
||||
Limit = PageItemLimit,
|
||||
Page = page += 1,
|
||||
DateLow = dateLow,
|
||||
State = new List<ActivityState> { ActivityState.Rejected },
|
||||
Product = dpEligibleProducts
|
||||
});
|
||||
|
||||
if (activityPage?.Items == null)
|
||||
{
|
||||
return new List<ActivityStateChange>();
|
||||
}
|
||||
var activities = activityPage.Items.ToList();
|
||||
var dpActivitiesForRejection = await _debtProtectionAccessoryService.GetNonRejectedDpAccessoriesWithRejectedMatchedSale(activities);
|
||||
|
||||
changes.AddRange(await RejectDpActivities(dpActivitiesForRejection));
|
||||
|
||||
processedItemCount += activities.Count;
|
||||
allItemsProcessed = processedItemCount == activityPage.Total;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failed to process all Debt Protection updates on RejectDpOnRejectedDpEligibleLoanActivities");
|
||||
allItemsProcessed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<ActivityStateChange>> UpdateAndDecideLicReferralActivities()
|
||||
{
|
||||
var changes = new List<ActivityStateChange>();
|
||||
var allItemsProcessed = false;
|
||||
var page = 0;
|
||||
var processedItemCount = 0;
|
||||
|
||||
while (!allItemsProcessed)
|
||||
{
|
||||
try
|
||||
{
|
||||
var activityPage = await _activityService.ListByFilterAsync(new ActivityFilter
|
||||
{
|
||||
Limit = PageItemLimit,
|
||||
Page = page += 1,
|
||||
State = new List<ActivityState> { ActivityState.ManualReview },
|
||||
Product = new List<Product> { Product.LIC }
|
||||
});
|
||||
|
||||
if (activityPage?.Items == null)
|
||||
{
|
||||
return new List<ActivityStateChange>();
|
||||
}
|
||||
|
||||
var activities = activityPage.Items.ToList();
|
||||
var updatedActivities = await _activityDetailService.TryUpdateLicReferralStationDetail(activities);
|
||||
|
||||
changes.AddRange(await AutoDecideActivities(updatedActivities));
|
||||
processedItemCount += activities.Count;
|
||||
allItemsProcessed = processedItemCount == activityPage.Total;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failed to process all LIC Referral updates");
|
||||
allItemsProcessed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
public async Task<List<ActivityStateChange>> AutoDecideActivities(IEnumerable<Activity> activities)
|
||||
{
|
||||
var changes = new List<ActivityStateChange>();
|
||||
foreach (var activity in activities.Where(x => x.CanAutoDecision()))
|
||||
{
|
||||
try
|
||||
{
|
||||
var decision = await TryDecisionActivity(activity);
|
||||
if (decision.State == activity.State) continue;
|
||||
await _activityService.ApplyStateChange(decision);
|
||||
changes.Add(decision);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failed to decision activity {activity.Id}");
|
||||
}
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
private async Task<List<ActivityStateChange>> RejectDpActivities(IEnumerable<Activity> activities)
|
||||
{
|
||||
var changes = new List<ActivityStateChange>();
|
||||
foreach (var activity in activities)
|
||||
{
|
||||
try
|
||||
{
|
||||
var change = new ActivityStateChange
|
||||
{
|
||||
ActivityId = activity.Id,
|
||||
ActivityMatchId = activity.MatchingDpAccessoryActivityId,
|
||||
Note = $"Debt Protection Matched Activity ID: {activity.MatchingDpAccessoryActivityId} Rejected",
|
||||
State = ActivityState.Rejected
|
||||
};
|
||||
await _activityService.ApplyStateChange(change);
|
||||
changes.Add(change);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failed to decision activity {activity.Id}");
|
||||
}
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<ActivityStateChange>> UpdateAndDecideDirectDepositPendingReferralActivities()
|
||||
{
|
||||
var activityPage = await _activityService.ListByFilterAsync(new ActivityFilter
|
||||
{
|
||||
Limit = PageItemLimit,
|
||||
Page = 1,
|
||||
State = new List<ActivityState> { ActivityState.PendingReferral },
|
||||
Product = new List<Product> { Product.DirectDeposit },
|
||||
Type = new List<ActivityType> { ActivityType.Referral }
|
||||
});
|
||||
|
||||
if (activityPage?.Items == null)
|
||||
{
|
||||
return new List<ActivityStateChange>();
|
||||
}
|
||||
|
||||
if (activityPage.Total > PageItemLimit)
|
||||
{
|
||||
Log.Warning($"More than {PageItemLimit} items to process in the Update And Decide Direct Deposit Pending Referrals!");
|
||||
}
|
||||
|
||||
var activities = activityPage.Items.ToList();
|
||||
var currentDateTime = DateTime.Now;
|
||||
var changes = new List<ActivityStateChange>();
|
||||
|
||||
foreach (var activity in activities)
|
||||
{
|
||||
try
|
||||
{
|
||||
var activityHistory =
|
||||
await _dnaService.GetTransactionHistory(activity.Detail.AccountNumber, activity.Created, currentDateTime, TransactionCodes);
|
||||
|
||||
if (activityHistory == null)
|
||||
{
|
||||
var decision = new ActivityStateChange
|
||||
{
|
||||
ActivityId = activity.Id,
|
||||
State = ActivityState.Rejected,
|
||||
Note = "No Account Was Found With The Given Account Number",
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12)
|
||||
};
|
||||
|
||||
await _activityService.ApplyStateChange(decision);
|
||||
changes.Add(decision);
|
||||
}
|
||||
|
||||
if (activityHistory != null && activityHistory.Any())
|
||||
{
|
||||
var decision = new ActivityStateChange
|
||||
{
|
||||
ActivityId = activity.Id,
|
||||
State = ActivityState.Accepted,
|
||||
Note = "Account Had At Least One External Deposit Transaction",
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12)
|
||||
};
|
||||
|
||||
await _activityService.ApplyStateChange(decision);
|
||||
changes.Add(decision);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failed to save direct deposit referral state as accepted for activity:{activity.Id}");
|
||||
}
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
using LCU.Library.Internal.IncentiveApiModels.Entities;
|
||||
using LCU.Library.Internal.IncentiveApiModels.Enums;
|
||||
|
||||
namespace LCU.API.Internal.Operations.IncentiveApi.Services.AutoDecision;
|
||||
|
||||
public class CardSaleDecisionStrategy : IDecisionStrategy
|
||||
{
|
||||
public bool CanDecideActivity(Activity activity)
|
||||
{
|
||||
return (activity.Product == Product.ATMCard || activity.Product == Product.CheckCard)
|
||||
&& activity.Type == ActivityType.Other;
|
||||
}
|
||||
|
||||
public async Task<ActivityStateChange> DecideActivity(Activity activity)
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Auto-Accept. Will Match with Referral.",
|
||||
State = ActivityState.Accepted
|
||||
});
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
using LCU.Library.Internal.IncentiveApiModels.Entities;
|
||||
using LCU.Library.Internal.IncentiveApiModels.Enums;
|
||||
|
||||
namespace LCU.API.Internal.Operations.IncentiveApi.Services.AutoDecision;
|
||||
|
||||
public class DebtProtectionAccessoryService
|
||||
{
|
||||
private readonly IActivityService _activityService;
|
||||
private readonly IActivityDetailService _activityDetailService;
|
||||
|
||||
public DebtProtectionAccessoryService(IActivityService activityService, IActivityDetailService activityDetailService)
|
||||
{
|
||||
_activityService = activityService;
|
||||
_activityDetailService = activityDetailService;
|
||||
}
|
||||
|
||||
public async Task<List<Activity>> GetNonRejectedDpAccessoriesWithRejectedMatchedSale(List<Activity> activities)
|
||||
{
|
||||
var returnActivities = new List<Activity>();
|
||||
|
||||
foreach (var activity in activities)
|
||||
{
|
||||
if (activity.MatchingDpAccessoryActivityId != 0)
|
||||
{
|
||||
var dpActivity = await _activityService.GetActivity(activity.MatchingDpAccessoryActivityId);
|
||||
if (dpActivity.State != ActivityState.Rejected)
|
||||
{
|
||||
returnActivities.Add(dpActivity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return returnActivities;
|
||||
}
|
||||
|
||||
public async Task<List<Activity>> ProcessDpAccessoryUpdates(IEnumerable<Activity> saleActivities)
|
||||
{
|
||||
var updatedActivities = new List<Activity>();
|
||||
var filteredSaleActivities = saleActivities.Where(x => x.HasDpAccessory());
|
||||
|
||||
foreach (var saleActivity in filteredSaleActivities)
|
||||
{
|
||||
if (saleActivity.MatchingDpAccessoryActivityId == 0 && saleActivity.Detail.DebtProtectionAddDate != null)
|
||||
{
|
||||
var newDpAccessoryActivity = Activity.CopyActivityForDpAccessory(saleActivity);
|
||||
saleActivity.MatchingDpAccessoryActivityId = await _activityService.SaveActivityAsync(newDpAccessoryActivity);
|
||||
var change = new ActivityStateChange
|
||||
{
|
||||
ActivityId = saleActivity.Id,
|
||||
ActivityMatchId = newDpAccessoryActivity.Id,
|
||||
Note = $"{newDpAccessoryActivity.Product} Added On Activity Id: {newDpAccessoryActivity.Id}",
|
||||
};
|
||||
await _activityService.ApplyStateChange(change);
|
||||
updatedActivities.Add(newDpAccessoryActivity);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (saleActivity.MatchingDpAccessoryActivityId != 0)
|
||||
{
|
||||
var existingDpAccessoryActivity = await _activityService.GetActivity(saleActivity.MatchingDpAccessoryActivityId);
|
||||
if (existingDpAccessoryActivity.Id != 0 && DpAccessoryRequiresUpdate(saleActivity, existingDpAccessoryActivity))
|
||||
{
|
||||
updatedActivities.Add(await _activityDetailService.TryUpdateDpAccessory(saleActivity, existingDpAccessoryActivity));
|
||||
Log.Information($"After TryUpdateDpAccessory {saleActivity.Id} existingDpAccessoryActivity {existingDpAccessoryActivity.Id})");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return updatedActivities;
|
||||
}
|
||||
|
||||
private static bool DpAccessoryRequiresUpdate(Activity saleActivity, Activity dpAccessoryActivity)
|
||||
{
|
||||
return saleActivity.Detail.DebtProtectionAddDate?.Date != dpAccessoryActivity.Detail.DebtProtectionAddDate?.Date
|
||||
|| saleActivity.Detail.DebtProtectionRemoveDate?.Date != dpAccessoryActivity.Detail.DebtProtectionRemoveDate?.Date;
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
using LCU.Library.Internal.IncentiveApiModels.Entities;
|
||||
using LCU.Library.Internal.IncentiveApiModels.Enums;
|
||||
|
||||
namespace LCU.API.Internal.Operations.IncentiveApi.Services.AutoDecision;
|
||||
|
||||
public class DebtProtectionDecisionStrategy : IDecisionStrategy
|
||||
{
|
||||
private const Product DecidableProduct = Product.DebtProtection;
|
||||
|
||||
public bool CanDecideActivity(Activity activity)
|
||||
{
|
||||
return activity.Product == DecidableProduct;
|
||||
}
|
||||
|
||||
public async Task<ActivityStateChange> DecideActivity(Activity activity)
|
||||
{
|
||||
if (
|
||||
activity.MatchingDpAccessoryActivityId != 0
|
||||
&& activity.Detail.DebtProtectionAddDate != null
|
||||
&& activity.Detail.DebtProtectionRemoveDate == null
|
||||
)
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Detail.DebtProtectionAddDate?.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
ActivityMatchId = activity.MatchingDpAccessoryActivityId,
|
||||
Note = $"{DecidableProduct} Added From Activity Id: {activity.MatchingDpAccessoryActivityId}, Auto-Decision: {ActivityState.Accepted}",
|
||||
State = ActivityState.Accepted
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
activity.MatchingDpAccessoryActivityId != 0
|
||||
&& activity.Detail.DebtProtectionAddDate != null
|
||||
&& activity.Detail.DebtProtectionRemoveDate != null
|
||||
)
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Detail.DebtProtectionRemoveDate?.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
ActivityMatchId = activity.MatchingDpAccessoryActivityId,
|
||||
Note = $"Debt Protection Removed From Activity Id: {activity.MatchingDpAccessoryActivityId}, Auto-Decision: {ActivityState.Rejected}",
|
||||
State = ActivityState.Rejected
|
||||
});
|
||||
}
|
||||
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = $"Unable To Auto-Decision: {ActivityState.ManualReview}",
|
||||
State = ActivityState.ManualReview
|
||||
});
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
using LCU.Library.Internal.IncentiveApiModels.Entities;
|
||||
using LCU.Library.Internal.IncentiveApiModels.Enums;
|
||||
|
||||
namespace LCU.API.Internal.Operations.IncentiveApi.Services.AutoDecision;
|
||||
|
||||
public class DebtProtectionEligibleLoanDecisionStrategy : IDecisionStrategy
|
||||
{
|
||||
public bool CanDecideActivity(Activity activity)
|
||||
{
|
||||
return activity.Product == Product.DebtProtectionEligibleLoan && activity.Type == ActivityType.Sale;
|
||||
}
|
||||
|
||||
public async Task<ActivityStateChange> DecideActivity(Activity activity)
|
||||
{
|
||||
if (activity.Detail.AccountStatus == null)
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange()
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Cannot Determine Account Status.",
|
||||
State = ActivityState.Rejected
|
||||
});
|
||||
}
|
||||
|
||||
if (activity.Detail.AccountStatus.Equals("ACT", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Account is Active",
|
||||
State = ActivityState.Accepted
|
||||
});
|
||||
}
|
||||
|
||||
if (activity.Detail.AccountStatus.Equals("CLS", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Account is closed.",
|
||||
State = ActivityState.Rejected
|
||||
});
|
||||
}
|
||||
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Account is not Closed or Active. Awaiting Manual Review or Status Update.",
|
||||
State = ActivityState.ManualReview
|
||||
});
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
using LCU.Library.Internal.IncentiveApiModels.Entities;
|
||||
using LCU.Library.Internal.IncentiveApiModels.Enums;
|
||||
|
||||
namespace LCU.API.Internal.Operations.IncentiveApi.Services.AutoDecision;
|
||||
|
||||
public class GapDecisionStrategy : IDecisionStrategy
|
||||
{
|
||||
public bool CanDecideActivity(Activity activity)
|
||||
{
|
||||
return activity.Product == Product.GAPInsurance;
|
||||
}
|
||||
|
||||
public async Task<ActivityStateChange> DecideActivity(Activity activity)
|
||||
{
|
||||
if (
|
||||
string.Equals(activity.Detail.AccountStatus, "Flagged", StringComparison.CurrentCultureIgnoreCase)
|
||||
|| string.Equals(activity.Detail.AccountStatus, "Reinstated", StringComparison.CurrentCultureIgnoreCase)
|
||||
|| string.Equals(activity.Detail.AccountStatus, "Sold", StringComparison.CurrentCultureIgnoreCase)
|
||||
)
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Auto-Decision: Gap Accepted",
|
||||
State = ActivityState.Accepted
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
string.Equals(activity.Detail.AccountStatus, "Cancelled", StringComparison.CurrentCultureIgnoreCase)
|
||||
|| string.Equals(activity.Detail.AccountStatus, "Cancelled/Re-Quoted", StringComparison.CurrentCultureIgnoreCase)
|
||||
)
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = $"Auto-Decision: Gap {activity.Detail.AccountStatus}",
|
||||
State = ActivityState.Rejected
|
||||
});
|
||||
}
|
||||
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Auto-Decision: Gap Accepted",
|
||||
State = ActivityState.Accepted
|
||||
});
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using LCU.Library.Internal.IncentiveApiModels.Entities;
|
||||
using LCU.Library.Internal.IncentiveApiModels.Enums;
|
||||
using LCU.Library.Internal.IncentiveApiModels.Requests;
|
||||
|
||||
namespace LCU.API.Internal.Operations.IncentiveApi.Services.AutoDecision
|
||||
{
|
||||
public interface IActivityDecisionService
|
||||
{
|
||||
Task RunNightlyDecisionProcess(NightlyIncentiveProcessRequest request);
|
||||
Task<ActivityStateChange> TryDecisionActivity(Activity activity);
|
||||
Task<IReadOnlyCollection<ActivityStateChange>> DecisionActivityByState(ActivityState state);
|
||||
Task<IReadOnlyCollection<ActivityStateChange>> UpdateAndDecideManualReviewActivities();
|
||||
Task<IReadOnlyCollection<ActivityStateChange>> UpdateAndDecideLicReferralActivities();
|
||||
Task<IReadOnlyCollection<ActivityStateChange>> UpdateAndDecideDpEligibleLoanActivities();
|
||||
Task<IReadOnlyCollection<ActivityStateChange>> UpdateAndDecideDirectDepositPendingReferralActivities();
|
||||
Task<List<ActivityStateChange>> AutoDecideActivities(IEnumerable<Activity> activities);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
using LCU.Library.Internal.IncentiveApiModels.Entities;
|
||||
|
||||
namespace LCU.API.Internal.Operations.IncentiveApi.Services.AutoDecision
|
||||
{
|
||||
public interface IDecisionStrategy
|
||||
{
|
||||
public bool CanDecideActivity(Activity activity);
|
||||
public Task<ActivityStateChange> DecideActivity(Activity activity);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
using LCU.Library.Internal.IncentiveApiModels.Entities;
|
||||
using LCU.Library.Internal.IncentiveApiModels.Enums;
|
||||
|
||||
namespace LCU.API.Internal.Operations.IncentiveApi.Services.AutoDecision;
|
||||
|
||||
public class ManualDecisionStrategy : IDecisionStrategy
|
||||
{
|
||||
public bool CanDecideActivity(Activity activity)
|
||||
{
|
||||
return activity.Product != Product.ATMCard &&
|
||||
activity.Product != Product.CheckCard &&
|
||||
activity.Product != Product.Mortgage &&
|
||||
activity.Product != Product.NewChecking &&
|
||||
activity.Product != Product.GAPInsurance &&
|
||||
activity.Product != Product.MechanicalBreakdownProtection &&
|
||||
activity.Product != Product.Vehicle &&
|
||||
activity.Product != Product.DebtProtectionEligibleLoan && (activity.Product == Product.DirectDeposit ||
|
||||
activity.Product == Product.CreditCardBalanceTransfer ||
|
||||
activity.Product == Product.LIC) && activity.Type != ActivityType.Referral;
|
||||
}
|
||||
|
||||
public async Task<ActivityStateChange> DecideActivity(Activity activity)
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
ActivityId = activity.Id,
|
||||
Note = "Auto-Decision Unavailable: Please Manually Review",
|
||||
State = ActivityState.ManualReview
|
||||
});
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
using LCU.Library.Internal.IncentiveApiModels.Entities;
|
||||
using LCU.Library.Internal.IncentiveApiModels.Enums;
|
||||
|
||||
namespace LCU.API.Internal.Operations.IncentiveApi.Services.AutoDecision;
|
||||
|
||||
public class MbpDecisionStrategy : IDecisionStrategy
|
||||
{
|
||||
public bool CanDecideActivity(Activity activity)
|
||||
{
|
||||
return activity.Product == Product.MechanicalBreakdownProtection;
|
||||
}
|
||||
|
||||
public async Task<ActivityStateChange> DecideActivity(Activity activity)
|
||||
{
|
||||
if (
|
||||
string.Equals(activity.Detail.AccountStatus, "Sold", StringComparison.CurrentCultureIgnoreCase)
|
||||
|| string.Equals(activity.Detail.AccountStatus, "Flagged", StringComparison.CurrentCultureIgnoreCase)
|
||||
)
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Auto-Decision: MBP Accepted",
|
||||
State = ActivityState.Accepted
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
string.Equals(activity.Detail.AccountStatus, "Cancelled", StringComparison.CurrentCultureIgnoreCase)
|
||||
|| string.Equals(activity.Detail.AccountStatus, "Cancelled/Re-Quoted", StringComparison.CurrentCultureIgnoreCase)
|
||||
)
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
ActivityId = activity.Id,
|
||||
Note = $"Auto-Decision: MBP {activity.Detail.AccountStatus}",
|
||||
State = ActivityState.Rejected
|
||||
});
|
||||
}
|
||||
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Auto-Decision: MBP Accepted",
|
||||
State = ActivityState.Accepted
|
||||
});
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
using LCU.Library.Internal.IncentiveApiModels.Entities;
|
||||
using LCU.Library.Internal.IncentiveApiModels.Enums;
|
||||
|
||||
namespace LCU.API.Internal.Operations.IncentiveApi.Services.AutoDecision;
|
||||
|
||||
public class MortgageSaleDecisionStrategy : IDecisionStrategy
|
||||
{
|
||||
public bool CanDecideActivity(Activity activity)
|
||||
{
|
||||
return
|
||||
(activity.Product.Equals(Product.Mortgage) || activity.Product.Equals(Product.FirstMortgageReferral))
|
||||
&& activity.Type == ActivityType.Sale;
|
||||
}
|
||||
|
||||
public async Task<ActivityStateChange> DecideActivity(Activity activity)
|
||||
{
|
||||
if (activity.Detail.AccountStatus == null)
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange()
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Could Not Determine Account Status.",
|
||||
State = ActivityState.Rejected
|
||||
});
|
||||
}
|
||||
|
||||
if (activity.Detail.AccountStatus.Equals("ACT", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Mortgage Loan is Active.",
|
||||
State = ActivityState.Accepted
|
||||
});
|
||||
}
|
||||
|
||||
if (activity.Detail.AccountStatus.Equals("CLS", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Mortgage Loan is closed.",
|
||||
State = ActivityState.Rejected
|
||||
});
|
||||
}
|
||||
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Mortgage is not Active or Closed. Waiting for Manual Review or Status Update.",
|
||||
State = ActivityState.ManualReview
|
||||
});
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
using LCU.Library.Internal.IncentiveApiModels.Entities;
|
||||
using LCU.Library.Internal.IncentiveApiModels.Enums;
|
||||
|
||||
namespace LCU.API.Internal.Operations.IncentiveApi.Services.AutoDecision;
|
||||
|
||||
public class NewCheckingDecisionStrategy : IDecisionStrategy
|
||||
{
|
||||
public bool CanDecideActivity(Activity activity)
|
||||
{
|
||||
return activity.Product == Product.NewChecking && activity.Type == ActivityType.Sale;
|
||||
}
|
||||
|
||||
public async Task<ActivityStateChange> DecideActivity(Activity activity)
|
||||
{
|
||||
if (activity.Detail.AccountStatus == null)
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange()
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Could Not Determine Account Status",
|
||||
State = ActivityState.Rejected
|
||||
});
|
||||
}
|
||||
|
||||
if (activity.Detail.AccountStatus != null && activity.SourceSystem == SystemType.DigitalBanking)
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange()
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Digital Banking Sourced Sale",
|
||||
State = ActivityState.Accepted
|
||||
});
|
||||
}
|
||||
|
||||
if (activity.Detail.AccountStatus.Equals("ACT", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Checking account is Active",
|
||||
State = ActivityState.Accepted
|
||||
});
|
||||
}
|
||||
|
||||
if (activity.Detail.AccountStatus.Equals("CLS", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Checking account is Closed",
|
||||
State = ActivityState.Rejected
|
||||
});
|
||||
}
|
||||
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Checking Account is not Active or Closed. Waiting for Manual Review or Status Update",
|
||||
State = ActivityState.ManualReview
|
||||
});
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
using LCU.Library.Internal.IncentiveApiModels.Entities;
|
||||
using LCU.Library.Internal.IncentiveApiModels.Enums;
|
||||
|
||||
namespace LCU.API.Internal.Operations.IncentiveApi.Services.AutoDecision;
|
||||
|
||||
public class ReferralDecisionStrategy : IDecisionStrategy
|
||||
{
|
||||
public bool CanDecideActivity(Activity activity)
|
||||
{
|
||||
return activity.Type == ActivityType.Referral &&
|
||||
(activity.Product == Product.ATMCard ||
|
||||
activity.Product == Product.CheckCard ||
|
||||
activity.Product == Product.NewChecking ||
|
||||
activity.Product == Product.Mortgage ||
|
||||
activity.Product == Product.DirectDeposit ||
|
||||
activity.Product == Product.CreditCardBalanceTransfer ||
|
||||
activity.Product == Product.LIC ||
|
||||
activity.Product == Product.FirstMortgageReferral ||
|
||||
activity.Product == Product.eStatement);
|
||||
}
|
||||
|
||||
public async Task<ActivityStateChange> DecideActivity(Activity activity)
|
||||
{
|
||||
if (activity.Product == Product.LIC)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(activity.Detail.StationName))
|
||||
{
|
||||
if (activity.Detail.StationName.Equals("LIC Referral-Qualified", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange()
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "LIC Referral In Station 'LIC Referral-Qualified'.",
|
||||
State = ActivityState.Accepted
|
||||
});
|
||||
}
|
||||
|
||||
if (activity.Detail.StationName.Equals("LIC Referral- Not Qualified", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange()
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "LIC Referral In Station 'LIC Referral- Not Qualified'.",
|
||||
State = ActivityState.Rejected
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "LIC Referral Not In Final Station. Waiting for Manual Review or Station Update.",
|
||||
State = ActivityState.ManualReview
|
||||
});
|
||||
}
|
||||
|
||||
var daysBeforeExpiration = activity.Product != Product.FirstMortgageReferral ? "60" : "120";
|
||||
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = $"Waiting for Matching Sale. Expires in {daysBeforeExpiration} days from creation.",
|
||||
State = ActivityState.PendingReferral
|
||||
});
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
using LCU.Library.Internal.IncentiveApiModels.Entities;
|
||||
using LCU.Library.Internal.IncentiveApiModels.Enums;
|
||||
|
||||
namespace LCU.API.Internal.Operations.IncentiveApi.Services.AutoDecision;
|
||||
|
||||
public class VehicleDecisionStrategy : IDecisionStrategy
|
||||
{
|
||||
public bool CanDecideActivity(Activity activity)
|
||||
{
|
||||
return activity.Product.Equals(Product.Vehicle) && activity.Type == ActivityType.Sale;
|
||||
}
|
||||
|
||||
public async Task<ActivityStateChange> DecideActivity(Activity activity)
|
||||
{
|
||||
if (activity.Detail.AccountStatus == null)
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange()
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Could Not Determine Account Status.",
|
||||
State = ActivityState.Rejected
|
||||
});
|
||||
}
|
||||
|
||||
if (activity.Detail.AccountStatus.Equals("ACT", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Vehicle Loan is Active.",
|
||||
State = ActivityState.Accepted
|
||||
});
|
||||
}
|
||||
|
||||
if (activity.Detail.AccountStatus.Equals("CLS", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Vehicle Loan is closed.",
|
||||
State = ActivityState.Rejected
|
||||
});
|
||||
}
|
||||
|
||||
return await Task.FromResult(new ActivityStateChange
|
||||
{
|
||||
AcceptedDate = activity.Accepted.Date.AddHours(12),
|
||||
ActivityId = activity.Id,
|
||||
Note = "Vehicle Loan is not Active or Closed. Waiting for Manual Review or Status Update.",
|
||||
State = ActivityState.ManualReview
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user