diff --git a/Marco.Pms.Model/Dtos/Tenant/AddSubscriptionDto.cs b/Marco.Pms.Model/Dtos/Tenant/AddSubscriptionDto.cs index 4bd4df9..6c56253 100644 --- a/Marco.Pms.Model/Dtos/Tenant/AddSubscriptionDto.cs +++ b/Marco.Pms.Model/Dtos/Tenant/AddSubscriptionDto.cs @@ -1,6 +1,4 @@ -using Marco.Pms.Model.TenantModels; - -namespace Marco.Pms.Model.Dtos.Tenant +namespace Marco.Pms.Model.Dtos.Tenant { public class AddSubscriptionDto { @@ -8,7 +6,6 @@ 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; } public bool IsTrial { get; set; } = false; public bool AutoRenew { get; set; } = true; } diff --git a/Marco.Pms.Services/Controllers/TenantController.cs b/Marco.Pms.Services/Controllers/TenantController.cs index 2d49fbc..a90f987 100644 --- a/Marco.Pms.Services/Controllers/TenantController.cs +++ b/Marco.Pms.Services/Controllers/TenantController.cs @@ -564,221 +564,221 @@ namespace Marco.Pms.Services.Controllers #region =================================================================== Subscription APIs =================================================================== - //[HttpPost("add-subscription")] - //public async Task AddSubscriptionAsync(AddSubscriptionDto model) - //{ - // var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + [HttpPost("add-subscription")] + public async Task AddSubscriptionAsync(AddSubscriptionDto model) + { + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); - // _logger.LogInfo("AddSubscription called by employee {EmployeeId} for Tenant {TenantId} and Plan {PlanId}", - // loggedInEmployee.Id, model.TenantId, model.PlanId); - // if (loggedInEmployee == null) - // { - // _logger.LogWarning("No logged-in employee found."); - // return Unauthorized(ApiResponse.ErrorResponse("Unauthorized", "User must be logged in.", 401)); - // } + _logger.LogInfo("AddSubscription called by employee {EmployeeId} for Tenant {TenantId} and Plan {PlanId}", + loggedInEmployee.Id, model.TenantId, model.PlanId); + if (loggedInEmployee == null) + { + _logger.LogWarning("No logged-in employee found."); + return Unauthorized(ApiResponse.ErrorResponse("Unauthorized", "User must be logged in.", 401)); + } - // await using var _context = await _dbContextFactory.CreateDbContextAsync(); - // using var scope = _serviceScopeFactory.CreateScope(); + await using var _context = await _dbContextFactory.CreateDbContextAsync(); + using var scope = _serviceScopeFactory.CreateScope(); - // var _permissionService = scope.ServiceProvider.GetRequiredService(); + var _permissionService = scope.ServiceProvider.GetRequiredService(); - // var isRootUser = loggedInEmployee.ApplicationUser?.IsRootUser ?? false; - // var hasPermission = await _permissionService.HasPermission(PermissionsMaster.ManageTenants, loggedInEmployee.Id); + var isRootUser = loggedInEmployee.ApplicationUser?.IsRootUser ?? false; + var hasPermission = await _permissionService.HasPermission(PermissionsMaster.ManageTenants, loggedInEmployee.Id); - // if (!hasPermission || !isRootUser) - // { - // _logger.LogWarning("Permission denied: User {EmployeeId} attempted to add subscription without permission or root access.", - // loggedInEmployee.Id); + if (!hasPermission || !isRootUser) + { + _logger.LogWarning("Permission denied: User {EmployeeId} attempted to add subscription without permission or root access.", + loggedInEmployee.Id); - // return StatusCode(403, - // ApiResponse.ErrorResponse("Access denied", - // "User does not have the required permissions for this action.", 403)); - // } + return StatusCode(403, + ApiResponse.ErrorResponse("Access denied", + "User does not have the required permissions for this action.", 403)); + } - // var subscriptionPlan = await _context.SubscriptionPlans.FirstOrDefaultAsync(sp => sp.Id == model.PlanId); + var subscriptionPlan = await _context.SubscriptionPlanDetails.Include(sp => sp.Plan).FirstOrDefaultAsync(sp => sp.Id == model.PlanId); - // var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.Id == model.TenantId); - // if (tenant == null) - // { - // _logger.LogWarning("Tenant {TenantId} not found in database", model.TenantId); - // return NotFound(ApiResponse.ErrorResponse("Tenant not found", "Tenant not found", 404)); - // } - // if (subscriptionPlan == null) - // { - // _logger.LogWarning("Subscription plan {PlanId} not found in database", model.PlanId); - // return NotFound(ApiResponse.ErrorResponse("Subscription plan not found", "Subscription plan not found", 404)); - // } + var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.Id == model.TenantId); + if (tenant == null) + { + _logger.LogWarning("Tenant {TenantId} not found in database", model.TenantId); + return NotFound(ApiResponse.ErrorResponse("Tenant not found", "Tenant not found", 404)); + } + if (subscriptionPlan == null) + { + _logger.LogWarning("Subscription plan {PlanId} not found in database", model.PlanId); + return NotFound(ApiResponse.ErrorResponse("Subscription plan not found", "Subscription plan not found", 404)); + } - // await using var transaction = await _context.Database.BeginTransactionAsync(); - // var utcNow = DateTime.UtcNow; + await using var transaction = await _context.Database.BeginTransactionAsync(); + var utcNow = DateTime.UtcNow; - // // Prepare subscription dates based on frequency - // 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 // default if unknown - // }; + // Prepare subscription dates based on frequency + 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 // default if unknown + }; - // var tenantSubscription = new TenantSubscriptions - // { - // TenantId = model.TenantId, - // PlanId = model.PlanId, - // StatusId = activePlanStatus, - // CreatedAt = utcNow, - // MaxUsers = model.MaxUsers, - // CreatedById = loggedInEmployee.Id, - // CurrencyId = model.CurrencyId, - // IsTrial = model.IsTrial, - // StartDate = utcNow, - // EndDate = endDate, - // NextBillingDate = endDate, - // AutoRenew = model.AutoRenew - // }; + var tenantSubscription = new TenantSubscriptions + { + TenantId = model.TenantId, + PlanId = model.PlanId, + StatusId = activePlanStatus, + CreatedAt = utcNow, + MaxUsers = model.MaxUsers, + CreatedById = loggedInEmployee.Id, + CurrencyId = model.CurrencyId, + IsTrial = model.IsTrial, + StartDate = utcNow, + EndDate = endDate, + NextBillingDate = endDate, + AutoRenew = model.AutoRenew + }; - // _context.TenantSubscriptions.Add(tenantSubscription); + _context.TenantSubscriptions.Add(tenantSubscription); - // try - // { - // await _context.SaveChangesAsync(); - // _logger.LogInfo("Tenant subscription added successfully for Tenant {TenantId}, Plan {PlanId}", - // model.TenantId, model.PlanId); - // } - // catch (DbUpdateException dbEx) - // { - // _logger.LogError(dbEx, "Database exception while adding subscription plan to tenant {TenantId}", model.TenantId); - // return StatusCode(500, ApiResponse.ErrorResponse("Internal error occured", ExceptionMapper(dbEx), 500)); - // } + try + { + await _context.SaveChangesAsync(); + _logger.LogInfo("Tenant subscription added successfully for Tenant {TenantId}, Plan {PlanId}", + model.TenantId, model.PlanId); + } + catch (DbUpdateException dbEx) + { + _logger.LogError(dbEx, "Database exception while adding subscription plan to tenant {TenantId}", model.TenantId); + return StatusCode(500, ApiResponse.ErrorResponse("Internal error occured", ExceptionMapper(dbEx), 500)); + } - // try - // { - // var features = await _featureDetailsHelper.GetFeatureDetails(subscriptionPlan.FeaturesId); - // if (features == null) - // { - // _logger.LogInfo("No features found for subscription plan {PlanId}", model.PlanId); - // await transaction.CommitAsync(); - // return Ok(ApiResponse.SuccessResponse(tenantSubscription, "Tenant subscription successfully added", 200)); - // } + try + { + var features = await _featureDetailsHelper.GetFeatureDetails(subscriptionPlan.FeaturesId); + if (features == null) + { + _logger.LogInfo("No features found for subscription plan {PlanId}", model.PlanId); + await transaction.CommitAsync(); + return Ok(ApiResponse.SuccessResponse(tenantSubscription, "Tenant subscription successfully added", 200)); + } - // // Helper to get permissions for a module asynchronously - // async Task> GetPermissionsForModuleAsync(List? featureIds) - // { - // if (featureIds == null || featureIds.Count == 0) return new List(); + // Helper to get permissions for a module asynchronously + async Task> GetPermissionsForModuleAsync(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(); + } - // // Fetch permission tasks for all modules in parallel - // var projectPermissionTask = GetPermissionsForModuleAsync(features.Modules?.ProjectManagement?.FeatureId); - // var attendancePermissionTask = GetPermissionsForModuleAsync(features.Modules?.Attendance?.FeatureId); - // var directoryPermissionTask = GetPermissionsForModuleAsync(features.Modules?.Directory?.FeatureId); - // var expensePermissionTask = GetPermissionsForModuleAsync(features.Modules?.Expense?.FeatureId); - // var employeePermissionTask = GetPermissionsForModuleAsync(new List { EmployeeFeatureId }); + // Fetch permission tasks for all modules in parallel + var projectPermissionTask = GetPermissionsForModuleAsync(features.Modules?.ProjectManagement?.FeatureId); + var attendancePermissionTask = GetPermissionsForModuleAsync(features.Modules?.Attendance?.FeatureId); + var directoryPermissionTask = GetPermissionsForModuleAsync(features.Modules?.Directory?.FeatureId); + var expensePermissionTask = GetPermissionsForModuleAsync(features.Modules?.Expense?.FeatureId); + var employeePermissionTask = GetPermissionsForModuleAsync(new List { EmployeeFeatureId }); - // await Task.WhenAll(projectPermissionTask, attendancePermissionTask, directoryPermissionTask, expensePermissionTask, employeePermissionTask); + await Task.WhenAll(projectPermissionTask, attendancePermissionTask, directoryPermissionTask, expensePermissionTask, employeePermissionTask); - // var newPermissionIds = new List(); - // var deletePermissionIds = new List(); + var newPermissionIds = new List(); + var deletePermissionIds = new List(); - // // Add or remove permissions based on modules enabled status - // void ProcessPermissions(bool? enabled, List permissions) - // { - // if (enabled == true) - // newPermissionIds.AddRange(permissions); - // else - // deletePermissionIds.AddRange(permissions); - // } + // Add or remove permissions based on modules enabled status + void ProcessPermissions(bool? enabled, List permissions) + { + if (enabled == true) + newPermissionIds.AddRange(permissions); + else + deletePermissionIds.AddRange(permissions); + } - // ProcessPermissions(features.Modules?.ProjectManagement?.Enabled, projectPermissionTask.Result); - // ProcessPermissions(features.Modules?.Attendance?.Enabled, attendancePermissionTask.Result); - // ProcessPermissions(features.Modules?.Directory?.Enabled, directoryPermissionTask.Result); - // ProcessPermissions(features.Modules?.Expense?.Enabled, expensePermissionTask.Result); + ProcessPermissions(features.Modules?.ProjectManagement?.Enabled, projectPermissionTask.Result); + ProcessPermissions(features.Modules?.Attendance?.Enabled, attendancePermissionTask.Result); + ProcessPermissions(features.Modules?.Directory?.Enabled, directoryPermissionTask.Result); + ProcessPermissions(features.Modules?.Expense?.Enabled, expensePermissionTask.Result); - // newPermissionIds = newPermissionIds.Distinct().ToList(); - // deletePermissionIds = deletePermissionIds.Distinct().ToList(); + newPermissionIds = newPermissionIds.Distinct().ToList(); + deletePermissionIds = deletePermissionIds.Distinct().ToList(); - // // Get root employee and 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); + // Get root employee and 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("Root employee not found for tenant {TenantId}", model.TenantId); - // await transaction.CommitAsync(); - // return Ok(ApiResponse.SuccessResponse(tenantSubscription, "Tenant subscription successfully added", 200)); - // } + if (rootEmployee == null) + { + _logger.LogWarning("Root employee not found for tenant {TenantId}", model.TenantId); + await transaction.CommitAsync(); + return Ok(ApiResponse.SuccessResponse(tenantSubscription, "Tenant subscription successfully added", 200)); + } - // var roleId = await _context.EmployeeRoleMappings - // .AsNoTracking() - // .Where(er => er.EmployeeId == rootEmployee.Id && er.TenantId == model.TenantId) - // .Select(er => er.RoleId) - // .FirstOrDefaultAsync(); + var roleId = await _context.EmployeeRoleMappings + .AsNoTracking() + .Where(er => er.EmployeeId == rootEmployee.Id && er.TenantId == model.TenantId) + .Select(er => er.RoleId) + .FirstOrDefaultAsync(); - // if (roleId == Guid.Empty) - // { - // _logger.LogWarning("RoleId for root employee {EmployeeId} in tenant {TenantId} not found", rootEmployee.Id, model.TenantId); - // await transaction.CommitAsync(); - // return Ok(ApiResponse.SuccessResponse(tenantSubscription, "Tenant subscription successfully added", 200)); - // } + if (roleId == Guid.Empty) + { + _logger.LogWarning("RoleId for root employee {EmployeeId} in tenant {TenantId} not found", rootEmployee.Id, model.TenantId); + await transaction.CommitAsync(); + return Ok(ApiResponse.SuccessResponse(tenantSubscription, "Tenant subscription successfully added", 200)); + } - // var oldRolePermissionMappings = await _context.RolePermissionMappings - // .Where(rp => rp.ApplicationRoleId == roleId) - // .ToListAsync(); + var oldRolePermissionMappings = await _context.RolePermissionMappings + .Where(rp => rp.ApplicationRoleId == roleId) + .ToListAsync(); - // var oldPermissionIds = oldRolePermissionMappings.Select(rp => rp.FeaturePermissionId).ToList(); + var oldPermissionIds = oldRolePermissionMappings.Select(rp => rp.FeaturePermissionId).ToList(); - // // Prevent accidentally deleting essential employee permissions - // var permissionIdCount = oldPermissionIds.Count - deletePermissionIds.Count; - // if (permissionIdCount <= 4 && deletePermissionIds.Any()) - // { - // var employeePermissionIds = employeePermissionTask.Result; - // deletePermissionIds = deletePermissionIds.Where(p => !employeePermissionIds.Contains(p)).ToList(); - // } + // Prevent accidentally deleting essential employee permissions + var permissionIdCount = oldPermissionIds.Count - deletePermissionIds.Count; + if (permissionIdCount <= 4 && deletePermissionIds.Any()) + { + var employeePermissionIds = employeePermissionTask.Result; + deletePermissionIds = deletePermissionIds.Where(p => !employeePermissionIds.Contains(p)).ToList(); + } - // // Prepare mappings to delete and add - // var deleteMappings = oldRolePermissionMappings.Where(rp => deletePermissionIds.Contains(rp.FeaturePermissionId)).ToList(); - // var addRolePermissionMappings = newPermissionIds - // .Where(p => !oldPermissionIds.Contains(p)) - // .Select(p => new RolePermissionMappings - // { - // ApplicationRoleId = roleId, - // FeaturePermissionId = p - // }) - // .ToList(); + // Prepare mappings to delete and add + var deleteMappings = oldRolePermissionMappings.Where(rp => deletePermissionIds.Contains(rp.FeaturePermissionId)).ToList(); + var addRolePermissionMappings = newPermissionIds + .Where(p => !oldPermissionIds.Contains(p)) + .Select(p => new RolePermissionMappings + { + ApplicationRoleId = roleId, + FeaturePermissionId = p + }) + .ToList(); - // if (addRolePermissionMappings.Any()) - // { - // _context.RolePermissionMappings.AddRange(addRolePermissionMappings); - // _logger.LogInfo("Added {Count} new role permission mappings for role {RoleId}", addRolePermissionMappings.Count, roleId); - // } - // if (deleteMappings.Any()) - // { - // _context.RolePermissionMappings.RemoveRange(deleteMappings); - // _logger.LogInfo("Removed {Count} role permission mappings for role {RoleId}", deleteMappings.Count, roleId); - // } + if (addRolePermissionMappings.Any()) + { + _context.RolePermissionMappings.AddRange(addRolePermissionMappings); + _logger.LogInfo("Added {Count} new role permission mappings for role {RoleId}", addRolePermissionMappings.Count, roleId); + } + if (deleteMappings.Any()) + { + _context.RolePermissionMappings.RemoveRange(deleteMappings); + _logger.LogInfo("Removed {Count} role permission mappings for role {RoleId}", deleteMappings.Count, roleId); + } - // await _context.SaveChangesAsync(); + await _context.SaveChangesAsync(); - // await transaction.CommitAsync(); + await transaction.CommitAsync(); - // _logger.LogInfo("Permissions updated successfully for tenant {TenantId} subscription", model.TenantId); + _logger.LogInfo("Permissions updated successfully for tenant {TenantId} subscription", model.TenantId); - // return Ok(ApiResponse.SuccessResponse(tenantSubscription, "Tenant Subscription Successfully", 200)); - // } - // catch (Exception ex) - // { - // await transaction.RollbackAsync(); - // _logger.LogError(ex, "Exception occurred while updating permissions for tenant {TenantId}", model.TenantId); - // return StatusCode(500, ApiResponse.ErrorResponse("Internal error occured", ExceptionMapper(ex), 500)); - // } - //} + return Ok(ApiResponse.SuccessResponse(tenantSubscription, "Tenant Subscription Successfully", 200)); + } + catch (Exception ex) + { + await transaction.RollbackAsync(); + _logger.LogError(ex, "Exception occurred while updating permissions for tenant {TenantId}", model.TenantId); + return StatusCode(500, ApiResponse.ErrorResponse("Internal error occured", ExceptionMapper(ex), 500)); + } + } //[HttpPut("update-subscription")] //public async Task UpdateSubscriptionAsync(UpdateSubscriptionDto model)