Added the Recurring payment update API

This commit is contained in:
ashutosh.nehete 2025-11-04 17:51:01 +05:30
parent 2a53e1c241
commit 177f44535a

View File

@ -891,11 +891,18 @@ namespace Marco.Pms.Services.Service
public async Task<ApiResponse<object>> UpdateExpanseAsync(Guid id, UpdateExpensesDto model, Employee loggedInEmployee, Guid tenantId) public async Task<ApiResponse<object>> UpdateExpanseAsync(Guid id, UpdateExpensesDto model, Employee loggedInEmployee, Guid tenantId)
{ {
// Validate if the employee Id from the URL path matches the Id in the request body (model)
if (id != model.Id) if (id != model.Id)
{ {
_logger.LogWarning("Id provided by path parameter and Id from body not matches for employee {EmployeeId}", loggedInEmployee.Id); // Log a warning with details for traceability when Ids do not match
return ApiResponse<object>.ErrorResponse("Invalid Parameters", "Invalid Parameters", 400); _logger.LogWarning("Mismatch detected: Path parameter Id ({PathId}) does not match body Id ({BodyId}) for employee {EmployeeId}",
id, model.Id, loggedInEmployee.Id);
// Return standardized error response with HTTP 400 Bad Request status and clear message
return ApiResponse<object>.ErrorResponse("The employee Id in the path does not match the Id in the request body.",
"The employee Id in the path does not match the Id in the request body.", 400);
} }
var existingExpense = await _context.Expenses var existingExpense = await _context.Expenses
.Include(e => e.ExpenseCategory) .Include(e => e.ExpenseCategory)
.Include(e => e.Project) .Include(e => e.Project)
@ -2337,7 +2344,6 @@ namespace Marco.Pms.Services.Service
}; };
return ApiResponse<object>.SuccessResponse(response, $"{results.Count} Recurring payments fetched successfully", 200); return ApiResponse<object>.SuccessResponse(response, $"{results.Count} Recurring payments fetched successfully", 200);
} }
public async Task<ApiResponse<object>> CreateRecurringPaymentAsync(RecurringTemplateDto model, Employee loggedInEmployee, Guid tenantId) public async Task<ApiResponse<object>> CreateRecurringPaymentAsync(RecurringTemplateDto model, Employee loggedInEmployee, Guid tenantId)
{ {
_logger.LogInfo("Start CreateRecurringPaymentAsync called by EmployeeId: {EmployeeId} for TenantId: {TenantId}", loggedInEmployee.Id, tenantId); _logger.LogInfo("Start CreateRecurringPaymentAsync called by EmployeeId: {EmployeeId} for TenantId: {TenantId}", loggedInEmployee.Id, tenantId);
@ -2450,42 +2456,62 @@ namespace Marco.Pms.Services.Service
_logger.LogInfo("End CreateRecurringPaymentAsync called by EmployeeId: {EmployeeId}", loggedInEmployee.Id); _logger.LogInfo("End CreateRecurringPaymentAsync called by EmployeeId: {EmployeeId}", loggedInEmployee.Id);
} }
} }
public async Task<ApiResponse<object>> EditRecurringPaymentAsync(Guid id, RecurringTemplateDto model, Employee loggedInEmployee, Guid tenantId) public async Task<ApiResponse<object>> EditRecurringPaymentAsync(Guid id, RecurringTemplateDto model, Employee loggedInEmployee, Guid tenantId)
{ {
_logger.LogInfo("Start EditRecurringPaymentAsync called by EmployeeId: {EmployeeId} for TenantId: {TenantId}, RecurringPaymentId: {RecurringPaymentId}",
loggedInEmployee.Id, tenantId, id);
try
{
// Validate if the employee Id from the URL path matches the Id in the request body (model)
if (id != model.Id || model.Id.HasValue)
{
// Log a warning with details for traceability when Ids do not match
_logger.LogWarning("Mismatch detected: Path parameter Id ({PathId}) does not match body Id ({BodyId}) for employee {EmployeeId}",
id, model.Id ?? Guid.Empty, loggedInEmployee.Id);
// Return standardized error response with HTTP 400 Bad Request status and clear message
return ApiResponse<object>.ErrorResponse("The employee Id in the path does not match the Id in the request body.",
"The employee Id in the path does not match the Id in the request body.", 400);
}
// Permission check for managing recurring payments
using var scope = _serviceScopeFactory.CreateScope(); using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>(); var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
var hasPermission = await permissionService.HasPermission(PermissionsMaster.ManageRecurring, loggedInEmployee.Id); var hasPermission = await permissionService.HasPermission(PermissionsMaster.ManageRecurring, loggedInEmployee.Id);
if (!hasPermission) if (!hasPermission)
{ {
_logger.LogWarning("Employee {EmployeeId} attempted to update recurring template", loggedInEmployee.Id); _logger.LogWarning("Access denied: Employee {EmployeeId} attempted to update recurring template without permission.", loggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("You do not have access to update recurring template", "You do not have access to update recurring template", 400); return ApiResponse<object>.ErrorResponse("You do not have access to update recurring template.", "Permission denied.", 403);
} }
// Concurrently fetch required related entities for validation
var expenseCategoryTask = Task.Run(async () => var expenseCategoryTask = Task.Run(async () =>
{ {
await using var context = await _dbContextFactory.CreateDbContextAsync(); await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.ExpenseCategoryMasters.FirstOrDefaultAsync(et => et.Id == model.ExpenseCategoryId && et.IsActive); return await context.ExpenseCategoryMasters.FirstOrDefaultAsync(et => et.Id == model.ExpenseCategoryId && et.IsActive);
}); });
var recurringStatusTask = Task.Run(async () => var recurringStatusTask = Task.Run(async () =>
{ {
await using var context = await _dbContextFactory.CreateDbContextAsync(); await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.RecurringPaymentStatus.FirstOrDefaultAsync(et => et.Id == model.StatusId); return await context.RecurringPaymentStatus.FirstOrDefaultAsync(rs => rs.Id == model.StatusId);
}); });
var currencyTask = Task.Run(async () => var currencyTask = Task.Run(async () =>
{ {
await using var context = await _dbContextFactory.CreateDbContextAsync(); await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.CurrencyMaster.FirstOrDefaultAsync(c => c.Id == model.CurrencyId); return await context.CurrencyMaster.FirstOrDefaultAsync(c => c.Id == model.CurrencyId);
}); });
var projectTask = Task.Run(async () => var projectTask = Task.Run(async () =>
{ {
await using var context = await _dbContextFactory.CreateDbContextAsync(); await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.Projects.FirstOrDefaultAsync(P => model.ProjectId.HasValue && P.Id == model.ProjectId.Value); return model.ProjectId.HasValue ? await context.Projects.FirstOrDefaultAsync(p => p.Id == model.ProjectId.Value) : null;
}); });
await Task.WhenAll(expenseCategoryTask, currencyTask, projectTask, recurringStatusTask); await Task.WhenAll(expenseCategoryTask, recurringStatusTask, currencyTask, projectTask);
var expenseCategory = await expenseCategoryTask; var expenseCategory = await expenseCategoryTask;
if (expenseCategory == null) if (expenseCategory == null)
@ -2495,7 +2521,7 @@ namespace Marco.Pms.Services.Service
} }
var recurringStatus = await recurringStatusTask; var recurringStatus = await recurringStatusTask;
if (expenseCategory == null) if (recurringStatus == null)
{ {
_logger.LogWarning("Recurring Payment Status not found with Id: {StatusId}", model.StatusId); _logger.LogWarning("Recurring Payment Status not found with Id: {StatusId}", model.StatusId);
return ApiResponse<object>.ErrorResponse("Recurring Payment Status not found.", "Recurring Payment Status not found.", 404); return ApiResponse<object>.ErrorResponse("Recurring Payment Status not found.", "Recurring Payment Status not found.", 404);
@ -2508,21 +2534,27 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("Currency not found.", "Currency not found.", 404); return ApiResponse<object>.ErrorResponse("Currency not found.", "Currency not found.", 404);
} }
var project = await projectTask; var project = await projectTask; // Optional
var recurringPayment = await _context.RecurringPayments.FirstOrDefaultAsync(rp => rp.Id == id && rp.IsActive && rp.TenantId == tenantId); // Retrieve the existing recurring payment record for update
var recurringPayment = await _context.RecurringPayments
.FirstOrDefaultAsync(rp => rp.Id == id && rp.IsActive && rp.TenantId == tenantId);
if (recurringPayment == null) if (recurringPayment == null)
{ {
return ApiResponse<object>.ErrorResponse("Recurring Payment Template not found", "Recurring Payment Template not found", 404); _logger.LogWarning("Recurring Payment Template not found with Id: {RecurringPaymentId}", id);
return ApiResponse<object>.ErrorResponse("Recurring Payment Template not found.", "Recurring Payment Template not found.", 404);
} }
// Map updated values from DTO to entity
_mapper.Map(model, recurringPayment); _mapper.Map(model, recurringPayment);
recurringPayment.UpdatedAt = DateTime.UtcNow; recurringPayment.UpdatedAt = DateTime.UtcNow;
recurringPayment.UpdatedById = loggedInEmployee.Id; recurringPayment.UpdatedById = loggedInEmployee.Id;
// Save changes to database
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
// Map entity to view model for response
var response = _mapper.Map<RecurringPaymentVM>(recurringPayment); var response = _mapper.Map<RecurringPaymentVM>(recurringPayment);
response.RecurringPaymentUId = $"{recurringPayment.UIDPrefix}/{recurringPayment.UIDPostfix:D5}"; response.RecurringPaymentUId = $"{recurringPayment.UIDPrefix}/{recurringPayment.UIDPostfix:D5}";
response.CreatedBy = _mapper.Map<BasicEmployeeVM>(loggedInEmployee); response.CreatedBy = _mapper.Map<BasicEmployeeVM>(loggedInEmployee);
@ -2531,8 +2563,21 @@ namespace Marco.Pms.Services.Service
response.Currency = currency; response.Currency = currency;
response.Project = _mapper.Map<BasicProjectVM>(project); response.Project = _mapper.Map<BasicProjectVM>(project);
return ApiResponse<object>.SuccessResponse(response, "Recurring Payment Template updated successfully", 200); _logger.LogInfo("Recurring Payment Template updated successfully with UID: {RecurringPaymentUId} by EmployeeId: {EmployeeId}", response.RecurringPaymentUId, loggedInEmployee.Id);
return ApiResponse<object>.SuccessResponse(response, "Recurring Payment Template updated successfully.", 200);
} }
catch (Exception ex)
{
_logger.LogError(ex, "Error in EditRecurringPaymentAsync called by EmployeeId: {EmployeeId}: {Message}", loggedInEmployee.Id, ex.Message);
return ApiResponse<object>.ErrorResponse("An error occurred while updating the recurring payment template.", ex.Message, 500);
}
finally
{
_logger.LogInfo("End EditRecurringPaymentAsync called by EmployeeId: {EmployeeId}", loggedInEmployee.Id);
}
}
#endregion #endregion
#region =================================================================== Helper Functions =================================================================== #region =================================================================== Helper Functions ===================================================================