Add new API to only fetch project information to load in dropdowns. Full project details including progress will be fatched latter when required

This commit is contained in:
Vikas Nale 2025-06-12 17:32:48 +05:30
parent d5e7d38101
commit d8ee5940fd
4 changed files with 158 additions and 28 deletions

View File

@ -91,5 +91,19 @@ namespace Marco.Pms.Model.Mapper
EndDate = project.EndDate, 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,
};
}
} }
} }

View File

@ -15,4 +15,17 @@
public double CompletedWork { get; set; } public double CompletedWork { get; set; }
public double PlannedWork { 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; }
}
} }

View File

@ -37,6 +37,55 @@ namespace MarcoBMS.Services.Controllers
_rolesHelper = rolesHelper; _rolesHelper = rolesHelper;
_projectsHelper = projectHelper; _projectsHelper = projectHelper;
} }
[HttpGet("list/basic")]
public async Task<IActionResult> GetAllProjects()
{
if (!ModelState.IsValid)
{
var errors = ModelState.Values
.SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage)
.ToList();
return BadRequest(ApiResponse<object>.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<object>.ErrorResponse("Employee not found.", null, 401));
}
List<Project> 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<ProjectInfoVM> response = projects
.Select(project => project.ToProjectInfoVMFromProject())
.ToList();
//List<ProjectInfoVM> response = new List<ProjectInfoVM>();
//foreach (var project in projects)
//{
// response.Add(project.ToProjectInfoVMFromProject());
//}
return Ok(ApiResponse<object>.SuccessResponse(response, "Success.", 200));
}
[HttpGet("list")] [HttpGet("list")]
public async Task<IActionResult> GetAll() public async Task<IActionResult> GetAll()
{ {
@ -51,21 +100,25 @@ namespace MarcoBMS.Services.Controllers
} }
Guid tenantId = _userHelper.GetTenantId(); Guid tenantId = _userHelper.GetTenantId();
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
List<FeaturePermission> featurePermission = await _rolesHelper.GetFeaturePermissionByEmployeeID(LoggedInEmployee.Id); //List<FeaturePermission> featurePermission = await _rolesHelper.GetFeaturePermissionByEmployeeID(LoggedInEmployee.Id);
string[] projectsId = []; //string[] projectsId = [];
List<Project> projects = new List<Project>(); //List<Project> projects = new List<Project>();
///* 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<ProjectAllocation> 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<Project> 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<ProjectAllocation> 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<ProjectListVM> response = new List<ProjectListVM>(); List<ProjectListVM> response = new List<ProjectListVM>();
@ -701,23 +754,24 @@ namespace MarcoBMS.Services.Controllers
if (!projectList.Any()) if (!projectList.Any())
{ {
return NotFound(ApiResponse<object>.SuccessResponse(new List<object>(), "No projects found.",200)); return NotFound(ApiResponse<object>.SuccessResponse(new List<object>(), "No projects found.", 200));
} }
List<Project> projectlist = await _context.Projects List<Project> projectlist = await _context.Projects
.Where(p => projectList.Contains(p.Id)) .Where(p => projectList.Contains(p.Id))
.ToListAsync(); .ToListAsync();
List<ProjectListVM> projects = new List<ProjectListVM>(); List<ProjectListVM> projects = new List<ProjectListVM>();
foreach (var project in projectlist) { foreach (var project in projectlist)
{
projects.Add(project.ToProjectListVMFromProject()); projects.Add(project.ToProjectListVMFromProject());
} }
return Ok(ApiResponse<object>.SuccessResponse(projects, "Success.", 200)); return Ok(ApiResponse<object>.SuccessResponse(projects, "Success.", 200));
} }
@ -728,19 +782,19 @@ namespace MarcoBMS.Services.Controllers
[HttpPost("assign-projects/{employeeId}")] [HttpPost("assign-projects/{employeeId}")]
public async Task<ActionResult> AssigneProjectsToEmployee([FromBody] List<ProjectsAllocationDto> projectAllocationDtos, [FromRoute] Guid employeeId) public async Task<ActionResult> AssigneProjectsToEmployee([FromBody] List<ProjectsAllocationDto> projectAllocationDtos, [FromRoute] Guid employeeId)
{ {
if(projectAllocationDtos != null && employeeId != Guid.Empty) if (projectAllocationDtos != null && employeeId != Guid.Empty)
{ {
Guid TenentID = GetTenantId(); Guid TenentID = GetTenantId();
List<object>? result = new List<object>(); List<object>? result = new List<object>();
foreach(var projectAllocationDto in projectAllocationDtos) foreach (var projectAllocationDto in projectAllocationDtos)
{ {
try 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(); 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<object>.ErrorResponse(ex.Message, ex, 400)); return Ok(ApiResponse<object>.ErrorResponse(ex.Message, ex, 400));
} }
@ -797,7 +852,7 @@ namespace MarcoBMS.Services.Controllers
{ {
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid details.", "All Field is required", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid details.", "All Field is required", 400));
} }
} }

View File

@ -1,16 +1,25 @@
using Marco.Pms.DataAccess.Data; using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.Projects; using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.Projects;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using ModelServices.Helpers;
namespace MarcoBMS.Services.Helpers namespace MarcoBMS.Services.Helpers
{ {
public class ProjectsHelper public class ProjectsHelper
{ {
private readonly ApplicationDbContext _context; private readonly ApplicationDbContext _context;
private readonly RolesHelper _rolesHelper;
public ProjectsHelper(ApplicationDbContext context)
public ProjectsHelper(ApplicationDbContext context, RolesHelper rolesHelper)
{ {
_context = context; _context = context;
_rolesHelper = rolesHelper;
} }
public async Task<List<Project>> GetAllProjectByTanentID(Guid tanentID) public async Task<List<Project>> GetAllProjectByTanentID(Guid tanentID)
@ -42,7 +51,46 @@ namespace MarcoBMS.Services.Helpers
} }
} }
public async Task<List<Project>> GetMyProjects(Guid tenantId, Employee LoggedInEmployee)
{
List<FeaturePermission> featurePermission = await _rolesHelper.GetFeaturePermissionByEmployeeID(LoggedInEmployee.Id);
string[] projectsId = [];
List<Project> projects = new List<Project>();
// Define a common queryable base for projects
IQueryable<Project> 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<Project>();
}
// 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;
}
} }
} }