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)
{
// Validate if the employee Id from the URL path matches the Id in the request body (model)
if (id != model.Id)
{
_logger.LogWarning("Id provided by path parameter and Id from body not matches for employee {EmployeeId}", loggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("Invalid Parameters", "Invalid Parameters", 400);
// 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, 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
.Include(e => e.ExpenseCategory)
.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);
}
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);
@ -2450,89 +2456,128 @@ namespace Marco.Pms.Services.Service
_logger.LogInfo("End CreateRecurringPaymentAsync called by EmployeeId: {EmployeeId}", loggedInEmployee.Id);
}
}
public async Task<ApiResponse<object>> EditRecurringPaymentAsync(Guid id, RecurringTemplateDto model, Employee loggedInEmployee, Guid tenantId)
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
var hasPermission = await permissionService.HasPermission(PermissionsMaster.ManageRecurring, loggedInEmployee.Id);
_logger.LogInfo("Start EditRecurringPaymentAsync called by EmployeeId: {EmployeeId} for TenantId: {TenantId}, RecurringPaymentId: {RecurringPaymentId}",
loggedInEmployee.Id, tenantId, id);
if (!hasPermission)
try
{
_logger.LogWarning("Employee {EmployeeId} attempted to update recurring template", 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);
// 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();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
var hasPermission = await permissionService.HasPermission(PermissionsMaster.ManageRecurring, loggedInEmployee.Id);
if (!hasPermission)
{
_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.", "Permission denied.", 403);
}
// Concurrently fetch required related entities for validation
var expenseCategoryTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.ExpenseCategoryMasters.FirstOrDefaultAsync(et => et.Id == model.ExpenseCategoryId && et.IsActive);
});
var recurringStatusTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.RecurringPaymentStatus.FirstOrDefaultAsync(rs => rs.Id == model.StatusId);
});
var currencyTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.CurrencyMaster.FirstOrDefaultAsync(c => c.Id == model.CurrencyId);
});
var projectTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return model.ProjectId.HasValue ? await context.Projects.FirstOrDefaultAsync(p => p.Id == model.ProjectId.Value) : null;
});
await Task.WhenAll(expenseCategoryTask, recurringStatusTask, currencyTask, projectTask);
var expenseCategory = await expenseCategoryTask;
if (expenseCategory == null)
{
_logger.LogWarning("Expense Category not found with Id: {ExpenseCategoryId}", model.ExpenseCategoryId);
return ApiResponse<object>.ErrorResponse("Expense Category not found.", "Expense Category not found.", 404);
}
var recurringStatus = await recurringStatusTask;
if (recurringStatus == null)
{
_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);
}
var currency = await currencyTask;
if (currency == null)
{
_logger.LogWarning("Currency not found with Id: {CurrencyId}", model.CurrencyId);
return ApiResponse<object>.ErrorResponse("Currency not found.", "Currency not found.", 404);
}
var project = await projectTask; // Optional
// 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)
{
_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);
recurringPayment.UpdatedAt = DateTime.UtcNow;
recurringPayment.UpdatedById = loggedInEmployee.Id;
// Save changes to database
await _context.SaveChangesAsync();
// Map entity to view model for response
var response = _mapper.Map<RecurringPaymentVM>(recurringPayment);
response.RecurringPaymentUId = $"{recurringPayment.UIDPrefix}/{recurringPayment.UIDPostfix:D5}";
response.CreatedBy = _mapper.Map<BasicEmployeeVM>(loggedInEmployee);
response.ExpenseCategory = _mapper.Map<ExpensesCategoryMasterVM>(expenseCategory);
response.Status = recurringStatus;
response.Currency = currency;
response.Project = _mapper.Map<BasicProjectVM>(project);
_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);
}
var expenseCategoryTask = Task.Run(async () =>
catch (Exception ex)
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.ExpenseCategoryMasters.FirstOrDefaultAsync(et => et.Id == model.ExpenseCategoryId && et.IsActive);
});
var recurringStatusTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.RecurringPaymentStatus.FirstOrDefaultAsync(et => et.Id == model.StatusId);
});
var currencyTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.CurrencyMaster.FirstOrDefaultAsync(c => c.Id == model.CurrencyId);
});
var projectTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.Projects.FirstOrDefaultAsync(P => model.ProjectId.HasValue && P.Id == model.ProjectId.Value);
});
await Task.WhenAll(expenseCategoryTask, currencyTask, projectTask, recurringStatusTask);
var expenseCategory = await expenseCategoryTask;
if (expenseCategory == null)
{
_logger.LogWarning("Expense Category not found with Id: {ExpenseCategoryId}", model.ExpenseCategoryId);
return ApiResponse<object>.ErrorResponse("Expense Category not found.", "Expense Category not found.", 404);
_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);
}
var recurringStatus = await recurringStatusTask;
if (expenseCategory == null)
finally
{
_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);
_logger.LogInfo("End EditRecurringPaymentAsync called by EmployeeId: {EmployeeId}", loggedInEmployee.Id);
}
var currency = await currencyTask;
if (currency == null)
{
_logger.LogWarning("Currency not found with Id: {CurrencyId}", model.CurrencyId);
return ApiResponse<object>.ErrorResponse("Currency not found.", "Currency not found.", 404);
}
var project = await projectTask;
var recurringPayment = await _context.RecurringPayments.FirstOrDefaultAsync(rp => rp.Id == id && rp.IsActive && rp.TenantId == tenantId);
if (recurringPayment == null)
{
return ApiResponse<object>.ErrorResponse("Recurring Payment Template not found", "Recurring Payment Template not found", 404);
}
_mapper.Map(model, recurringPayment);
recurringPayment.UpdatedAt = DateTime.UtcNow;
recurringPayment.UpdatedById = loggedInEmployee.Id;
await _context.SaveChangesAsync();
var response = _mapper.Map<RecurringPaymentVM>(recurringPayment);
response.RecurringPaymentUId = $"{recurringPayment.UIDPrefix}/{recurringPayment.UIDPostfix:D5}";
response.CreatedBy = _mapper.Map<BasicEmployeeVM>(loggedInEmployee);
response.ExpenseCategory = _mapper.Map<ExpensesCategoryMasterVM>(expenseCategory);
response.Status = recurringStatus;
response.Currency = currency;
response.Project = _mapper.Map<BasicProjectVM>(project);
return ApiResponse<object>.SuccessResponse(response, "Recurring Payment Template updated successfully", 200);
}
#endregion
#region =================================================================== Helper Functions ===================================================================