diff --git a/Marco.Pms.Services/Controllers/EmployeeController.cs b/Marco.Pms.Services/Controllers/EmployeeController.cs index cdc28ed..8093471 100644 --- a/Marco.Pms.Services/Controllers/EmployeeController.cs +++ b/Marco.Pms.Services/Controllers/EmployeeController.cs @@ -9,6 +9,7 @@ using Marco.Pms.Model.Projects; using Marco.Pms.Model.Utilities; using Marco.Pms.Model.ViewModels.Activities; using Marco.Pms.Model.ViewModels.Employee; +using Marco.Pms.Services.Helpers; using Marco.Pms.Services.Hubs; using Marco.Pms.Services.Service; using Marco.Pms.Services.Service.ServiceInterfaces; @@ -36,6 +37,7 @@ namespace MarcoBMS.Services.Controllers private readonly IEmailSender _emailSender; private readonly EmployeeHelper _employeeHelper; private readonly UserHelper _userHelper; + private readonly GeneralHelper _generalHelper; private readonly IConfiguration _configuration; private readonly ILoggingService _logger; private readonly IHubContext _signalR; @@ -47,13 +49,14 @@ namespace MarcoBMS.Services.Controllers public EmployeeController(UserManager userManager, IEmailSender emailSender, ApplicationDbContext context, EmployeeHelper employeeHelper, UserHelper userHelper, IConfiguration configuration, ILoggingService logger, - IHubContext signalR, PermissionServices permission, IProjectServices projectServices, IMapper mapper) + IHubContext signalR, PermissionServices permission, IProjectServices projectServices, IMapper mapper, GeneralHelper generalHelper) { _context = context; _userManager = userManager; _emailSender = emailSender; _employeeHelper = employeeHelper; _userHelper = userHelper; + _generalHelper = generalHelper; _configuration = configuration; _logger = logger; _signalR = signalR; @@ -191,6 +194,7 @@ namespace MarcoBMS.Services.Controllers var response = await employeeQuery.Take(10).Select(e => _mapper.Map(e)).ToListAsync(); return Ok(ApiResponse.SuccessResponse(response, $"{response.Count} records of employees fetched successfully", 200)); } + [HttpGet] [Route("search/{name}/{projectid?}")] public async Task SearchEmployee(string name, Guid? projectid) @@ -233,11 +237,6 @@ namespace MarcoBMS.Services.Controllers 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) @@ -294,7 +293,12 @@ namespace MarcoBMS.Services.Controllers TenantId = tenantId }; - + var isSeatsAvaiable = await _generalHelper.CheckSeatsRemaningAsync(tenantId); + if (!isSeatsAvaiable) + { + _logger.LogWarning("Maximum number of users reached for Tenant {TenantId}", tenantId); + return BadRequest(ApiResponse.ErrorResponse("Maximum number of users reached. Cannot add new user", "Maximum number of users reached. Cannot add new user", 400)); + } // Create Identity User var result = await _userManager.CreateAsync(user, "User@123"); if (!result.Succeeded) diff --git a/Marco.Pms.Services/Helpers/GeneralHelper.cs b/Marco.Pms.Services/Helpers/GeneralHelper.cs index 93f256f..45cdb13 100644 --- a/Marco.Pms.Services/Helpers/GeneralHelper.cs +++ b/Marco.Pms.Services/Helpers/GeneralHelper.cs @@ -296,5 +296,80 @@ namespace Marco.Pms.Services.Helpers } } + /// + /// Checks whether the tenant still has available seats (MaxUsers not exceeded). + /// + /// The ID of the tenant to check. + /// True if seats are available; otherwise false. + public async Task CheckSeatsRemainingAsync(Guid tenantId) + { + _logger.LogInfo("Checking seats remaining for TenantId: {TenantId}", tenantId); + + try + { + // Run both queries concurrently + var totalSeatsTask = GetMaxSeatsAsync(tenantId); + var totalSeatsTakenTask = GetActiveEmployeesCountAsync(tenantId); + + await Task.WhenAll(totalSeatsTask, totalSeatsTakenTask); + + var totalSeats = await totalSeatsTask; + var totalSeatsTaken = await totalSeatsTakenTask; + + _logger.LogInfo( + "TenantId: {TenantId} | TotalSeats: {TotalSeats} | SeatsTaken: {SeatsTaken}", + tenantId, totalSeats, totalSeatsTaken); + + bool seatsAvailable = totalSeats >= totalSeatsTaken; + + _logger.LogDebug("TenantId: {TenantId} | Seats Available: {SeatsAvailable}", + tenantId, seatsAvailable); + + return seatsAvailable; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error checking seats for TenantId: {TenantId}", tenantId); + throw; + } + } + + /// + /// Retrieves the maximum number of allowed seats (MaxUsers) for a tenant. + /// + private async Task GetMaxSeatsAsync(Guid tenantId) + { + _logger.LogDebug("Fetching maximum seats for TenantId: {TenantId}", tenantId); + + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + + var maxSeats = await dbContext.TenantSubscriptions + .Where(ts => ts.TenantId == tenantId && !ts.IsCancelled) + .Select(ts => ts.MaxUsers) + .FirstOrDefaultAsync(); + + _logger.LogDebug("TenantId: {TenantId} | MaxSeats: {MaxSeats}", tenantId, maxSeats); + + return maxSeats; + } + + /// + /// Counts the number of active employees for a tenant. + /// + private async Task GetActiveEmployeesCountAsync(Guid tenantId) + { + _logger.LogDebug("Counting active employees for TenantId: {TenantId}", tenantId); + + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + + var activeEmployees = await dbContext.Employees + .Where(e => e.TenantId == tenantId && e.IsActive) + .CountAsync(); + + _logger.LogDebug("TenantId: {TenantId} | ActiveEmployees: {ActiveEmployees}", tenantId, activeEmployees); + + return activeEmployees; + } + } }