From d8ee5940fd183d489b1dcccece672ca4296b4db3 Mon Sep 17 00:00:00 2001 From: Vikas Nale Date: Thu, 12 Jun 2025 17:32:48 +0530 Subject: [PATCH] Add new API to only fetch project information to load in dropdowns. Full project details including progress will be fatched latter when required --- Marco.Pms.Model/Mapper/ProjectMapper.cs | 14 +++ .../ViewModels/Projects/ProjectListVM.cs | 13 +++ .../Controllers/ProjectController.cs | 109 +++++++++++++----- Marco.Pms.Services/Helpers/ProjectsHelper.cs | 50 +++++++- 4 files changed, 158 insertions(+), 28 deletions(-) diff --git a/Marco.Pms.Model/Mapper/ProjectMapper.cs b/Marco.Pms.Model/Mapper/ProjectMapper.cs index 8422c79..1283d99 100644 --- a/Marco.Pms.Model/Mapper/ProjectMapper.cs +++ b/Marco.Pms.Model/Mapper/ProjectMapper.cs @@ -91,5 +91,19 @@ namespace Marco.Pms.Model.Mapper EndDate = project.EndDate, }; } + public static ProjectInfoVM ToProjectInfoVMFromProject(this Project project) + { + return new ProjectInfoVM + { + Id = project.Id, + Name = project.Name, + ShortName = project.ShortName, + ProjectAddress = project.ProjectAddress, + ProjectStatusId = project.ProjectStatusId, + ContactPerson = project.ContactPerson, + StartDate = project.StartDate, + EndDate = project.EndDate, + }; + } } } diff --git a/Marco.Pms.Model/ViewModels/Projects/ProjectListVM.cs b/Marco.Pms.Model/ViewModels/Projects/ProjectListVM.cs index d2cb405..c488437 100644 --- a/Marco.Pms.Model/ViewModels/Projects/ProjectListVM.cs +++ b/Marco.Pms.Model/ViewModels/Projects/ProjectListVM.cs @@ -15,4 +15,17 @@ public double CompletedWork { get; set; } public double PlannedWork { get; set; } } + + public class ProjectInfoVM + { + public Guid Id { get; set; } + public string? Name { get; set; } + public string? ShortName { get; set; } + public string? ProjectAddress { get; set; } + public string? ContactPerson { get; set; } + + public DateTime? StartDate { get; set; } + public DateTime? EndDate { get; set; } + public Guid ProjectStatusId { get; set; } + } } diff --git a/Marco.Pms.Services/Controllers/ProjectController.cs b/Marco.Pms.Services/Controllers/ProjectController.cs index 43e7252..18a790b 100644 --- a/Marco.Pms.Services/Controllers/ProjectController.cs +++ b/Marco.Pms.Services/Controllers/ProjectController.cs @@ -37,6 +37,55 @@ namespace MarcoBMS.Services.Controllers _rolesHelper = rolesHelper; _projectsHelper = projectHelper; } + + [HttpGet("list/basic")] + public async Task GetAllProjects() + { + if (!ModelState.IsValid) + { + var errors = ModelState.Values + .SelectMany(v => v.Errors) + .Select(e => e.ErrorMessage) + .ToList(); + return BadRequest(ApiResponse.ErrorResponse("Invalid data", errors, 400)); + + } + Guid tenantId = _userHelper.GetTenantId(); + var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + + // Defensive check for null employee (important for robust APIs) + if (LoggedInEmployee == null) + { + return Unauthorized(ApiResponse.ErrorResponse("Employee not found.", null, 401)); + } + + + List projects = await _projectsHelper.GetMyProjects(tenantId, LoggedInEmployee); + + + // 4. Project projection to ProjectInfoVM + // This part is already quite efficient. + // Ensure ToProjectInfoVMFromProject() is also optimized and doesn't perform N+1 queries. + // If ProjectInfoVM only needs a subset of Project properties, + // you can use a LINQ Select directly on the IQueryable before ToListAsync() + // to fetch only the required columns from the database. + List response = projects + .Select(project => project.ToProjectInfoVMFromProject()) + .ToList(); + + + //List response = new List(); + + //foreach (var project in projects) + //{ + // response.Add(project.ToProjectInfoVMFromProject()); + //} + + return Ok(ApiResponse.SuccessResponse(response, "Success.", 200)); + } + + + [HttpGet("list")] public async Task GetAll() { @@ -51,21 +100,25 @@ namespace MarcoBMS.Services.Controllers } Guid tenantId = _userHelper.GetTenantId(); var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); - List featurePermission = await _rolesHelper.GetFeaturePermissionByEmployeeID(LoggedInEmployee.Id); - string[] projectsId = []; - List projects = new List(); + //List featurePermission = await _rolesHelper.GetFeaturePermissionByEmployeeID(LoggedInEmployee.Id); + //string[] projectsId = []; + //List projects = new List(); + + ///* User with permission manage project can see all projects */ + //if (featurePermission != null && featurePermission.Exists(c => c.Id.ToString() == "172fc9b6-755b-4f62-ab26-55c34a330614")) + //{ + // projects = await _projectsHelper.GetAllProjectByTanentID(LoggedInEmployee.TenantId); + //} + //else + //{ + // List allocation = await _projectsHelper.GetProjectByEmployeeID(LoggedInEmployee.Id); + // projectsId = allocation.Select(c => c.ProjectId.ToString()).ToArray(); + // projects = await _context.Projects.Where(c => projectsId.Contains(c.Id.ToString()) && c.TenantId == tenantId).ToListAsync(); + //} + + List projects = await _projectsHelper.GetMyProjects(tenantId, LoggedInEmployee); + - /* User with permission manage project can see all projects */ - if (featurePermission != null && featurePermission.Exists(c => c.Id.ToString() == "172fc9b6-755b-4f62-ab26-55c34a330614")) - { - projects = await _projectsHelper.GetAllProjectByTanentID(LoggedInEmployee.TenantId); - } - else - { - List allocation = await _projectsHelper.GetProjectByEmployeeID(LoggedInEmployee.Id); - projectsId = allocation.Select(c => c.ProjectId.ToString()).ToArray(); - projects = await _context.Projects.Where(c => projectsId.Contains(c.Id.ToString()) && c.TenantId == tenantId).ToListAsync(); - } List response = new List(); @@ -701,23 +754,24 @@ namespace MarcoBMS.Services.Controllers if (!projectList.Any()) { - return NotFound(ApiResponse.SuccessResponse(new List(), "No projects found.",200)); + return NotFound(ApiResponse.SuccessResponse(new List(), "No projects found.", 200)); } - List projectlist = await _context.Projects - .Where(p => projectList.Contains(p.Id)) - .ToListAsync(); + List projectlist = await _context.Projects + .Where(p => projectList.Contains(p.Id)) + .ToListAsync(); List projects = new List(); - foreach (var project in projectlist) { - + foreach (var project in projectlist) + { + projects.Add(project.ToProjectListVMFromProject()); } - + return Ok(ApiResponse.SuccessResponse(projects, "Success.", 200)); } @@ -728,19 +782,19 @@ namespace MarcoBMS.Services.Controllers [HttpPost("assign-projects/{employeeId}")] public async Task AssigneProjectsToEmployee([FromBody] List projectAllocationDtos, [FromRoute] Guid employeeId) { - if(projectAllocationDtos != null && employeeId != Guid.Empty) + if (projectAllocationDtos != null && employeeId != Guid.Empty) { Guid TenentID = GetTenantId(); List? result = new List(); - foreach(var projectAllocationDto in projectAllocationDtos) + foreach (var projectAllocationDto in projectAllocationDtos) { try { - ProjectAllocation projectAllocation = projectAllocationDto.ToProjectAllocationFromProjectsAllocationDto(TenentID,employeeId); + ProjectAllocation projectAllocation = projectAllocationDto.ToProjectAllocationFromProjectsAllocationDto(TenentID, employeeId); ProjectAllocation? projectAllocationFromDb = await _context.ProjectAllocations.Where(c => c.EmployeeId == employeeId && c.ProjectId == projectAllocationDto.ProjectId && c.ReAllocationDate == null && c.TenantId == TenentID).SingleOrDefaultAsync(); - if(projectAllocationFromDb != null) + if (projectAllocationFromDb != null) { @@ -785,7 +839,8 @@ namespace MarcoBMS.Services.Controllers } - catch (Exception ex) { + catch (Exception ex) + { return Ok(ApiResponse.ErrorResponse(ex.Message, ex, 400)); } @@ -797,7 +852,7 @@ namespace MarcoBMS.Services.Controllers { return BadRequest(ApiResponse.ErrorResponse("Invalid details.", "All Field is required", 400)); } - + } diff --git a/Marco.Pms.Services/Helpers/ProjectsHelper.cs b/Marco.Pms.Services/Helpers/ProjectsHelper.cs index 74daf88..8ccbc85 100644 --- a/Marco.Pms.Services/Helpers/ProjectsHelper.cs +++ b/Marco.Pms.Services/Helpers/ProjectsHelper.cs @@ -1,16 +1,25 @@ using Marco.Pms.DataAccess.Data; +using Marco.Pms.Model.Employees; +using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.Projects; +using Marco.Pms.Model.Utilities; +using Marco.Pms.Model.ViewModels.Projects; +using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; +using ModelServices.Helpers; namespace MarcoBMS.Services.Helpers { public class ProjectsHelper { private readonly ApplicationDbContext _context; + private readonly RolesHelper _rolesHelper; - public ProjectsHelper(ApplicationDbContext context) + + public ProjectsHelper(ApplicationDbContext context, RolesHelper rolesHelper) { _context = context; + _rolesHelper = rolesHelper; } public async Task> GetAllProjectByTanentID(Guid tanentID) @@ -42,7 +51,46 @@ namespace MarcoBMS.Services.Helpers } } + public async Task> GetMyProjects(Guid tenantId, Employee LoggedInEmployee) + { + List featurePermission = await _rolesHelper.GetFeaturePermissionByEmployeeID(LoggedInEmployee.Id); + string[] projectsId = []; + List projects = new List(); + + // Define a common queryable base for projects + IQueryable projectQuery = _context.Projects.Where(c => c.TenantId == tenantId); + + // 2. Optimized Project Retrieval Logic + // User with permission 'manage project' can see all projects + if (featurePermission != null && featurePermission.Exists(c => c.Id.ToString() == "172fc9b6-755b-4f62-ab26-55c34a330614")) + { + // If GetAllProjectByTanentID is already optimized and directly returns IQueryable or + // directly executes with ToListAsync(), keep it. + // If it does more complex logic or extra trips, consider inlining here. + projects = await projectQuery.ToListAsync(); // Directly query the context + } + else + { + // 3. Efficiently get project allocations and then filter projects + // Load allocations only once + var allocation = await GetProjectByEmployeeID(LoggedInEmployee.Id); + + // If there are no allocations, return an empty list early + if (allocation == null || !allocation.Any()) + { + return new List(); + } + + // Use LINQ's Contains for efficient filtering by ProjectId + var projectIds = allocation.Select(c => c.ProjectId).Distinct().ToList(); // Get distinct Guids + + // Filter projects based on the retrieved ProjectIds + projects = await projectQuery.Where(c => projectIds.Contains(c.Id)).ToListAsync(); + } + + return projects; + } } }