ProjectDetails_Split_API #103

Merged
admin merged 47 commits from ProjectDetails_Split_API into main 2025-07-11 11:32:34 +00:00
2 changed files with 179 additions and 115 deletions
Showing only changes of commit 3dd5e7f626 - Show all commits

View File

@ -1,10 +1,17 @@
using Marco.Pms.Model.Dtos.Project;
using Marco.Pms.Model.Master;
namespace Marco.Pms.Model.ViewModels.Projects
{
public class ProjectVM : ProjectDto
public class ProjectVM
{
public List<BuildingVM>? Buildings { get; set; }
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 StatusMaster? ProjectStatus { get; set; }
}
}

View File

@ -1,14 +1,13 @@
using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Activities;
using Marco.Pms.Model.Dtos.Project;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.Mapper;
using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.Employee;
using Marco.Pms.Model.ViewModels.Projects;
using Marco.Pms.Services.Hubs;
using Marco.Pms.Services.Service;
using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service;
using Microsoft.AspNetCore.Authorization;
@ -29,9 +28,16 @@ namespace MarcoBMS.Services.Controllers
private readonly RolesHelper _rolesHelper;
private readonly ProjectsHelper _projectsHelper;
private readonly IHubContext<MarcoHub> _signalR;
private readonly PermissionServices _permission;
private readonly Guid ViewProjects;
private readonly Guid ManageProject;
private readonly Guid ViewInfra;
private readonly Guid ManageInfra;
private readonly Guid tenantId;
public ProjectController(ApplicationDbContext context, UserHelper userHelper, ILoggingService logger, RolesHelper rolesHelper, ProjectsHelper projectHelper, IHubContext<MarcoHub> signalR)
public ProjectController(ApplicationDbContext context, UserHelper userHelper, ILoggingService logger, RolesHelper rolesHelper, ProjectsHelper projectHelper,
IHubContext<MarcoHub> signalR, PermissionServices permission)
{
_context = context;
_userHelper = userHelper;
@ -39,6 +45,12 @@ namespace MarcoBMS.Services.Controllers
_rolesHelper = rolesHelper;
_projectsHelper = projectHelper;
_signalR = signalR;
_permission = permission;
ViewProjects = Guid.Parse("6ea44136-987e-44ba-9e5d-1cf8f5837ebc");
ManageProject = Guid.Parse("172fc9b6-755b-4f62-ab26-55c34a330614");
ViewInfra = Guid.Parse("8d7cc6e3-9147-41f7-aaa7-fa507e450bd4");
ManageInfra = Guid.Parse("f2aee20a-b754-4537-8166-f9507b44585b");
tenantId = _userHelper.GetTenantId();
}
@ -177,133 +189,68 @@ namespace MarcoBMS.Services.Controllers
[HttpGet("details/{id}")]
public async Task<IActionResult> Details([FromRoute] Guid id)
{
// ProjectDetailsVM vm = new ProjectDetailsVM();
// Step 1: Validate model state
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));
_logger.LogWarning("Invalid model state in Details endpoint. Errors: {@Errors}", errors);
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
}
var project = await _context.Projects.Where(c => c.TenantId == _userHelper.GetTenantId() && c.Id == id).Include(c => c.ProjectStatus).SingleOrDefaultAsync(); // includeProperties: "ProjectStatus,Tenant"); //_context.Stock.FindAsync(id);
// Step 2: Get logged-in employee
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
_logger.LogInfo("Details requested by EmployeeId: {EmployeeId} for ProjectId: {ProjectId}", loggedInEmployee.Id, id);
// Step 3: Check global view project permission
var hasViewProjectPermission = await _permission.HasPermission(ViewProjects, loggedInEmployee.Id);
if (!hasViewProjectPermission)
{
_logger.LogWarning("ViewProjects permission denied for EmployeeId: {EmployeeId}", loggedInEmployee.Id);
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to view projects", 403));
}
// Step 4: Check permission for this specific project
var hasProjectPermission = await _permission.HasProjectPermission(loggedInEmployee, id.ToString());
if (!hasProjectPermission)
{
_logger.LogWarning("Project-specific access denied. EmployeeId: {EmployeeId}, ProjectId: {ProjectId}", loggedInEmployee.Id, id);
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access denied", "You don't have access to this project", 403));
}
// Step 5: Fetch project with status
var project = await _context.Projects
.Include(c => c.ProjectStatus)
.FirstOrDefaultAsync(c => c.TenantId == tenantId && c.Id == id);
if (project == null)
{
_logger.LogWarning("Project not found. ProjectId: {ProjectId}", id);
return NotFound(ApiResponse<object>.ErrorResponse("Project not found", "Project not found", 404));
}
else
{
//var project = projects.Where(c => c.Id == id).SingleOrDefault();
ProjectDetailsVM vm = await GetProjectViewModel(id, project);
ProjectVM projectVM = new ProjectVM();
if (vm.project != null)
{
projectVM.Id = vm.project.Id;
projectVM.Name = vm.project.Name;
projectVM.ShortName = vm.project.ShortName;
projectVM.ProjectAddress = vm.project.ProjectAddress;
projectVM.ContactPerson = vm.project.ContactPerson;
projectVM.StartDate = vm.project.StartDate;
projectVM.EndDate = vm.project.EndDate;
projectVM.ProjectStatusId = vm.project.ProjectStatusId;
}
projectVM.Buildings = new List<BuildingVM>();
if (vm.buildings != null)
{
foreach (Building build in vm.buildings)
{
BuildingVM buildVM = new BuildingVM() { Id = build.Id, Description = build.Description, Name = build.Name };
buildVM.Floors = new List<FloorsVM>();
if (vm.floors != null)
{
foreach (Floor floorDto in vm.floors.Where(c => c.BuildingId == build.Id).ToList())
{
FloorsVM floorVM = new FloorsVM() { FloorName = floorDto.FloorName, Id = floorDto.Id };
floorVM.WorkAreas = new List<WorkAreaVM>();
if (vm.workAreas != null)
{
foreach (WorkArea workAreaDto in vm.workAreas.Where(c => c.FloorId == floorVM.Id).ToList())
{
WorkAreaVM workAreaVM = new WorkAreaVM() { Id = workAreaDto.Id, AreaName = workAreaDto.AreaName, WorkItems = new List<WorkItemVM>() };
if (vm.workItems != null)
{
foreach (WorkItem workItemDto in vm.workItems.Where(c => c.WorkAreaId == workAreaDto.Id).ToList())
{
WorkItemVM workItemVM = new WorkItemVM() { WorkItemId = workItemDto.Id, WorkItem = workItemDto };
workItemVM.WorkItem.WorkArea = new WorkArea();
if (workItemVM.WorkItem.ActivityMaster != null)
{
workItemVM.WorkItem.ActivityMaster.Tenant = new Tenant();
}
workItemVM.WorkItem.Tenant = new Tenant();
double todaysAssigned = 0;
if (vm.Tasks != null)
{
var tasks = vm.Tasks.Where(t => t.WorkItemId == workItemDto.Id).ToList();
foreach (TaskAllocation task in tasks)
{
todaysAssigned += task.PlannedTask;
}
}
workItemVM.TodaysAssigned = todaysAssigned;
workAreaVM.WorkItems.Add(workItemVM);
}
}
floorVM.WorkAreas.Add(workAreaVM);
}
}
buildVM.Floors.Add(floorVM);
}
}
projectVM.Buildings.Add(buildVM);
}
}
return Ok(ApiResponse<object>.SuccessResponse(projectVM, "Success.", 200));
}
// Step 6: Map and return result
var projectVM = GetProjectViewModel(project);
_logger.LogInfo("Project details fetched successfully. ProjectId: {ProjectId}", id);
return Ok(ApiResponse<object>.SuccessResponse(projectVM, "Project details fetched successfully", 200));
}
private async Task<ProjectDetailsVM> GetProjectViewModel(Guid? id, Project project)
private ProjectVM GetProjectViewModel(Project project)
{
ProjectDetailsVM vm = new ProjectDetailsVM();
// List<Building> buildings = _unitOfWork.Building.GetAll(c => c.ProjectId == id).ToList();
List<Building> buildings = await _context.Buildings.Where(c => c.ProjectId == id).ToListAsync();
List<Guid> idList = buildings.Select(o => o.Id).ToList();
// List<Floor> floors = _unitOfWork.Floor.GetAll(c => idList.Contains(c.Id)).ToList();
List<Floor> floors = await _context.Floor.Where(c => idList.Contains(c.BuildingId)).ToListAsync();
idList = floors.Select(o => o.Id).ToList();
//List<WorkArea> workAreas = _unitOfWork.WorkArea.GetAll(c => idList.Contains(c.Id), includeProperties: "WorkItems,WorkItems.ActivityMaster").ToList();
List<WorkArea> workAreas = await _context.WorkAreas.Where(c => idList.Contains(c.FloorId)).ToListAsync();
idList = workAreas.Select(o => o.Id).ToList();
List<WorkItem> workItems = await _context.WorkItems.Include(c => c.WorkCategoryMaster).Where(c => idList.Contains(c.WorkAreaId)).Include(c => c.ActivityMaster).ToListAsync();
// List <WorkItem> workItems = _unitOfWork.WorkItem.GetAll(c => idList.Contains(c.WorkAreaId), includeProperties: "ActivityMaster").ToList();
idList = workItems.Select(t => t.Id).ToList();
List<TaskAllocation> tasks = await _context.TaskAllocations.Where(t => idList.Contains(t.WorkItemId) && t.AssignmentDate.Date == DateTime.UtcNow.Date).ToListAsync();
vm.project = project;
vm.buildings = buildings;
vm.floors = floors;
vm.workAreas = workAreas;
vm.workItems = workItems;
vm.Tasks = tasks;
return vm;
return new ProjectVM
{
Id = project.Id,
Name = project.Name,
ShortName = project.ShortName,
StartDate = project.StartDate,
EndDate = project.EndDate,
ProjectStatus = project.ProjectStatus,
ContactPerson = project.ContactPerson,
ProjectAddress = project.ProjectAddress,
};
}
private Guid GetTenantId()
@ -594,6 +541,116 @@ namespace MarcoBMS.Services.Controllers
}
[HttpGet("infra-details/{projectId}")]
public async Task<IActionResult> GetInfraDetails(Guid projectId)
{
_logger.LogInfo("GetInfraDetails called for ProjectId: {ProjectId}", projectId);
// Step 1: Get logged-in employee
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
// Step 2: Check project-specific permission
var hasProjectPermission = await _permission.HasProjectPermission(loggedInEmployee, projectId.ToString());
if (!hasProjectPermission)
{
_logger.LogWarning("Project access denied for EmployeeId: {EmployeeId} on ProjectId: {ProjectId}", loggedInEmployee.Id, projectId);
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access denied", "You don't have access to this project", 403));
}
// Step 3: Check 'ViewInfra' permission
var hasViewInfraPermission = await _permission.HasPermission(ViewInfra, loggedInEmployee.Id);
if (!hasViewInfraPermission)
{
_logger.LogWarning("ViewInfra permission denied for EmployeeId: {EmployeeId}", loggedInEmployee.Id);
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access denied", "You don't have access to view infra", 403));
}
// Step 4: Fetch buildings for the project
var buildings = await _context.Buildings
.Where(b => b.ProjectId == projectId)
.ToListAsync();
var buildingIds = buildings.Select(b => b.Id).ToList();
// Step 5: Fetch floors associated with the buildings
var floors = await _context.Floor
.Where(f => buildingIds.Contains(f.BuildingId))
.ToListAsync();
var floorIds = floors.Select(f => f.Id).ToList();
// Step 6: Fetch work areas associated with the floors
var workAreas = await _context.WorkAreas
.Where(wa => floorIds.Contains(wa.FloorId))
.ToListAsync();
// Step 7: Build the infra hierarchy (Building > Floors > Work Areas)
var infraVM = buildings.Select(b =>
{
var selectedFloors = floors
.Where(f => f.BuildingId == b.Id)
.Select(f => new
{
Id = f.Id,
FloorName = f.FloorName,
WorkAreas = workAreas
.Where(wa => wa.FloorId == f.Id)
.Select(wa => new { wa.Id, wa.AreaName })
.ToList()
}).ToList();
return new
{
Id = b.Id,
BuildingName = b.Name,
Floors = selectedFloors
};
}).ToList();
_logger.LogInfo("Infra details fetched successfully for ProjectId: {ProjectId}, EmployeeId: {EmployeeId}, Buildings: {Count}",
projectId, loggedInEmployee.Id, infraVM.Count);
return Ok(ApiResponse<object>.SuccessResponse(infraVM, "Infra details fetched successfully", 200));
}
[HttpGet("tasks/{workAreaId}")]
public async Task<IActionResult> GetWorkItems(Guid workAreaId)
{
_logger.LogInfo("GetWorkItems called for WorkAreaId: {WorkAreaId}", workAreaId);
// Step 1: Get the currently logged-in employee
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
// Step 2: Check if the employee has ViewInfra permission
var hasViewInfraPermission = await _permission.HasPermission(ViewInfra, loggedInEmployee.Id);
if (!hasViewInfraPermission)
{
_logger.LogWarning("ViewInfra permission denied for EmployeeId: {EmployeeId}", loggedInEmployee.Id);
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to view infrastructure", 403));
}
// Step 3: Check if the specified Work Area exists
var isWorkAreaExist = await _context.WorkAreas.AnyAsync(wa => wa.Id == workAreaId);
if (!isWorkAreaExist)
{
_logger.LogWarning("Work Area not found for WorkAreaId: {WorkAreaId}", workAreaId);
return NotFound(ApiResponse<object>.ErrorResponse("Work Area not found", "Work Area not found in database", 404));
}
// Step 4: Fetch WorkItems with related Activity and Work Category data
var workItems = await _context.WorkItems
.Include(wi => wi.ActivityMaster)
.Include(wi => wi.WorkCategoryMaster)
.Where(wi => wi.WorkAreaId == workAreaId)
.ToListAsync();
_logger.LogInfo("{Count} work items fetched successfully for WorkAreaId: {WorkAreaId}", workItems.Count, workAreaId);
// Step 5: Return result
return Ok(ApiResponse<object>.SuccessResponse(workItems, $"{workItems.Count} records of tasks fetched successfully", 200));
}
[HttpPost("task")]
public async Task<IActionResult> CreateProjectTask(List<WorkItemDot> workItemDtos)
{