using System.Data; using System.Net; using Marco.Pms.DataAccess.Data; using Marco.Pms.Model.Dtos.Attendance; using Marco.Pms.Model.Dtos.Employees; using Marco.Pms.Model.Employees; using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.Mapper; using Marco.Pms.Model.Projects; using Marco.Pms.Model.Utilities; using Marco.Pms.Model.ViewModels; using Marco.Pms.Model.ViewModels.Employee; using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Service; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace MarcoBMS.Services.Controllers { [Route("api/[controller]")] [ApiController] [Authorize] public class EmployeeController : ControllerBase { private readonly ApplicationDbContext _context; private readonly UserManager _userManager; private readonly IEmailSender _emailSender; private readonly EmployeeHelper _employeeHelper; private readonly UserHelper _userHelper; private readonly IConfiguration _configuration; private readonly ILoggingService _logger; public EmployeeController(UserManager userManager, IEmailSender emailSender, ApplicationDbContext context, EmployeeHelper employeeHelper, UserHelper userHelper, IConfiguration configuration, ILoggingService logger) { _context = context; _userManager = userManager; _emailSender = emailSender; _employeeHelper = employeeHelper; _userHelper = userHelper; _configuration = configuration; _logger = logger; } [HttpGet] [Route("roles/{employeeId?}")] public async Task GetRoles(Guid employeeId) { 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(); var empRoles = await _context.EmployeeRoleMappings.Where(c => c.EmployeeId == employeeId).Include(c => c.Role).Include(c => c.Employee).ToListAsync(); if (empRoles.Any()) { List roles = new List(); foreach (EmployeeRoleMapping mapping in empRoles) { roles.Add(new EmployeeRolesVM() { Id = mapping.Id, EmployeeId = mapping.EmployeeId, Name = mapping.Role != null ? mapping.Role.Role : null, Description = mapping.Role != null ? mapping.Role.Description : null, IsEnabled = mapping.IsEnabled, RoleId = mapping.RoleId, }); } return Ok(ApiResponse.SuccessResponse(roles, "Success.", 200)); } else { return BadRequest(ApiResponse.ErrorResponse("This employee has no assigned permissions.", "This employee has no assigned permissions.", 400)); } } [HttpPost] [Route("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)); } 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.ErrorResponse(ex.Message, ex, 400)); } return Ok(ApiResponse.SuccessResponse("success", "Roles modified.", 200)); } [HttpGet] [Route("list/{projectid?}")] public async Task GetEmployeesByProject(Guid? projectid, [FromQuery] bool ShowInactive) { 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 result = await _employeeHelper.GetEmployeeByProjectId(GetTenantId(), projectid, ShowInactive); return Ok(ApiResponse.SuccessResponse(result, "Filter applied.", 200)); } [HttpGet] [Route("search/{name}/{projectid?}")] public async Task SearchEmployee(string name, Guid? projectid) { 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 result = await _employeeHelper.SearchEmployeeByProjectId(GetTenantId(), name.ToLower(), projectid); return Ok(ApiResponse.SuccessResponse(result, "Filter applied.", 200)); } [HttpGet] [Route("profile/get/{employeeId}")] public async Task GetEmployeeProfileById(Guid employeeId) { if (!ModelState.IsValid) { var errors = ModelState.Values .SelectMany(v => v.Errors) .Select(e => e.ErrorMessage) .ToList(); return BadRequest(ApiResponse.ErrorResponse("Invalid data", errors, 400)); } Employee emp = await _employeeHelper.GetEmployeeByID(employeeId); EmployeeVM employeeVM = EmployeeMapper.ToEmployeeVMFromEmployee(emp); return Ok(ApiResponse.SuccessResponse(employeeVM, "Employee Profile.", 200)); } private Guid GetTenantId() { return _userHelper.GetTenantId(); } //[HttpPost("manage/quick")] //public async Task CreateQuickUser([FromBody] CreateQuickUserDto model) //{ // return Ok("Pending implementation"); //} [HttpPost("manage")] public async Task CreateUser([FromForm] CreateUserDto model) { Guid tenantId = _userHelper.GetTenantId(); if (model == null) return BadRequest(ApiResponse.ErrorResponse("Invalid data", "Invaild Data", 400)); string responsemessage = ""; if (model.Email != null) { // Check if user already exists by email IdentityUser? existingUser = await _userHelper.GetRegisteredUser(model.Email); var existingEmployee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == model.Id && e.IsActive == true); var demo = existingUser != new IdentityUser(); if (existingUser != null) { /* Identity user Exists - Create/update employee Employee */ // Update Employee record existingEmployee = await _context.Employees.FirstOrDefaultAsync(e => e.Email == model.Email && e.Id == model.Id && e.IsActive == true); if (existingEmployee != null) { existingEmployee = GetUpdateEmployeeModel(model, existingEmployee, existingUser); _context.Employees.Update(existingEmployee); await _context.SaveChangesAsync(); responsemessage = "User updated successfully."; } else { // Create Employee record if missing //Employee newEmployee = GetNewEmployeeModel(model, TenantId, existingUser.Id); //_context.Employees.Add(newEmployee); return Conflict(ApiResponse.ErrorResponse("Email already exist", "Email already exist", 409)); } } else { var user = new ApplicationUser { UserName = model.Email, Email = model.Email, EmailConfirmed = true, TenantId = tenantId }; // Create Identity User var result = await _userManager.CreateAsync(user, "User@123"); if (!result.Succeeded) return Ok(ApiResponse.ErrorResponse("Failed to create user", result.Errors, 400)); if (existingEmployee == null) { Employee newEmployee = GetNewEmployeeModel(model, tenantId, user.Id); _context.Employees.Add(newEmployee); await _context.SaveChangesAsync(); /* SEND USER REGISTRATION MAIL*/ var token = await _userManager.GeneratePasswordResetTokenAsync(user); var resetLink = $"{_configuration["AppSettings:WebFrontendUrl"]}/reset-password?token={WebUtility.UrlEncode(token)}"; if (newEmployee.FirstName != null) { await _emailSender.SendResetPasswordEmailOnRegister(user.Email, newEmployee.FirstName, resetLink); } } else { existingEmployee.Email = model.Email; existingEmployee = GetUpdateEmployeeModel(model, existingEmployee, existingUser); _context.Employees.Update(existingEmployee); await _context.SaveChangesAsync(); /* SEND USER REGISTRATION MAIL*/ var token = await _userManager.GeneratePasswordResetTokenAsync(user); var resetLink = $"{_configuration["AppSettings:WebFrontendUrl"]}/reset-password?token={WebUtility.UrlEncode(token)}"; if (existingEmployee.FirstName != null) { await _emailSender.SendResetPasswordEmailOnRegister(user.Email, existingEmployee.FirstName, resetLink); } } responsemessage = "User created successfully. Password reset link is sent to registered email"; } } else { var existingEmployee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == model.Id && e.IsActive == true); if (existingEmployee != null) { existingEmployee = GetUpdateEmployeeModel(model, existingEmployee); _context.Employees.Update(existingEmployee); responsemessage = "User updated successfully."; } else { // Create Employee record if missing Employee newEmployee = GetNewEmployeeModel(model, tenantId, string.Empty); _context.Employees.Add(newEmployee); } try { await _context.SaveChangesAsync(); } catch (Exception ex) { return BadRequest(ex.InnerException?.Message ?? ex.Message); } responsemessage = "User created successfully."; } return Ok(ApiResponse.SuccessResponse("Success.", responsemessage, 200)); } [HttpDelete("{id}")] public async Task SuspendEmployee(Guid id) { Guid tenantId = _userHelper.GetTenantId(); Employee? employee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == id && e.TenantId == tenantId); if (employee != null) { var assignedToTasks = await _context.TaskMembers.Where(t => t.EmployeeId == employee.Id).ToListAsync(); if (assignedToTasks.Count != 0) { List taskIds = assignedToTasks.Select(t => t.TaskAllocationId).ToList(); var tasks = await _context.TaskAllocations.Where(t => taskIds.Contains(t.Id)).ToListAsync(); foreach (var assignedToTask in assignedToTasks) { 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.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.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 allocations = new List(); 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); } else { _logger.LogError("Employee with ID {EmploueeId} not found in database", id); } return Ok(ApiResponse.SuccessResponse(new { }, "Employee Suspended successfully", 200)); } private static Employee GetNewEmployeeModel(CreateUserDto model, Guid TenantId, string ApplicationUserId) { var newEmployee = new Employee { ApplicationUserId = String.IsNullOrEmpty(ApplicationUserId) ? null : ApplicationUserId, FirstName = model.FirstName, LastName = model.LastName, Email = model.Email, TenantId = TenantId, CurrentAddress = model.CurrentAddress, BirthDate = Convert.ToDateTime(model.BirthDate), EmergencyPhoneNumber = model.EmergencyPhoneNumber, EmergencyContactPerson = model.EmergencyContactPerson, AadharNumber = model.AadharNumber, Gender = model.Gender, MiddleName = model.MiddleName, PanNumber = model.PanNumber, PermanentAddress = model.PermanentAddress, PhoneNumber = model.PhoneNumber, Photo = null, // GetFileDetails(model.Photo).Result.FileData, JobRoleId = model.JobRoleId, JoiningDate = Convert.ToDateTime(model.JoiningDate), }; return newEmployee; } private static Employee GetUpdateEmployeeModel(CreateUserDto model, Employee existingEmployee, IdentityUser? existingIdentityUser = null) { if (existingEmployee.ApplicationUserId == null && existingIdentityUser != null) { existingEmployee.ApplicationUserId = existingIdentityUser.Id; } existingEmployee.FirstName = model.FirstName; existingEmployee.LastName = model.LastName; existingEmployee.CurrentAddress = model.CurrentAddress; existingEmployee.BirthDate = Convert.ToDateTime(model.BirthDate); existingEmployee.JoiningDate = Convert.ToDateTime(model.JoiningDate); existingEmployee.EmergencyPhoneNumber = model.EmergencyPhoneNumber; existingEmployee.EmergencyContactPerson = model.EmergencyContactPerson; existingEmployee.AadharNumber = model.AadharNumber; existingEmployee.Gender = model.Gender; existingEmployee.MiddleName = model.MiddleName; existingEmployee.PanNumber = model.PanNumber; existingEmployee.PermanentAddress = model.PermanentAddress; existingEmployee.PhoneNumber = model.PhoneNumber; existingEmployee.Photo = null; // GetFileDetails(model.Photo).Result.FileData, existingEmployee.JobRoleId = model.JobRoleId; return existingEmployee; } private static async Task GetFileDetails(IFormFile file) { FileDetails info = new FileDetails(); info.ContentType = file.ContentType; info.FileName = file.FileName; using (var memoryStream = new MemoryStream()) { await file.CopyToAsync(memoryStream); info.FileData = memoryStream.ToArray(); } return info; } } }