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,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;
}
}
@@ -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
});
}
}
@@ -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;
}
}
@@ -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
});
}
}
@@ -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
});
}
}
@@ -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
});
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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
});
}
}
@@ -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
});
}
}
@@ -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
});
}
}
@@ -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
});
}
}
@@ -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
});
}
}
@@ -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
});
}
}