Merge pull request 'Ashutosh_Enhancement#198_Added_IsSystem' (#39) from Ashutosh_Enhancement#198_Added_IsSystem into Issues_May_2W

Reviewed-on: #39
This commit is contained in:
Vikas Nale 2025-05-08 10:22:14 +00:00
commit 141d001673
12 changed files with 2684 additions and 101 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_IsSystem_Flag : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsSystem",
table: "Employees",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<bool>(
name: "IsSystem",
table: "ApplicationRoles",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsSystem",
table: "Employees");
migrationBuilder.DropColumn(
name: "IsSystem",
table: "ApplicationRoles");
}
}
}

View File

@ -342,6 +342,9 @@ namespace Marco.Pms.DataAccess.Migrations
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsSystem")
.HasColumnType("tinyint(1)");
b.Property<Guid?>("JobRoleId")
.HasColumnType("char(36)");
@ -468,6 +471,9 @@ namespace Marco.Pms.DataAccess.Migrations
b.Property<string>("Description")
.HasColumnType("longtext");
b.Property<bool>("IsSystem")
.HasColumnType("tinyint(1)");
b.Property<string>("Role")
.HasColumnType("longtext");

View File

@ -39,6 +39,8 @@ namespace Marco.Pms.Model.Employees
public bool IsActive { get; set; } = true;
public bool IsSystem { get; set; } = false;
public Guid RoleId { get; set; }
//[ForeignKey(nameof(RoleId))]
//public EmployeeRole EmployeeRole { get; set; }

View File

@ -5,6 +5,7 @@
public Guid Id { get; set; }
public string? Role { get; set; }
public string? Description { get; set; }
public bool IsSystem { get; set; } = false;
public Guid TenantId { get; set; }

View File

@ -12,6 +12,7 @@ namespace Marco.Pms.Model.Mapper
{
Id = model.Id,
Role = model.Role,
IsSystem = model.IsSystem,
Description = model.Description,
//FeaturePermission = model.FeaturePermissions
};

View File

@ -29,6 +29,7 @@ namespace Marco.Pms.Model.Mapper
PhoneNumber = model.PhoneNumber,
Photo = model.Photo,
IsActive = model.IsActive,
IsSystem = model.IsSystem,
JoiningDate = model.JoiningDate
};
}

View File

@ -5,6 +5,7 @@
public Guid Id { get; set; }
public string? Role { get; set; }
public string? Description { get; set; }
public bool IsSystem { get; set; }
public ICollection<FeaturePermissionVM>? FeaturePermission { get; set; }
}
}

View File

@ -28,6 +28,7 @@
public string? ApplicationUserId { get; set; }
public Guid? JobRoleId { get; set; }
public bool IsSystem { get; set; }
public string? JobRole { get; set; }
}

View File

@ -85,50 +85,7 @@ namespace MarcoBMS.Services.Controllers
}
}
[HttpPost]
[Route("roles")]
public async Task<IActionResult> ManageRoles([FromBody] List<EmployeeRoleDot> employeeRoleDots)
{
if (!ModelState.IsValid)
{
var errors = ModelState.Values
.SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage)
.ToList();
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
}
Guid TenantId = GetTenantId();
try
{
foreach (EmployeeRoleDot role in employeeRoleDots)
{
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<object>.ErrorResponse(ex.Message, ex, 400));
}
return Ok(ApiResponse<object>.SuccessResponse("success", "Roles modified.", 200));
}
[HttpGet]
[Route("list/{projectid?}")]
@ -323,66 +280,75 @@ namespace MarcoBMS.Services.Controllers
public async Task<IActionResult> SuspendEmployee(Guid id)
{
Guid tenantId = _userHelper.GetTenantId();
Employee? employee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == id && e.TenantId == tenantId);
var LoggedEmployee = await _userHelper.GetCurrentEmployeeAsync();
Employee? employee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == id && e.IsActive && e.TenantId == tenantId);
if (employee != null)
{
var assignedToTasks = await _context.TaskMembers.Where(t => t.EmployeeId == employee.Id).ToListAsync();
if (assignedToTasks.Count != 0)
if (employee.IsSystem)
{
List<Guid> taskIds = assignedToTasks.Select(t => t.TaskAllocationId).ToList();
var tasks = await _context.TaskAllocations.Where(t => taskIds.Contains(t.Id)).ToListAsync();
foreach (var assignedToTask in assignedToTasks)
_logger.LogWarning("Employee with ID {LoggedEmployeeId} tries to suspend system-defined employee with ID {EmployeeId}", LoggedEmployee.Id, employee.Id);
return BadRequest(ApiResponse<object>.ErrorResponse("System-defined employees cannot be suspended.", "System-defined employees cannot be suspended.", 400));
}
else
{
var assignedToTasks = await _context.TaskMembers.Where(t => t.EmployeeId == employee.Id).ToListAsync();
if (assignedToTasks.Count != 0)
{
var task = tasks.Find(t => t.Id == assignedToTask.TaskAllocationId);
if (task != null && task.CompletedTask == 0)
List<Guid> taskIds = assignedToTasks.Select(t => t.TaskAllocationId).ToList();
var tasks = await _context.TaskAllocations.Where(t => taskIds.Contains(t.Id)).ToListAsync();
foreach (var assignedToTask in assignedToTasks)
{
_logger.LogWarning("Employee with ID {EmployeeId} is currently assigned to any incomplete task", employee.Id);
return BadRequest(ApiResponse<object>.ErrorResponse("Employee is currently assigned to any incomplete task", "Employee is currently assigned to any incomplete task", 400));
var task = tasks.Find(t => t.Id == assignedToTask.TaskAllocationId);
if (task != null && task.CompletedTask == 0)
{
_logger.LogWarning("Employee with ID {EmployeeId} is currently assigned to any incomplete task", employee.Id);
return BadRequest(ApiResponse<object>.ErrorResponse("Employee is currently assigned to any incomplete task", "Employee is currently assigned to any incomplete task", 400));
}
}
}
}
var attendance = await _context.Attendes.Where(a => a.EmployeeID == employee.Id && (a.OutTime == null || a.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE)).ToListAsync();
if (attendance.Count != 0)
{
_logger.LogWarning("Employee with ID {EmployeeId} have any pending check-out or regularization requests", employee.Id);
return BadRequest(ApiResponse<object>.ErrorResponse("Employee have any pending check-out or regularization requests", "Employee have any pending check-out or regularization requests", 400));
}
employee.IsActive = false;
var projectAllocations = await _context.ProjectAllocations.Where(a => a.EmployeeId == employee.Id).ToListAsync();
if (projectAllocations.Count != 0)
{
List<ProjectAllocation> allocations = new List<ProjectAllocation>();
foreach (var projectAllocation in projectAllocations)
var attendance = await _context.Attendes.Where(a => a.EmployeeID == employee.Id && (a.OutTime == null || a.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE)).ToListAsync();
if (attendance.Count != 0)
{
projectAllocation.ReAllocationDate = DateTime.UtcNow;
projectAllocation.IsActive = false;
allocations.Add(projectAllocation);
_logger.LogWarning("Employee with ID {EmployeeId} have any pending check-out or regularization requests", employee.Id);
return BadRequest(ApiResponse<object>.ErrorResponse("Employee have any pending check-out or regularization requests", "Employee have any pending check-out or regularization requests", 400));
}
_logger.LogInfo("Employee with ID {EmployeeId} has been removed from all assigned projects.", employee.Id);
}
var user = await _context.ApplicationUsers.FirstOrDefaultAsync(u => u.Id == employee.ApplicationUserId);
if (user != null)
{
user.IsActive = false;
_logger.LogInfo("The application user associated with employee ID {EmployeeId} has been suspended.", employee.Id);
var refreshTokens = await _context.RefreshTokens.AsNoTracking().Where(t => t.UserId == user.Id).ToListAsync();
if (refreshTokens.Count != 0)
employee.IsActive = false;
var projectAllocations = await _context.ProjectAllocations.Where(a => a.EmployeeId == employee.Id).ToListAsync();
if (projectAllocations.Count != 0)
{
_context.RefreshTokens.RemoveRange(refreshTokens);
_logger.LogInfo("Refresh tokens associated with employee ID {EmployeeId} has been removed.", employee.Id);
List<ProjectAllocation> allocations = new List<ProjectAllocation>();
foreach (var projectAllocation in projectAllocations)
{
projectAllocation.ReAllocationDate = DateTime.UtcNow;
projectAllocation.IsActive = false;
allocations.Add(projectAllocation);
}
_logger.LogInfo("Employee with ID {EmployeeId} has been removed from all assigned projects.", employee.Id);
}
var user = await _context.ApplicationUsers.FirstOrDefaultAsync(u => u.Id == employee.ApplicationUserId);
if (user != null)
{
user.IsActive = false;
_logger.LogInfo("The application user associated with employee ID {EmployeeId} has been suspended.", employee.Id);
var refreshTokens = await _context.RefreshTokens.AsNoTracking().Where(t => t.UserId == user.Id).ToListAsync();
if (refreshTokens.Count != 0)
{
_context.RefreshTokens.RemoveRange(refreshTokens);
_logger.LogInfo("Refresh tokens associated with employee ID {EmployeeId} has been removed.", employee.Id);
}
}
var roleMapping = await _context.EmployeeRoleMappings.AsNoTracking().Where(r => r.EmployeeId == employee.Id).ToListAsync();
if (roleMapping.Count != 0)
{
_context.EmployeeRoleMappings.RemoveRange(roleMapping);
_logger.LogInfo("Application role mapping associated with employee ID {EmployeeId} has been removed.", employee.Id);
}
await _context.SaveChangesAsync();
_logger.LogInfo("Employee with ID {EmployeId} Deleted successfully", employee.Id);
}
var roleMapping = await _context.EmployeeRoleMappings.AsNoTracking().Where(r => r.EmployeeId == employee.Id).ToListAsync();
if (roleMapping.Count != 0)
{
_context.EmployeeRoleMappings.RemoveRange(roleMapping);
_logger.LogInfo("Application role mapping associated with employee ID {EmployeeId} has been removed.", employee.Id);
}
await _context.SaveChangesAsync();
_logger.LogInfo("Employee with ID {EmployeId} Deleted successfully", employee.Id);
}
else
{

View File

@ -1,11 +1,13 @@
using System.Data;
using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Dtos.Employees;
using Marco.Pms.Model.Dtos.Roles;
using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.Mapper;
using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels;
using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
@ -21,16 +23,16 @@ namespace MarcoBMS.Services.Controllers
private readonly ApplicationDbContext _context;
private readonly RolesHelper _rolesHelper;
private readonly UserHelper _userHelper;
private readonly UserManager<ApplicationUser> _userManager;
private readonly ILoggingService _logger;
public RolesController(UserManager<ApplicationUser> userManager, ApplicationDbContext context, RolesHelper rolesHelper, UserHelper userHelper)
public RolesController(UserManager<ApplicationUser> userManager, ApplicationDbContext context, RolesHelper rolesHelper, UserHelper userHelper, ILoggingService logger)
{
_context = context;
_userManager = userManager;
_rolesHelper = rolesHelper;
_userHelper = userHelper;
_logger = logger;
}
private Guid GetTenantId()
@ -99,7 +101,30 @@ namespace MarcoBMS.Services.Controllers
return BadRequest(ApiResponse<object>.ErrorResponse(ex.Message, ex, 400));
}
}
[HttpDelete("jobrole/{id}")]
public async Task<IActionResult> 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<object>.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<object>.SuccessResponse(new { }, "Job role deleted successfully", 200));
}
[HttpGet]
public async Task<IActionResult> GetAllRoles()
@ -137,6 +162,7 @@ namespace MarcoBMS.Services.Controllers
{
Id = item.Id,
Role = item.Role,
IsSystem = item.IsSystem,
Description = item.Description,
FeaturePermission = []
};
@ -225,17 +251,21 @@ namespace MarcoBMS.Services.Controllers
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<object>.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<object>.ErrorResponse("You don't have any authority to update role", "You don't have any authority to update role", 401));
var projectModel = _context.ApplicationRoles.Update(role);
if (projectModel == null)
if (existingRole != null)
{
return NotFound(ApiResponse<object>.ErrorResponse("Project not found", "Project not found", 404));
_context.ApplicationRoles.Update(role);
await _context.SaveChangesAsync();
}
bool modified = false;
@ -265,7 +295,13 @@ namespace MarcoBMS.Services.Controllers
}
catch (Exception ex)
{
return BadRequest(ApiResponse<object>.ErrorResponse(ex.Message, ex, 400));
var response = new
{
message = ex.Message,
detail = ex.StackTrace,
statusCode = StatusCodes.Status500InternalServerError
};
return BadRequest(ApiResponse<object>.ErrorResponse(ex.Message, response, 400));
}
}
@ -303,11 +339,101 @@ namespace MarcoBMS.Services.Controllers
{
Id = role.Id,
Role = role.Role,
IsSystem = role.IsSystem,
FeaturePermission = featurePermissions
};
return Ok(ApiResponse<object>.SuccessResponse(vm, "Roles Perimssions fetched successfully.", 200));
}
[HttpDelete("{id}")]
public async Task<IActionResult> 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<object>.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<object>.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<object>.SuccessResponse(new { }, "Application role is deleted successfully", 200));
}
[HttpPost]
[Route("assign-roles")]
public async Task<IActionResult> ManageRoles([FromBody] List<EmployeeRoleDot> employeeRoleDots)
{
if (!ModelState.IsValid)
{
var errors = ModelState.Values
.SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage)
.ToList();
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
}
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<object>.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<object>.ErrorResponse(ex.Message, ex, 400));
}
return Ok(ApiResponse<object>.SuccessResponse("success", "Roles modified.", 200));
}
}
}

View File

@ -63,6 +63,7 @@ namespace Marco.Pms.Services.Service
{
Role = "Super User",
Description = "Super User",
IsSystem = true,
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
};
if (!await dbContext.ApplicationRoles.Where(a => a.Role == "Super User").AnyAsync())
@ -93,6 +94,7 @@ namespace Marco.Pms.Services.Service
PhoneNumber = "9876543210",
Photo = null, // GetFileDetails(model.Photo).Result.FileData,
JobRoleId = jobRole != null ? jobRole.Id : Guid.Empty,
IsSystem = true,
JoiningDate = Convert.ToDateTime("2000-04-20 10:11:17.588000"),
};
if ((!await dbContext.Employees.Where(e => e.FirstName == "Admin").AnyAsync()) && (jobRole != null ? jobRole.Id : Guid.Empty) != Guid.Empty)