Added the API to create the payment request
This commit is contained in:
parent
eccd4f6d76
commit
b54b83c63d
@ -157,6 +157,18 @@ namespace Marco.Pms.Services.Controllers
|
||||
var response = await _expensesService.GetPaymentRequestFilterObjectAsync(loggedInEmployee, tenantId);
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
[HttpPost("payment-request/create")]
|
||||
public async Task<IActionResult> CreatePaymentRequest([FromBody] PaymentRequestDto model)
|
||||
{
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _expensesService.CreatePaymentRequestAsync(model, loggedInEmployee, tenantId);
|
||||
if (response.Success)
|
||||
{
|
||||
var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Payment_Request", Response = response.Data };
|
||||
await _signalR.SendNotificationAsync(notification);
|
||||
}
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region =================================================================== Payment Request Functions ===================================================================
|
||||
|
||||
@ -7,6 +7,7 @@ using Marco.Pms.Model.Employees;
|
||||
using Marco.Pms.Model.Entitlements;
|
||||
using Marco.Pms.Model.Expenses;
|
||||
using Marco.Pms.Model.Filters;
|
||||
using Marco.Pms.Model.Master;
|
||||
using Marco.Pms.Model.MongoDBModels;
|
||||
using Marco.Pms.Model.MongoDBModels.Employees;
|
||||
using Marco.Pms.Model.MongoDBModels.Expenses;
|
||||
@ -1654,6 +1655,170 @@ namespace Marco.Pms.Services.Service
|
||||
return ApiResponse<object>.ErrorResponse("Internal Exception Occured", ExceptionMapper(ex), 500);
|
||||
}
|
||||
}
|
||||
public async Task<ApiResponse<object>> CreatePaymentRequestAsync(PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId)
|
||||
{
|
||||
_logger.LogInfo("Start CreatePaymentRequestAsync for EmployeeId: {EmployeeId} TenantId: {TenantId}", loggedInEmployee.Id, tenantId);
|
||||
|
||||
string uIDPrefix = $"PR/{DateTime.Now:MMyy}";
|
||||
|
||||
try
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
|
||||
var hasUploadPermission = await permissionService.HasPermission(PermissionsMaster.ExpenseUpload, loggedInEmployee.Id);
|
||||
|
||||
if (!hasUploadPermission)
|
||||
{
|
||||
_logger.LogWarning("Access DENIED: Employee {EmployeeId} has no permission to create payment requests.", loggedInEmployee.Id);
|
||||
return ApiResponse<object>.ErrorResponse("Access Denied", "You do not have permission to create any payment request.", 409);
|
||||
}
|
||||
|
||||
// Execute database lookups concurrently
|
||||
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 currencyTask = Task.Run(async () =>
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await context.CurrencyMaster.FirstOrDefaultAsync(c => c.Id == model.CurrencyId);
|
||||
});
|
||||
var expenseStatusTask = Task.Run(async () =>
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await context.ExpensesStatusMaster.FirstOrDefaultAsync(es => es.Id == Draft && es.IsActive);
|
||||
});
|
||||
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, expenseStatusTask, 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 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 expenseStatus = await expenseStatusTask;
|
||||
if (expenseStatus == null)
|
||||
{
|
||||
_logger.LogWarning("Expense Status not found for Draft.");
|
||||
return ApiResponse<object>.ErrorResponse("Expense Status (Draft) not found.", "Expense Status not found.", 404);
|
||||
}
|
||||
|
||||
var project = await projectTask;
|
||||
// Project is optional so no error if not found
|
||||
|
||||
// Generate unique UID postfix based on existing requests for the current prefix
|
||||
var lastPR = await _context.PaymentRequests.Where(pr => pr.UIDPrefix == uIDPrefix)
|
||||
.OrderByDescending(pr => pr.UIDPostfix)
|
||||
.FirstOrDefaultAsync();
|
||||
int uIDPostfix = lastPR == null ? 1 : (lastPR.UIDPostfix + 1);
|
||||
|
||||
// Map DTO to PaymentRequest entity and set additional mandatory fields
|
||||
var paymentRequest = _mapper.Map<PaymentRequest>(model);
|
||||
paymentRequest.ExpenseStatusId = Draft;
|
||||
paymentRequest.UIDPrefix = uIDPrefix;
|
||||
paymentRequest.UIDPostfix = uIDPostfix;
|
||||
paymentRequest.IsActive = true;
|
||||
paymentRequest.CreatedAt = DateTime.UtcNow;
|
||||
paymentRequest.CreatedById = loggedInEmployee.Id;
|
||||
paymentRequest.TenantId = tenantId;
|
||||
|
||||
await _context.PaymentRequests.AddAsync(paymentRequest);
|
||||
|
||||
// 8. Add paymentRequest Log Entry
|
||||
_context.StatusUpdateLogs.Add(new StatusUpdateLog
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
EntityId = paymentRequest.Id,
|
||||
StatusId = Draft,
|
||||
NextStatusId = Draft,
|
||||
UpdatedById = loggedInEmployee.Id,
|
||||
UpdatedAt = DateTime.UtcNow,
|
||||
Comment = "Payment request is submited as draft",
|
||||
TenantId = tenantId
|
||||
});
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// Process bill attachments if any
|
||||
if (model.BillAttachments?.Any() == true)
|
||||
{
|
||||
_logger.LogInfo("Processing {AttachmentCount} attachments for PaymentRequest Id: {PaymentRequestId}",
|
||||
model.BillAttachments.Count, paymentRequest.Id);
|
||||
|
||||
// Validate base64 attachments data before processing
|
||||
foreach (var attachment in model.BillAttachments)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(attachment.Base64Data) || !_s3Service.IsBase64String(attachment.Base64Data))
|
||||
{
|
||||
_logger.LogWarning("Invalid or missing Base64 data for attachment: {FileName}", attachment.FileName ?? "N/A");
|
||||
throw new ArgumentException($"Invalid or missing Base64 data for attachment: {attachment.FileName ?? "N/A"}");
|
||||
}
|
||||
}
|
||||
|
||||
var batchId = Guid.NewGuid();
|
||||
|
||||
// Process all attachments concurrently
|
||||
var processingTasks = model.BillAttachments.Select(attachment =>
|
||||
ProcessSinglePaymentRequestAttachmentAsync(attachment, paymentRequest, loggedInEmployee.Id, tenantId, batchId)
|
||||
).ToList();
|
||||
|
||||
var results = await Task.WhenAll(processingTasks);
|
||||
|
||||
// Add documents and payment request attachments after concurrent processing
|
||||
foreach (var (document, paymentRequestAttachment) in results)
|
||||
{
|
||||
_context.Documents.Add(document);
|
||||
_context.PaymentRequestAttachments.Add(paymentRequestAttachment);
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInfo("{AttachmentCount} attachments processed and saved for PaymentRequest Id: {PaymentRequestId}",
|
||||
results.Length, paymentRequest.Id);
|
||||
}
|
||||
|
||||
// Prepare response VM
|
||||
var response = _mapper.Map<PaymentRequestVM>(paymentRequest);
|
||||
response.PaymentRequestUID = $"{paymentRequest.UIDPrefix}/{paymentRequest.UIDPostfix:D5}";
|
||||
response.Currency = currency;
|
||||
response.ExpenseCategory = _mapper.Map<ExpenseCategoryMasterVM>(expenseCategory);
|
||||
response.ExpenseStatus = _mapper.Map<ExpensesStatusMasterVM>(expenseStatus);
|
||||
response.Project = _mapper.Map<BasicProjectVM>(project);
|
||||
response.CreatedBy = _mapper.Map<BasicEmployeeVM>(loggedInEmployee);
|
||||
|
||||
_logger.LogInfo("Payment request created successfully with UID: {PaymentRequestUID}", response.PaymentRequestUID);
|
||||
return ApiResponse<object>.SuccessResponse(response, "Created the Payment Request Successfully.", 201);
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
_logger.LogError(ex, "Argument error in CreatePaymentRequestAsync: {Message}", ex.Message);
|
||||
return ApiResponse<object>.ErrorResponse(ex.Message, "Invalid data.", 400);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Unexpected error in CreatePaymentRequestAsync: {Message}", ex.Message);
|
||||
return ApiResponse<object>.ErrorResponse("An error occurred while creating the payment request.", ex.Message, 500);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_logger.LogInfo("End CreatePaymentRequestAsync for EmployeeId: {EmployeeId}", loggedInEmployee.Id);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region =================================================================== Payment Request Functions ===================================================================
|
||||
|
||||
@ -22,6 +22,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
|
||||
Task<ApiResponse<object>> GetPaymentRequestDetailsAsync(Guid? id, string? paymentRequestUId, Employee loggedInEmployee, Guid tenantId);
|
||||
Task<ApiResponse<object>> GetPayeeNameListAsync(Employee loggedInEmployee, Guid tenantId);
|
||||
Task<ApiResponse<object>> GetPaymentRequestFilterObjectAsync(Employee loggedInEmployee, Guid tenantId);
|
||||
Task<ApiResponse<object>> CreatePaymentRequestAsync(PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId);
|
||||
#endregion
|
||||
#region =================================================================== Payment Request Functions ===================================================================
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user