using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; using LCU.API.Internal.Operations.IncentiveApiLib.Extensions; using LCU.API.Internal.Operations.IncentiveApiLib.Persistence; using LCU.Library.Internal.IncentiveApiModels.Common; using LCU.Library.Internal.IncentiveApiModels.Entities; using LCU.Library.Internal.IncentiveApiModels.Enums; using Microsoft.EntityFrameworkCore; namespace LCU.API.Internal.Operations.IncentiveApiLib.Repositories; public class ActivityRepository : BaseRepository, IActivityRepository { public ActivityRepository(IApplicationDbContext context) : base(context) { } public async Task SaveActivityAsync(Activity activity) { await _context.Activities.AddAsync(activity); return activity.Id; } public async Task SaveActivityRangeAsync(List activities) { await _context.Activities.AddRangeAsync(activities); } public void UpdateActivityRangeAsync(List activities) { var existing = activities.Where(x => _context.Activities.Contains(x)); _context.Activities.UpdateRange(activities); } public async Task> GetActivitiesAsync() { return await _context.Activities .Include(activity => activity.Detail) .Include(activity => activity.MetaData) .OrderBy(t => t.Id) .ToListAsync(); } public async Task> GetActivitiesAsync(int page, int limit) { var results = new PagedResult(); var skip = (page - 1) * limit; var activities = _context.Activities .Include(activity => activity.Detail) .Include(activity => activity.MetaData) .OrderByDescending(t => t.Created); results.Total = activities.Count(); results.Items = await activities.Skip(skip).Take(limit).ToListAsync(); return results; } public async Task> FindUnmatchedReferrals() { var activities = _context.Activities.Include(activity => activity.Detail).Include(activity => activity.MetaData); IQueryable querySet = activities .Where(x => x.Type == ActivityType.Referral && x.State == ActivityState.PendingReferral) .OrderByDescending(t => t.LastUpdated); return await querySet.ToListAsync(); } public async Task> FindPendingEStatementsReferrals() { var activities = _context.Activities.Include(activity => activity.Detail).Include(activity => activity.MetaData); IQueryable querySet = activities .Where(x => x.Type == ActivityType.Referral && x.State == ActivityState.PendingReferral && x.Product == Product.eStatement && string.IsNullOrEmpty(x.Detail.AccountNumber)) .OrderByDescending(t => t.LastUpdated); return await querySet.ToListAsync(); } public async Task FindUnmatchedSaleForReferral(Activity referral) { var activities = _context.Activities .Include(activity => activity.Detail) .Include(activity => activity.MetaData); var matchOnMetaData = string.IsNullOrEmpty(referral.Detail.SubjectOrgNbr) && string.IsNullOrEmpty(referral.Detail.SubjectPersonNbr); var matchedActivities = matchOnMetaData ? await activities.Where(MetaDataMatchPredicate(referral)).ToListAsync() : await activities.Where(PersonDataMatchPredicate(referral)).ToListAsync(); if (matchOnMetaData && matchedActivities.Any()) { matchedActivities = matchedActivities.Where(x => ListsContainMatchingItem( x.MetaData.Where(e => e.MetaDataType == MetaDataType.Email).Select(e => e.MetaData).ToList(), referral.MetaData.Where(r => r.MetaDataType == MetaDataType.Email).Select(e => e.MetaData).ToList() ) || ListsContainMatchingItem( x.MetaData.Where(e => e.MetaDataType == MetaDataType.Phone).Select(e => e.MetaData).ToList(), referral.MetaData.Where(r => r.MetaDataType == MetaDataType.Phone).Select(e => e.MetaData).ToList() ) ) .ToList(); } var matchingSaleActivityIds = matchedActivities.Select(x => x.Id); var activityIDsAssociatedToStateChange = _context.ActivityStateChanges .Where(change => matchingSaleActivityIds.Contains(change.ActivityMatchId)) .Select(x => x.ActivityMatchId); var activityIDsNotAssociatedToStateChange = matchingSaleActivityIds.Except(activityIDsAssociatedToStateChange); return matchedActivities.Where(x => activityIDsNotAssociatedToStateChange.Contains(x.Id)) .OrderByDescending(t => t.LastUpdated) .FirstOrDefault(); } public EStatementDeliveryMethod FindDeliveryMethodForEStatement(Activity referral, List deliveryMethods) { var matchedMethods = new List(); var nullReferralOrgNumber = "0"; var matchableDeliveryMethods = deliveryMethods.Where(x => x.ActivityDateTime.Date >= referral.SourceCreated.Date).ToList(); matchedMethods.AddRange(matchableDeliveryMethods.Where(x => x.PersonNumber == referral.Detail.SubjectPersonNbr || x.OrganizationNumber == (referral.Detail.SubjectOrgNbr ?? nullReferralOrgNumber) && x.OrganizationNumber != nullReferralOrgNumber && referral.Detail.SubjectOrgNbr != nullReferralOrgNumber)); matchedMethods.AddRange(matchableDeliveryMethods.Where(x => ListsContainMatchingItem( x.MetaData.Where(e => e.MetaDataType == MetaDataType.Email).Select(e => e.MetaData).ToList(), referral.MetaData.Where(r => r.MetaDataType == MetaDataType.Email).Select(e => e.MetaData).ToList() ) || ListsContainMatchingItem( x.MetaData.Where(e => e.MetaDataType == MetaDataType.Phone).Select(e => e.MetaData).ToList(), referral.MetaData.Where(r => r.MetaDataType == MetaDataType.Phone).Select(e => e.MetaData).ToList() ) ) .ToList()); var matchedMethod = matchedMethods.OrderByDescending(a => a.ActivityDateTime).FirstOrDefault(); if (matchedMethod != null) { deliveryMethods.Remove(matchedMethod); } return matchedMethod; } private static Expression> MetaDataMatchPredicate(Activity referral) { return x => (x.Type == ActivityType.Sale || x.Type == ActivityType.Other) && x.Product == referral.Product && x.MetaData != null && referral.MetaData != null && x.State == ActivityState.Accepted && x.SourceCreated.Date >= referral.SourceCreated.Date; } private static bool ListsContainMatchingItem(IReadOnlyList list1, IReadOnlyList list2) { if ((list1?.Any() ?? false) && (list2?.Any() ?? false)) { return list1.Any(x => list2.Any(t => string.Equals(t, x, StringComparison.CurrentCultureIgnoreCase))); } return false; } private static Expression> PersonDataMatchPredicate(Activity referral) { return x => (x.Type == ActivityType.Sale || x.Type == ActivityType.Other) && x.Product == referral.Product && x.Detail.SubjectPersonNbr == referral.Detail.SubjectPersonNbr && x.Detail.SubjectOrgNbr == referral.Detail.SubjectOrgNbr && x.State == ActivityState.Accepted && x.SourceCreated.Date >= referral.SourceCreated.Date; } public async Task FindDuplicateSaleAsync(Activity queryActivity) { if (queryActivity.Detail == null || queryActivity.Type != ActivityType.Sale) { return null; } var activities = _context.Activities .Include(activity => activity.Detail) .Include(activity => activity.MetaData); IQueryable querySet = activities .Where(x => x.Id != queryActivity.Id && x.Product == queryActivity.Product && x.Type == queryActivity.Type && x.Detail.AccountNumber == queryActivity.Detail.AccountNumber && x.Detail.SubAccountNumber == queryActivity.Detail.SubAccountNumber && x.Detail.SubjectOrgNbr == queryActivity.Detail.SubjectOrgNbr && x.Detail.SubjectPersonNbr == queryActivity.Detail.SubjectPersonNbr && ( queryActivity.SourceId == null || x.SourceId == queryActivity.SourceId) ) .OrderByDescending(t => t.LastUpdated); return await querySet.FirstOrDefaultAsync(); } public async Task FindDuplicateReferralAsync(Activity queryActivity) { if (queryActivity.Detail == null || queryActivity.Type != ActivityType.Referral) { return null; } var activities = _context.Activities .Include(activity => activity.Detail); IQueryable querySet = activities .Where(x => x.Id != queryActivity.Id && x.Type == queryActivity.Type && x.Product == queryActivity.Product && x.Detail.SubjectPersonNbr != null && queryActivity.Detail.SubjectPersonNbr != null && x.Detail.SubjectOrgNbr != null && queryActivity.Detail.SubjectOrgNbr != null && x.Detail.SubjectPersonNbr == queryActivity.Detail.SubjectPersonNbr && x.Detail.SubjectOrgNbr == queryActivity.Detail.SubjectOrgNbr ) .OrderByDescending(t => t.LastUpdated); return await querySet.FirstOrDefaultAsync(); } public async Task FindActivityForStateUpdate(Activity queryActivity) { if (queryActivity.Detail == null) { return null; } var activities = _context.Activities .Include(activity => activity.Detail) .Include(activity => activity.MetaData); IQueryable querySet = activities .Where(x => x.EmployeeEmail == queryActivity.EmployeeEmail && x.EmployeeId == queryActivity.EmployeeId && x.DepartmentId == queryActivity.DepartmentId && x.Product == queryActivity.Product && x.SourceCreated.Date == queryActivity.SourceCreated.Date && x.SourceSystem == queryActivity.SourceSystem && x.Type == queryActivity.Type && x.Detail.AccountNumber == queryActivity.Detail.AccountNumber && x.Detail.SubAccountNumber == queryActivity.Detail.SubAccountNumber ) .OrderByDescending(t => t.LastUpdated); var returnedItems = await querySet.ToListAsync(); Func activityStateCondition = x => x.State != queryActivity.State; var returnItem = returnedItems.Count(activityStateCondition) > 1 ? returnedItems.FirstOrDefault(activityStateCondition) : returnedItems.FirstOrDefault(); return returnItem; } public async Task FindActivityForDetailUpdate(Activity queryActivity) { if (queryActivity.Detail == null) { return null; } var returnedItems = await GetActivityForDetailUpdate(queryActivity); if (returnedItems.Count == 0) { returnedItems = await GetActivityForDetailUpdate(queryActivity, false); } if (returnedItems.Count == 0) { return null; } Func activityStatePredicate = x => x.State == ActivityState.Accepted; var returnItem = returnedItems.Count(activityStatePredicate) >= 1 ? returnedItems.FirstOrDefault(activityStatePredicate) : returnedItems.FirstOrDefault(); return returnItem; } private async Task> GetActivityForDetailUpdate(Activity queryActivity, bool filterOnSubAccountNumber = true) { var activities = _context.Activities .Include(activity => activity.Detail) .Include(activity => activity.MetaData); IQueryable querySet = activities .Where(x => x.EmployeeEmail == queryActivity.EmployeeEmail && x.EmployeeId == queryActivity.EmployeeId && x.DepartmentId == queryActivity.DepartmentId && x.Product == queryActivity.Product && x.SourceSystem == queryActivity.SourceSystem && x.Type == queryActivity.Type && x.Detail.AccountNumber == queryActivity.Detail.AccountNumber ) .OrderByDescending(t => t.LastUpdated); if (filterOnSubAccountNumber) { return await querySet.Where(x => x.Detail.SubAccountNumber == queryActivity.Detail.SubAccountNumber).ToListAsync(); } return await querySet.Where(x => x.State == ActivityState.Accepted).ToListAsync(); } public async Task> GetPagedFilteredActivities(ActivityFilter filter) { var results = new PagedResult(); var skip = (filter.Page - 1) * filter.Limit; var activities = _context.Activities.Include(activity => activity.Detail).Include(activity => activity.MetaData); IQueryable querySet = activities; if (filter.Product != null && filter.Product.Any()) { var products = filter.Product; querySet = querySet.Where(x => products.Contains(x.Product)); } if (filter.Type != null && filter.Type.Any()) { var types = filter.Type; querySet = querySet.Where(x => types.Contains(x.Type)); } if (filter.State != null && filter.State.Any()) { var states = filter.State; querySet = querySet.Where(x => states.Contains(x.State)); } if (filter.DateLow != null) { querySet = querySet.Where(x => x.Accepted.Date >= filter.DateLow); } if (filter.DateHigh != null) { querySet = querySet.Where(x => x.Accepted.Date <= filter.DateHigh); } if (filter.DepartmentId != 0) { querySet = querySet.Where(x => x.DepartmentId == filter.DepartmentId); } if (!string.IsNullOrWhiteSpace(filter.EmployeeId)) { querySet = querySet.Where(x => x.EmployeeId == filter.EmployeeId); } if (!string.IsNullOrWhiteSpace(filter.Query)) { var query = filter.Query.ToLower().Trim(); var hasActivityId = int.TryParse(query, out var activityId); var sourceIdMatches = _context.Activities .Where(x => x.SourceId != null && x.SourceId.ToLower() == query) .Select(x => x.Id); var memberNameMatches = _context.Activities .Where(x => x.Detail != null && x.Detail.MemberName != null && x.Detail.MemberName.ToLower().Contains(query)) .Select(x => x.Id); var accountNumberMatches = _context.Activities .Where(x => x.Detail != null && x.Detail.AccountNumber != null && x.Detail.AccountNumber.ToLower().Contains(query)) .Select(x => x.Id); var subjectPersonNbrMatches = _context.Activities .Where(x => x.Detail != null && x.Detail.SubjectPersonNbr != null && x.Detail.SubjectPersonNbr.ToLower().Contains(query)) .Select(x => x.Id); var subjectOrgNbrMatches = _context.Activities .Where(x => x.Detail != null && x.Detail.SubjectOrgNbr != null && x.Detail.SubjectOrgNbr.ToLower().Contains(query)) .Select(x => x.Id); var matchingIds = sourceIdMatches .Union(memberNameMatches) .Union(accountNumberMatches) .Union(subjectPersonNbrMatches) .Union(subjectOrgNbrMatches); if (hasActivityId) { matchingIds = matchingIds.Union( _context.Activities .Where(x => x.Id == activityId) .Select(x => x.Id)); } querySet = querySet.Where(x => matchingIds.Contains(x.Id)); } querySet = SortQuerySet(querySet, filter); results.Total = querySet.Count(); results.Items = await querySet.Skip(skip).Take(filter.Limit).ToListAsync(); return results; } public async Task> GetAllActivitiesWithPayoutsForEmployee(string employeeId) { return await _context.Activities.Include(activity => activity.ActivityPayouts).Where(x => x.EmployeeId == employeeId).ToListAsync(); } private IQueryable SortQuerySet(IQueryable querySet, ActivityFilter filter) { if (string.IsNullOrWhiteSpace(filter.SortBy)) { return querySet .OrderByDescending(t => t.Accepted); } if (filter.SortDescending.HasValue && filter.SortDescending.Value) { return (IQueryable)querySet.OrderByDescending(filter.SortBy); } return (IQueryable)querySet.OrderBy(filter.SortBy); } public async Task GetActivityById(int activityId) { return await _context.Activities.FindAsync(activityId); } public async Task> FindEmployeeActivityByMonthRange(AppUser appUser, MonthRange monthRange) { return await _context.Activities .Include(activity => activity.Detail) .Include(activity => activity.MetaData) .Where(x => x.Accepted.Date >= monthRange.StartTime.Date && x.Accepted.Date <= monthRange.EndTime.Date && appUser.EmployeeId == x.EmployeeId) .ToListAsync(); } public async Task> FindDepartmentActivityByMonthRange(int departmentId, MonthRange monthRange) { return await _context.Activities .Include(activity => activity.Detail) .Include(activity => activity.MetaData) .Where(x => x.Accepted.Date >= monthRange.StartTime.Date && x.Accepted.Date <= monthRange.EndTime.Date && x.DepartmentId == departmentId) .ToListAsync(); } public async Task GetActivityIncludingPayouts(int activityId) { return await _context.Activities .Include(activity => activity.Detail) .Include(activity => activity.MetaData) .Include(activity => activity.ActivityPayouts) .ThenInclude(payouts => payouts.PayoutLineItem) .FirstOrDefaultAsync(x => x.Id == activityId); } public async Task> GetActivitiesBySourceId(List sourceIds) { return await _context.Activities.Where(x => sourceIds.Contains(x.SourceId)).ToListAsync(); } public async Task FindDuplicateReferralBySourceIdAsync(Activity queryActivity) { if (queryActivity.Detail == null || queryActivity.Type != ActivityType.Referral) { return null; } var activities = _context.Activities .Include(activity => activity.Detail) .Include(activity => activity.MetaData); IQueryable querySet = activities .Where(x => x.Id != queryActivity.Id && x.Type == queryActivity.Type && x.Product == queryActivity.Product && x.SourceId == queryActivity.SourceId) .OrderByDescending(t => t.LastUpdated); return await querySet.FirstOrDefaultAsync(); } public async Task> FindById( List ids, bool withDetail = true, bool withTracking = true ) { if (!ids.Any()) { return new List(); } var query = withTracking ? _context.Activities : _context.Activities.AsNoTracking(); if (withDetail) { query = query.Include(activity => activity.Detail); } query = query.Where(activity => ids.Contains(activity.Id)); return await query.ToListAsync(); } public async Task> FindLatestBySourceId( List sourceIds, bool withDetail = true, bool withTracking = true ) { if (!sourceIds.Any()) { return new List(); } var query = withTracking ? _context.Activities : _context.Activities.AsNoTracking(); if (withDetail) { query = query.Include(activity => activity.Detail); } return (await query .Where(activity => sourceIds.Contains(activity.SourceId)) .GroupBy(activity => activity.SourceId) .Select(group => group.OrderBy(activity => activity.Created).FirstOrDefault()) .ToListAsync()) .Where(activity => activity != null) .ToList(); } public async Task> FindGapInsuranceEligible( MonthRange monthRange, bool withTracking = true ) { var query = withTracking ? _context.Activities : _context.Activities.AsNoTracking(); return await query .Include(activity => activity.Detail) .Where(activity => activity.Detail != null && activity.Detail.IsGapInsuranceEligible && activity.Accepted.Date >= monthRange.StartTime.Date && activity.Accepted.Date <= monthRange.EndTime.Date) .ToListAsync(); } public async Task> GetFilteredActivities( MonthRange monthRange, Product product, ActivityState activityState, bool withTracking = true ) { var querySet = withTracking ? _context.Activities : _context.Activities.AsNoTracking(); return await querySet .Include(activity => activity.Detail) .Where( activity => activity.Accepted.Date >= monthRange.StartTime.Date && activity.Accepted.Date <= monthRange.EndTime.Date && activity.Product == product && activity.State == activityState ) .ToListAsync(); } }