using System.Data; using Marco.Pms.DataAccess.Data; using Marco.Pms.Model.Dtos.Employees; using Marco.Pms.Model.Dtos.Roles; using Marco.Pms.Model.Employees; using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.Mapper; using Marco.Pms.Model.Roles; using Marco.Pms.Model.Utilities; using Marco.Pms.Model.ViewModels; using Marco.Pms.Model.ViewModels.Master; using Marco.Pms.Model.ViewModels.Roles; using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Service; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; #nullable disable namespace MarcoBMS.Services.Controllers { [Route("api/[controller]")] [ApiController] [Authorize] public class RolesController : ControllerBase { private readonly ApplicationDbContext _context; private readonly RolesHelper _rolesHelper; private readonly UserHelper _userHelper; private readonly UserManager _userManager; private readonly ILoggingService _logger; public RolesController(UserManager userManager, ApplicationDbContext context, RolesHelper rolesHelper, UserHelper userHelper, ILoggingService logger) { _context = context; _userManager = userManager; _rolesHelper = rolesHelper; _userHelper = userHelper; _logger = logger; } private Guid GetTenantId() { return _userHelper.GetTenantId(); } [HttpGet("jobrole")] public async Task GetAllJobRoles() { Guid TenantId = GetTenantId(); var roles = await _context.JobRoles.Where(c => c.TenantId == TenantId).Select(x => new JobRoleVM() { Id = x.Id, Name = x.Name, Description = x.Description }).ToListAsync(); return Ok(ApiResponse.SuccessResponse(roles, "Success.", 200)); } [HttpPost("jobrole")] public async Task AddJobRole([FromBody] CreateJobRoleDto createJobRoleDto) { Guid TenantId = GetTenantId(); if (await _context.JobRoles.AnyAsync(c => c.Name.ToLower() == createJobRoleDto.Name.ToLower() && c.TenantId == TenantId)) { return BadRequest(ApiResponse.ErrorResponse("Role with same name already Exists.", "Role with same name already Exists.", 400)); } else { JobRole jr = createJobRoleDto.ToJobRoleFromCreateJobRoleDot(TenantId); _context.JobRoles.Add(jr); await _context.SaveChangesAsync(); return Ok(ApiResponse.SuccessResponse(jr, "Success.", 200)); } } [HttpPut("jobrole/{id}")] public async Task UpdateJobRole(string id, [FromBody] UpdateJobRoleDto updateRoleDto) { if (!ModelState.IsValid) { var errors = ModelState.Values .SelectMany(v => v.Errors) .Select(e => e.ErrorMessage) .ToList(); return BadRequest(ApiResponse.ErrorResponse("Invalid data", errors, 400)); } if (id != updateRoleDto.Id.ToString()) return BadRequest(ApiResponse.ErrorResponse("Role ID mismatch", "Role ID mismatch", 400)); try { Guid TenantId = GetTenantId(); JobRole jr = updateRoleDto.ToJobRoleFromUpdateJobRoleDot(TenantId); _context.JobRoles.Update(jr); await _context.SaveChangesAsync(); return Ok(ApiResponse.SuccessResponse(jr, "Success.", 200)); } catch (Exception ex) { return BadRequest(ApiResponse.ErrorResponse(ex.Message, ex, 400)); } } [HttpDelete("jobrole/{id}")] public async Task DeleteJobRole(Guid id) { Guid tenantId = GetTenantId(); var LoggedEmployee = await _userHelper.GetCurrentEmployeeAsync(); var jobRole = await _context.JobRoles.FirstOrDefaultAsync(r => r.Id == id && r.TenantId == tenantId); if (jobRole != null) { var employee = await _context.Employees.Where(e => e.JobRoleId == jobRole.Id).ToListAsync(); if (employee.Any()) { _logger.LogWarning("Employee with ID {LoggedEmployeeId} tries to delete the job role with ID {JobRoleId} which is assigned to one or more employees", LoggedEmployee.Id, jobRole.Id); return BadRequest(ApiResponse.ErrorResponse("Cannot delete the job role because it is assigned to one or more employees.", "Cannot delete the job role because it is assigned to one or more employees.", 400)); } _context.JobRoles.Remove(jobRole); await _context.SaveChangesAsync(); _logger.LogInfo("Employee with ID {LoggedEmployeeId} deleted the job role with ID {JobRoleId}", LoggedEmployee.Id, jobRole.Id); } else { _logger.LogWarning("Job role with ID {JobRoleId} not found in database", id); } return Ok(ApiResponse.SuccessResponse(new { }, "Job role deleted successfully", 200)); } [HttpGet] public async Task GetAllRoles() { Guid TenantId = GetTenantId(); var roles = await _context.ApplicationRoles.Where(c => c.TenantId == TenantId).ToListAsync(); var roleFeaturePermissions = await _context.RolePermissionMappings .Join( _context.FeaturePermissions, rfp => rfp.FeaturePermissionId, fp => fp.Id, (rfp, fp) => new { rfp.ApplicationRoleId, FeaturePermission = fp }) .Join( _context.ApplicationRoles, result => result.ApplicationRoleId, role => role.Id, (result, role) => new { Role = role, result.FeaturePermission }) .Where(x => x.Role.TenantId == TenantId) // Filter by TenantId .Select(x => new { RoleId = x.Role.Id, RoleName = x.Role.Role, FeaturePermission = x.FeaturePermission }) .OrderByDescending(r => r.RoleName) .ToListAsync(); List applicationRoles = new List(); foreach (var item in roles) { var rolesVM = new ApplicationRolesVM() { Id = item.Id, Role = item.Role, IsSystem = item.IsSystem, Description = item.Description, FeaturePermission = [] }; ICollection permissions = roleFeaturePermissions.Where(c => c.RoleId == item.Id) .Select(c => c.FeaturePermission).ToList(); foreach (var permission in permissions) { rolesVM.FeaturePermission.Add( new FeaturePermissionVM() { Id = permission.Id, Description = permission.Description, FeatureId = permission.FeatureId, IsEnabled = permission.IsEnabled, Name = permission.Name }); } applicationRoles.Add(rolesVM); } return Ok(ApiResponse.SuccessResponse(applicationRoles, "Roles list fetched successfully.", 200)); } [HttpPost] public async Task AddRole([FromBody] CreateApplicationRoleDto createRoleDto) { if (!ModelState.IsValid) { var errors = ModelState.Values .SelectMany(v => v.Errors) .Select(e => e.ErrorMessage) .ToList(); return BadRequest(ApiResponse.ErrorResponse("Invalid data", errors, 400)); } Guid TenantId = GetTenantId(); if (createRoleDto.FeaturesPermission == null || (createRoleDto.FeaturesPermission != null && createRoleDto.FeaturesPermission.Count == 0)) { return BadRequest(ApiResponse.ErrorResponse("Feature Permission is required.", "Feature Permission is required.", 400)); } bool roleExists = _context.ApplicationRoles .Any(r => r.TenantId == TenantId && r.Role.ToLower() == createRoleDto.Role.ToLower());// assuming role name is unique per tenant if (roleExists) { return BadRequest(ApiResponse.ErrorResponse("Role already exists.", "Role already exists.", 400)); } ApplicationRole role = createRoleDto.ToApplicationRoleFromCreateDto(TenantId); _context.ApplicationRoles.Add(role); foreach (var permission in createRoleDto.FeaturesPermission) { var item = new RolePermissionMappings() { ApplicationRoleId = role.Id, FeaturePermissionId = permission.Id }; bool assigned = _context.RolePermissionMappings.Any(c => c.ApplicationRoleId == role.Id && c.FeaturePermissionId == permission.Id); if (permission.IsEnabled && !assigned) _context.RolePermissionMappings.Add(item); else _context.RolePermissionMappings.Remove(item); } await _context.SaveChangesAsync(); return Ok(ApiResponse.SuccessResponse(role.ToRoleVMFromApplicationRole(), "Roles created successfully.", 200)); } [HttpPut("{id}")] public async Task UpdateRole(Guid id, [FromBody] UpdateApplicationRoleDto updateRoleDto) { if (!ModelState.IsValid) { var errors = ModelState.Values .SelectMany(v => v.Errors) .Select(e => e.ErrorMessage) .ToList(); return BadRequest(ApiResponse.ErrorResponse("Invalid data", errors, 400)); } if (id != updateRoleDto.Id) return BadRequest("Role ID mismatch"); try { Guid TenantId = GetTenantId(); var LoggedEmployee = await _userHelper.GetCurrentEmployeeAsync(); var existingRole = await _context.ApplicationRoles.AsNoTracking().FirstOrDefaultAsync(r => r.Id == id); if (existingRole != null && existingRole.IsSystem) { _logger.LogWarning("Employee with ID {LoggedEmployeeId} tries to update System-defined application roles {AppcationRoleId}", LoggedEmployee.Id, existingRole.Id); return BadRequest(ApiResponse.ErrorResponse("System-defined roles cannot be updated", "System-defined roles cannot be updated", 400)); } ApplicationRole role = updateRoleDto.ToApplicationRoleFromUpdateDto(TenantId); if (role.TenantId != TenantId) return Unauthorized(ApiResponse.ErrorResponse("You don't have any authority to update role", "You don't have any authority to update role", 401)); if (existingRole != null) { _context.ApplicationRoles.Update(role); await _context.SaveChangesAsync(); } bool modified = false; foreach (var permission in updateRoleDto.FeaturesPermission) { var item = new RolePermissionMappings() { ApplicationRoleId = role.Id, FeaturePermissionId = permission.Id }; bool assigned = _context.RolePermissionMappings.Any(c => c.ApplicationRoleId == role.Id && c.FeaturePermissionId == permission.Id); if (permission.IsEnabled == false && assigned == true) { _context.RolePermissionMappings.Remove(item); modified = true; } else if (permission.IsEnabled && !assigned) { _context.RolePermissionMappings.Add(item); modified = true; } } if (modified) await _context.SaveChangesAsync(); ApplicationRolesVM response = role.ToRoleVMFromApplicationRole(); List permissions = await _rolesHelper.GetFeaturePermissionByRoleID(response.Id); response.FeaturePermission = permissions.Select(c => c.ToFeaturePermissionVMFromFeaturePermission()).ToList(); return Ok(ApiResponse.SuccessResponse(response, "Roles perimssions updated.", 200)); } catch (Exception ex) { var response = new { message = ex.Message, detail = ex.StackTrace, statusCode = StatusCodes.Status500InternalServerError }; return BadRequest(ApiResponse.ErrorResponse(ex.Message, response, 400)); } } [HttpGet("{id}")] public async Task GetRoleById(Guid id) { Guid TenantId = GetTenantId(); var role = await _context.ApplicationRoles.FindAsync(id); if (role == null) return NotFound(ApiResponse.ErrorResponse("Role not found", "Role not found", 404)); if (role.TenantId != TenantId) return Unauthorized(ApiResponse.ErrorResponse("You don't have any authority", "You don't have any authority", 401)); var featurePermissions = await _context.RolePermissionMappings .Where(rfp => rfp.ApplicationRoleId == id) .Join( _context.FeaturePermissions, rfp => rfp.FeaturePermissionId, fp => fp.Id, (rfp, fp) => new FeaturePermissionVM() { Id = fp.Id, Name = fp.Name, Description = fp.Description, IsEnabled = fp.IsEnabled, FeatureId = fp.FeatureId }) .ToListAsync(); ApplicationRolesVM vm = new ApplicationRolesVM() { Id = role.Id, Role = role.Role, IsSystem = role.IsSystem, FeaturePermission = featurePermissions }; return Ok(ApiResponse.SuccessResponse(vm, "Roles Perimssions fetched successfully.", 200)); } [HttpDelete("{id}")] public async Task DeleteApplicationRole(Guid id) { Guid tenantId = GetTenantId(); var LoggedEmployee = await _userHelper.GetCurrentEmployeeAsync(); var role = await _context.ApplicationRoles.AsNoTracking().FirstOrDefaultAsync(r => r.Id == id && r.TenantId == tenantId); if (role != null) { if (role.IsSystem) { _logger.LogInfo("Employee with ID {LoggedEmployeeId} tries to delete system-defined application role with ID {ApplicationRoleId}", LoggedEmployee.Id, role.Id); return BadRequest(ApiResponse.ErrorResponse("This role cannot be deleted because it is system-defined.", "This role cannot be deleted because it is system-defined.", 400)); } var employeeRoleMapping = await _context.EmployeeRoleMappings.Where(erm => erm.RoleId == role.Id).ToListAsync(); if (employeeRoleMapping.Count != 0) { _logger.LogInfo("Employee with ID {LoggedEmployeeId} tries to delete application role with ID {ApplicationRoleId} with is assigned to an employee", LoggedEmployee.Id, role.Id); return BadRequest(ApiResponse.ErrorResponse("This role cannot be deleted because it is currently assigned to employees.", "This role cannot be deleted because it is currently assigned to employees.", 400)); } _context.ApplicationRoles.Remove(role); var rolePermissionMapping = await _context.RolePermissionMappings.Where(r => r.ApplicationRoleId == role.Id).ToListAsync(); if (rolePermissionMapping.Count != 0) { _context.RolePermissionMappings.RemoveRange(rolePermissionMapping); _logger.LogInfo("All permissions assigned to the application role with ID {ApplicationRoleId} have been removed.", role.Id); } await _context.SaveChangesAsync(); _logger.LogInfo("Employee with ID {LoggedEmployeeId} deleted application role with ID {ApplicationRoleId}", LoggedEmployee.Id, role.Id); } else { _logger.LogWarning("Application role with ID {ApplicationRoleId} not found in database", id); } return Ok(ApiResponse.SuccessResponse(new { }, "Application role is deleted successfully", 200)); } [HttpPost] [Route("assign-roles")] public async Task ManageRoles([FromBody] List employeeRoleDots) { if (!ModelState.IsValid) { var errors = ModelState.Values .SelectMany(v => v.Errors) .Select(e => e.ErrorMessage) .ToList(); return BadRequest(ApiResponse.ErrorResponse("Invalid data", errors, 400)); } var employeesIds = employeeRoleDots.Select(e => e.EmployeeId).Distinct().ToList(); var LoggedEmployee = await _userHelper.GetCurrentEmployeeAsync(); var employees = await _context.Employees.Where(e => employeesIds.Contains(e.Id)).ToListAsync(); Guid TenantId = GetTenantId(); try { foreach (EmployeeRoleDot role in employeeRoleDots) { var employee = employees.Find(e => e.Id == role.EmployeeId && e.IsSystem); if (employee != null) { _logger.LogWarning("Employee with ID {LoggedEmployeeId} tries to assign or remove the application role to System-defined employee with ID {EmployeeId}", LoggedEmployee.Id, employee.Id); return BadRequest(ApiResponse.ErrorResponse("System-defined employee cannot have application roles assigned or removed.", "System-defined employee cannot have application roles assigned or removed.", 400)); } EmployeeRoleMapping mapping = role.ToEmployeeRoleMappingFromEmployeeRoleDot(TenantId); var existingItem = await _context.EmployeeRoleMappings.AsNoTracking().SingleOrDefaultAsync(c => c.Id == mapping.Id); if (existingItem == null) { if (role.IsEnabled == true) { _context.EmployeeRoleMappings.Add(mapping); } } else if (role.IsEnabled == false) { _context.EmployeeRoleMappings.Remove(existingItem); } } await _context.SaveChangesAsync(); } catch (Exception ex) { return BadRequest(ApiResponse.ErrorResponse(ex.Message, ex, 400)); } return Ok(ApiResponse.SuccessResponse("success", "Roles modified.", 200)); } } }