Added the API to create payment request from recurring payment

This commit is contained in:
ashutosh.nehete 2025-11-07 17:58:15 +05:30
parent 5910517d01
commit c71343d550
3 changed files with 137 additions and 0 deletions

View File

@ -257,6 +257,15 @@ namespace Marco.Pms.Services.Controllers
}
[HttpPost("recurring-payment/convert/payment-request")]
public async Task<IActionResult> PaymentRequestConversion(PaymentRequestConversionDto model)
{
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _expensesService.PaymentRequestConversionAsync(model.RecurringTemplateIds, loggedInEmployee, tenantId);
return StatusCode(response.StatusCode, response);
}
[HttpPut("recurring-payment/edit/{id}")]
public async Task<IActionResult> EditRecurringPaymentAsync(Guid id, [FromBody] UpdateRecurringTemplateDto model)
{

View File

@ -2897,6 +2897,133 @@ namespace Marco.Pms.Services.Service
_logger.LogInfo("End CreateRecurringPaymentAsync called by EmployeeId: {EmployeeId}", loggedInEmployee.Id);
}
}
public async Task<ApiResponse<object>> PaymentRequestConversionAsync(List<Guid> recurringTemplateIds, Employee loggedInEmployee, Guid tenantId)
{
_logger.LogInfo("Start PaymentRequestConversionAsync called by EmployeeId: {EmployeeId} for TenantId: {TenantId} with RecurringTemplateIds: {RecurringTemplateIds}",
loggedInEmployee.Id, tenantId, recurringTemplateIds);
// SuperAdmin check - restrict access only to specific user
var superAdminId = Guid.Parse("08dd8b35-d98b-44f1-896d-12aec3f035aa");
if (loggedInEmployee.Id != superAdminId)
{
_logger.LogWarning("Access denied for EmployeeId: {EmployeeId}. Only super admin can perform this operation.", loggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("Access Denied", "User does not have permission to access this function", 403);
}
// Get active recurring payments matching the provided IDs and tenant
var recurringPayments = await _context.RecurringPayments
.AsNoTracking()
.Where(rp => recurringTemplateIds.Contains(rp.Id)
&& rp.IsActive
&& rp.StatusId == ActiveTemplateStatus
&& rp.TenantId == tenantId)
.ToListAsync();
if (!recurringPayments.Any())
{
_logger.LogWarning("No active recurring payments found for TenantId: {TenantId} and given IDs.", tenantId);
return ApiResponse<object>.ErrorResponse("Recurring template not found", "Recurring template not found", 404);
}
var updatedRecurringPayments = new List<RecurringPayment>();
var paymentRequests = new List<PaymentRequest>();
// Generate UID prefix for payment requests for this period (month/year)
string uIDPrefix = $"PR/{DateTime.Now:MM.yy}";
// Get last generated payment request for UID postfixing (to maintain unique numbering)
var lastPR = await _context.PaymentRequests
.Where(pr => pr.UIDPrefix == uIDPrefix)
.OrderByDescending(pr => pr.UIDPostfix)
.FirstOrDefaultAsync();
int uIDPostfix = lastPR == null ? 1 : lastPR.UIDPostfix + 1;
foreach (var recurringPayment in recurringPayments)
{
// Check if recurring payment is applicable for generating a new payment request
var isApplicable = IsRecurringApplicable(
recurringPayment.NumberOfIteration,
recurringPayment.Frequency,
recurringPayment.StrikeDate.Date,
recurringPayment.LatestPRGeneratedAt);
if (isApplicable)
{
// Update latest generated date to today (UTC)
recurringPayment.LatestPRGeneratedAt = DateTime.UtcNow.Date;
updatedRecurringPayments.Add(recurringPayment);
// Create new payment request mapped from recurring payment data
var newPaymentRequest = new PaymentRequest
{
Id = Guid.NewGuid(),
Title = recurringPayment.Title,
Description = recurringPayment.Description,
UIDPrefix = uIDPrefix,
UIDPostfix = uIDPostfix,
Payee = recurringPayment.Payee,
IsAdvancePayment = false,
CurrencyId = recurringPayment.CurrencyId,
Amount = recurringPayment.Amount,
DueDate = DateTime.UtcNow.AddDays(recurringPayment.PaymentBufferDays),
ProjectId = recurringPayment.ProjectId,
RecurringPaymentId = recurringPayment.Id,
ExpenseCategoryId = recurringPayment.ExpenseCategoryId,
ExpenseStatusId = Review,
IsExpenseCreated = false,
CreatedAt = DateTime.UtcNow,
CreatedById = loggedInEmployee.Id,
IsActive = true,
TenantId = recurringPayment.TenantId
};
paymentRequests.Add(newPaymentRequest);
uIDPostfix++;
}
}
if (!updatedRecurringPayments.Any())
{
_logger.LogWarning("No applicable recurring payments for conversion found for TenantId: {TenantId}.", tenantId);
return ApiResponse<object>.ErrorResponse("No applicable recurring templates found to convert", "No applicable recurring templates found", 404);
}
try
{
// Update recurring payments with latest generated dates
_context.RecurringPayments.UpdateRange(updatedRecurringPayments);
// Add newly created payment requests
if (paymentRequests.Any())
{
_context.PaymentRequests.AddRange(paymentRequests);
}
await _context.SaveChangesAsync();
_logger.LogInfo("{Count} payment requests created successfully from recurring payments by EmployeeId: {EmployeeId} for TenantId: {TenantId}",
paymentRequests.Count, loggedInEmployee.Id, tenantId);
return ApiResponse<object>.SuccessResponse(recurringTemplateIds, $"{paymentRequests.Count} conversion(s) to payment request completed successfully.", 201);
}
catch (DbUpdateException ex)
{
_logger.LogError(ex, "Database error during PaymentRequestConversionAsync for TenantId: {TenantId}, EmployeeId: {EmployeeId}: {Message}. Inner exception: {InnerException}",
tenantId, loggedInEmployee.Id, ex.Message, ex.InnerException?.Message ?? "");
return ApiResponse<object>.ErrorResponse("Database error occurred", ExceptionMapper(ex), 500);
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected error during PaymentRequestConversionAsync for TenantId: {TenantId}, EmployeeId: {EmployeeId}: {Message}",
tenantId, loggedInEmployee.Id, ex.Message);
return ApiResponse<object>.ErrorResponse("An unexpected error occurred", ex.Message, 500);
}
finally
{
_logger.LogInfo("End PaymentRequestConversionAsync called by EmployeeId: {EmployeeId} for TenantId: {TenantId}", loggedInEmployee.Id, tenantId);
}
}
public async Task<ApiResponse<object>> EditRecurringPaymentAsync(Guid id, UpdateRecurringTemplateDto model, Employee loggedInEmployee, Guid tenantId)
{
_logger.LogInfo("Start EditRecurringPaymentAsync called by EmployeeId: {EmployeeId} for TenantId: {TenantId}, RecurringPaymentId: {RecurringPaymentId}",

View File

@ -33,6 +33,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
Task<ApiResponse<object>> GetRecurringPaymentListAsync(string? searchString, string? filter, bool isActive, int pageSize, int pageNumber, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> GetRecurringPaymentDetailsAsync(Guid? id, string? recurringPaymentUId, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> CreateRecurringPaymentAsync(CreateRecurringTemplateDto model, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> PaymentRequestConversionAsync(List<Guid> RecurringTemplateIds, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> EditRecurringPaymentAsync(Guid id, UpdateRecurringTemplateDto model, Employee loggedInEmployee, Guid tenantId);
#endregion