diff --git a/Marco.Pms.Model/Dtos/Employees/CreateUserDto.cs b/Marco.Pms.Model/Dtos/Employees/CreateUserDto.cs index ad27e8c..6cd8981 100644 --- a/Marco.Pms.Model/Dtos/Employees/CreateUserDto.cs +++ b/Marco.Pms.Model/Dtos/Employees/CreateUserDto.cs @@ -28,11 +28,13 @@ public required string FirstName { get; set; } public required string LastName { get; set; } public required string PhoneNumber { get; set; } + public string? Email { get; set; } public required DateTime JoiningDate { get; set; } public required string Gender { get; set; } public Guid JobRoleId { get; set; } public string? ProfileImage { get; set; } public required Guid OrganizationId { get; set; } + public required bool HasApplicationAccess { get; set; } } } diff --git a/Marco.Pms.Services/Controllers/EmployeeController.cs b/Marco.Pms.Services/Controllers/EmployeeController.cs index 53b846b..ceb7bc4 100644 --- a/Marco.Pms.Services/Controllers/EmployeeController.cs +++ b/Marco.Pms.Services/Controllers/EmployeeController.cs @@ -704,14 +704,6 @@ namespace MarcoBMS.Services.Controllers createdIdentityUser.Id, createdIdentityUser.Email); } - // Prepare reset link sender helper - async Task SendResetIfApplicableAsync(ApplicationUser u, string firstName) - { - var token = await _userManager.GeneratePasswordResetTokenAsync(u); - var resetLink = $"{_configuration["AppSettings:WebFrontendUrl"]}/reset-password?token={WebUtility.UrlEncode(token)}"; - await _emailSender.SendResetPasswordEmailOnRegister(u.Email ?? "", firstName, resetLink); - _logger.LogInfo("Reset password email queued. Email={Email}", u.Email ?? ""); - } Guid employeeId; EmployeeVM employeeVM; @@ -915,6 +907,15 @@ namespace MarcoBMS.Services.Controllers } if (model.Id == null || model.Id == Guid.Empty) { + var emailExists = await _context.Employees + .AnyAsync(e => e.Email == model.Email && e.OrganizationId == model.OrganizationId); + + if (emailExists) + { + _logger.LogInfo("Employee email already exists in org. Email={Email}, Org={OrgId}", model.Email ?? string.Empty, model.OrganizationId); + return StatusCode(409, ApiResponse.ErrorResponse("Employee with email already exists", "Employee with this email already exists", 409)); + } + // Create path: map only allowed fields var employee = new Employee { @@ -922,15 +923,42 @@ namespace MarcoBMS.Services.Controllers TenantId = tenantId, FirstName = model.FirstName.Trim(), LastName = model.LastName?.Trim(), + Email = model.Email, Gender = model.Gender, PhoneNumber = model.PhoneNumber, JoiningDate = model.JoiningDate, JobRoleId = model.JobRoleId, Photo = imageBytes, - OrganizationId = model.OrganizationId + OrganizationId = model.OrganizationId, + HasApplicationAccess = model.HasApplicationAccess, }; - await _context.Employees.AddAsync(employee); + if (!string.IsNullOrWhiteSpace(model.Email) && model.HasApplicationAccess) + { + var existingUser = await _userManager.FindByEmailAsync(model.Email); + if (existingUser == null) + { + existingUser = new ApplicationUser + { + UserName = model.Email, + Email = model.Email, + EmailConfirmed = true + }; + var createResult = await _userManager.CreateAsync(existingUser, "User@123"); + if (!createResult.Succeeded) + { + _logger.LogWarning("Failed to create identity user for {Email}. Errors={Errors}", + existingUser.Email, + string.Join(", ", createResult.Errors.Select(e => $"{e.Code}:{e.Description}"))); + return BadRequest(ApiResponse.ErrorResponse("Failed to create user", createResult.Errors, 400)); + } + + await SendResetIfApplicableAsync(existingUser, employee.FirstName ?? "User"); + employee.ApplicationUserId = existingUser.Id; + } + } + + _context.Employees.Add(employee); await _context.SaveChangesAsync(); var employeeVM = _mapper.Map(employee); @@ -970,6 +998,45 @@ namespace MarcoBMS.Services.Controllers existingEmployee.JoiningDate = model.JoiningDate; existingEmployee.JobRoleId = model.JobRoleId; existingEmployee.OrganizationId = model.OrganizationId; + existingEmployee.HasApplicationAccess = model.HasApplicationAccess; + + if (string.IsNullOrWhiteSpace(existingEmployee.Email) && !string.IsNullOrWhiteSpace(model.Email)) + { + var emailExists = await _context.Employees + .AnyAsync(e => e.Email == model.Email && e.OrganizationId == model.OrganizationId); + + if (emailExists) + { + _logger.LogInfo("Employee email already exists in org. Email={Email}, Org={OrgId}", model.Email, model.OrganizationId); + return StatusCode(409, ApiResponse.ErrorResponse("Employee with email already exists", "Employee with this email already exists", 409)); + } + existingEmployee.Email = model.Email; + } + + if (model.HasApplicationAccess && !string.IsNullOrWhiteSpace(model.Email) && string.IsNullOrWhiteSpace(existingEmployee.ApplicationUserId)) + { + var existingUser = await _userManager.FindByEmailAsync(model.Email); + if (existingUser == null) + { + existingUser = new ApplicationUser + { + UserName = model.Email, + Email = model.Email, + EmailConfirmed = true + }; + var createResult = await _userManager.CreateAsync(existingUser, "User@123"); + if (!createResult.Succeeded) + { + _logger.LogWarning("Failed to create identity user for {Email}. Errors={Errors}", + existingUser.Email, + string.Join(", ", createResult.Errors.Select(e => $"{e.Code}:{e.Description}"))); + return BadRequest(ApiResponse.ErrorResponse("Failed to create user", createResult.Errors, 400)); + } + + await SendResetIfApplicableAsync(existingUser, existingEmployee.FirstName ?? "User"); + existingEmployee.ApplicationUserId = existingUser.Id; + } + } if (imageBytes != null) { @@ -1173,5 +1240,15 @@ namespace MarcoBMS.Services.Controllers return info; } + + + // Prepare reset link sender helper + private async Task SendResetIfApplicableAsync(ApplicationUser u, string firstName) + { + var token = await _userManager.GeneratePasswordResetTokenAsync(u); + var resetLink = $"{_configuration["AppSettings:WebFrontendUrl"]}/reset-password?token={WebUtility.UrlEncode(token)}"; + await _emailSender.SendResetPasswordEmailOnRegister(u.Email ?? "", firstName, resetLink); + _logger.LogInfo("Reset password email queued. Email={Email}", u.Email ?? ""); + } } }