Optimized the CreateSubscriptionPlan API
This commit is contained in:
parent
640b80ea82
commit
cbcae9fb57
@ -10,6 +10,6 @@
|
||||
|
||||
public enum PLAN_FREQUENCY
|
||||
{
|
||||
MONTHLY = 0, QUARTERLY = 1, HALF_MONTHLY = 2, YEARLY = 3
|
||||
MONTHLY = 0, QUARTERLY = 1, HALF_YEARLY = 2, YEARLY = 3
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
/// <param name="pageSize">The number of records to return per page.</param>
|
||||
/// <param name="pageNumber">The page number to retrieve.</param>
|
||||
/// <returns>A paginated list of tenants matching the criteria.</returns>
|
||||
|
||||
[HttpGet("list")]
|
||||
public async Task<IActionResult> GetTenantList([FromQuery] string? searchString, string? filter, int pageSize = 20, int pageNumber = 1)
|
||||
{
|
||||
@ -617,7 +618,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
{
|
||||
PLAN_FREQUENCY.MONTHLY => utcNow.AddMonths(1),
|
||||
PLAN_FREQUENCY.QUARTERLY => utcNow.AddMonths(3),
|
||||
PLAN_FREQUENCY.HALF_MONTHLY => utcNow.AddMonths(6),
|
||||
PLAN_FREQUENCY.HALF_YEARLY => utcNow.AddMonths(6),
|
||||
PLAN_FREQUENCY.YEARLY => utcNow.AddMonths(12),
|
||||
_ => utcNow // default if unknown
|
||||
};
|
||||
@ -845,7 +846,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
{
|
||||
PLAN_FREQUENCY.MONTHLY => currentSubscription.EndDate.AddMonths(1),
|
||||
PLAN_FREQUENCY.QUARTERLY => currentSubscription.EndDate.AddMonths(3),
|
||||
PLAN_FREQUENCY.HALF_MONTHLY => currentSubscription.EndDate.AddMonths(6),
|
||||
PLAN_FREQUENCY.HALF_YEARLY => currentSubscription.EndDate.AddMonths(6),
|
||||
PLAN_FREQUENCY.YEARLY => currentSubscription.EndDate.AddMonths(12),
|
||||
_ => currentSubscription.EndDate
|
||||
};
|
||||
@ -856,7 +857,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
{
|
||||
PLAN_FREQUENCY.MONTHLY => utcNow.AddMonths(1),
|
||||
PLAN_FREQUENCY.QUARTERLY => utcNow.AddMonths(3),
|
||||
PLAN_FREQUENCY.HALF_MONTHLY => utcNow.AddMonths(6),
|
||||
PLAN_FREQUENCY.HALF_YEARLY => utcNow.AddMonths(6),
|
||||
PLAN_FREQUENCY.YEARLY => utcNow.AddMonths(12),
|
||||
_ => utcNow
|
||||
};
|
||||
@ -891,7 +892,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
{
|
||||
PLAN_FREQUENCY.MONTHLY => utcNow.AddMonths(1),
|
||||
PLAN_FREQUENCY.QUARTERLY => utcNow.AddMonths(3),
|
||||
PLAN_FREQUENCY.HALF_MONTHLY => utcNow.AddMonths(6),
|
||||
PLAN_FREQUENCY.HALF_YEARLY => utcNow.AddMonths(6),
|
||||
PLAN_FREQUENCY.YEARLY => utcNow.AddMonths(12),
|
||||
_ => utcNow
|
||||
};
|
||||
@ -1047,8 +1048,6 @@ namespace Marco.Pms.Services.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region =================================================================== Subscription Plan APIs ===================================================================
|
||||
@ -1110,183 +1109,81 @@ namespace Marco.Pms.Services.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new subscription plan with details for each frequency (monthly, quarterly, half-yearly, yearly).
|
||||
/// Only employees with root status and 'ManageTenants' permission can create plans.
|
||||
/// </summary>
|
||||
[HttpPost("create/subscription-plan")]
|
||||
public async Task<IActionResult> CreateSubscriptionPlan1([FromBody] SubscriptionPlanDto model)
|
||||
public async Task<IActionResult> CreateSubscriptionPlan([FromBody] SubscriptionPlanDto model)
|
||||
{
|
||||
// Step 1: Authenticate and check permissions
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
|
||||
var _permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
|
||||
|
||||
// A root user should have access regardless of the specific permission.
|
||||
// Permission check: root user or explicit ManageTenants permission
|
||||
var isRootUser = loggedInEmployee.ApplicationUser?.IsRootUser ?? false;
|
||||
var hasPermission = await _permissionService.HasPermission(PermissionsMaster.ManageTenants, loggedInEmployee.Id);
|
||||
|
||||
if (!hasPermission || !isRootUser)
|
||||
if (!(hasPermission && isRootUser))
|
||||
{
|
||||
_logger.LogWarning("Permission denied: User {EmployeeId} attempted to list tenants without 'ManageTenants' permission or root access.", loggedInEmployee.Id);
|
||||
_logger.LogWarning("Permission denied: User {EmployeeId} attempted to create a subscription plan.", loggedInEmployee.Id);
|
||||
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access denied", "User does not have the required permissions for this action.", 403));
|
||||
}
|
||||
|
||||
_logger.LogInfo("User {EmployeeId} authorized to create subscription plan.", loggedInEmployee.Id);
|
||||
|
||||
// Step 2: Map DTO to entity and persist the base SubscriptionPlan
|
||||
var plan = _mapper.Map<SubscriptionPlan>(model);
|
||||
_context.SubscriptionPlans.Add(plan);
|
||||
|
||||
List<SubscriptionPlanVM> response = new List<SubscriptionPlanVM>();
|
||||
|
||||
if (model.MonthlyPlan != null)
|
||||
{
|
||||
var currencyMaster = await _context.CurrencyMaster.AsNoTracking().FirstOrDefaultAsync(c => c.Id == model.MonthlyPlan.CurrencyId);
|
||||
if (currencyMaster == null)
|
||||
{
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Currency not found", "Currency not found", 404));
|
||||
}
|
||||
|
||||
var monthlyPlan = _mapper.Map<SubscriptionPlanDetails>(model.MonthlyPlan);
|
||||
var features = _mapper.Map<FeatureDetails>(model.MonthlyPlan.Features);
|
||||
|
||||
try
|
||||
{
|
||||
await _featureDetailsHelper.AddFeatureDetails(features);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Exception occured while saving feature in mongoDB");
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500));
|
||||
}
|
||||
|
||||
monthlyPlan.PlanId = plan.Id;
|
||||
monthlyPlan.Frequency = PLAN_FREQUENCY.MONTHLY;
|
||||
monthlyPlan.FeaturesId = features.Id;
|
||||
monthlyPlan.CreatedById = loggedInEmployee.Id;
|
||||
monthlyPlan.CreateAt = DateTime.UtcNow;
|
||||
|
||||
_context.SubscriptionPlanDetails.Add(monthlyPlan);
|
||||
var VM = _mapper.Map<SubscriptionPlanVM>(monthlyPlan);
|
||||
VM.PlanName = plan.PlanName;
|
||||
VM.Description = plan.Description;
|
||||
VM.Currency = currencyMaster;
|
||||
response.Add(VM);
|
||||
}
|
||||
|
||||
if (model.QuarterlyPlan != null)
|
||||
{
|
||||
var currencyMaster = await _context.CurrencyMaster.AsNoTracking().FirstOrDefaultAsync(c => c.Id == model.QuarterlyPlan.CurrencyId);
|
||||
if (currencyMaster == null)
|
||||
{
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Currency not found", "Currency not found", 404));
|
||||
}
|
||||
|
||||
var quarterlyPlan = _mapper.Map<SubscriptionPlanDetails>(model.QuarterlyPlan);
|
||||
var features = _mapper.Map<FeatureDetails>(model.QuarterlyPlan.Features);
|
||||
|
||||
try
|
||||
{
|
||||
await _featureDetailsHelper.AddFeatureDetails(features);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Exception occured while saving feature in mongoDB");
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500));
|
||||
}
|
||||
|
||||
quarterlyPlan.PlanId = plan.Id;
|
||||
quarterlyPlan.Frequency = PLAN_FREQUENCY.QUARTERLY;
|
||||
quarterlyPlan.FeaturesId = features.Id;
|
||||
quarterlyPlan.CreatedById = loggedInEmployee.Id;
|
||||
quarterlyPlan.CreateAt = DateTime.UtcNow;
|
||||
|
||||
_context.SubscriptionPlanDetails.Add(quarterlyPlan);
|
||||
var VM = _mapper.Map<SubscriptionPlanVM>(quarterlyPlan);
|
||||
VM.PlanName = plan.PlanName;
|
||||
VM.Description = plan.Description;
|
||||
VM.Currency = currencyMaster;
|
||||
response.Add(VM);
|
||||
}
|
||||
if (model.HalfYearlyPlan != null)
|
||||
{
|
||||
var currencyMaster = await _context.CurrencyMaster.AsNoTracking().FirstOrDefaultAsync(c => c.Id == model.HalfYearlyPlan.CurrencyId);
|
||||
if (currencyMaster == null)
|
||||
{
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Currency not found", "Currency not found", 404));
|
||||
}
|
||||
|
||||
var halfYearlyPlan = _mapper.Map<SubscriptionPlanDetails>(model.HalfYearlyPlan);
|
||||
var features = _mapper.Map<FeatureDetails>(model.HalfYearlyPlan.Features);
|
||||
|
||||
try
|
||||
{
|
||||
await _featureDetailsHelper.AddFeatureDetails(features);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Exception occured while saving feature in mongoDB");
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500));
|
||||
}
|
||||
|
||||
halfYearlyPlan.PlanId = plan.Id;
|
||||
halfYearlyPlan.Frequency = PLAN_FREQUENCY.HALF_MONTHLY;
|
||||
halfYearlyPlan.FeaturesId = features.Id;
|
||||
halfYearlyPlan.CreatedById = loggedInEmployee.Id;
|
||||
halfYearlyPlan.CreateAt = DateTime.UtcNow;
|
||||
|
||||
_context.SubscriptionPlanDetails.Add(halfYearlyPlan);
|
||||
var VM = _mapper.Map<SubscriptionPlanVM>(halfYearlyPlan);
|
||||
VM.PlanName = plan.PlanName;
|
||||
VM.Description = plan.Description;
|
||||
VM.Currency = currencyMaster;
|
||||
response.Add(VM);
|
||||
}
|
||||
if (model.YearlyPlan != null)
|
||||
{
|
||||
var currencyMaster = await _context.CurrencyMaster.AsNoTracking().FirstOrDefaultAsync(c => c.Id == model.YearlyPlan.CurrencyId);
|
||||
if (currencyMaster == null)
|
||||
{
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Currency not found", "Currency not found", 404));
|
||||
}
|
||||
|
||||
var yearlyPlan = _mapper.Map<SubscriptionPlanDetails>(model.YearlyPlan);
|
||||
var features = _mapper.Map<FeatureDetails>(model.YearlyPlan.Features);
|
||||
|
||||
try
|
||||
{
|
||||
await _featureDetailsHelper.AddFeatureDetails(features);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Exception occured while saving feature in mongoDB");
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500));
|
||||
}
|
||||
|
||||
yearlyPlan.PlanId = plan.Id;
|
||||
yearlyPlan.Frequency = PLAN_FREQUENCY.YEARLY;
|
||||
yearlyPlan.FeaturesId = features.Id;
|
||||
yearlyPlan.CreatedById = loggedInEmployee.Id;
|
||||
yearlyPlan.CreateAt = DateTime.UtcNow;
|
||||
|
||||
_context.SubscriptionPlanDetails.Add(yearlyPlan);
|
||||
var VM = _mapper.Map<SubscriptionPlanVM>(yearlyPlan);
|
||||
VM.PlanName = plan.PlanName;
|
||||
VM.Description = plan.Description;
|
||||
VM.Currency = currencyMaster;
|
||||
response.Add(VM);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
_logger.LogInfo("Base subscription plan {PlanId} saved by user {EmployeeId}.", plan.Id, loggedInEmployee.Id);
|
||||
}
|
||||
catch (DbUpdateException dbEx)
|
||||
{
|
||||
_logger.LogError(dbEx, "Database Exception occured while saving subscription plan");
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(dbEx), 500));
|
||||
_logger.LogError(dbEx, "Database exception occurred while saving the base subscription plan.");
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal error occurred", ExceptionMapper(dbEx), 500));
|
||||
}
|
||||
|
||||
return StatusCode(201, ApiResponse<object>.SuccessResponse(response, "Plan Created Successfully", 201));
|
||||
// Step 3: Prepare tasks for each plan frequency
|
||||
var frequencies = new[]
|
||||
{
|
||||
(model.MonthlyPlan, PLAN_FREQUENCY.MONTHLY),
|
||||
(model.QuarterlyPlan, PLAN_FREQUENCY.QUARTERLY),
|
||||
(model.HalfYearlyPlan, PLAN_FREQUENCY.HALF_YEARLY),
|
||||
(model.YearlyPlan, PLAN_FREQUENCY.YEARLY)
|
||||
};
|
||||
|
||||
var tasks = frequencies
|
||||
.Select(f => CreateSubscriptionPlanDetails(f.Item1, plan, loggedInEmployee, f.Item2))
|
||||
.ToArray();
|
||||
|
||||
// Await all frequency tasks
|
||||
await Task.WhenAll(tasks);
|
||||
|
||||
// Step 4: Collect successful plan details or return errors if any
|
||||
var responseList = new List<SubscriptionPlanVM>();
|
||||
for (int i = 0; i < tasks.Length; i++)
|
||||
{
|
||||
var result = tasks[i].Result;
|
||||
if (result.StatusCode == 200 && result.Data != null)
|
||||
{
|
||||
responseList.Add(result.Data);
|
||||
}
|
||||
// status code 400 - skip, e.g. missing data for this frequency
|
||||
else if (result.StatusCode != 400)
|
||||
{
|
||||
_logger.LogWarning("Failed to create plan details for {Frequency}: {Error}", frequencies[i].Item2, result.Message);
|
||||
return StatusCode(result.StatusCode, result);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInfo("Subscription plan {PlanId} created successfully with {DetailCount} details.", plan.Id, responseList.Count);
|
||||
return StatusCode(201, ApiResponse<object>.SuccessResponse(responseList, "Plan Created Successfully", 201));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -1347,7 +1244,6 @@ namespace Marco.Pms.Services.Controllers
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private TenantFilter? TryDeserializeFilter(string? filter)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filter))
|
||||
@ -1387,6 +1283,71 @@ namespace Marco.Pms.Services.Controllers
|
||||
return expenseFilter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the creation and persistence of SubscriptionPlanDetails for a particular frequency.
|
||||
/// </summary>
|
||||
private async Task<ApiResponse<SubscriptionPlanVM>> CreateSubscriptionPlanDetails(SubscriptionPlanDetailsDto? model, SubscriptionPlan plan, Employee loggedInEmployee, PLAN_FREQUENCY frequency)
|
||||
{
|
||||
if (model == null)
|
||||
{
|
||||
_logger.LogInfo("No plan detail provided for {Frequency} - skipping.", frequency);
|
||||
return ApiResponse<SubscriptionPlanVM>.ErrorResponse("Invalid", "No data provided for this frequency", 400);
|
||||
}
|
||||
|
||||
await using var _dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
|
||||
// Fetch currency master record
|
||||
var currencyMaster = await _dbContext.CurrencyMaster.AsNoTracking().FirstOrDefaultAsync(c => c.Id == model.CurrencyId);
|
||||
if (currencyMaster == null)
|
||||
{
|
||||
_logger.LogWarning("Currency with Id {CurrencyId} not found for plan {PlanId}/{Frequency}.", model.CurrencyId, plan.Id, frequency);
|
||||
return ApiResponse<SubscriptionPlanVM>.ErrorResponse("Currency not found", "Specified currency not found", 404);
|
||||
}
|
||||
|
||||
// Map to entity and create related feature details
|
||||
var planDetails = _mapper.Map<SubscriptionPlanDetails>(model);
|
||||
var features = _mapper.Map<FeatureDetails>(model.Features);
|
||||
|
||||
try
|
||||
{
|
||||
await _featureDetailsHelper.AddFeatureDetails(features);
|
||||
_logger.LogInfo("FeatureDetails for plan {PlanId}/{Frequency} saved in MongoDB.", plan.Id, frequency);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Exception occurred while saving features in MongoDB for {PlanId}/{Frequency}.", plan.Id, frequency);
|
||||
return ApiResponse<SubscriptionPlanVM>.ErrorResponse("Internal error occurred", ExceptionMapper(ex), 500);
|
||||
}
|
||||
|
||||
planDetails.PlanId = plan.Id;
|
||||
planDetails.Frequency = frequency;
|
||||
planDetails.FeaturesId = features.Id;
|
||||
planDetails.CreatedById = loggedInEmployee.Id;
|
||||
planDetails.CreateAt = DateTime.UtcNow;
|
||||
|
||||
_dbContext.SubscriptionPlanDetails.Add(planDetails);
|
||||
|
||||
// Prepare view model
|
||||
var VM = _mapper.Map<SubscriptionPlanVM>(planDetails);
|
||||
VM.PlanName = plan.PlanName;
|
||||
VM.Description = plan.Description;
|
||||
VM.Features = features;
|
||||
VM.Currency = currencyMaster;
|
||||
|
||||
try
|
||||
{
|
||||
await _dbContext.SaveChangesAsync();
|
||||
_logger.LogInfo("Subscription plan details for {PlanId}/{Frequency} saved to SQL.", plan.Id, frequency);
|
||||
}
|
||||
catch (DbUpdateException dbEx)
|
||||
{
|
||||
_logger.LogError(dbEx, "Database exception occurred while saving plan details for {PlanId}/{Frequency}.", plan.Id, frequency);
|
||||
return ApiResponse<SubscriptionPlanVM>.ErrorResponse("Internal error occurred", ExceptionMapper(dbEx), 500);
|
||||
}
|
||||
|
||||
return ApiResponse<SubscriptionPlanVM>.SuccessResponse(VM, "Success", 200);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user