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)); } } [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([FromBody] CreateUserDto model) { Guid tenantId = _userHelper.GetTenantId(); if (model == null) return BadRequest(ApiResponse.ErrorResponse("Invalid data", "Invaild Data", 400)); if (model.FirstName == null && model.PhoneNumber == 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)); } //[HttpPost("manage-mobile")] //public async Task CreateUserMoblie([FromBody] CreateUserDto model) //{ // Guid tenantId = _userHelper.GetTenantId(); // if (model == null) // return BadRequest(ApiResponse.ErrorResponse("Invalid data", "Invaild Data", 400)); // if (model.FirstName == null && model.PhoneNumber == 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(); var LoggedEmployee = await _userHelper.GetCurrentEmployeeAsync(); Employee? employee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == id && e.IsActive && e.TenantId == tenantId); if (employee != null) { if (employee.IsSystem) { _logger.LogWarning("Employee with ID {LoggedEmployeeId} tries to suspend system-defined employee with ID {EmployeeId}", LoggedEmployee.Id, employee.Id); return BadRequest(ApiResponse.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) { 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; } } }