diff --git a/Marco.Pms.Model/Dtos/Tenant/UpdateSubscriptionDto.cs b/Marco.Pms.Model/Dtos/Tenant/UpdateSubscriptionDto.cs index 148f845..50a3d7e 100644 --- a/Marco.Pms.Model/Dtos/Tenant/UpdateSubscriptionDto.cs +++ b/Marco.Pms.Model/Dtos/Tenant/UpdateSubscriptionDto.cs @@ -1,6 +1,4 @@ -using Marco.Pms.Model.TenantModels; - -namespace Marco.Pms.Model.Dtos.Tenant +namespace Marco.Pms.Model.Dtos.Tenant { public class UpdateSubscriptionDto { @@ -8,6 +6,5 @@ namespace Marco.Pms.Model.Dtos.Tenant public Guid PlanId { get; set; } public Guid CurrencyId { get; set; } public double? MaxUsers { get; set; } - public PLAN_FREQUENCY Frequency { get; set; } } } diff --git a/Marco.Pms.Services/Controllers/TenantController.cs b/Marco.Pms.Services/Controllers/TenantController.cs index a90f987..21fa0c8 100644 --- a/Marco.Pms.Services/Controllers/TenantController.cs +++ b/Marco.Pms.Services/Controllers/TenantController.cs @@ -780,271 +780,272 @@ namespace Marco.Pms.Services.Controllers } } - //[HttpPut("update-subscription")] - //public async Task UpdateSubscriptionAsync(UpdateSubscriptionDto model) - //{ - // // 1. Get the logged-in user's employee record. - // var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + [HttpPut("update-subscription")] + public async Task UpdateSubscriptionAsync(UpdateSubscriptionDto model) + { + // 1. Get the logged-in user's employee record. + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); - // // 2. Create a new DbContext instance for this request. - // await using var context = await _dbContextFactory.CreateDbContextAsync(); + // 2. Create a new DbContext instance for this request. + await using var context = await _dbContextFactory.CreateDbContextAsync(); - // // 3. Get PermissionServices from DI inside a fresh scope (rarely needed, but retained for your design). - // using var scope = _serviceScopeFactory.CreateScope(); - // var permissionService = scope.ServiceProvider.GetRequiredService(); + // 3. Get PermissionServices from DI inside a fresh scope (rarely needed, but retained for your design). + using var scope = _serviceScopeFactory.CreateScope(); + var permissionService = scope.ServiceProvider.GetRequiredService(); - // // 4. Check user permissions: must be both Root user and have ManageTenants permission. - // var isRootUser = loggedInEmployee.ApplicationUser?.IsRootUser ?? false; - // var hasPermission = await permissionService.HasPermission(PermissionsMaster.ManageTenants, loggedInEmployee.Id); + // 4. Check user permissions: must be both Root user and have ManageTenants permission. + var isRootUser = loggedInEmployee.ApplicationUser?.IsRootUser ?? false; + var hasPermission = await permissionService.HasPermission(PermissionsMaster.ManageTenants, loggedInEmployee.Id); - // if (!isRootUser || !hasPermission) - // { - // _logger.LogWarning("Permission denied for EmployeeId={EmployeeId}. Root: {IsRoot}, HasPermission: {HasPermission}", - // loggedInEmployee.Id, isRootUser, hasPermission); - // return StatusCode(403, ApiResponse.ErrorResponse("Access denied", - // "User does not have the required permissions.", 403)); - // } + if (!isRootUser || !hasPermission) + { + _logger.LogWarning("Permission denied for EmployeeId={EmployeeId}. Root: {IsRoot}, HasPermission: {HasPermission}", + loggedInEmployee.Id, isRootUser, hasPermission); + return StatusCode(403, ApiResponse.ErrorResponse("Access denied", + "User does not have the required permissions.", 403)); + } - // // 5. Fetch Tenant, SubscriptionPlan, and TenantSubscription in parallel (efficiently). - // var tenantTask = _dbContextFactory.CreateDbContextAsync().ContinueWith(ctx => - // ctx.Result.Tenants.AsNoTracking().FirstOrDefaultAsync(t => t.Id == model.TenantId)).Unwrap(); + // 5. Fetch Tenant, SubscriptionPlan, and TenantSubscription in parallel (efficiently). + var tenantTask = _dbContextFactory.CreateDbContextAsync().ContinueWith(ctx => + ctx.Result.Tenants.AsNoTracking().FirstOrDefaultAsync(t => t.Id == model.TenantId)).Unwrap(); - // var subscriptionPlanTask = _dbContextFactory.CreateDbContextAsync().ContinueWith(ctx => - // ctx.Result.SubscriptionPlans.AsNoTracking().FirstOrDefaultAsync(sp => sp.Id == model.PlanId)).Unwrap(); + var planDetailsTask = _dbContextFactory.CreateDbContextAsync().ContinueWith(ctx => + ctx.Result.SubscriptionPlanDetails.Include(sp => sp.Plan).AsNoTracking().FirstOrDefaultAsync(sp => sp.Id == model.PlanId)).Unwrap(); - // var currentSubscriptionTask = _dbContextFactory.CreateDbContextAsync().ContinueWith(ctx => - // ctx.Result.TenantSubscriptions.AsNoTracking().FirstOrDefaultAsync(ts => ts.TenantId == model.TenantId)).Unwrap(); + var currentSubscriptionTask = _dbContextFactory.CreateDbContextAsync().ContinueWith(ctx => + ctx.Result.TenantSubscriptions.Include(ts => ts.Currency).AsNoTracking().FirstOrDefaultAsync(ts => ts.TenantId == model.TenantId && !ts.IsCancelled)).Unwrap(); - // await Task.WhenAll(tenantTask, subscriptionPlanTask, currentSubscriptionTask); + await Task.WhenAll(tenantTask, planDetailsTask, currentSubscriptionTask); - // var tenant = tenantTask.Result; - // if (tenant == null) - // { - // _logger.LogWarning("Tenant {TenantId} not found.", model.TenantId); - // return NotFound(ApiResponse.ErrorResponse("Tenant not found", "Tenant not found", 404)); - // } + var tenant = tenantTask.Result; + if (tenant == null) + { + _logger.LogWarning("Tenant {TenantId} not found.", model.TenantId); + return NotFound(ApiResponse.ErrorResponse("Tenant not found", "Tenant not found", 404)); + } - // var subscriptionPlan = subscriptionPlanTask.Result; - // if (subscriptionPlan == null) - // { - // _logger.LogWarning("Subscription plan {PlanId} not found.", model.PlanId); - // return NotFound(ApiResponse.ErrorResponse("Subscription plan not found", "Subscription plan not found", 404)); - // } + var subscriptionPlan = planDetailsTask.Result; + if (subscriptionPlan == null) + { + _logger.LogWarning("Subscription plan {PlanId} not found.", model.PlanId); + return NotFound(ApiResponse.ErrorResponse("Subscription plan not found", "Subscription plan not found", 404)); + } - // var currentSubscription = currentSubscriptionTask.Result; - // var utcNow = DateTime.UtcNow; + var currentSubscription = currentSubscriptionTask.Result; + var utcNow = DateTime.UtcNow; - // // 6. If the tenant already has this plan, extend/renew it. - // if (currentSubscription != null && currentSubscription.PlanId == model.PlanId) - // { - // DateTime newEndDate; - // // 6a. If the subscription is still active, extend from current EndDate; else start from now. - // if (currentSubscription.EndDate.Date >= utcNow.Date) - // { - // newEndDate = model.Frequency switch - // { - // PLAN_FREQUENCY.MONTHLY => currentSubscription.EndDate.AddMonths(1), - // PLAN_FREQUENCY.QUARTERLY => currentSubscription.EndDate.AddMonths(3), - // PLAN_FREQUENCY.HALF_MONTHLY => currentSubscription.EndDate.AddMonths(6), - // PLAN_FREQUENCY.YEARLY => currentSubscription.EndDate.AddMonths(12), - // _ => currentSubscription.EndDate - // }; - // } - // else - // { - // newEndDate = model.Frequency switch - // { - // PLAN_FREQUENCY.MONTHLY => utcNow.AddMonths(1), - // PLAN_FREQUENCY.QUARTERLY => utcNow.AddMonths(3), - // PLAN_FREQUENCY.HALF_MONTHLY => utcNow.AddMonths(6), - // PLAN_FREQUENCY.YEARLY => utcNow.AddMonths(12), - // _ => utcNow - // }; - // } + // 6. If the tenant already has this plan, extend/renew it. + if (currentSubscription != null && currentSubscription.PlanId == subscriptionPlan.Id) + { + DateTime newEndDate; + // 6a. If the subscription is still active, extend from current EndDate; else start from now. + if (currentSubscription.EndDate.Date >= utcNow.Date) + { + newEndDate = subscriptionPlan.Frequency switch + { + PLAN_FREQUENCY.MONTHLY => currentSubscription.EndDate.AddMonths(1), + PLAN_FREQUENCY.QUARTERLY => currentSubscription.EndDate.AddMonths(3), + PLAN_FREQUENCY.HALF_MONTHLY => currentSubscription.EndDate.AddMonths(6), + PLAN_FREQUENCY.YEARLY => currentSubscription.EndDate.AddMonths(12), + _ => currentSubscription.EndDate + }; + } + else + { + newEndDate = subscriptionPlan.Frequency switch + { + PLAN_FREQUENCY.MONTHLY => utcNow.AddMonths(1), + PLAN_FREQUENCY.QUARTERLY => utcNow.AddMonths(3), + PLAN_FREQUENCY.HALF_MONTHLY => utcNow.AddMonths(6), + PLAN_FREQUENCY.YEARLY => utcNow.AddMonths(12), + _ => utcNow + }; + } - // // 6b. Update subscription details - // if (model.MaxUsers != null && model.MaxUsers > 0) - // { - // currentSubscription.MaxUsers = model.MaxUsers.Value; - // } - // currentSubscription.EndDate = newEndDate; - // currentSubscription.NextBillingDate = newEndDate; - // currentSubscription.UpdateAt = utcNow; - // currentSubscription.UpdatedById = loggedInEmployee.Id; + // 6b. Update subscription details + if (model.MaxUsers != null && model.MaxUsers > 0) + { + currentSubscription.MaxUsers = model.MaxUsers.Value; + } + currentSubscription.StartDate = utcNow; + currentSubscription.EndDate = newEndDate; + currentSubscription.NextBillingDate = newEndDate; + currentSubscription.UpdateAt = utcNow; + currentSubscription.UpdatedById = loggedInEmployee.Id; - // context.TenantSubscriptions.Update(currentSubscription); - // await context.SaveChangesAsync(); + context.TenantSubscriptions.Update(currentSubscription); + await context.SaveChangesAsync(); - // _logger.LogInfo("Subscription renewed: Tenant={TenantId}, Plan={PlanId}, NewEnd={EndDate}", - // model.TenantId, model.PlanId, newEndDate); + _logger.LogInfo("Subscription renewed: Tenant={TenantId}, Plan={PlanId}, NewEnd={EndDate}", + model.TenantId, model.PlanId, newEndDate); - // return Ok(ApiResponse.SuccessResponse(currentSubscription, "Subscription renewed/extended", 200)); - // } + return Ok(ApiResponse.SuccessResponse(currentSubscription, "Subscription renewed/extended", 200)); + } - // // 7. Else, change plan: new subscription record, close the old if exists. - // await using var transaction = await context.Database.BeginTransactionAsync(); - // try - // { - // // 7a. Compute new plan dates - // var endDate = model.Frequency switch - // { - // PLAN_FREQUENCY.MONTHLY => utcNow.AddMonths(1), - // PLAN_FREQUENCY.QUARTERLY => utcNow.AddMonths(3), - // PLAN_FREQUENCY.HALF_MONTHLY => utcNow.AddMonths(6), - // PLAN_FREQUENCY.YEARLY => utcNow.AddMonths(12), - // _ => utcNow - // }; + // 7. Else, change plan: new subscription record, close the old if exists. + await using var transaction = await context.Database.BeginTransactionAsync(); + try + { + // 7a. Compute new plan dates + var endDate = subscriptionPlan.Frequency switch + { + PLAN_FREQUENCY.MONTHLY => utcNow.AddMonths(1), + PLAN_FREQUENCY.QUARTERLY => utcNow.AddMonths(3), + PLAN_FREQUENCY.HALF_MONTHLY => utcNow.AddMonths(6), + PLAN_FREQUENCY.YEARLY => utcNow.AddMonths(12), + _ => utcNow + }; - // var newSubscription = new TenantSubscriptions - // { - // TenantId = model.TenantId, - // PlanId = model.PlanId, - // StatusId = activePlanStatus, - // CreatedAt = utcNow, - // MaxUsers = model.MaxUsers ?? (currentSubscription?.MaxUsers ?? subscriptionPlan.MaxUser), - // CreatedById = loggedInEmployee.Id, - // CurrencyId = model.CurrencyId, - // StartDate = utcNow, - // EndDate = endDate, - // NextBillingDate = endDate, - // IsTrial = currentSubscription?.IsTrial ?? false, - // AutoRenew = currentSubscription?.AutoRenew ?? false - // }; - // context.TenantSubscriptions.Add(newSubscription); + var newSubscription = new TenantSubscriptions + { + TenantId = model.TenantId, + PlanId = model.PlanId, + StatusId = activePlanStatus, + CreatedAt = utcNow, + MaxUsers = model.MaxUsers ?? (currentSubscription?.MaxUsers ?? subscriptionPlan.MaxUser), + CreatedById = loggedInEmployee.Id, + CurrencyId = model.CurrencyId, + StartDate = utcNow, + EndDate = endDate, + NextBillingDate = endDate, + IsTrial = currentSubscription?.IsTrial ?? false, + AutoRenew = currentSubscription?.AutoRenew ?? false + }; + context.TenantSubscriptions.Add(newSubscription); - // // 7b. If an old subscription exists, cancel it. - // if (currentSubscription != null) - // { - // currentSubscription.IsCancelled = true; - // currentSubscription.CancellationDate = utcNow; - // currentSubscription.UpdateAt = utcNow; - // currentSubscription.UpdatedById = loggedInEmployee.Id; - // context.TenantSubscriptions.Update(currentSubscription); - // } - // await context.SaveChangesAsync(); - // _logger.LogInfo("Subscription plan changed: Tenant={TenantId}, NewPlan={PlanId}", - // model.TenantId, model.PlanId); + // 7b. If an old subscription exists, cancel it. + if (currentSubscription != null) + { + currentSubscription.IsCancelled = true; + currentSubscription.CancellationDate = utcNow; + currentSubscription.UpdateAt = utcNow; + currentSubscription.UpdatedById = loggedInEmployee.Id; + context.TenantSubscriptions.Update(currentSubscription); + } + await context.SaveChangesAsync(); + _logger.LogInfo("Subscription plan changed: Tenant={TenantId}, NewPlan={PlanId}", + model.TenantId, model.PlanId); - // // 8. Update tenant permissions based on subscription features. - // var features = await _featureDetailsHelper.GetFeatureDetails(subscriptionPlan.FeaturesId); - // if (features == null) - // { - // _logger.LogInfo("No features for Plan={PlanId}.", model.PlanId); - // await transaction.CommitAsync(); - // return Ok(ApiResponse.SuccessResponse(newSubscription, "Tenant subscription updated (no features)", 200)); - // } + // 8. Update tenant permissions based on subscription features. + var features = await _featureDetailsHelper.GetFeatureDetails(subscriptionPlan.FeaturesId); + if (features == null) + { + _logger.LogInfo("No features for Plan={PlanId}.", model.PlanId); + await transaction.CommitAsync(); + return Ok(ApiResponse.SuccessResponse(newSubscription, "Tenant subscription updated (no features)", 200)); + } - // // 8a. Async helper to get all permission IDs for a given module. - // async Task> GetPermissionsForFeaturesAsync(List? featureIds) - // { - // if (featureIds == null || featureIds.Count == 0) return new List(); + // 8a. Async helper to get all permission IDs for a given module. + async Task> GetPermissionsForFeaturesAsync(List? featureIds) + { + if (featureIds == null || featureIds.Count == 0) return new List(); - // await using var ctx = await _dbContextFactory.CreateDbContextAsync(); - // return await ctx.FeaturePermissions.AsNoTracking() - // .Where(fp => featureIds.Contains(fp.FeatureId)) - // .Select(fp => fp.Id) - // .ToListAsync(); - // } + await using var ctx = await _dbContextFactory.CreateDbContextAsync(); + return await ctx.FeaturePermissions.AsNoTracking() + .Where(fp => featureIds.Contains(fp.FeatureId)) + .Select(fp => fp.Id) + .ToListAsync(); + } - // // 8b. Fetch all module permissions concurrently. - // var projectPermTask = GetPermissionsForFeaturesAsync(features.Modules?.ProjectManagement?.FeatureId); - // var attendancePermTask = GetPermissionsForFeaturesAsync(features.Modules?.Attendance?.FeatureId); - // var directoryPermTask = GetPermissionsForFeaturesAsync(features.Modules?.Directory?.FeatureId); - // var expensePermTask = GetPermissionsForFeaturesAsync(features.Modules?.Expense?.FeatureId); - // var employeePermTask = GetPermissionsForFeaturesAsync(new List { EmployeeFeatureId }); // assumed defined + // 8b. Fetch all module permissions concurrently. + var projectPermTask = GetPermissionsForFeaturesAsync(features.Modules?.ProjectManagement?.FeatureId); + var attendancePermTask = GetPermissionsForFeaturesAsync(features.Modules?.Attendance?.FeatureId); + var directoryPermTask = GetPermissionsForFeaturesAsync(features.Modules?.Directory?.FeatureId); + var expensePermTask = GetPermissionsForFeaturesAsync(features.Modules?.Expense?.FeatureId); + var employeePermTask = GetPermissionsForFeaturesAsync(new List { EmployeeFeatureId }); // assumed defined - // await Task.WhenAll(projectPermTask, attendancePermTask, directoryPermTask, expensePermTask, employeePermTask); + await Task.WhenAll(projectPermTask, attendancePermTask, directoryPermTask, expensePermTask, employeePermTask); - // // 8c. Prepare add and remove permission lists. - // var newPermissionIds = new List(); - // var revokePermissionIds = new List(); + // 8c. Prepare add and remove permission lists. + var newPermissionIds = new List(); + var revokePermissionIds = new List(); - // void ProcessPerms(bool? enabled, List ids) - // { - // if (enabled == true) newPermissionIds.AddRange(ids); - // else revokePermissionIds.AddRange(ids); - // } - // ProcessPerms(features.Modules?.ProjectManagement?.Enabled, projectPermTask.Result); - // ProcessPerms(features.Modules?.Attendance?.Enabled, attendancePermTask.Result); - // ProcessPerms(features.Modules?.Directory?.Enabled, directoryPermTask.Result); - // ProcessPerms(features.Modules?.Expense?.Enabled, expensePermTask.Result); + void ProcessPerms(bool? enabled, List ids) + { + if (enabled == true) newPermissionIds.AddRange(ids); + else revokePermissionIds.AddRange(ids); + } + ProcessPerms(features.Modules?.ProjectManagement?.Enabled, projectPermTask.Result); + ProcessPerms(features.Modules?.Attendance?.Enabled, attendancePermTask.Result); + ProcessPerms(features.Modules?.Directory?.Enabled, directoryPermTask.Result); + ProcessPerms(features.Modules?.Expense?.Enabled, expensePermTask.Result); - // newPermissionIds = newPermissionIds.Distinct().ToList(); - // revokePermissionIds = revokePermissionIds.Distinct().ToList(); + newPermissionIds = newPermissionIds.Distinct().ToList(); + revokePermissionIds = revokePermissionIds.Distinct().ToList(); - // // 8d. Find root employee & role for this tenant. - // var rootEmployee = await context.Employees - // .Include(e => e.ApplicationUser) - // .FirstOrDefaultAsync(e => e.ApplicationUser != null && (e.ApplicationUser.IsRootUser ?? false) && e.TenantId == model.TenantId); + // 8d. Find root employee & role for this tenant. + var rootEmployee = await context.Employees + .Include(e => e.ApplicationUser) + .FirstOrDefaultAsync(e => e.ApplicationUser != null && (e.ApplicationUser.IsRootUser ?? false) && e.TenantId == model.TenantId); - // if (rootEmployee == null) - // { - // _logger.LogWarning("No root employee for Tenant={TenantId}.", model.TenantId); - // await transaction.CommitAsync(); - // return Ok(ApiResponse.SuccessResponse(newSubscription, "Tenant subscription updated (no root employee)", 200)); - // } + if (rootEmployee == null) + { + _logger.LogWarning("No root employee for Tenant={TenantId}.", model.TenantId); + await transaction.CommitAsync(); + return Ok(ApiResponse.SuccessResponse(newSubscription, "Tenant subscription updated (no root employee)", 200)); + } - // var rootRoleId = await context.EmployeeRoleMappings - // .AsNoTracking() - // .Where(er => er.EmployeeId == rootEmployee.Id && er.TenantId == model.TenantId) - // .Select(er => er.RoleId) - // .FirstOrDefaultAsync(); + var rootRoleId = await context.EmployeeRoleMappings + .AsNoTracking() + .Where(er => er.EmployeeId == rootEmployee.Id && er.TenantId == model.TenantId) + .Select(er => er.RoleId) + .FirstOrDefaultAsync(); - // if (rootRoleId == Guid.Empty) - // { - // _logger.LogWarning("No root role for Employee={EmployeeId}, Tenant={TenantId}.", rootEmployee.Id, model.TenantId); - // await transaction.CommitAsync(); - // return Ok(ApiResponse.SuccessResponse(newSubscription, "Tenant subscription updated (no root role)", 200)); - // } + if (rootRoleId == Guid.Empty) + { + _logger.LogWarning("No root role for Employee={EmployeeId}, Tenant={TenantId}.", rootEmployee.Id, model.TenantId); + await transaction.CommitAsync(); + return Ok(ApiResponse.SuccessResponse(newSubscription, "Tenant subscription updated (no root role)", 200)); + } - // var dbOldRolePerms = await context.RolePermissionMappings.Where(x => x.ApplicationRoleId == rootRoleId).ToListAsync(); - // var oldPermIds = dbOldRolePerms.Select(rp => rp.FeaturePermissionId).ToList(); + var dbOldRolePerms = await context.RolePermissionMappings.Where(x => x.ApplicationRoleId == rootRoleId).ToListAsync(); + var oldPermIds = dbOldRolePerms.Select(rp => rp.FeaturePermissionId).ToList(); - // // 8e. Prevent accidental loss of basic employee permissions. - // if (oldPermIds.Count - revokePermissionIds.Count <= 4 && revokePermissionIds.Any()) - // { - // var employeePerms = employeePermTask.Result; - // revokePermissionIds = revokePermissionIds.Where(pid => !employeePerms.Contains(pid)).ToList(); - // } + // 8e. Prevent accidental loss of basic employee permissions. + if ((oldPermIds.Count - revokePermissionIds.Count) >= 4 && revokePermissionIds.Any()) + { + var employeePerms = employeePermTask.Result; + revokePermissionIds = revokePermissionIds.Where(pid => !employeePerms.Contains(pid)).ToList(); + } - // // 8f. Prepare permission-mapping records to add/remove. - // var mappingsToRemove = dbOldRolePerms.Where(rp => revokePermissionIds.Contains(rp.FeaturePermissionId)).ToList(); - // var mappingsToAdd = newPermissionIds - // .Where(pid => !oldPermIds.Contains(pid)) - // .Select(pid => new RolePermissionMappings { ApplicationRoleId = rootRoleId, FeaturePermissionId = pid }) - // .ToList(); + // 8f. Prepare permission-mapping records to add/remove. + var mappingsToRemove = dbOldRolePerms.Where(rp => revokePermissionIds.Contains(rp.FeaturePermissionId)).ToList(); + var mappingsToAdd = newPermissionIds + .Where(pid => !oldPermIds.Contains(pid)) + .Select(pid => new RolePermissionMappings { ApplicationRoleId = rootRoleId, FeaturePermissionId = pid }) + .ToList(); - // if (mappingsToAdd.Any()) - // { - // context.RolePermissionMappings.AddRange(mappingsToAdd); - // _logger.LogInfo("Permissions granted: {Count} for Role={RoleId}", mappingsToAdd.Count, rootRoleId); - // } - // if (mappingsToRemove.Any()) - // { - // context.RolePermissionMappings.RemoveRange(mappingsToRemove); - // _logger.LogInfo("Permissions revoked: {Count} for Role={RoleId}", mappingsToRemove.Count, rootRoleId); - // } + if (mappingsToAdd.Any()) + { + context.RolePermissionMappings.AddRange(mappingsToAdd); + _logger.LogInfo("Permissions granted: {Count} for Role={RoleId}", mappingsToAdd.Count, rootRoleId); + } + if (mappingsToRemove.Any()) + { + context.RolePermissionMappings.RemoveRange(mappingsToRemove); + _logger.LogInfo("Permissions revoked: {Count} for Role={RoleId}", mappingsToRemove.Count, rootRoleId); + } - // await context.SaveChangesAsync(); - // await transaction.CommitAsync(); + await context.SaveChangesAsync(); + await transaction.CommitAsync(); - // _logger.LogInfo("Tenant subscription and permissions updated: Tenant={TenantId}", model.TenantId); + _logger.LogInfo("Tenant subscription and permissions updated: Tenant={TenantId}", model.TenantId); - // return Ok(ApiResponse.SuccessResponse(newSubscription, "Tenant subscription successfully updated", 200)); - // } - // catch (DbUpdateException dbEx) - // { - // await transaction.RollbackAsync(); - // _logger.LogError(dbEx, "Database exception updating subscription for TenantId={TenantId}", model.TenantId); - // return StatusCode(500, ApiResponse.ErrorResponse("Internal database error", ExceptionMapper(dbEx), 500)); - // } - // catch (Exception ex) - // { - // await transaction.RollbackAsync(); - // _logger.LogError(ex, "General exception for TenantId={TenantId}", model.TenantId); - // return StatusCode(500, ApiResponse.ErrorResponse("Internal error occurred", ExceptionMapper(ex), 500)); - // } - //} + return Ok(ApiResponse.SuccessResponse(newSubscription, "Tenant subscription successfully updated", 200)); + } + catch (DbUpdateException dbEx) + { + await transaction.RollbackAsync(); + _logger.LogError(dbEx, "Database exception updating subscription for TenantId={TenantId}", model.TenantId); + return StatusCode(500, ApiResponse.ErrorResponse("Internal database error", ExceptionMapper(dbEx), 500)); + } + catch (Exception ex) + { + await transaction.RollbackAsync(); + _logger.LogError(ex, "General exception for TenantId={TenantId}", model.TenantId); + return StatusCode(500, ApiResponse.ErrorResponse("Internal error occurred", ExceptionMapper(ex), 500)); + } + }