Merge pull request 'Restrict the user creation if the maximum user limit is reached' (#111) from Ashutosh_Bug#935 into Tenant_Management

Reviewed-on: #111
This commit is contained in:
ashutosh.nehete 2025-08-20 06:50:20 +00:00
commit 445d02662a
2 changed files with 86 additions and 7 deletions

View File

@ -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<MarcoHub> _signalR;
@ -47,13 +49,14 @@ namespace MarcoBMS.Services.Controllers
public EmployeeController(UserManager<ApplicationUser> userManager, IEmailSender emailSender,
ApplicationDbContext context, EmployeeHelper employeeHelper, UserHelper userHelper, IConfiguration configuration, ILoggingService logger,
IHubContext<MarcoHub> signalR, PermissionServices permission, IProjectServices projectServices, IMapper mapper)
IHubContext<MarcoHub> 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<BasicEmployeeVM>(e)).ToListAsync();
return Ok(ApiResponse<object>.SuccessResponse(response, $"{response.Count} records of employees fetched successfully", 200));
}
[HttpGet]
[Route("search/{name}/{projectid?}")]
public async Task<IActionResult> SearchEmployee(string name, Guid? projectid)
@ -233,11 +237,6 @@ namespace MarcoBMS.Services.Controllers
return _userHelper.GetTenantId();
}
//[HttpPost("manage/quick")]
//public async Task<IActionResult> CreateQuickUser([FromBody] CreateQuickUserDto model)
//{
// return Ok("Pending implementation");
//}
[HttpPost("manage")]
public async Task<IActionResult> 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<object>.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)

View File

@ -296,5 +296,80 @@ namespace Marco.Pms.Services.Helpers
}
}
/// <summary>
/// Checks whether the tenant still has available seats (MaxUsers not exceeded).
/// </summary>
/// <param name="tenantId">The ID of the tenant to check.</param>
/// <returns>True if seats are available; otherwise false.</returns>
public async Task<bool> 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;
}
}
/// <summary>
/// Retrieves the maximum number of allowed seats (MaxUsers) for a tenant.
/// </summary>
private async Task<double> 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;
}
/// <summary>
/// Counts the number of active employees for a tenant.
/// </summary>
private async Task<int> 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;
}
}
}