From 122e7074b8cb12e1c3cce848de960cacf32a134d Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Tue, 16 Sep 2025 13:34:22 +0530 Subject: [PATCH 01/10] Added the GetAllPermissionFroProject API --- .../Controllers/ProjectController.cs | 7 ++ Marco.Pms.Services/Service/ProjectServices.cs | 112 ++++++++++++++++-- .../ServiceInterfaces/IProjectServices.cs | 1 + 3 files changed, 108 insertions(+), 12 deletions(-) diff --git a/Marco.Pms.Services/Controllers/ProjectController.cs b/Marco.Pms.Services/Controllers/ProjectController.cs index eb3619c..f5aa8f8 100644 --- a/Marco.Pms.Services/Controllers/ProjectController.cs +++ b/Marco.Pms.Services/Controllers/ProjectController.cs @@ -470,6 +470,13 @@ namespace MarcoBMS.Services.Controllers var response = await _projectServices.GetAssignedProjectLevelPermissionAsync(employeeId, projectId, tenantId, loggedInEmployee); return StatusCode(response.StatusCode, response); } + [HttpGet("get/all/project-level-permission/{projectId}")] + public async Task GetAllPermissionFroProject(Guid projectId) + { + Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + var response = await _projectServices.GetAllPermissionFroProjectAsync(projectId, loggedInEmployee, tenantId); + return StatusCode(response.StatusCode, response); + } [HttpGet("get/proejct-level/modules")] public async Task AssignProjectLevelModules() { diff --git a/Marco.Pms.Services/Service/ProjectServices.cs b/Marco.Pms.Services/Service/ProjectServices.cs index 7856993..4b23f5b 100644 --- a/Marco.Pms.Services/Service/ProjectServices.cs +++ b/Marco.Pms.Services/Service/ProjectServices.cs @@ -16,6 +16,7 @@ using Marco.Pms.Model.ViewModels.Master; using Marco.Pms.Model.ViewModels.Projects; using Marco.Pms.Services.Helpers; using Marco.Pms.Services.Service.ServiceInterfaces; +using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Service; using Microsoft.CodeAnalysis; using Microsoft.EntityFrameworkCore; @@ -28,26 +29,23 @@ namespace Marco.Pms.Services.Service private readonly IDbContextFactory _dbContextFactory; private readonly ApplicationDbContext _context; // Keeping this for direct scoped context use where appropriate private readonly ILoggingService _logger; - private readonly PermissionServices _permission; private readonly CacheUpdateHelper _cache; private readonly IMapper _mapper; - private readonly GeneralHelper _generalHelper; + private readonly IServiceScopeFactory _serviceScopeFactory; public ProjectServices( IDbContextFactory dbContextFactory, ApplicationDbContext context, ILoggingService logger, - PermissionServices permission, + IServiceScopeFactory serviceScopeFactory, CacheUpdateHelper cache, - IMapper mapper, - GeneralHelper generalHelper) + IMapper mapper) { _dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory)); + _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory)); _context = context ?? throw new ArgumentNullException(nameof(context)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _permission = permission ?? throw new ArgumentNullException(nameof(permission)); _cache = cache ?? throw new ArgumentNullException(nameof(cache)); _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); - _generalHelper = generalHelper ?? throw new ArgumentNullException(nameof(generalHelper)); } #region =================================================================== Project Get APIs =================================================================== @@ -87,7 +85,6 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("An internal server error occurred. Please try again later.", null, 500); } } - public async Task> GetAllProjectsAsync(Guid tenantId, Employee loggedInEmployee) { try @@ -146,11 +143,12 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("An internal server error occurred. Please try again later.", null, 500); } } - public async Task> GetProjectAsync(Guid id, Guid tenantId, Employee loggedInEmployee) { try { + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); // --- Step 1: Run independent operations in PARALLEL --- // We can check permissions and fetch data at the same time to reduce latency. var permissionTask = _permission.HasProjectPermission(loggedInEmployee, id); @@ -190,13 +188,15 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("An internal server error occurred.", null, 500); } } - public async Task> GetProjectDetailsAsync(Guid id, Guid tenantId, Employee loggedInEmployee) { try { _logger.LogInfo("Details requested by EmployeeId: {EmployeeId} for ProjectId: {ProjectId}", loggedInEmployee.Id, id); + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + // Step 1: Check global view project permission var hasViewProjectPermission = await _permission.HasPermission(PermissionsMaster.ViewProject, loggedInEmployee.Id, id); if (!hasViewProjectPermission) @@ -252,7 +252,6 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("An internal server error occurred. Please try again later.", null, 500); } } - public async Task> GetProjectDetailsOldAsync(Guid id, Guid tenantId, Employee loggedInEmployee) { var project = await _context.Projects @@ -401,6 +400,8 @@ namespace Marco.Pms.Services.Service { try { + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); // --- Step 1: Fetch the Existing Entity from the Database --- // This is crucial to avoid the data loss bug. We only want to modify an existing record. var existingProject = await _context.Projects @@ -489,6 +490,9 @@ namespace Marco.Pms.Services.Service try { + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + // --- CRITICAL: Security Check --- // Before fetching data, you MUST verify the user has permission to see it. // This is a placeholder for your actual permission logic. @@ -559,6 +563,9 @@ namespace Marco.Pms.Services.Service try { + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + // --- Step 2: Security and Existence Checks --- // Before fetching data, you MUST verify the user has permission to see it. // This is a placeholder for your actual permission logic. @@ -627,6 +634,9 @@ namespace Marco.Pms.Services.Service _logger.LogInfo("Starting to manage {AllocationCount} allocations for user {UserId}.", allocationsDto.Count, loggedInEmployee.Id); + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + // --- (Placeholder) Security Check --- // In a real application, you would check if the loggedInEmployee has permission // to manage allocations for ALL projects involved in this batch. @@ -735,6 +745,9 @@ namespace Marco.Pms.Services.Service try { + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + // --- Step 2: Clarified Security Check --- // The permission should be about viewing another employee's assignments, not a generic "Manage Team". // This is a placeholder for your actual, more specific permission logic. @@ -808,6 +821,9 @@ namespace Marco.Pms.Services.Service _logger.LogInfo("Starting to manage {AllocationCount} project assignments for Employee {EmployeeId}.", allocationsDto.Count, employeeId); + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + // --- (Placeholder) Security Check --- // You MUST verify that the loggedInEmployee has permission to modify the assignments for the target employeeId. var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageTeam, loggedInEmployee.Id); @@ -977,6 +993,9 @@ namespace Marco.Pms.Services.Service try { + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + var _generalHelper = scope.ServiceProvider.GetRequiredService(); // --- Step 1: Run independent permission checks in PARALLEL --- var projectPermissionTask = _permission.HasProjectPermission(loggedInEmployee, projectId); var viewInfraPermissionTask = _permission.HasPermission(PermissionsMaster.ViewProjectInfra, loggedInEmployee.Id, projectId); @@ -1033,6 +1052,9 @@ namespace Marco.Pms.Services.Service try { + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + var _generalHelper = scope.ServiceProvider.GetRequiredService(); // --- Step 1: Cache-First Strategy --- var cachedWorkItems = await _cache.GetWorkItemDetailsByWorkArea(workAreaId); if (cachedWorkItems != null) @@ -1268,6 +1290,9 @@ namespace Marco.Pms.Services.Service { _logger.LogInfo("CreateProjectTask called with {Count} items by user {UserId}", workItemDtos?.Count ?? 0, loggedInEmployee.Id); + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + // --- Step 1: Input Validation --- if (workItemDtos == null || !workItemDtos.Any()) { @@ -1382,7 +1407,6 @@ namespace Marco.Pms.Services.Service return ApiResponse>.SuccessResponse(responseList, message, 200); } - public async Task DeleteProjectTaskAsync(Guid id, Guid tenantId, Employee loggedInEmployee) { // 1. Fetch the task and its parent data in a single query. @@ -1483,6 +1507,9 @@ namespace Marco.Pms.Services.Service _logger.LogInfo("ManageProjectLevelPermissionAsync started for EmployeeId: {EmployeeId}, ProjectId: {ProjectId}, TenantId: {TenantId}", model.EmployeeId, model.ProjectId, tenantId); + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + var hasTeamPermission = await _permission.HasPermission(PermissionsMaster.ManageTeam, loggedInEmployee.Id, model.ProjectId); if (!hasTeamPermission) { @@ -1768,6 +1795,61 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("An error occurred while retrieving employees with project-level permissions.", 500); } } + public async Task> GetAllPermissionFroProjectAsync(Guid projectId, Employee loggedInEmployee, Guid tenantId) + { + using var scope = _serviceScopeFactory.CreateScope(); + var _rolesHelper = scope.ServiceProvider.GetRequiredService(); + // 1. Try fetching permissions from cache (fast-path lookup). + var featurePermissionIds = await _cache.GetPermissions(loggedInEmployee.Id); + + // If not found in cache, fallback to database (slower). + if (featurePermissionIds == null) + { + var featurePermissions = await _rolesHelper.GetFeaturePermissionByEmployeeId(loggedInEmployee.Id); + featurePermissionIds = featurePermissions.Select(fp => fp.Id).ToList(); + } + + // 2. Handle project-level permission overrides if a project is specified. + if (projectId != Guid.Empty) + { + // Fetch permissions explicitly assigned to this employee in the project. + var projectLevelPermissionIds = await _context.ProjectLevelPermissionMappings + .Where(pl => pl.ProjectId == projectId && pl.EmployeeId == loggedInEmployee.Id) + .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(); + } + } + return ApiResponse.SuccessResponse(featurePermissionIds, "Successfully featched the permission ids", 200); + } #endregion #region =================================================================== Helper Functions =================================================================== @@ -1803,6 +1885,9 @@ namespace Marco.Pms.Services.Service public async Task> GetMyProjects(Guid tenantId, Employee LoggedInEmployee) { + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + var projectIds = await _cache.GetProjects(LoggedInEmployee.Id); if (projectIds == null) @@ -1829,6 +1914,9 @@ namespace Marco.Pms.Services.Service public async Task> GetMyProjectIdsAsync(Guid tenantId, Employee loggedInEmployee) { + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + // 1. Attempt to retrieve the list of project IDs from the cache first. // This is the "happy path" and should be as fast as possible. List? projectIds = await _cache.GetProjects(loggedInEmployee.Id); diff --git a/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs b/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs index c95559a..4f7eb5e 100644 --- a/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs +++ b/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs @@ -40,6 +40,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces Task> GetAssignedProjectLevelPermissionAsync(Guid employeeId, Guid projectId, Guid tenantId, Employee loggedInEmployee); Task> AssignProjectLevelModulesAsync(Guid tenantId, Employee loggedInEmployee); Task> GetEmployeeToWhomProjectLevelAssignedAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee); + Task> GetAllPermissionFroProjectAsync(Guid projectId, Employee loggedInEmployee, Guid tenantId); } } From d9a1832e2e8faecf6d27b13634261b2b8c87e882 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Tue, 16 Sep 2025 15:05:32 +0530 Subject: [PATCH 02/10] updated the get project-level permissions --- Marco.Pms.Services/Service/ProjectServices.cs | 185 +++++++++--------- 1 file changed, 97 insertions(+), 88 deletions(-) diff --git a/Marco.Pms.Services/Service/ProjectServices.cs b/Marco.Pms.Services/Service/ProjectServices.cs index 4b23f5b..0fcc9e2 100644 --- a/Marco.Pms.Services/Service/ProjectServices.cs +++ b/Marco.Pms.Services/Service/ProjectServices.cs @@ -1648,31 +1648,30 @@ namespace Marco.Pms.Services.Service employeeId, projectId, tenantId, loggedInEmployee.Id); // Query the database for relevant project-level permission mappings - var permissionMappings = await _context.ProjectLevelPermissionMappings - .Include(p => p.Employee) - .ThenInclude(e => e!.JobRole) - .Include(p => p.Project) - .Include(p => p.Permission) - .ThenInclude(fp => fp!.Feature) - .AsNoTracking() - .Where(p => p.EmployeeId == employeeId - && p.ProjectId == projectId - && p.TenantId == tenantId) - .ToListAsync(); - - if (permissionMappings == null || !permissionMappings.Any()) + var employeeTask = Task.Run(async () => { - _logger.LogWarning("No project-level permissions found for EmployeeId: {EmployeeId}, ProjectId: {ProjectId}, TenantId: {TenantId}", - employeeId, projectId, tenantId); - return ApiResponse.ErrorResponse("Project-Level Permissions not found", "Project-Level Permissions not found in database", 404); - } + await using var context = await _dbContextFactory.CreateDbContextAsync(); + return await context.Employees.FirstOrDefaultAsync(e => e.Id == employeeId); + }); + var projectTask = Task.Run(async () => + { + await using var context = await _dbContextFactory.CreateDbContextAsync(); + return await context.Projects.FirstOrDefaultAsync(p => p.Id == projectId && p.TenantId == tenantId); + }); + var permissionIdsTask = Task.Run(async () => + { + return await GetPermissionIdsByProject(projectId, employeeId, tenantId); + }); - // Map the employee, project, and permissions. - var employee = _mapper.Map(permissionMappings.First().Employee); - var project = _mapper.Map(permissionMappings.First().Project); - var permissions = permissionMappings - .Select(p => _mapper.Map(p.Permission)) - .ToList(); + await Task.WhenAll(employeeTask, projectTask, permissionIdsTask); + + var employee = employeeTask.Result; + var project = projectTask.Result; + var permissionIds = permissionIdsTask.Result; + + var permissions = await _context.FeaturePermissions + .Include(fp => fp.Feature) + .Where(fp => permissionIds.Contains(fp.Id)).ToListAsync(); if (employee == null) { @@ -1685,12 +1684,26 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("Project not found", "Project not found in database", 404); } + if (permissions == null || !permissions.Any()) + { + _logger.LogWarning("No project-level permissions found for EmployeeId: {EmployeeId}, ProjectId: {ProjectId}, TenantId: {TenantId}", + employeeId, projectId, tenantId); + return ApiResponse.ErrorResponse("Project-Level Permissions not found", "Project-Level Permissions not found in database", 404); + } + + // Map the employee, project, and permissions. + var employeeVm = _mapper.Map(employee); + var projectVm = _mapper.Map(project); + var permissionVms = permissions + .Select(p => _mapper.Map(p)) + .ToList(); + // Prepare the result object. var result = new { - Employee = employee, - Project = project, - Permissions = permissions + Employee = employeeVm, + Project = projectVm, + Permissions = permissionVms }; _logger.LogInfo("Project-level permissions fetched successfully for EmployeeId: {EmployeeId}, ProjectId: {ProjectId}, TenantId: {TenantId}", @@ -1797,57 +1810,7 @@ namespace Marco.Pms.Services.Service } public async Task> GetAllPermissionFroProjectAsync(Guid projectId, Employee loggedInEmployee, Guid tenantId) { - using var scope = _serviceScopeFactory.CreateScope(); - var _rolesHelper = scope.ServiceProvider.GetRequiredService(); - // 1. Try fetching permissions from cache (fast-path lookup). - var featurePermissionIds = await _cache.GetPermissions(loggedInEmployee.Id); - - // If not found in cache, fallback to database (slower). - if (featurePermissionIds == null) - { - var featurePermissions = await _rolesHelper.GetFeaturePermissionByEmployeeId(loggedInEmployee.Id); - featurePermissionIds = featurePermissions.Select(fp => fp.Id).ToList(); - } - - // 2. Handle project-level permission overrides if a project is specified. - if (projectId != Guid.Empty) - { - // Fetch permissions explicitly assigned to this employee in the project. - var projectLevelPermissionIds = await _context.ProjectLevelPermissionMappings - .Where(pl => pl.ProjectId == projectId && pl.EmployeeId == loggedInEmployee.Id) - .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(); - } - } + var featurePermissionIds = await GetPermissionIdsByProject(projectId, loggedInEmployee.Id, tenantId); return ApiResponse.SuccessResponse(featurePermissionIds, "Successfully featched the permission ids", 200); } #endregion @@ -1859,13 +1822,11 @@ namespace Marco.Pms.Services.Service List alloc = await _context.Projects.Where(c => c.TenantId == tanentId).ToListAsync(); return alloc; } - public async Task> GetProjectByEmployeeID(Guid employeeId) { List alloc = await _context.ProjectAllocations.Where(c => c.EmployeeId == employeeId && c.IsActive == true).Include(c => c.Project).ToListAsync(); return alloc; } - public async Task> GetTeamByProject(Guid TenantId, Guid ProjectId, bool IncludeInactive) { if (IncludeInactive) @@ -1882,7 +1843,6 @@ namespace Marco.Pms.Services.Service return employees; } } - public async Task> GetMyProjects(Guid tenantId, Employee LoggedInEmployee) { using var scope = _serviceScopeFactory.CreateScope(); @@ -1911,7 +1871,6 @@ namespace Marco.Pms.Services.Service } return projectIds; } - public async Task> GetMyProjectIdsAsync(Guid tenantId, Employee loggedInEmployee) { using var scope = _serviceScopeFactory.CreateScope(); @@ -1963,7 +1922,6 @@ namespace Marco.Pms.Services.Service return newProjectIds; } - /// /// Retrieves a list of ProjectInfoVMs by their IDs, using an efficient partial cache-hit strategy. /// It fetches what it can from the cache (as ProjectMongoDB), gets the rest from the @@ -2014,7 +1972,6 @@ namespace Marco.Pms.Services.Service return finalViewModels; } - private async Task GetProjectViewModel(Guid? id, Project project) { ProjectDetailsVM vm = new ProjectDetailsVM(); @@ -2164,7 +2121,6 @@ namespace Marco.Pms.Services.Service // Map from the database entity to the response ViewModel. return dbProject; } - private async Task UpdateCacheInBackground(Project project) { try @@ -2182,7 +2138,6 @@ namespace Marco.Pms.Services.Service _logger.LogError(ex, "Background cache update failed for project {ProjectId} ", project.Id); } } - private async Task UpdateCacheAndNotify(Dictionary workDelta, List affectedItems) { try @@ -2204,7 +2159,6 @@ namespace Marco.Pms.Services.Service _logger.LogError(ex, "An error occurred during background cache update/notification."); } } - private void ProcessBuilding(BuildingDto dto, Guid tenantId, InfraVM responseData, List messages, ISet projectIds, List cacheTasks) { Building building = _mapper.Map(dto); @@ -2227,7 +2181,6 @@ namespace Marco.Pms.Services.Service responseData.building = building; projectIds.Add(building.ProjectId); } - private void ProcessFloor(FloorDto dto, Guid tenantId, InfraVM responseData, List messages, ISet projectIds, List cacheTasks, IDictionary buildings) { Floor floor = _mapper.Map(dto); @@ -2253,7 +2206,6 @@ namespace Marco.Pms.Services.Service responseData.floor = floor; if (parentBuilding != null) projectIds.Add(parentBuilding.ProjectId); } - private void ProcessWorkArea(WorkAreaDto dto, Guid tenantId, InfraVM responseData, List messages, ISet projectIds, List cacheTasks, IDictionary floors) { WorkArea workArea = _mapper.Map(dto); @@ -2280,6 +2232,63 @@ namespace Marco.Pms.Services.Service responseData.workArea = workArea; if (parentBuilding != null) projectIds.Add(parentBuilding.ProjectId); } + private async Task> GetPermissionIdsByProject(Guid projectId, Guid EmployeeId, Guid tenantId) + { + await using var context = await _dbContextFactory.CreateDbContextAsync(); + using var scope = _serviceScopeFactory.CreateScope(); + + var _rolesHelper = scope.ServiceProvider.GetRequiredService(); + // 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) + { + var featurePermissions = await _rolesHelper.GetFeaturePermissionByEmployeeId(EmployeeId); + featurePermissionIds = featurePermissions.Select(fp => fp.Id).ToList(); + } + + // 2. Handle project-level permission overrides if a project is specified. + if (projectId != Guid.Empty) + { + // Fetch permissions explicitly assigned to this employee in the project. + var projectLevelPermissionIds = await context.ProjectLevelPermissionMappings + .Where(pl => pl.ProjectId == projectId && pl.EmployeeId == EmployeeId && pl.TenantId == tenantId) + .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(); + } + } + return featurePermissionIds; + } #endregion } From 84baa3a1477e7b100fdb7867f893982ce2f22e88 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Tue, 16 Sep 2025 18:17:43 +0530 Subject: [PATCH 03/10] Added the subscription list API in Market Controller --- .../Controllers/MarketController.cs | 94 ++++++++++++++++--- 1 file changed, 83 insertions(+), 11 deletions(-) diff --git a/Marco.Pms.Services/Controllers/MarketController.cs b/Marco.Pms.Services/Controllers/MarketController.cs index e9bfde7..c9e1923 100644 --- a/Marco.Pms.Services/Controllers/MarketController.cs +++ b/Marco.Pms.Services/Controllers/MarketController.cs @@ -1,9 +1,12 @@ -using Marco.Pms.DataAccess.Data; +using AutoMapper; +using Marco.Pms.DataAccess.Data; +using Marco.Pms.Helpers.Utility; using Marco.Pms.Model.Dtos.Util; using Marco.Pms.Model.Mapper; using Marco.Pms.Model.Master; +using Marco.Pms.Model.TenantModels; using Marco.Pms.Model.Utilities; -using MarcoBMS.Services.Helpers; +using Marco.Pms.Model.ViewModels.Tenant; using MarcoBMS.Services.Service; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -14,24 +17,30 @@ namespace Marco.Pms.Services.Controllers [ApiController] public class MarketController : ControllerBase { - private readonly ApplicationDbContext _context; - private readonly UserHelper _userHelper; + private readonly IDbContextFactory _dbContextFactory; + private readonly FeatureDetailsHelper _featureDetailsHelper; + private readonly IServiceScopeFactory _serviceScopeFactory; private readonly IEmailSender _emailSender; private readonly IConfiguration _configuration; - public MarketController(ApplicationDbContext context, UserHelper userHelper, RefreshTokenService refreshTokenService, - IEmailSender emailSender, IConfiguration configuration, EmployeeHelper employeeHelper) + public MarketController(IDbContextFactory dbContextFactory, + IServiceScopeFactory serviceScopeFactory, + IEmailSender emailSender, + IConfiguration configuration, + FeatureDetailsHelper featureDetailsHelper) { - _context = context; - _userHelper = userHelper; - _emailSender = emailSender; - _configuration = configuration; + _dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory)); + _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory)); + _emailSender = emailSender ?? throw new ArgumentNullException(nameof(emailSender)); + _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + _featureDetailsHelper = featureDetailsHelper ?? throw new ArgumentNullException(nameof(featureDetailsHelper)); } [HttpGet] [Route("industries")] public async Task GetIndustries() { - var tenantId = _userHelper.GetTenantId(); + await using var _context = await _dbContextFactory.CreateDbContextAsync(); + var industries = await _context.Industries.ToListAsync(); return Ok(ApiResponse.SuccessResponse(industries, "Success.", 200)); @@ -40,6 +49,8 @@ namespace Marco.Pms.Services.Controllers [HttpPost("enquire")] public async Task RequestDemo([FromBody] InquiryDto inquiryDto) { + await using var _context = await _dbContextFactory.CreateDbContextAsync(); + Inquiries inquiry = inquiryDto.ToInquiriesFromInquiriesDto(); _context.Inquiries.Add(inquiry); await _context.SaveChangesAsync(); @@ -58,5 +69,66 @@ namespace Marco.Pms.Services.Controllers } return NotFound(ApiResponse.ErrorResponse("Industry not found.", "Industry not found.", 404)); } + + [HttpGet("list/subscription-plan")] + public async Task GetSubscriptionPlanList([FromQuery] PLAN_FREQUENCY? frequency) + { + using var scope = _serviceScopeFactory.CreateScope(); + + var _logger = scope.ServiceProvider.GetRequiredService(); + var _mapper = scope.ServiceProvider.GetRequiredService(); + _logger.LogInfo("GetSubscriptionPlanList called with frequency: {Frequency}", frequency ?? PLAN_FREQUENCY.MONTHLY); + + // Initialize the list to store subscription plan view models + List detailsVM = new List(); + + try + { + // Create DbContext + await using var _context = await _dbContextFactory.CreateDbContextAsync(); + + // Load subscription plans with optional frequency filtering + IQueryable query = _context.SubscriptionPlanDetails.Include(sp => sp.Plan).Include(sp => sp.Currency); + + if (frequency.HasValue) + { + query = query.Where(sp => sp.Frequency == frequency.Value); + _logger.LogInfo("Filtering subscription plans by frequency: {Frequency}", frequency); + } + else + { + _logger.LogInfo("Fetching all subscription plans without frequency filter"); + } + + var subscriptionPlans = await query.ToListAsync(); + + // Map and fetch feature details for each subscription plan + foreach (var subscriptionPlan in subscriptionPlans) + { + var response = _mapper.Map(subscriptionPlan); + + try + { + response.Features = await _featureDetailsHelper.GetFeatureDetails(subscriptionPlan.FeaturesId); + } + catch (Exception exFeature) + { + _logger.LogError(exFeature, "Failed to fetch features for FeaturesId: {FeaturesId}", subscriptionPlan.FeaturesId); + response.Features = null; // or set to a default/fallback value + } + + detailsVM.Add(response); + } + + _logger.LogInfo("Successfully fetched {Count} subscription plans", detailsVM.Count); + + return Ok(ApiResponse.SuccessResponse(detailsVM, "List of plans fetched successfully", 200)); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error occurred while fetching subscription plans"); + return StatusCode(500, ApiResponse.ErrorResponse("An error occurred while fetching subscription plans.")); + } + } } } From 368fd37115e3fb092d2a590c068d4f6a73b22cb8 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Wed, 17 Sep 2025 12:47:09 +0530 Subject: [PATCH 04/10] Removed the employee management from project-level permissions --- Marco.Pms.Services/Service/PermissionServices.cs | 1 - Marco.Pms.Services/Service/ProjectServices.cs | 2 -- 2 files changed, 3 deletions(-) diff --git a/Marco.Pms.Services/Service/PermissionServices.cs b/Marco.Pms.Services/Service/PermissionServices.cs index d56c8bf..979c06d 100644 --- a/Marco.Pms.Services/Service/PermissionServices.cs +++ b/Marco.Pms.Services/Service/PermissionServices.cs @@ -88,7 +88,6 @@ namespace Marco.Pms.Services.Service { 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") }; diff --git a/Marco.Pms.Services/Service/ProjectServices.cs b/Marco.Pms.Services/Service/ProjectServices.cs index 0fcc9e2..113c478 100644 --- a/Marco.Pms.Services/Service/ProjectServices.cs +++ b/Marco.Pms.Services/Service/ProjectServices.cs @@ -1728,7 +1728,6 @@ namespace Marco.Pms.Services.Service { 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") }; @@ -2265,7 +2264,6 @@ namespace Marco.Pms.Services.Service { 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") }; From 4884bf5de0e86610b6ce8bd0614fcd56b531743e Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Wed, 17 Sep 2025 13:17:38 +0530 Subject: [PATCH 05/10] Checking the manage infra permission when getting the infra --- Marco.Pms.Services/Service/ProjectServices.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Marco.Pms.Services/Service/ProjectServices.cs b/Marco.Pms.Services/Service/ProjectServices.cs index 113c478..6124bba 100644 --- a/Marco.Pms.Services/Service/ProjectServices.cs +++ b/Marco.Pms.Services/Service/ProjectServices.cs @@ -999,15 +999,20 @@ namespace Marco.Pms.Services.Service // --- Step 1: Run independent permission checks in PARALLEL --- var projectPermissionTask = _permission.HasProjectPermission(loggedInEmployee, projectId); var viewInfraPermissionTask = _permission.HasPermission(PermissionsMaster.ViewProjectInfra, loggedInEmployee.Id, projectId); + var manageInfraPermissionTask = _permission.HasPermission(PermissionsMaster.ManageProjectInfra, loggedInEmployee.Id, projectId); - await Task.WhenAll(projectPermissionTask, viewInfraPermissionTask); + await Task.WhenAll(projectPermissionTask, viewInfraPermissionTask, manageInfraPermissionTask); - if (!await projectPermissionTask) + var hasProjectPermission = projectPermissionTask.Result; + var hasViewInfraPermission = viewInfraPermissionTask.Result; + var hasManageInfraPermission = manageInfraPermissionTask.Result; + + if (!hasProjectPermission) { _logger.LogWarning("Project access denied for EmployeeId: {EmployeeId} on ProjectId: {ProjectId}", loggedInEmployee.Id, projectId); return ApiResponse.ErrorResponse("Access denied", "You don't have access to this project", 403); } - if (!await viewInfraPermissionTask) + if (!hasViewInfraPermission && !hasManageInfraPermission) { _logger.LogWarning("ViewInfra permission denied for EmployeeId: {EmployeeId}", loggedInEmployee.Id); return ApiResponse.ErrorResponse("Access denied", "You don't have access to view this project's infrastructure", 403); From d9e0c2ee574aa923a3f37b2c2744b442b87ab099 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Thu, 18 Sep 2025 11:33:59 +0530 Subject: [PATCH 06/10] Resloved the issue #1125 --- .../Controllers/EmployeeController.cs | 13 +++++++------ Marco.Pms.Services/Helpers/EmployeeHelper.cs | 6 +++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Marco.Pms.Services/Controllers/EmployeeController.cs b/Marco.Pms.Services/Controllers/EmployeeController.cs index 2926db1..37a5527 100644 --- a/Marco.Pms.Services/Controllers/EmployeeController.cs +++ b/Marco.Pms.Services/Controllers/EmployeeController.cs @@ -148,12 +148,13 @@ namespace MarcoBMS.Services.Controllers .Distinct() .ToListAsync(); - result = await _context.Employees - .Include(fp => fp.JobRole) - .Where(e => employeeIds.Contains(e.Id) && e.IsActive && e.TenantId == tenantId) - .Select(e => e.ToEmployeeVMFromEmployee()) - .Distinct() - .ToListAsync(); + var employees = await _context.Employees + .Include(fp => fp.JobRole) + .Where(e => employeeIds.Contains(e.Id) && e.JobRole != null && e.IsActive && e.TenantId == tenantId) + .Distinct() + .ToListAsync(); + + result = employees.Select(e => e.ToEmployeeVMFromEmployee()).ToList(); _logger.LogInfo("Employee list fetched using limited access (active only)."); } diff --git a/Marco.Pms.Services/Helpers/EmployeeHelper.cs b/Marco.Pms.Services/Helpers/EmployeeHelper.cs index edda815..6187628 100644 --- a/Marco.Pms.Services/Helpers/EmployeeHelper.cs +++ b/Marco.Pms.Services/Helpers/EmployeeHelper.cs @@ -85,12 +85,12 @@ namespace MarcoBMS.Services.Helpers .Distinct() .ToListAsync(); - result = await _context.Employees + var employees = await _context.Employees .Include(fp => fp.JobRole) - .Where(e => employeeIds.Contains(e.Id) && e.IsActive && e.TenantId == tenantId) - .Select(e => e.ToEmployeeVMFromEmployee()) + .Where(e => employeeIds.Contains(e.Id) && e.JobRole != null && e.IsActive && e.TenantId == tenantId) .Distinct() .ToListAsync(); + result = employees.Select(e => e.ToEmployeeVMFromEmployee()).ToList(); } else if (ShowInActive) From 0dd5633f2e08af6c04e82ce573b6c6917ebf97b5 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Thu, 18 Sep 2025 11:53:05 +0530 Subject: [PATCH 07/10] Resolved the multi-threading issue --- Marco.Pms.Services/Service/ProjectServices.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Marco.Pms.Services/Service/ProjectServices.cs b/Marco.Pms.Services/Service/ProjectServices.cs index 6124bba..e8572cf 100644 --- a/Marco.Pms.Services/Service/ProjectServices.cs +++ b/Marco.Pms.Services/Service/ProjectServices.cs @@ -995,11 +995,12 @@ namespace Marco.Pms.Services.Service { using var scope = _serviceScopeFactory.CreateScope(); var _permission = scope.ServiceProvider.GetRequiredService(); + var permission = scope.ServiceProvider.GetRequiredService(); var _generalHelper = scope.ServiceProvider.GetRequiredService(); // --- Step 1: Run independent permission checks in PARALLEL --- var projectPermissionTask = _permission.HasProjectPermission(loggedInEmployee, projectId); var viewInfraPermissionTask = _permission.HasPermission(PermissionsMaster.ViewProjectInfra, loggedInEmployee.Id, projectId); - var manageInfraPermissionTask = _permission.HasPermission(PermissionsMaster.ManageProjectInfra, loggedInEmployee.Id, projectId); + var manageInfraPermissionTask = permission.HasPermission(PermissionsMaster.ManageProjectInfra, loggedInEmployee.Id, projectId); await Task.WhenAll(projectPermissionTask, viewInfraPermissionTask, manageInfraPermissionTask); From 0ecc07777f33bb8a2c522e774b3b2a4817fc35d0 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Thu, 18 Sep 2025 12:01:08 +0530 Subject: [PATCH 08/10] Resolved the multi-threading issue --- Marco.Pms.Services/Service/ProjectServices.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Marco.Pms.Services/Service/ProjectServices.cs b/Marco.Pms.Services/Service/ProjectServices.cs index e8572cf..8fcfb26 100644 --- a/Marco.Pms.Services/Service/ProjectServices.cs +++ b/Marco.Pms.Services/Service/ProjectServices.cs @@ -995,12 +995,22 @@ namespace Marco.Pms.Services.Service { using var scope = _serviceScopeFactory.CreateScope(); var _permission = scope.ServiceProvider.GetRequiredService(); - var permission = scope.ServiceProvider.GetRequiredService(); + var _generalHelper = scope.ServiceProvider.GetRequiredService(); // --- Step 1: Run independent permission checks in PARALLEL --- var projectPermissionTask = _permission.HasProjectPermission(loggedInEmployee, projectId); - var viewInfraPermissionTask = _permission.HasPermission(PermissionsMaster.ViewProjectInfra, loggedInEmployee.Id, projectId); - var manageInfraPermissionTask = permission.HasPermission(PermissionsMaster.ManageProjectInfra, loggedInEmployee.Id, projectId); + var viewInfraPermissionTask = Task.Run(async () => + { + using var newScope = _serviceScopeFactory.CreateScope(); + var permission = newScope.ServiceProvider.GetRequiredService(); + return await permission.HasPermission(PermissionsMaster.ViewProjectInfra, loggedInEmployee.Id, projectId); + }); + var manageInfraPermissionTask = Task.Run(async () => + { + using var newScope = _serviceScopeFactory.CreateScope(); + var permission = newScope.ServiceProvider.GetRequiredService(); + return await permission.HasPermission(PermissionsMaster.ManageProjectInfra, loggedInEmployee.Id, projectId); + }); await Task.WhenAll(projectPermissionTask, viewInfraPermissionTask, manageInfraPermissionTask); From 2ce294904ba5070615a68778d4c52735fbf12957 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Thu, 18 Sep 2025 18:04:22 +0530 Subject: [PATCH 09/10] Checking the project levelmpermission in project controller --- .../Controllers/DocumentController.cs | 7 +++---- Marco.Pms.Services/Service/ProjectServices.cs | 14 +++++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Marco.Pms.Services/Controllers/DocumentController.cs b/Marco.Pms.Services/Controllers/DocumentController.cs index ef73d3c..c17fd78 100644 --- a/Marco.Pms.Services/Controllers/DocumentController.cs +++ b/Marco.Pms.Services/Controllers/DocumentController.cs @@ -1331,15 +1331,14 @@ namespace Marco.Pms.Services.Controllers } // Check if the logged in employee has permission to delete OR is the owner of the document attachment - var hasDeletePermission = await _permission.HasPermission(PermissionsMaster.DeleteDocument, loggedInEmployee.Id); - var hasViewPermission = false; + ar hasDeletePermission = false; if (ProjectEntity == documentAttachment.DocumentType?.DocumentCategory?.EntityTypeId) { - hasViewPermission = await _permission.HasPermission(PermissionsMaster.ViewDocument, loggedInEmployee.Id, documentAttachment.EntityId); + hasDeletePermission = await _permission.HasPermission(PermissionsMaster.DeleteDocument, loggedInEmployee.Id, documentAttachment.EntityId); } else if (EmployeeEntity == documentAttachment.DocumentType?.DocumentCategory?.EntityTypeId) { - hasViewPermission = await _permission.HasPermission(PermissionsMaster.ViewDocument, loggedInEmployee.Id); + hasDeletePermission = await _permission.HasPermission(PermissionsMaster.DeleteDocument, loggedInEmployee.Id); } if (!hasDeletePermission && loggedInEmployee.Id != documentAttachment.EntityId) { diff --git a/Marco.Pms.Services/Service/ProjectServices.cs b/Marco.Pms.Services/Service/ProjectServices.cs index 8fcfb26..e9817af 100644 --- a/Marco.Pms.Services/Service/ProjectServices.cs +++ b/Marco.Pms.Services/Service/ProjectServices.cs @@ -641,7 +641,8 @@ namespace Marco.Pms.Services.Service // In a real application, you would check if the loggedInEmployee has permission // to manage allocations for ALL projects involved in this batch. var projectIdsInBatch = allocationsDto.Select(a => a.ProjectId).Distinct().ToList(); - var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageTeam, loggedInEmployee.Id); + var projectId = projectIdsInBatch.FirstOrDefault(); + var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageTeam, loggedInEmployee.Id, projectId); if (!hasPermission) { _logger.LogWarning("Access DENIED for user {UserId} trying to manage allocations for projects.", loggedInEmployee.Id); @@ -826,13 +827,16 @@ namespace Marco.Pms.Services.Service // --- (Placeholder) Security Check --- // You MUST verify that the loggedInEmployee has permission to modify the assignments for the target employeeId. - var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageTeam, loggedInEmployee.Id); - if (!hasPermission) + foreach (var allocation in allocationsDto) { - _logger.LogWarning("Access DENIED for user {UserId} trying to manage assignments for employee {TargetEmployeeId}.", loggedInEmployee.Id, employeeId); - return ApiResponse>.ErrorResponse("Access Denied.", "You do not have permission to manage this employee's assignments.", 403); + if (!await _permission.HasPermission(PermissionsMaster.ManageTeam, loggedInEmployee.Id, allocation.ProjectId)) + { + _logger.LogWarning("Access DENIED for user {UserId} trying to manage assignments for employee {TargetEmployeeId}.", loggedInEmployee.Id, employeeId); + return ApiResponse>.ErrorResponse("Access Denied.", "You do not have permission to manage this employee's assignments.", 403); + } } + // --- Step 2: Fetch all relevant existing data in ONE database call --- var projectIdsInDto = allocationsDto.Select(p => p.ProjectId).ToList(); From d452faf6a9112c8cf73be4ba52a4a4b92c099345 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Thu, 18 Sep 2025 18:11:57 +0530 Subject: [PATCH 10/10] Solved the misssig key word --- Marco.Pms.Services/Controllers/DocumentController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marco.Pms.Services/Controllers/DocumentController.cs b/Marco.Pms.Services/Controllers/DocumentController.cs index c17fd78..f6ca9f9 100644 --- a/Marco.Pms.Services/Controllers/DocumentController.cs +++ b/Marco.Pms.Services/Controllers/DocumentController.cs @@ -1331,7 +1331,7 @@ namespace Marco.Pms.Services.Controllers } // Check if the logged in employee has permission to delete OR is the owner of the document attachment - ar hasDeletePermission = false; + var hasDeletePermission = false; if (ProjectEntity == documentAttachment.DocumentType?.DocumentCategory?.EntityTypeId) { hasDeletePermission = await _permission.HasPermission(PermissionsMaster.DeleteDocument, loggedInEmployee.Id, documentAttachment.EntityId);