From b336bd34811ae3a4d42d68562af35dae37e6dbe3 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Thu, 4 Sep 2025 12:19:27 +0530 Subject: [PATCH] Added the API to get list of employee whom project-level is assigned --- .../DocumentManager/AttachmentVersionVM.cs | 2 + .../Controllers/DocumentController.cs | 5 + .../Controllers/ProjectController.cs | 7 ++ .../Service/PermissionServices.cs | 94 ++++++++++++++++++- Marco.Pms.Services/Service/ProjectServices.cs | 31 +++++- .../ServiceInterfaces/IProjectServices.cs | 1 + 6 files changed, 132 insertions(+), 8 deletions(-) diff --git a/Marco.Pms.Model/ViewModels/DocumentManager/AttachmentVersionVM.cs b/Marco.Pms.Model/ViewModels/DocumentManager/AttachmentVersionVM.cs index e064522..f91e513 100644 --- a/Marco.Pms.Model/ViewModels/DocumentManager/AttachmentVersionVM.cs +++ b/Marco.Pms.Model/ViewModels/DocumentManager/AttachmentVersionVM.cs @@ -14,6 +14,8 @@ namespace Marco.Pms.Model.ViewModels.DocumentManager public BasicEmployeeVM? UploadedBy { get; set; } public DateTime? UpdatedAt { get; set; } public BasicEmployeeVM? UpdatedBy { get; set; } + public DateTime? VerifiedAt { get; set; } + public BasicEmployeeVM? VerifiedBy { get; set; } public bool? IsVerified { get; set; } } } diff --git a/Marco.Pms.Services/Controllers/DocumentController.cs b/Marco.Pms.Services/Controllers/DocumentController.cs index 262c819..f70ddcf 100644 --- a/Marco.Pms.Services/Controllers/DocumentController.cs +++ b/Marco.Pms.Services/Controllers/DocumentController.cs @@ -424,8 +424,13 @@ namespace Marco.Pms.Services.Controllers var versionMappingsQuery = _context.AttachmentVersionMappings .Include(av => av.ChildAttachment) .ThenInclude(da => da!.UploadedBy) + .ThenInclude(e => e!.JobRole) .Include(av => av.ChildAttachment) .ThenInclude(da => da!.UpdatedBy) + .ThenInclude(e => e!.JobRole) + .Include(av => av.ChildAttachment) + .ThenInclude(da => da!.VerifiedBy) + .ThenInclude(e => e!.JobRole) .Include(av => av.ChildAttachment) .ThenInclude(da => da!.Document) .Where(av => av.ParentAttachmentId == parentAttachmentId && av.TenantId == tenantId); diff --git a/Marco.Pms.Services/Controllers/ProjectController.cs b/Marco.Pms.Services/Controllers/ProjectController.cs index 7499843..eb3619c 100644 --- a/Marco.Pms.Services/Controllers/ProjectController.cs +++ b/Marco.Pms.Services/Controllers/ProjectController.cs @@ -477,6 +477,13 @@ namespace MarcoBMS.Services.Controllers var response = await _projectServices.AssignProjectLevelModulesAsync(tenantId, loggedInEmployee); return StatusCode(response.StatusCode, response); } + [HttpGet("get/proejct-level/employees/{projectId}")] + public async Task GetEmployeeToWhomProjectLevelAssigned(Guid projectId) + { + Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + var response = await _projectServices.GetEmployeeToWhomProjectLevelAssignedAsync(projectId, tenantId, loggedInEmployee); + return StatusCode(response.StatusCode, response); + } } }; \ No newline at end of file diff --git a/Marco.Pms.Services/Service/PermissionServices.cs b/Marco.Pms.Services/Service/PermissionServices.cs index e3374f5..d56c8bf 100644 --- a/Marco.Pms.Services/Service/PermissionServices.cs +++ b/Marco.Pms.Services/Service/PermissionServices.cs @@ -19,16 +19,100 @@ namespace Marco.Pms.Services.Service _cache = cache; } - public async Task HasPermission(Guid featurePermissionId, Guid employeeId) + //public async Task HasPermission(Guid featurePermissionId, Guid employeeId, Guid? projectId = null) + //{ + // var featurePermissionIds = await _cache.GetPermissions(employeeId); + // if (featurePermissionIds == null) + // { + // List featurePermission = await _rolesHelper.GetFeaturePermissionByEmployeeId(employeeId); + // featurePermissionIds = featurePermission.Select(fp => fp.Id).ToList(); + // } + // if (projectId != null) + // { + // var projectLevelPerissionIds = await _context.ProjectLevelPermissionMappings + // .Where(pl => pl.ProjectId == projectId.Value && pl.EmployeeId == employeeId).Select(pl => pl.PermissionId).ToListAsync(); + + // var projectLevelModuleIds = new HashSet + // { + // Guid.Parse("53176ebf-c75d-42e5-839f-4508ffac3def"), + // Guid.Parse("9d4b5489-2079-40b9-bd77-6e1bf90bc19f"), + // Guid.Parse("81ab8a87-8ccd-4015-a917-0627cee6a100"), + // Guid.Parse("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"), + // Guid.Parse("a8cf4331-8f04-4961-8360-a3f7c3cc7462") + // }; + + // var allProjectLevelPermissionIds = await _context.FeaturePermissions + // .Where(fp => projectLevelModuleIds.Contains(fp.FeatureId) && !projectLevelPerissionIds.Contains(fp.Id)).Select(fp => fp.Id).ToListAsync(); + // featurePermissionIds.RemoveRange(allProjectLevelPermissionIds); + + // featurePermissionIds.AddRange(projectLevelPerissionIds); + // featurePermissionIds = featurePermissionIds.Distinct().ToList(); + // } + // var hasPermission = featurePermissionIds.Contains(featurePermissionId); + // return hasPermission; + //} + + /// + /// Checks whether an employee has a specific feature permission, optionally within a project context. + /// + /// The target feature permission ID to check. + /// The ID of the employee. + /// Optional project ID for project-scoped permissions. + /// True if the user has the permission, otherwise false. + public async Task HasPermission(Guid featurePermissionId, Guid employeeId, Guid? projectId = null) { + // 1. Try fetching permissions from cache (fast-path lookup). var featurePermissionIds = await _cache.GetPermissions(employeeId); + + // If not found in cache, fallback to database (slower). if (featurePermissionIds == null) { - List featurePermission = await _rolesHelper.GetFeaturePermissionByEmployeeId(employeeId); - featurePermissionIds = featurePermission.Select(fp => fp.Id).ToList(); + var featurePermissions = await _rolesHelper.GetFeaturePermissionByEmployeeId(employeeId); + featurePermissionIds = featurePermissions.Select(fp => fp.Id).ToList(); } - var hasPermission = featurePermissionIds.Contains(featurePermissionId); - return hasPermission; + + // 2. Handle project-level permission overrides if a project is specified. + if (projectId.HasValue) + { + // Fetch permissions explicitly assigned to this employee in the project. + var projectLevelPermissionIds = await _context.ProjectLevelPermissionMappings + .Where(pl => pl.ProjectId == projectId.Value && pl.EmployeeId == employeeId) + .Select(pl => pl.PermissionId) + .ToListAsync(); + + if (projectLevelPermissionIds?.Any() ?? false) + { + + // Define modules where project-level overrides apply. + var projectLevelModuleIds = new HashSet + { + Guid.Parse("53176ebf-c75d-42e5-839f-4508ffac3def"), + Guid.Parse("9d4b5489-2079-40b9-bd77-6e1bf90bc19f"), + Guid.Parse("81ab8a87-8ccd-4015-a917-0627cee6a100"), + Guid.Parse("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"), + Guid.Parse("a8cf4331-8f04-4961-8360-a3f7c3cc7462") + }; + + // Get all feature permissions under those modules where the user didn't have explicit project-level grants. + var allOverriddenPermissions = await _context.FeaturePermissions + .Where(fp => projectLevelModuleIds.Contains(fp.FeatureId) && + !projectLevelPermissionIds.Contains(fp.Id)) + .Select(fp => fp.Id) + .ToListAsync(); + + // Apply overrides: + // - Remove global permissions overridden by project-level rules. + // - Add explicit project-level permissions. + featurePermissionIds = featurePermissionIds + .Except(allOverriddenPermissions) // Remove overridden + .Concat(projectLevelPermissionIds) // Add project-level + .Distinct() // Ensure no duplicates + .ToList(); + } + } + + // 3. Final check: does the employee have the requested permission? + return featurePermissionIds.Contains(featurePermissionId); } public async Task HasPermissionAny(List featurePermissionIds, Guid employeeId) { diff --git a/Marco.Pms.Services/Service/ProjectServices.cs b/Marco.Pms.Services/Service/ProjectServices.cs index ea6cc8a..ad7bd1a 100644 --- a/Marco.Pms.Services/Service/ProjectServices.cs +++ b/Marco.Pms.Services/Service/ProjectServices.cs @@ -1484,6 +1484,14 @@ namespace Marco.Pms.Services.Service _logger.LogInfo("ManageProjectLevelPermissionAsync started for EmployeeId: {EmployeeId}, ProjectId: {ProjectId}, TenantId: {TenantId}", model.EmployeeId, model.ProjectId, tenantId); + var hasTeamPermission = await _permission.HasPermission(PermissionsMaster.ManageTeam, loggedInEmployee.Id, model.ProjectId); + if (!hasTeamPermission) + { + _logger.LogWarning("Access Denied. User {UserId} tried to Manage the project-level permission for Employee {EmployeeId} and Project {ProjectId}" + , loggedInEmployee.Id, model.EmployeeId, model.ProjectId); + return ApiResponse.ErrorResponse("Access Denied.", "You do not have permission to Manage the project-level permission", 403); + } + // Fetch all required entities in parallel where possible var featurePermissionIds = model.Permission.Select(p => p.Id).ToList(); @@ -1677,7 +1685,7 @@ namespace Marco.Pms.Services.Service _logger.LogInfo("AssignProjectLevelModulesAsync called for TenantId: {TenantId}, EmployeeId: {EmployeeId}", tenantId, loggedInEmployee.Id); // Define target module IDs. These could alternatively be stored in a config file or DB. - var projectModuleIds = new HashSet + var projectLevelModuleIds = new HashSet { Guid.Parse("53176ebf-c75d-42e5-839f-4508ffac3def"), Guid.Parse("9d4b5489-2079-40b9-bd77-6e1bf90bc19f"), @@ -1689,13 +1697,13 @@ namespace Marco.Pms.Services.Service try { // Query features associated with specified modules. Also include module and permissions. - _logger.LogDebug("Querying Features with module filters: {ModuleIds}", string.Join(", ", projectModuleIds)); + _logger.LogDebug("Querying Features with module filters: {ModuleIds}", string.Join(", ", projectLevelModuleIds)); var features = await _context.Features .AsNoTracking() // Improves read-only query performance .Include(f => f.FeaturePermissions) .Include(f => f.Module) - .Where(f => projectModuleIds.Contains(f.Id) && f.Module != null) + .Where(f => projectLevelModuleIds.Contains(f.Id) && f.Module != null) .Select(f => new FeatureVM { Id = f.Id, @@ -1724,6 +1732,23 @@ namespace Marco.Pms.Services.Service } } + public async Task> GetEmployeeToWhomProjectLevelAssignedAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee) + { + var assignedEmployees = await _context.ProjectLevelPermissionMappings + .Include(pl => pl.Employee) + .ThenInclude(e => e!.JobRole) + .AsNoTracking() + .Where(pl => pl.ProjectId == projectId + && pl.TenantId == tenantId) + .Select(pl => pl.Employee) + .Distinct() + .ToListAsync(); + + var response = _mapper.Map>(assignedEmployees); + + return ApiResponse.SuccessResponse(response, "The list of employees with project-level permissions has been successfully retrieved.", 200); + } + #endregion #region =================================================================== Helper Functions =================================================================== diff --git a/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs b/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs index 1a8aa35..c95559a 100644 --- a/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs +++ b/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs @@ -39,6 +39,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces Task> ManageProjectLevelPermissionAsync(ProjctLevelPermissionDto model, Guid tenantId, Employee loggedInEmployee); Task> GetAssignedProjectLevelPermissionAsync(Guid employeeId, Guid projectId, Guid tenantId, Employee loggedInEmployee); Task> AssignProjectLevelModulesAsync(Guid tenantId, Employee loggedInEmployee); + Task> GetEmployeeToWhomProjectLevelAssignedAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee); } }