From 08e8e8d75fea2ab069d6f7b15b6fda845aae5af5 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Wed, 16 Jul 2025 12:39:16 +0530 Subject: [PATCH 1/6] Changed the business logic of teams and tasks API in DashboardController to accept project ID and provide data according to project ID or project IDs assigned to logged in user --- .../Controllers/DashboardController.cs | 210 +++++++++++++++--- 1 file changed, 176 insertions(+), 34 deletions(-) diff --git a/Marco.Pms.Services/Controllers/DashboardController.cs b/Marco.Pms.Services/Controllers/DashboardController.cs index 8ed0ba0..432459c 100644 --- a/Marco.Pms.Services/Controllers/DashboardController.cs +++ b/Marco.Pms.Services/Controllers/DashboardController.cs @@ -21,12 +21,15 @@ namespace Marco.Pms.Services.Controllers { private readonly ApplicationDbContext _context; private readonly UserHelper _userHelper; + private readonly ProjectsHelper _projectsHelper; private readonly ILoggingService _logger; private readonly PermissionServices _permissionServices; - public DashboardController(ApplicationDbContext context, UserHelper userHelper, ILoggingService logger, PermissionServices permissionServices) + public static readonly Guid ActiveId = Guid.Parse("b74da4c2-d07e-46f2-9919-e75e49b12731"); + public DashboardController(ApplicationDbContext context, UserHelper userHelper, ProjectsHelper projectsHelper, ILoggingService logger, PermissionServices permissionServices) { _context = context; _userHelper = userHelper; + _projectsHelper = projectsHelper; _logger = logger; _permissionServices = permissionServices; } @@ -162,46 +165,185 @@ namespace Marco.Pms.Services.Controllers return Ok(ApiResponse.SuccessResponse(projectDashboardVM, "Success", 200)); } + /// + /// Retrieves a dashboard summary of total employees and today's attendance. + /// If a projectId is provided, it returns totals for that project; otherwise, for all accessible active projects. + /// + /// Optional. The ID of a specific project to get totals for. [HttpGet("teams")] - public async Task GetTotalEmployees() + public async Task GetTotalEmployees([FromQuery] Guid? projectId) { - var tenantId = _userHelper.GetTenantId(); - var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); - var date = DateTime.UtcNow.Date; - - var Employees = await _context.Employees.Where(e => e.TenantId == tenantId && e.IsActive == true).Select(e => e.Id).ToListAsync(); - - var checkedInEmployee = await _context.Attendes.Where(e => e.InTime != null ? e.InTime.Value.Date == date : false).Select(e => e.EmployeeID).ToListAsync(); - - TeamDashboardVM teamDashboardVM = new TeamDashboardVM + try { - TotalEmployees = Employees.Count(), - InToday = checkedInEmployee.Distinct().Count() - }; - _logger.LogInfo("Today's total checked in employees fetched by employee {EmployeeId}", LoggedInEmployee.Id); - return Ok(ApiResponse.SuccessResponse(teamDashboardVM, "Success", 200)); - } + var tenantId = _userHelper.GetTenantId(); + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); - [HttpGet("tasks")] - public async Task GetTotalTasks() - { - var tenantId = _userHelper.GetTenantId(); - var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); - var Tasks = await _context.WorkItems.Where(t => t.TenantId == tenantId).Select(t => new { PlannedWork = t.PlannedWork, CompletedWork = t.CompletedWork }).ToListAsync(); - TasksDashboardVM tasksDashboardVM = new TasksDashboardVM - { - TotalTasks = 0, - CompletedTasks = 0 - }; - foreach (var task in Tasks) - { - tasksDashboardVM.TotalTasks += task.PlannedWork; - tasksDashboardVM.CompletedTasks += task.CompletedWork; + _logger.LogInfo("GetTotalEmployees called by user {UserId} for ProjectId: {ProjectId}", loggedInEmployee.Id, projectId ?? Guid.Empty); + + // --- Step 1: Get the list of projects the user can access --- + // This query is more efficient as it only selects the IDs needed. + var projects = await _projectsHelper.GetMyProjects(tenantId, loggedInEmployee); + var accessibleActiveProjectIds = projects + .Where(p => p.ProjectStatusId == ActiveId) + .Select(p => p.Id) + .ToList(); + if (!accessibleActiveProjectIds.Any()) + { + _logger.LogInfo("User {UserId} has no accessible active projects.", loggedInEmployee.Id); + return Ok(ApiResponse.SuccessResponse(new TeamDashboardVM(), "No accessible active projects found.", 200)); + } + + // --- Step 2: Build the list of project IDs to query against --- + List finalProjectIds; + + if (projectId.HasValue) + { + // Security Check: Ensure the requested project is in the user's accessible list. + if (!accessibleActiveProjectIds.Contains(projectId.Value)) + { + _logger.LogWarning("Access DENIED for user {UserId} on project {ProjectId} (not active or not accessible).", loggedInEmployee.Id, projectId.Value); + return StatusCode(403, ApiResponse.ErrorResponse("Access Denied.", "You do not have permission to view this project, or it is not active.", 403)); + } + finalProjectIds = new List { projectId.Value }; + } + else + { + finalProjectIds = accessibleActiveProjectIds; + } + + // --- Step 3: Run efficient aggregation queries SEQUENTIALLY --- + // Since we only have one DbContext instance, we await each query one by one. + + // Query 1: Count total distinct employees allocated to the final project list + int totalEmployees = await _context.ProjectAllocations + .Where(pa => pa.TenantId == tenantId && + finalProjectIds.Contains(pa.ProjectId) && + pa.IsActive) + .Select(pa => pa.EmployeeId) + .Distinct() + .CountAsync(); + + // Query 2: Count total distinct employees who checked in today + // Use an efficient date range check + var today = DateTime.UtcNow.Date; + var tomorrow = today.AddDays(1); + + int inTodays = await _context.Attendes + .Where(a => a.InTime >= today && a.InTime < tomorrow && + finalProjectIds.Contains(a.ProjectID)) + .Select(a => a.EmployeeID) + .Distinct() + .CountAsync(); + + // --- Step 4: Assemble the response --- + var teamDashboardVM = new TeamDashboardVM + { + TotalEmployees = totalEmployees, + InToday = inTodays + }; + + _logger.LogInfo("Successfully fetched team dashboard for user {UserId}. Total: {TotalEmployees}, InToday: {InToday}", + loggedInEmployee.Id, teamDashboardVM.TotalEmployees, teamDashboardVM.InToday); + + return Ok(ApiResponse.SuccessResponse(teamDashboardVM, "Dashboard data retrieved successfully.", 200)); + } + catch (Exception ex) + { + _logger.LogError("An unexpected error occurred in GetTotalEmployees for projectId {ProjectId} \n {Error}", projectId ?? Guid.Empty, ex.Message); + return StatusCode(500, ApiResponse.ErrorResponse("An internal server error occurred.", null, 500)); } - _logger.LogInfo("Total targeted tasks and total completed tasks fetched by employee {EmployeeId}", LoggedInEmployee.Id); - return Ok(ApiResponse.SuccessResponse(tasksDashboardVM, "Success", 200)); } + /// + /// Retrieves a dashboard summary of total planned and completed tasks. + /// If a projectId is provided, it returns totals for that project; otherwise, for all accessible projects. + /// + /// Optional. The ID of a specific project to get totals for. + /// An ApiResponse containing the task dashboard summary. + [HttpGet("tasks")] // Example route + public async Task GetTotalTasks1([FromQuery] Guid? projectId) // Changed to FromQuery as it's optional + { + try + { + var tenantId = _userHelper.GetTenantId(); + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + + _logger.LogInfo("GetTotalTasks called by user {UserId} for ProjectId: {ProjectId}", loggedInEmployee.Id, projectId ?? Guid.Empty); + + // --- Step 1: Build the base IQueryable for WorkItems --- + // This query is NOT executed yet. We will add more filters to it. + var baseWorkItemQuery = _context.WorkItems.Where(t => t.TenantId == tenantId); + + // --- Step 2: Apply Filters based on the request (Project or All Accessible) --- + if (projectId.HasValue) + { + // --- Logic for a SINGLE Project --- + + // 2a. Security Check: Verify permission for the specific project. + var hasPermission = await _permissionServices.HasProjectPermission(loggedInEmployee, projectId.Value.ToString()); + if (!hasPermission) + { + _logger.LogWarning("Access DENIED for user {UserId} on project {ProjectId}.", loggedInEmployee.Id, projectId.Value); + return StatusCode(403, ApiResponse.ErrorResponse("Access Denied.", "You do not have permission to view this project.", 403)); + } + + // 2b. Add project-specific filter to the base query. + // This is more efficient than fetching workAreaIds separately. + baseWorkItemQuery = baseWorkItemQuery + .Where(wi => wi.WorkArea != null && + wi.WorkArea.Floor != null && + wi.WorkArea.Floor.Building != null && + wi.WorkArea.Floor.Building.ProjectId == projectId.Value); + } + else + { + // --- Logic for ALL Accessible Projects --- + + // 2c. Get a list of all projects the user is allowed to see. + var accessibleProject = await _projectsHelper.GetMyProjects(tenantId, loggedInEmployee); + var accessibleProjectIds = accessibleProject.Select(p => p.Id).ToList(); + if (!accessibleProjectIds.Any()) + { + _logger.LogInfo("User {UserId} has no accessible projects.", loggedInEmployee.Id); + // Return a zeroed-out dashboard if the user has no projects. + return Ok(ApiResponse.SuccessResponse(new TasksDashboardVM(), "No accessible projects found.", 200)); + } + + // 2d. Add a filter to include all work items from all accessible projects. + baseWorkItemQuery = baseWorkItemQuery + .Where(wi => wi.WorkArea != null && + wi.WorkArea.Floor != null && + wi.WorkArea.Floor.Building != null && + accessibleProjectIds.Contains(wi.WorkArea.Floor.Building.ProjectId)); + } + + // --- Step 3: Execute the Aggregation Query ON THE DATABASE SERVER --- + // This is the most powerful optimization. The database does all the summing. + // EF Core translates this into a single, efficient SQL query like: + // SELECT SUM(PlannedWork), SUM(CompletedWork) FROM WorkItems WHERE ... + var tasksDashboardVM = await baseWorkItemQuery + .GroupBy(x => 1) // Group by a constant to aggregate all rows into one result. + .Select(g => new TasksDashboardVM + { + TotalTasks = g.Sum(wi => wi.PlannedWork), + CompletedTasks = g.Sum(wi => wi.CompletedWork) + }) + .FirstOrDefaultAsync(); // Use FirstOrDefaultAsync as GroupBy might return no rows. + + // If the query returned no work items, the result will be null. Default to a zeroed object. + tasksDashboardVM ??= new TasksDashboardVM(); + + _logger.LogInfo("Successfully fetched task dashboard for user {UserId}. Total: {TotalTasks}, Completed: {CompletedTasks}", + loggedInEmployee.Id, tasksDashboardVM.TotalTasks, tasksDashboardVM.CompletedTasks); + + return Ok(ApiResponse.SuccessResponse(tasksDashboardVM, "Dashboard data retrieved successfully.", 200)); + } + catch (Exception ex) + { + _logger.LogError("An unexpected error occurred in GetTotalTasks for projectId {ProjectId} \n {Error}", projectId ?? Guid.Empty, ex.Message); + return StatusCode(500, ApiResponse.ErrorResponse("An internal server error occurred.", null, 500)); + } + } [HttpGet("pending-attendance")] public async Task GetPendingAttendance() { From 2889620c1c9ce75e57685741bac9db6bcb8abdba Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Wed, 16 Jul 2025 14:49:34 +0530 Subject: [PATCH 2/6] only checking if the user have permission of project or not only --- Marco.Pms.Services/Controllers/DashboardController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Marco.Pms.Services/Controllers/DashboardController.cs b/Marco.Pms.Services/Controllers/DashboardController.cs index 432459c..3829cdc 100644 --- a/Marco.Pms.Services/Controllers/DashboardController.cs +++ b/Marco.Pms.Services/Controllers/DashboardController.cs @@ -199,7 +199,8 @@ namespace Marco.Pms.Services.Controllers if (projectId.HasValue) { // Security Check: Ensure the requested project is in the user's accessible list. - if (!accessibleActiveProjectIds.Contains(projectId.Value)) + var hasPermission = await _permissionServices.HasProjectPermission(loggedInEmployee, projectId.Value.ToString()); + if (!hasPermission) { _logger.LogWarning("Access DENIED for user {UserId} on project {ProjectId} (not active or not accessible).", loggedInEmployee.Id, projectId.Value); return StatusCode(403, ApiResponse.ErrorResponse("Access Denied.", "You do not have permission to view this project, or it is not active.", 403)); From e4246df315f5f36a5e44abee3504028cc21a1a8c Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Wed, 16 Jul 2025 12:39:16 +0530 Subject: [PATCH 3/6] Changed the business logic of teams and tasks API in DashboardController to accept project ID and provide data according to project ID or project IDs assigned to logged in user --- .../Controllers/DashboardController.cs | 210 +++++++++++++++--- 1 file changed, 176 insertions(+), 34 deletions(-) diff --git a/Marco.Pms.Services/Controllers/DashboardController.cs b/Marco.Pms.Services/Controllers/DashboardController.cs index 8ed0ba0..432459c 100644 --- a/Marco.Pms.Services/Controllers/DashboardController.cs +++ b/Marco.Pms.Services/Controllers/DashboardController.cs @@ -21,12 +21,15 @@ namespace Marco.Pms.Services.Controllers { private readonly ApplicationDbContext _context; private readonly UserHelper _userHelper; + private readonly ProjectsHelper _projectsHelper; private readonly ILoggingService _logger; private readonly PermissionServices _permissionServices; - public DashboardController(ApplicationDbContext context, UserHelper userHelper, ILoggingService logger, PermissionServices permissionServices) + public static readonly Guid ActiveId = Guid.Parse("b74da4c2-d07e-46f2-9919-e75e49b12731"); + public DashboardController(ApplicationDbContext context, UserHelper userHelper, ProjectsHelper projectsHelper, ILoggingService logger, PermissionServices permissionServices) { _context = context; _userHelper = userHelper; + _projectsHelper = projectsHelper; _logger = logger; _permissionServices = permissionServices; } @@ -162,46 +165,185 @@ namespace Marco.Pms.Services.Controllers return Ok(ApiResponse.SuccessResponse(projectDashboardVM, "Success", 200)); } + /// + /// Retrieves a dashboard summary of total employees and today's attendance. + /// If a projectId is provided, it returns totals for that project; otherwise, for all accessible active projects. + /// + /// Optional. The ID of a specific project to get totals for. [HttpGet("teams")] - public async Task GetTotalEmployees() + public async Task GetTotalEmployees([FromQuery] Guid? projectId) { - var tenantId = _userHelper.GetTenantId(); - var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); - var date = DateTime.UtcNow.Date; - - var Employees = await _context.Employees.Where(e => e.TenantId == tenantId && e.IsActive == true).Select(e => e.Id).ToListAsync(); - - var checkedInEmployee = await _context.Attendes.Where(e => e.InTime != null ? e.InTime.Value.Date == date : false).Select(e => e.EmployeeID).ToListAsync(); - - TeamDashboardVM teamDashboardVM = new TeamDashboardVM + try { - TotalEmployees = Employees.Count(), - InToday = checkedInEmployee.Distinct().Count() - }; - _logger.LogInfo("Today's total checked in employees fetched by employee {EmployeeId}", LoggedInEmployee.Id); - return Ok(ApiResponse.SuccessResponse(teamDashboardVM, "Success", 200)); - } + var tenantId = _userHelper.GetTenantId(); + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); - [HttpGet("tasks")] - public async Task GetTotalTasks() - { - var tenantId = _userHelper.GetTenantId(); - var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); - var Tasks = await _context.WorkItems.Where(t => t.TenantId == tenantId).Select(t => new { PlannedWork = t.PlannedWork, CompletedWork = t.CompletedWork }).ToListAsync(); - TasksDashboardVM tasksDashboardVM = new TasksDashboardVM - { - TotalTasks = 0, - CompletedTasks = 0 - }; - foreach (var task in Tasks) - { - tasksDashboardVM.TotalTasks += task.PlannedWork; - tasksDashboardVM.CompletedTasks += task.CompletedWork; + _logger.LogInfo("GetTotalEmployees called by user {UserId} for ProjectId: {ProjectId}", loggedInEmployee.Id, projectId ?? Guid.Empty); + + // --- Step 1: Get the list of projects the user can access --- + // This query is more efficient as it only selects the IDs needed. + var projects = await _projectsHelper.GetMyProjects(tenantId, loggedInEmployee); + var accessibleActiveProjectIds = projects + .Where(p => p.ProjectStatusId == ActiveId) + .Select(p => p.Id) + .ToList(); + if (!accessibleActiveProjectIds.Any()) + { + _logger.LogInfo("User {UserId} has no accessible active projects.", loggedInEmployee.Id); + return Ok(ApiResponse.SuccessResponse(new TeamDashboardVM(), "No accessible active projects found.", 200)); + } + + // --- Step 2: Build the list of project IDs to query against --- + List finalProjectIds; + + if (projectId.HasValue) + { + // Security Check: Ensure the requested project is in the user's accessible list. + if (!accessibleActiveProjectIds.Contains(projectId.Value)) + { + _logger.LogWarning("Access DENIED for user {UserId} on project {ProjectId} (not active or not accessible).", loggedInEmployee.Id, projectId.Value); + return StatusCode(403, ApiResponse.ErrorResponse("Access Denied.", "You do not have permission to view this project, or it is not active.", 403)); + } + finalProjectIds = new List { projectId.Value }; + } + else + { + finalProjectIds = accessibleActiveProjectIds; + } + + // --- Step 3: Run efficient aggregation queries SEQUENTIALLY --- + // Since we only have one DbContext instance, we await each query one by one. + + // Query 1: Count total distinct employees allocated to the final project list + int totalEmployees = await _context.ProjectAllocations + .Where(pa => pa.TenantId == tenantId && + finalProjectIds.Contains(pa.ProjectId) && + pa.IsActive) + .Select(pa => pa.EmployeeId) + .Distinct() + .CountAsync(); + + // Query 2: Count total distinct employees who checked in today + // Use an efficient date range check + var today = DateTime.UtcNow.Date; + var tomorrow = today.AddDays(1); + + int inTodays = await _context.Attendes + .Where(a => a.InTime >= today && a.InTime < tomorrow && + finalProjectIds.Contains(a.ProjectID)) + .Select(a => a.EmployeeID) + .Distinct() + .CountAsync(); + + // --- Step 4: Assemble the response --- + var teamDashboardVM = new TeamDashboardVM + { + TotalEmployees = totalEmployees, + InToday = inTodays + }; + + _logger.LogInfo("Successfully fetched team dashboard for user {UserId}. Total: {TotalEmployees}, InToday: {InToday}", + loggedInEmployee.Id, teamDashboardVM.TotalEmployees, teamDashboardVM.InToday); + + return Ok(ApiResponse.SuccessResponse(teamDashboardVM, "Dashboard data retrieved successfully.", 200)); + } + catch (Exception ex) + { + _logger.LogError("An unexpected error occurred in GetTotalEmployees for projectId {ProjectId} \n {Error}", projectId ?? Guid.Empty, ex.Message); + return StatusCode(500, ApiResponse.ErrorResponse("An internal server error occurred.", null, 500)); } - _logger.LogInfo("Total targeted tasks and total completed tasks fetched by employee {EmployeeId}", LoggedInEmployee.Id); - return Ok(ApiResponse.SuccessResponse(tasksDashboardVM, "Success", 200)); } + /// + /// Retrieves a dashboard summary of total planned and completed tasks. + /// If a projectId is provided, it returns totals for that project; otherwise, for all accessible projects. + /// + /// Optional. The ID of a specific project to get totals for. + /// An ApiResponse containing the task dashboard summary. + [HttpGet("tasks")] // Example route + public async Task GetTotalTasks1([FromQuery] Guid? projectId) // Changed to FromQuery as it's optional + { + try + { + var tenantId = _userHelper.GetTenantId(); + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + + _logger.LogInfo("GetTotalTasks called by user {UserId} for ProjectId: {ProjectId}", loggedInEmployee.Id, projectId ?? Guid.Empty); + + // --- Step 1: Build the base IQueryable for WorkItems --- + // This query is NOT executed yet. We will add more filters to it. + var baseWorkItemQuery = _context.WorkItems.Where(t => t.TenantId == tenantId); + + // --- Step 2: Apply Filters based on the request (Project or All Accessible) --- + if (projectId.HasValue) + { + // --- Logic for a SINGLE Project --- + + // 2a. Security Check: Verify permission for the specific project. + var hasPermission = await _permissionServices.HasProjectPermission(loggedInEmployee, projectId.Value.ToString()); + if (!hasPermission) + { + _logger.LogWarning("Access DENIED for user {UserId} on project {ProjectId}.", loggedInEmployee.Id, projectId.Value); + return StatusCode(403, ApiResponse.ErrorResponse("Access Denied.", "You do not have permission to view this project.", 403)); + } + + // 2b. Add project-specific filter to the base query. + // This is more efficient than fetching workAreaIds separately. + baseWorkItemQuery = baseWorkItemQuery + .Where(wi => wi.WorkArea != null && + wi.WorkArea.Floor != null && + wi.WorkArea.Floor.Building != null && + wi.WorkArea.Floor.Building.ProjectId == projectId.Value); + } + else + { + // --- Logic for ALL Accessible Projects --- + + // 2c. Get a list of all projects the user is allowed to see. + var accessibleProject = await _projectsHelper.GetMyProjects(tenantId, loggedInEmployee); + var accessibleProjectIds = accessibleProject.Select(p => p.Id).ToList(); + if (!accessibleProjectIds.Any()) + { + _logger.LogInfo("User {UserId} has no accessible projects.", loggedInEmployee.Id); + // Return a zeroed-out dashboard if the user has no projects. + return Ok(ApiResponse.SuccessResponse(new TasksDashboardVM(), "No accessible projects found.", 200)); + } + + // 2d. Add a filter to include all work items from all accessible projects. + baseWorkItemQuery = baseWorkItemQuery + .Where(wi => wi.WorkArea != null && + wi.WorkArea.Floor != null && + wi.WorkArea.Floor.Building != null && + accessibleProjectIds.Contains(wi.WorkArea.Floor.Building.ProjectId)); + } + + // --- Step 3: Execute the Aggregation Query ON THE DATABASE SERVER --- + // This is the most powerful optimization. The database does all the summing. + // EF Core translates this into a single, efficient SQL query like: + // SELECT SUM(PlannedWork), SUM(CompletedWork) FROM WorkItems WHERE ... + var tasksDashboardVM = await baseWorkItemQuery + .GroupBy(x => 1) // Group by a constant to aggregate all rows into one result. + .Select(g => new TasksDashboardVM + { + TotalTasks = g.Sum(wi => wi.PlannedWork), + CompletedTasks = g.Sum(wi => wi.CompletedWork) + }) + .FirstOrDefaultAsync(); // Use FirstOrDefaultAsync as GroupBy might return no rows. + + // If the query returned no work items, the result will be null. Default to a zeroed object. + tasksDashboardVM ??= new TasksDashboardVM(); + + _logger.LogInfo("Successfully fetched task dashboard for user {UserId}. Total: {TotalTasks}, Completed: {CompletedTasks}", + loggedInEmployee.Id, tasksDashboardVM.TotalTasks, tasksDashboardVM.CompletedTasks); + + return Ok(ApiResponse.SuccessResponse(tasksDashboardVM, "Dashboard data retrieved successfully.", 200)); + } + catch (Exception ex) + { + _logger.LogError("An unexpected error occurred in GetTotalTasks for projectId {ProjectId} \n {Error}", projectId ?? Guid.Empty, ex.Message); + return StatusCode(500, ApiResponse.ErrorResponse("An internal server error occurred.", null, 500)); + } + } [HttpGet("pending-attendance")] public async Task GetPendingAttendance() { From bbd20548677a9af4183be24f867fa66476562a8c Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Wed, 16 Jul 2025 14:49:34 +0530 Subject: [PATCH 4/6] only checking if the user have permission of project or not only --- Marco.Pms.Services/Controllers/DashboardController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Marco.Pms.Services/Controllers/DashboardController.cs b/Marco.Pms.Services/Controllers/DashboardController.cs index 432459c..3829cdc 100644 --- a/Marco.Pms.Services/Controllers/DashboardController.cs +++ b/Marco.Pms.Services/Controllers/DashboardController.cs @@ -199,7 +199,8 @@ namespace Marco.Pms.Services.Controllers if (projectId.HasValue) { // Security Check: Ensure the requested project is in the user's accessible list. - if (!accessibleActiveProjectIds.Contains(projectId.Value)) + var hasPermission = await _permissionServices.HasProjectPermission(loggedInEmployee, projectId.Value.ToString()); + if (!hasPermission) { _logger.LogWarning("Access DENIED for user {UserId} on project {ProjectId} (not active or not accessible).", loggedInEmployee.Id, projectId.Value); return StatusCode(403, ApiResponse.ErrorResponse("Access Denied.", "You do not have permission to view this project, or it is not active.", 403)); From d1106bc86b362d528ddecd8acb2a9e0550486f19 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Thu, 17 Jul 2025 12:01:47 +0530 Subject: [PATCH 5/6] Can able update note of deleted not also --- Marco.Pms.Services/Helpers/DirectoryHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marco.Pms.Services/Helpers/DirectoryHelper.cs b/Marco.Pms.Services/Helpers/DirectoryHelper.cs index 37f58cf..199a410 100644 --- a/Marco.Pms.Services/Helpers/DirectoryHelper.cs +++ b/Marco.Pms.Services/Helpers/DirectoryHelper.cs @@ -1086,7 +1086,7 @@ namespace Marco.Pms.Services.Helpers var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); if (noteDto != null && id == noteDto.Id) { - Contact? contact = await _context.Contacts.FirstOrDefaultAsync(c => c.Id == noteDto.ContactId && c.IsActive && c.TenantId == tenantId); + Contact? contact = await _context.Contacts.FirstOrDefaultAsync(c => c.Id == noteDto.ContactId && c.TenantId == tenantId); if (contact != null) { ContactNote? contactNote = await _context.ContactNotes.Include(cn => cn.Createdby).Include(cn => cn.Contact).FirstOrDefaultAsync(n => n.Id == noteDto.Id && n.ContactId == contact.Id && n.IsActive); From 0ec507c97c2512faaa8094beb9aa98609322e28b Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Thu, 17 Jul 2025 15:31:05 +0530 Subject: [PATCH 6/6] Included the jobrole when feaching employee list --- Marco.Pms.Services/Helpers/EmployeeHelper.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Marco.Pms.Services/Helpers/EmployeeHelper.cs b/Marco.Pms.Services/Helpers/EmployeeHelper.cs index 926e7fd..343144a 100644 --- a/Marco.Pms.Services/Helpers/EmployeeHelper.cs +++ b/Marco.Pms.Services/Helpers/EmployeeHelper.cs @@ -81,6 +81,7 @@ namespace MarcoBMS.Services.Helpers result = await _context.ProjectAllocations .Include(pa => pa.Employee) + .ThenInclude(e => e!.JobRole) .Where(c => c.ProjectId == ProjectId.Value && c.IsActive && c.Employee != null) .Select(pa => pa.Employee!.ToEmployeeVMFromEmployee()) .ToListAsync();