1053 lines
42 KiB
C#
1053 lines
42 KiB
C#
using AutoMapper;
|
|
using Marco.Pms.DataAccess.Data;
|
|
using Marco.Pms.Helpers;
|
|
using Marco.Pms.Helpers.CacheHelper;
|
|
using Marco.Pms.Model.Expenses;
|
|
using Marco.Pms.Model.Master;
|
|
using Marco.Pms.Model.MongoDBModels.Expenses;
|
|
using Marco.Pms.Model.MongoDBModels.Masters;
|
|
using Marco.Pms.Model.MongoDBModels.Project;
|
|
using Marco.Pms.Model.MongoDBModels.Utility;
|
|
using Marco.Pms.Model.Projects;
|
|
using Marco.Pms.Model.Utilities;
|
|
using MarcoBMS.Services.Service;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using MongoDB.Driver;
|
|
using Project = Marco.Pms.Model.Projects.Project;
|
|
|
|
namespace Marco.Pms.Services.Helpers
|
|
{
|
|
public class CacheUpdateHelper
|
|
{
|
|
private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
|
|
private readonly ApplicationDbContext _context;
|
|
private readonly IMapper _mapper;
|
|
private readonly ProjectCache _projectCache;
|
|
private readonly EmployeeCache _employeeCache;
|
|
private readonly ReportCache _reportCache;
|
|
private readonly ExpenseCache _expenseCache;
|
|
private readonly ILoggingService _logger;
|
|
private readonly GeneralHelper _generalHelper;
|
|
private static readonly Guid Draft = Guid.Parse("297e0d8f-f668-41b5-bfea-e03b354251c8");
|
|
private static readonly Guid Rejected = Guid.Parse("d1ee5eec-24b6-4364-8673-a8f859c60729");
|
|
|
|
public CacheUpdateHelper(
|
|
IMapper mapper,
|
|
ProjectCache projectCache,
|
|
EmployeeCache employeeCache,
|
|
ReportCache reportCache,
|
|
ExpenseCache expenseCache,
|
|
ILoggingService logger,
|
|
IDbContextFactory<ApplicationDbContext> dbContextFactory,
|
|
ApplicationDbContext context,
|
|
GeneralHelper generalHelper)
|
|
{
|
|
_mapper = mapper;
|
|
_projectCache = projectCache;
|
|
_employeeCache = employeeCache;
|
|
_reportCache = reportCache;
|
|
_expenseCache = expenseCache;
|
|
_logger = logger;
|
|
_dbContextFactory = dbContextFactory;
|
|
_context = context;
|
|
_generalHelper = generalHelper;
|
|
}
|
|
#region ======================================================= Project Details Cache =======================================================
|
|
|
|
public async Task AddProjectDetails(Project project)
|
|
{
|
|
// --- Step 1: Fetch all required data from the database in parallel ---
|
|
|
|
// Each task uses its own DbContext instance to avoid concurrency issues.
|
|
var statusTask = Task.Run(async () =>
|
|
{
|
|
using var context = _dbContextFactory.CreateDbContext();
|
|
return await context.StatusMasters
|
|
.AsNoTracking()
|
|
.Where(s => s.Id == project.ProjectStatusId)
|
|
.Select(s => new { s.Id, s.Status }) // Projection
|
|
.FirstOrDefaultAsync();
|
|
});
|
|
|
|
var teamSizeTask = Task.Run(async () =>
|
|
{
|
|
using var context = _dbContextFactory.CreateDbContext();
|
|
return await context.ProjectAllocations
|
|
.AsNoTracking()
|
|
.CountAsync(pa => pa.ProjectId == project.Id && pa.IsActive); // Server-side count is efficient
|
|
});
|
|
|
|
// This task fetches the entire infrastructure hierarchy and performs aggregations in the database.
|
|
var infrastructureTask = Task.Run(async () =>
|
|
{
|
|
using var context = _dbContextFactory.CreateDbContext();
|
|
|
|
// 1. Fetch all hierarchical data using projections.
|
|
// This is still a chain, but it's inside one task and much faster due to projections.
|
|
var buildings = await context.Buildings.AsNoTracking()
|
|
.Where(b => b.ProjectId == project.Id)
|
|
.Select(b => new { b.Id, b.ProjectId, b.Name, b.Description })
|
|
.ToListAsync();
|
|
var buildingIds = buildings.Select(b => b.Id).ToList();
|
|
|
|
var floors = await context.Floor.AsNoTracking()
|
|
.Where(f => buildingIds.Contains(f.BuildingId))
|
|
.Select(f => new { f.Id, f.BuildingId, f.FloorName })
|
|
.ToListAsync();
|
|
var floorIds = floors.Select(f => f.Id).ToList();
|
|
|
|
var workAreas = await context.WorkAreas.AsNoTracking()
|
|
.Where(wa => floorIds.Contains(wa.FloorId))
|
|
.Select(wa => new { wa.Id, wa.FloorId, wa.AreaName })
|
|
.ToListAsync();
|
|
var workAreaIds = workAreas.Select(wa => wa.Id).ToList();
|
|
|
|
// 2. THE KEY OPTIMIZATION: Aggregate work items in the database.
|
|
var workSummaries = await context.WorkItems.AsNoTracking()
|
|
.Where(wi => workAreaIds.Contains(wi.WorkAreaId))
|
|
.GroupBy(wi => wi.WorkAreaId) // Group by parent on the DB server
|
|
.Select(g => new // Let the DB do the SUM
|
|
{
|
|
WorkAreaId = g.Key,
|
|
PlannedWork = g.Sum(i => i.PlannedWork),
|
|
CompletedWork = g.Sum(i => i.CompletedWork)
|
|
})
|
|
.ToDictionaryAsync(x => x.WorkAreaId); // Return a ready-to-use dictionary
|
|
|
|
return (buildings, floors, workAreas, workSummaries);
|
|
});
|
|
|
|
// Wait for all parallel database operations to complete.
|
|
await Task.WhenAll(statusTask, teamSizeTask, infrastructureTask);
|
|
|
|
// Get the results from the completed tasks.
|
|
var status = await statusTask;
|
|
var teamSize = await teamSizeTask;
|
|
var (allBuildings, allFloors, allWorkAreas, workSummariesByWorkAreaId) = await infrastructureTask;
|
|
|
|
// --- Step 2: Process the fetched data and build the MongoDB model ---
|
|
|
|
var projectDetails = new ProjectMongoDB
|
|
{
|
|
Id = project.Id.ToString(),
|
|
Name = project.Name,
|
|
ShortName = project.ShortName,
|
|
ProjectAddress = project.ProjectAddress,
|
|
StartDate = project.StartDate,
|
|
EndDate = project.EndDate,
|
|
ContactPerson = project.ContactPerson,
|
|
TeamSize = teamSize
|
|
};
|
|
|
|
projectDetails.ProjectStatus = new StatusMasterMongoDB
|
|
{
|
|
Id = status!.Id.ToString(),
|
|
Status = status.Status
|
|
};
|
|
|
|
// Use fast in-memory lookups instead of .Where() in loops.
|
|
var floorsByBuildingId = allFloors.ToLookup(f => f.BuildingId);
|
|
var workAreasByFloorId = allWorkAreas.ToLookup(wa => wa.FloorId);
|
|
|
|
double totalPlannedWork = 0, totalCompletedWork = 0;
|
|
var buildingMongoList = new List<BuildingMongoDB>();
|
|
|
|
foreach (var building in allBuildings)
|
|
{
|
|
double buildingPlanned = 0, buildingCompleted = 0;
|
|
var floorMongoList = new List<FloorMongoDB>();
|
|
|
|
foreach (var floor in floorsByBuildingId[building.Id]) // Fast lookup
|
|
{
|
|
double floorPlanned = 0, floorCompleted = 0;
|
|
var workAreaMongoList = new List<WorkAreaMongoDB>();
|
|
|
|
foreach (var wa in workAreasByFloorId[floor.Id]) // Fast lookup
|
|
{
|
|
// Get the pre-calculated summary from the dictionary. O(1) operation.
|
|
workSummariesByWorkAreaId.TryGetValue(wa.Id, out var summary);
|
|
var waPlanned = summary?.PlannedWork ?? 0;
|
|
var waCompleted = summary?.CompletedWork ?? 0;
|
|
|
|
workAreaMongoList.Add(new WorkAreaMongoDB
|
|
{
|
|
Id = wa.Id.ToString(),
|
|
FloorId = wa.FloorId.ToString(),
|
|
AreaName = wa.AreaName,
|
|
PlannedWork = waPlanned,
|
|
CompletedWork = waCompleted
|
|
});
|
|
|
|
floorPlanned += waPlanned;
|
|
floorCompleted += waCompleted;
|
|
}
|
|
|
|
floorMongoList.Add(new FloorMongoDB
|
|
{
|
|
Id = floor.Id.ToString(),
|
|
BuildingId = floor.BuildingId.ToString(),
|
|
FloorName = floor.FloorName,
|
|
PlannedWork = floorPlanned,
|
|
CompletedWork = floorCompleted,
|
|
WorkAreas = workAreaMongoList
|
|
});
|
|
|
|
buildingPlanned += floorPlanned;
|
|
buildingCompleted += floorCompleted;
|
|
}
|
|
|
|
buildingMongoList.Add(new BuildingMongoDB
|
|
{
|
|
Id = building.Id.ToString(),
|
|
ProjectId = building.ProjectId.ToString(),
|
|
BuildingName = building.Name,
|
|
Description = building.Description,
|
|
PlannedWork = buildingPlanned,
|
|
CompletedWork = buildingCompleted,
|
|
Floors = floorMongoList
|
|
});
|
|
|
|
totalPlannedWork += buildingPlanned;
|
|
totalCompletedWork += buildingCompleted;
|
|
}
|
|
|
|
projectDetails.Buildings = buildingMongoList;
|
|
projectDetails.PlannedWork = totalPlannedWork;
|
|
projectDetails.CompletedWork = totalCompletedWork;
|
|
|
|
try
|
|
{
|
|
await _projectCache.AddProjectDetailsToCache(projectDetails);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occurred while adding project {ProjectId} to Cache", project.Id);
|
|
}
|
|
}
|
|
public async Task AddProjectDetailsList(List<Project> projects)
|
|
{
|
|
var projectIds = projects.Select(p => p.Id).ToList();
|
|
if (!projectIds.Any())
|
|
{
|
|
return; // Nothing to do
|
|
}
|
|
var projectStatusIds = projects.Select(p => p.ProjectStatusId).Distinct().ToList();
|
|
|
|
// --- Step 1: Fetch all required data in maximum parallel ---
|
|
// Each task uses its own DbContext and selects only the required columns (projection).
|
|
|
|
var statusTask = Task.Run(async () =>
|
|
{
|
|
using var context = _dbContextFactory.CreateDbContext();
|
|
return await context.StatusMasters
|
|
.AsNoTracking()
|
|
.Where(s => projectStatusIds.Contains(s.Id))
|
|
.Select(s => new { s.Id, s.Status }) // Projection
|
|
.ToDictionaryAsync(s => s.Id);
|
|
});
|
|
|
|
var teamSizeTask = Task.Run(async () =>
|
|
{
|
|
using var context = _dbContextFactory.CreateDbContext();
|
|
// Server-side aggregation and projection into a dictionary
|
|
return await context.ProjectAllocations
|
|
.AsNoTracking()
|
|
.Where(pa => projectIds.Contains(pa.ProjectId) && pa.IsActive)
|
|
.GroupBy(pa => pa.ProjectId)
|
|
.Select(g => new { ProjectId = g.Key, Count = g.Count() })
|
|
.ToDictionaryAsync(x => x.ProjectId, x => x.Count);
|
|
});
|
|
|
|
var buildingsTask = Task.Run(async () =>
|
|
{
|
|
using var context = _dbContextFactory.CreateDbContext();
|
|
return await context.Buildings
|
|
.AsNoTracking()
|
|
.Where(b => projectIds.Contains(b.ProjectId))
|
|
.Select(b => new { b.Id, b.ProjectId, b.Name, b.Description }) // Projection
|
|
.ToListAsync();
|
|
});
|
|
|
|
// We need the building IDs for the next level, so we must await this one first.
|
|
var allBuildings = await buildingsTask;
|
|
var buildingIds = allBuildings.Select(b => b.Id).ToList();
|
|
|
|
var floorsTask = Task.Run(async () =>
|
|
{
|
|
using var context = _dbContextFactory.CreateDbContext();
|
|
return await context.Floor
|
|
.AsNoTracking()
|
|
.Where(f => buildingIds.Contains(f.BuildingId))
|
|
.Select(f => new { f.Id, f.BuildingId, f.FloorName }) // Projection
|
|
.ToListAsync();
|
|
});
|
|
|
|
// We need floor IDs for the next level.
|
|
var allFloors = await floorsTask;
|
|
var floorIds = allFloors.Select(f => f.Id).ToList();
|
|
|
|
var workAreasTask = Task.Run(async () =>
|
|
{
|
|
using var context = _dbContextFactory.CreateDbContext();
|
|
return await context.WorkAreas
|
|
.AsNoTracking()
|
|
.Where(wa => floorIds.Contains(wa.FloorId))
|
|
.Select(wa => new { wa.Id, wa.FloorId, wa.AreaName }) // Projection
|
|
.ToListAsync();
|
|
});
|
|
|
|
// The most powerful optimization: Aggregate work items in the database.
|
|
var workSummaryTask = Task.Run(async () =>
|
|
{
|
|
using var context = _dbContextFactory.CreateDbContext();
|
|
var workAreaIds = await context.WorkAreas
|
|
.Where(wa => floorIds.Contains(wa.FloorId))
|
|
.Select(wa => wa.Id)
|
|
.ToListAsync();
|
|
|
|
// Let the DB do the SUM. This is much faster and transfers less data.
|
|
return await context.WorkItems
|
|
.AsNoTracking()
|
|
.Where(wi => workAreaIds.Contains(wi.WorkAreaId))
|
|
.GroupBy(wi => wi.WorkAreaId)
|
|
.Select(g => new
|
|
{
|
|
WorkAreaId = g.Key,
|
|
PlannedWork = g.Sum(wi => wi.PlannedWork),
|
|
CompletedWork = g.Sum(wi => wi.CompletedWork)
|
|
})
|
|
.ToDictionaryAsync(x => x.WorkAreaId);
|
|
});
|
|
|
|
// Await the remaining parallel tasks.
|
|
await Task.WhenAll(statusTask, teamSizeTask, workAreasTask, workSummaryTask);
|
|
|
|
// --- Step 2: Process the fetched data and build the MongoDB models ---
|
|
|
|
var allStatuses = await statusTask;
|
|
var teamSizesByProjectId = await teamSizeTask;
|
|
var allWorkAreas = await workAreasTask;
|
|
var workSummariesByWorkAreaId = await workSummaryTask;
|
|
|
|
// Create fast in-memory lookups for hierarchical data
|
|
var buildingsByProjectId = allBuildings.ToLookup(b => b.ProjectId);
|
|
var floorsByBuildingId = allFloors.ToLookup(f => f.BuildingId);
|
|
var workAreasByFloorId = allWorkAreas.ToLookup(wa => wa.FloorId);
|
|
|
|
var projectDetailsList = new List<ProjectMongoDB>(projects.Count);
|
|
foreach (var project in projects)
|
|
{
|
|
var projectDetails = new ProjectMongoDB
|
|
{
|
|
Id = project.Id.ToString(),
|
|
Name = project.Name,
|
|
ShortName = project.ShortName,
|
|
ProjectAddress = project.ProjectAddress,
|
|
StartDate = project.StartDate,
|
|
EndDate = project.EndDate,
|
|
ContactPerson = project.ContactPerson,
|
|
TeamSize = teamSizesByProjectId.GetValueOrDefault(project.Id, 0)
|
|
};
|
|
|
|
if (allStatuses.TryGetValue(project.ProjectStatusId, out var status))
|
|
{
|
|
projectDetails.ProjectStatus = new StatusMasterMongoDB
|
|
{
|
|
Id = status.Id.ToString(),
|
|
Status = status.Status
|
|
};
|
|
}
|
|
|
|
double totalPlannedWork = 0, totalCompletedWork = 0;
|
|
var buildingMongoList = new List<BuildingMongoDB>();
|
|
|
|
foreach (var building in buildingsByProjectId[project.Id])
|
|
{
|
|
double buildingPlanned = 0, buildingCompleted = 0;
|
|
var floorMongoList = new List<FloorMongoDB>();
|
|
|
|
foreach (var floor in floorsByBuildingId[building.Id])
|
|
{
|
|
double floorPlanned = 0, floorCompleted = 0;
|
|
var workAreaMongoList = new List<WorkAreaMongoDB>();
|
|
|
|
foreach (var wa in workAreasByFloorId[floor.Id])
|
|
{
|
|
double waPlanned = 0, waCompleted = 0;
|
|
if (workSummariesByWorkAreaId.TryGetValue(wa.Id, out var summary))
|
|
{
|
|
waPlanned = summary.PlannedWork;
|
|
waCompleted = summary.CompletedWork;
|
|
}
|
|
|
|
workAreaMongoList.Add(new WorkAreaMongoDB
|
|
{
|
|
Id = wa.Id.ToString(),
|
|
FloorId = wa.FloorId.ToString(),
|
|
AreaName = wa.AreaName,
|
|
PlannedWork = waPlanned,
|
|
CompletedWork = waCompleted
|
|
});
|
|
|
|
floorPlanned += waPlanned;
|
|
floorCompleted += waCompleted;
|
|
}
|
|
|
|
floorMongoList.Add(new FloorMongoDB
|
|
{
|
|
Id = floor.Id.ToString(),
|
|
BuildingId = floor.BuildingId.ToString(),
|
|
FloorName = floor.FloorName,
|
|
PlannedWork = floorPlanned,
|
|
CompletedWork = floorCompleted,
|
|
WorkAreas = workAreaMongoList
|
|
});
|
|
|
|
buildingPlanned += floorPlanned;
|
|
buildingCompleted += floorCompleted;
|
|
}
|
|
|
|
buildingMongoList.Add(new BuildingMongoDB
|
|
{
|
|
Id = building.Id.ToString(),
|
|
ProjectId = building.ProjectId.ToString(),
|
|
BuildingName = building.Name,
|
|
Description = building.Description,
|
|
PlannedWork = buildingPlanned,
|
|
CompletedWork = buildingCompleted,
|
|
Floors = floorMongoList
|
|
});
|
|
|
|
totalPlannedWork += buildingPlanned;
|
|
totalCompletedWork += buildingCompleted;
|
|
}
|
|
|
|
projectDetails.Buildings = buildingMongoList;
|
|
projectDetails.PlannedWork = totalPlannedWork;
|
|
projectDetails.CompletedWork = totalCompletedWork;
|
|
|
|
projectDetailsList.Add(projectDetails);
|
|
}
|
|
|
|
// --- Step 3: Update the cache ---
|
|
try
|
|
{
|
|
await _projectCache.AddProjectDetailsListToCache(projectDetailsList);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occurred while adding project list to Cache");
|
|
}
|
|
}
|
|
public async Task<bool> UpdateProjectDetailsOnly(Project project)
|
|
{
|
|
StatusMaster projectStatus = await _context.StatusMasters
|
|
.FirstOrDefaultAsync(s => s.Id == project.ProjectStatusId) ?? new StatusMaster();
|
|
try
|
|
{
|
|
bool response = await _projectCache.UpdateProjectDetailsOnlyToCache(project, projectStatus);
|
|
return response;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occured while updating project {ProjectId} to Cache", project.Id);
|
|
return false;
|
|
}
|
|
}
|
|
public async Task<ProjectMongoDB?> GetProjectDetails(Guid projectId)
|
|
{
|
|
try
|
|
{
|
|
var response = await _projectCache.GetProjectDetailsFromCache(projectId);
|
|
return response;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occured while getting project {ProjectId} to Cache");
|
|
return null;
|
|
}
|
|
}
|
|
public async Task<ProjectMongoDB?> GetProjectDetailsWithBuildings(Guid projectId)
|
|
{
|
|
try
|
|
{
|
|
var response = await _projectCache.GetProjectDetailsWithBuildingsFromCache(projectId);
|
|
return response;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occured while getting project {ProjectId} to Cache");
|
|
return null;
|
|
}
|
|
}
|
|
public async Task<List<ProjectMongoDB>?> GetProjectDetailsList(List<Guid> projectIds)
|
|
{
|
|
try
|
|
{
|
|
var response = await _projectCache.GetProjectDetailsListFromCache(projectIds);
|
|
if (response.Any())
|
|
{
|
|
return response;
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occured while getting list of project details from to Cache");
|
|
return null;
|
|
}
|
|
}
|
|
public async Task DeleteProjectByIdAsync(Guid projectId)
|
|
{
|
|
try
|
|
{
|
|
var response = await _projectCache.DeleteProjectByIdFromCacheAsync(projectId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occured while deleting project from to Cache");
|
|
|
|
}
|
|
}
|
|
public async Task RemoveProjectsAsync(List<Guid> projectIds)
|
|
{
|
|
try
|
|
{
|
|
var response = await _projectCache.RemoveProjectsFromCacheAsync(projectIds);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occured while deleting project list from to Cache");
|
|
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ======================================================= Project Infrastructure Cache =======================================================
|
|
|
|
public async Task AddBuildngInfra(Guid projectId, Building? building = null, Floor? floor = null, WorkArea? workArea = null, Guid? buildingId = null)
|
|
{
|
|
try
|
|
{
|
|
await _projectCache.AddBuildngInfraToCache(projectId, building, floor, workArea, buildingId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while adding project infra for project {ProjectId} to Cache: {Error}", projectId, ex.Message);
|
|
}
|
|
}
|
|
public async Task UpdateBuildngInfra(Guid projectId, Building? building = null, Floor? floor = null, WorkArea? workArea = null, Guid? buildingId = null)
|
|
{
|
|
try
|
|
{
|
|
var response = await _projectCache.UpdateBuildngInfraToCache(projectId, building, floor, workArea, buildingId);
|
|
if (!response)
|
|
{
|
|
await _projectCache.AddBuildngInfraToCache(projectId, building, floor, workArea, buildingId);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while updating project infra for project {ProjectId} to Cache: {Error}", projectId, ex.Message);
|
|
}
|
|
}
|
|
public async Task<List<BuildingMongoDB>?> GetBuildingInfra(Guid projectId)
|
|
{
|
|
try
|
|
{
|
|
var response = await _projectCache.GetBuildingInfraFromCache(projectId);
|
|
return response;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while getting project infra for project {ProjectId} form Cache: {Error}", projectId, ex.Message);
|
|
return null;
|
|
}
|
|
}
|
|
public async Task UpdatePlannedAndCompleteWorksInBuilding(Guid workAreaId, double plannedWork = 0, double completedWork = 0)
|
|
{
|
|
try
|
|
{
|
|
await _projectCache.UpdatePlannedAndCompleteWorksInBuildingFromCache(workAreaId, plannedWork, completedWork);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while updating planned work and completed work in building infra form Cache: {Error}", ex.Message);
|
|
}
|
|
}
|
|
public async Task<WorkAreaInfoMongoDB?> GetBuildingAndFloorByWorkAreaId(Guid workAreaId)
|
|
{
|
|
try
|
|
{
|
|
var response = await _projectCache.GetBuildingAndFloorByWorkAreaIdFromCache(workAreaId);
|
|
return response;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while fetching workArea Details using its ID form Cache: {Error}", ex.Message);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ======================================================= WorkItem Cache =======================================================
|
|
|
|
public async Task<List<WorkItemMongoDB>?> GetWorkItemsByWorkAreaIds(List<Guid> workAreaIds)
|
|
{
|
|
try
|
|
{
|
|
var response = await _projectCache.GetWorkItemsByWorkAreaIdsFromCache(workAreaIds);
|
|
if (response.Count > 0)
|
|
{
|
|
return response;
|
|
}
|
|
return null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while fetching workItems list using workArea IDs list form Cache: {Error}", ex.Message);
|
|
return null;
|
|
}
|
|
}
|
|
public async Task ManageWorkItemDetails(List<WorkItem> workItems)
|
|
{
|
|
try
|
|
{
|
|
var workAreaId = workItems.First().WorkAreaId;
|
|
var workItemDB = await _generalHelper.GetWorkItemsListFromDB(workAreaId);
|
|
await _projectCache.ManageWorkItemDetailsToCache(workItemDB);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while saving workItems form Cache: {Error}", ex.Message);
|
|
}
|
|
}
|
|
public async Task ManageWorkItemDetailsByVM(List<WorkItemMongoDB> workItems)
|
|
{
|
|
try
|
|
{
|
|
await _projectCache.ManageWorkItemDetailsToCache(workItems);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while saving workItems form Cache: {Error}", ex.Message);
|
|
}
|
|
}
|
|
public async Task<List<WorkItemMongoDB>?> GetWorkItemDetailsByWorkArea(Guid workAreaId)
|
|
{
|
|
try
|
|
{
|
|
var workItems = await _projectCache.GetWorkItemDetailsByWorkAreaFromCache(workAreaId);
|
|
if (workItems.Count > 0)
|
|
{
|
|
return workItems;
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while fetching list of workItems form Cache: {Error}", ex.Message);
|
|
return null;
|
|
}
|
|
}
|
|
public async Task<WorkItemMongoDB?> GetWorkItemDetailsById(Guid id)
|
|
{
|
|
try
|
|
{
|
|
var workItem = await _projectCache.GetWorkItemDetailsByIdFromCache(id);
|
|
if (workItem.Id != "")
|
|
{
|
|
return workItem;
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while fetching list of workItems form Cache: {Error}", ex.Message);
|
|
return null;
|
|
}
|
|
}
|
|
public async Task UpdatePlannedAndCompleteWorksInWorkItem(Guid id, double plannedWork = 0, double completedWork = 0, double todaysAssigned = 0)
|
|
{
|
|
try
|
|
{
|
|
var response = await _projectCache.UpdatePlannedAndCompleteWorksInWorkItemToCache(id, plannedWork, completedWork, todaysAssigned);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while updating planned work, completed work, and today's assigned work in workItems in Cache: {Error}", ex.Message);
|
|
}
|
|
}
|
|
public async Task DeleteWorkItemByIdAsync(Guid workItemId)
|
|
{
|
|
try
|
|
{
|
|
var response = await _projectCache.DeleteWorkItemByIdFromCacheAsync(workItemId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while deleting work item from to Cache: {Error}", ex.Message);
|
|
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ======================================================= Employee Profile Cache =======================================================
|
|
|
|
public async Task AddApplicationRole(Guid employeeId, List<Guid> roleIds)
|
|
{
|
|
// 1. Guard Clause: Avoid unnecessary database work if there are no roles to add.
|
|
if (roleIds == null || !roleIds.Any())
|
|
{
|
|
return; // Nothing to add, so the operation did not result in a change.
|
|
}
|
|
Task<List<string>> getPermissionIdsTask = Task.Run(async () =>
|
|
{
|
|
using var context = _dbContextFactory.CreateDbContext();
|
|
|
|
return await context.RolePermissionMappings
|
|
.Where(rp => roleIds.Contains(rp.ApplicationRoleId))
|
|
.Select(p => p.FeaturePermissionId.ToString())
|
|
.Distinct()
|
|
.ToListAsync();
|
|
});
|
|
|
|
// 3. Prepare role IDs in parallel with the database query.
|
|
var newRoleIds = roleIds.Select(r => r.ToString()).ToList();
|
|
|
|
// 4. Await the database query result.
|
|
var newPermissionIds = await getPermissionIdsTask;
|
|
try
|
|
{
|
|
var response = await _employeeCache.AddApplicationRoleToCache(employeeId, newRoleIds, newPermissionIds);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while adding Application roleIds to Cache to employee {Employee}: {Error}", employeeId, ex.Message);
|
|
}
|
|
}
|
|
public async Task<bool> AddProjects(Guid employeeId, List<Guid> projectIds)
|
|
{
|
|
try
|
|
{
|
|
var response = await _employeeCache.AddProjectsToCache(employeeId, projectIds);
|
|
return response;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while adding projectIds for employee {EmployeeId} to Cache: {Error}", employeeId, ex.Message);
|
|
return false;
|
|
}
|
|
}
|
|
public async Task<List<Guid>?> GetProjects(Guid employeeId)
|
|
{
|
|
try
|
|
{
|
|
var response = await _employeeCache.GetProjectsFromCache(employeeId);
|
|
if (response.Count > 0)
|
|
{
|
|
return response;
|
|
}
|
|
return null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while getting projectIds for employee {EmployeeId} from Cache: {Error}", employeeId, ex.Message);
|
|
return null;
|
|
}
|
|
}
|
|
public async Task<List<Guid>?> GetPermissions(Guid employeeId)
|
|
{
|
|
try
|
|
{
|
|
var response = await _employeeCache.GetPermissionsFromCache(employeeId);
|
|
if (response.Count > 0)
|
|
{
|
|
return response;
|
|
}
|
|
return null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while getting permissionIds for employee {EmployeeId} from Cache: {Error}", employeeId, ex.Message);
|
|
return null;
|
|
}
|
|
}
|
|
public async Task ClearAllProjectIds(Guid employeeId)
|
|
{
|
|
try
|
|
{
|
|
var response = await _employeeCache.ClearAllProjectIdsFromCache(employeeId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while deleting projectIds from Cache for employee {EmployeeId}: {Error}", employeeId, ex.Message);
|
|
}
|
|
}
|
|
public async Task ClearAllProjectIdsByRoleId(Guid roleId)
|
|
{
|
|
try
|
|
{
|
|
await _employeeCache.ClearAllProjectIdsByRoleIdFromCache(roleId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while deleting projectIds from Cache for Application Role {RoleId}: {Error}", roleId, ex.Message);
|
|
}
|
|
}
|
|
public async Task ClearAllProjectIdsByPermissionId(Guid permissionId)
|
|
{
|
|
try
|
|
{
|
|
await _employeeCache.ClearAllProjectIdsByPermissionIdFromCache(permissionId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while deleting projectIds from Cache for Permission {PermissionId}: {Error}", permissionId, ex.Message);
|
|
}
|
|
}
|
|
public async Task ClearAllPermissionIdsByEmployeeID(Guid employeeId)
|
|
{
|
|
try
|
|
{
|
|
var response = await _employeeCache.ClearAllPermissionIdsByEmployeeIDFromCache(employeeId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while deleting permissionIds from Cache for employee {EmployeeId}: {Error}", employeeId, ex.Message);
|
|
}
|
|
}
|
|
public async Task ClearAllPermissionIdsByRoleId(Guid roleId)
|
|
{
|
|
try
|
|
{
|
|
var response = await _employeeCache.ClearAllPermissionIdsByRoleIdFromCache(roleId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while deleting permissionIds from Cache for Application role {RoleId}: {Error}", roleId, ex.Message);
|
|
}
|
|
}
|
|
public async Task RemoveRoleId(Guid employeeId, Guid roleId)
|
|
{
|
|
try
|
|
{
|
|
var response = await _employeeCache.RemoveRoleIdFromCache(employeeId, roleId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning("Error occured while deleting Application role {RoleId} from Cache for employee {EmployeeId}: {Error}", roleId, employeeId, ex.Message);
|
|
}
|
|
}
|
|
public async Task ClearAllEmployees()
|
|
{
|
|
try
|
|
{
|
|
var response = await _employeeCache.ClearAllEmployeesFromCache();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occured while deleting all employees from Cache");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ======================================================= Expenses Cache =======================================================
|
|
public async Task AddExpenseByObjectAsync(Expenses expense)
|
|
{
|
|
var expenseCache = _mapper.Map<ExpenseDetailsMongoDB>(expense);
|
|
|
|
try
|
|
{
|
|
var billAttachments = await _context.BillAttachments
|
|
.Include(ba => ba.Document)
|
|
.AsNoTracking()
|
|
.Where(ba => ba.ExpensesId == expense.Id && ba.Document != null)
|
|
.GroupBy(ba => ba.ExpensesId)
|
|
.Select(g => new
|
|
{
|
|
S3Keys = g.Select(ba => ba.Document!.S3Key).ToList(),
|
|
ThumbS3Keys = g.Select(ba => ba.Document!.ThumbS3Key ?? ba.Document.S3Key).ToList()
|
|
})
|
|
.FirstOrDefaultAsync(); ;
|
|
if (billAttachments != null)
|
|
{
|
|
expenseCache.S3Key = billAttachments.S3Keys;
|
|
expenseCache.ThumbS3Key = billAttachments.ThumbS3Keys;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occurd while fetched expense related tables to save in cahce");
|
|
}
|
|
try
|
|
{
|
|
await _expenseCache.AddExpenseToCacheAsync(expenseCache);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occurd while storing expense related table in cahce");
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
public async Task<ExpenseDetailsMongoDB?> AddExpenseByIdAsync(Guid Id, Guid tenantId)
|
|
{
|
|
var expense = await _context.Expenses.AsNoTracking().FirstOrDefaultAsync(e => e.Id == Id && e.TenantId == tenantId);
|
|
var expenseCache = _mapper.Map<ExpenseDetailsMongoDB>(expense);
|
|
if (expense == null)
|
|
{
|
|
return null;
|
|
}
|
|
try
|
|
{
|
|
var billAttachments = await _context.BillAttachments
|
|
.Include(ba => ba.Document)
|
|
.AsNoTracking()
|
|
.Where(ba => ba.ExpensesId == expense.Id && ba.Document != null)
|
|
.GroupBy(ba => ba.ExpensesId)
|
|
.Select(g => new
|
|
{
|
|
S3Keys = g.Select(ba => ba.Document!.S3Key).ToList(),
|
|
ThumbS3Keys = g.Select(ba => ba.Document!.ThumbS3Key ?? ba.Document.S3Key).ToList()
|
|
})
|
|
.FirstOrDefaultAsync();
|
|
if (billAttachments != null)
|
|
{
|
|
expenseCache.S3Key = billAttachments.S3Keys;
|
|
expenseCache.ThumbS3Key = billAttachments.ThumbS3Keys;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occurd while fetched expense related tables to save in cahce");
|
|
return null;
|
|
}
|
|
try
|
|
{
|
|
await _expenseCache.AddExpenseToCacheAsync(expenseCache);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occurd while storing expense related table in cahce");
|
|
return null;
|
|
}
|
|
|
|
return expenseCache;
|
|
|
|
|
|
}
|
|
public async Task AddExpensesListToCache(List<Expenses> expenses)
|
|
{
|
|
var expensesCache = _mapper.Map<List<ExpenseDetailsMongoDB>>(expenses);
|
|
var expenseIds = expenses.Select(e => e.Id).ToList();
|
|
try
|
|
{
|
|
var billAttachments = await _context.BillAttachments
|
|
.Include(ba => ba.Document)
|
|
.AsNoTracking()
|
|
.Where(ba => expenseIds.Contains(ba.ExpensesId) && ba.Document != null)
|
|
.GroupBy(ba => ba.ExpensesId)
|
|
.Select(g => new
|
|
{
|
|
ExpensesId = g.Key,
|
|
S3Keys = g.Select(ba => ba.Document!.S3Key).ToList(),
|
|
ThumbS3Keys = g.Select(ba => ba.Document!.ThumbS3Key ?? ba.Document.S3Key).ToList()
|
|
})
|
|
.ToListAsync();
|
|
foreach (var expenseCache in expensesCache)
|
|
{
|
|
expenseCache.S3Key = billAttachments.Where(ba => ba.ExpensesId == Guid.Parse(expenseCache.Id)).Select(ba => ba.S3Keys).FirstOrDefault() ?? new List<string>();
|
|
expenseCache.ThumbS3Key = billAttachments.Where(ba => ba.ExpensesId == Guid.Parse(expenseCache.Id)).Select(ba => ba.ThumbS3Keys).FirstOrDefault();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occurd while fetched expense related tables to save in cahce");
|
|
}
|
|
|
|
try
|
|
{
|
|
await _expenseCache.AddExpensesListToCacheAsync(expensesCache);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occured while saving the list of expenses to Cache");
|
|
}
|
|
}
|
|
|
|
public async Task<(int totalPages, long totalCount, List<ExpenseDetailsMongoDB>? expenseList)> GetExpenseListAsync(Guid tenantId, Guid loggedInEmployeeId, bool viewAll,
|
|
bool viewSelf, int pageNumber, int pageSize, ExpensesFilter? filter)
|
|
{
|
|
try
|
|
{
|
|
var (totalPages, totalCount, expenseList) = await _expenseCache.GetExpenseListFromCacheAsync(tenantId, loggedInEmployeeId, viewAll, viewSelf, pageNumber, pageSize, filter);
|
|
if (expenseList.Any())
|
|
{
|
|
return (totalPages, totalCount, expenseList);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occured while fetching the list of expenses to Cache");
|
|
}
|
|
return (0, 0, null);
|
|
}
|
|
|
|
public async Task<ExpenseDetailsMongoDB?> GetExpenseDetailsById(Guid id, Guid tenantId)
|
|
{
|
|
var response = await _expenseCache.GetExpenseDetailsByIdAsync(id, tenantId);
|
|
if (response == null || response.Id == string.Empty)
|
|
{
|
|
return null;
|
|
}
|
|
return response;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ======================================================= Report Cache =======================================================
|
|
|
|
public async Task<List<ProjectReportEmailMongoDB>?> GetProjectReportMail(bool IsSend)
|
|
{
|
|
try
|
|
{
|
|
var response = await _reportCache.GetProjectReportMailFromCache(IsSend);
|
|
return response;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occured while fetching project report mail bodys");
|
|
return null;
|
|
}
|
|
}
|
|
public async Task AddProjectReportMail(ProjectReportEmailMongoDB report)
|
|
{
|
|
try
|
|
{
|
|
await _reportCache.AddProjectReportMailToCache(report);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occured while adding project report mail bodys");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|