Added the API to update payment request
This commit is contained in:
parent
73b85bee84
commit
6f2903c0c7
@ -197,6 +197,19 @@ namespace Marco.Pms.Services.Controllers
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
[HttpPut("payment-request/edit/{id}")]
|
||||
public async Task<IActionResult> EditPaymentRequest(Guid id, [FromBody] PaymentRequestDto model)
|
||||
{
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _expensesService.EditPaymentRequestAsync(id, 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 ===================================================================
|
||||
|
||||
|
||||
@ -2191,6 +2191,218 @@ namespace Marco.Pms.Services.Service
|
||||
_logger.LogInfo("End ChangeToExpanseFromPaymentRequestAsync called by EmployeeId: {EmployeeId}", loggedInEmployee.Id);
|
||||
}
|
||||
}
|
||||
public async Task<ApiResponse<object>> EditPaymentRequestAsync(Guid id, PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId)
|
||||
{
|
||||
_logger.LogInfo("Start EditPaymentRequestAsync for PaymentRequestId: {PaymentRequestId}, EmployeeId: {EmployeeId}", id, loggedInEmployee.Id);
|
||||
|
||||
if (model.Id == null || id != model.Id)
|
||||
{
|
||||
_logger.LogWarning("Mismatch between URL id and payload id: {Id} vs {ModelId}", id, model?.Id ?? Guid.Empty);
|
||||
return ApiResponse<object>.ErrorResponse("Invalid argument: ID mismatch.", "Invalid argument: ID mismatch.", 400);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Concurrently fetch related entities to validate input references
|
||||
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 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;
|
||||
});
|
||||
var hasManagePermissionTask = Task.Run(async () =>
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
|
||||
return await permissionService.HasPermission(PermissionsMaster.ExpenseManage, loggedInEmployee.Id);
|
||||
});
|
||||
|
||||
await Task.WhenAll(expenseCategoryTask, currencyTask, projectTask, hasManagePermissionTask);
|
||||
|
||||
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 project = await projectTask; // Project can be null (optional)
|
||||
|
||||
// Retrieve the existing payment request with relevant navigation properties for validation and mapping
|
||||
var paymentRequest = await _context.PaymentRequests
|
||||
.Include(pr => pr.ExpenseCategory)
|
||||
.Include(pr => pr.Project)
|
||||
.Include(pr => pr.ExpenseStatus)
|
||||
.Include(pr => pr.RecurringPayment)
|
||||
.Include(pr => pr.Currency)
|
||||
.Include(pr => pr.CreatedBy).ThenInclude(e => e!.JobRole)
|
||||
.Include(pr => pr.UpdatedBy).ThenInclude(e => e!.JobRole)
|
||||
.FirstOrDefaultAsync(pr => pr.Id == id);
|
||||
|
||||
if (paymentRequest == null)
|
||||
{
|
||||
_logger.LogWarning("Payment Request not found with Id: {PaymentRequestId}", id);
|
||||
return ApiResponse<object>.ErrorResponse("Payment Request not found.", "Payment Request not found.", 404);
|
||||
}
|
||||
var hasManagePermission = hasManagePermissionTask.Result;
|
||||
if (!hasManagePermission && paymentRequest.CreatedById != loggedInEmployee.Id)
|
||||
{
|
||||
_logger.LogWarning("Access DENIED: Employee {EmployeeId} has no permission to edit payment requests.", loggedInEmployee.Id);
|
||||
return ApiResponse<object>.ErrorResponse("Access Denied", "You do not have permission to edit any payment request.", 409);
|
||||
}
|
||||
// Check if status prevents editing (only allow edit if status Draft, RejectedByReviewer, or RejectedByApprover)
|
||||
bool statusCheck = paymentRequest.ExpenseStatusId != Draft &&
|
||||
paymentRequest.ExpenseStatusId != RejectedByReviewer &&
|
||||
paymentRequest.ExpenseStatusId != RejectedByApprover;
|
||||
|
||||
bool isVariableRecurring = paymentRequest.RecurringPayment?.IsVariable ?? false;
|
||||
|
||||
// Capture existing state for auditing
|
||||
var existingEntityBson = _updateLogHelper.EntityToBsonDocument(paymentRequest);
|
||||
|
||||
// Only map updates if allowed by status
|
||||
if (!statusCheck && paymentRequest.CreatedById == loggedInEmployee.Id)
|
||||
{
|
||||
_mapper.Map(model, paymentRequest);
|
||||
paymentRequest.UpdatedAt = DateTime.UtcNow;
|
||||
paymentRequest.UpdatedById = loggedInEmployee.Id;
|
||||
}
|
||||
|
||||
if (isVariableRecurring)
|
||||
{
|
||||
paymentRequest.Amount = model.Amount;
|
||||
}
|
||||
|
||||
paymentRequest.IsAdvancePayment = model.IsAdvancePayment;
|
||||
|
||||
var paymentRequestUID = $"{paymentRequest.UIDPrefix}/{paymentRequest.UIDPostfix:D5}";
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
_logger.LogInfo("PaymentRequest updated successfully with UID: {PaymentRequestUID}", paymentRequestUID);
|
||||
}
|
||||
catch (DbUpdateException dbEx)
|
||||
{
|
||||
_logger.LogError(dbEx, "Database Exception during Payment Request update");
|
||||
return ApiResponse<object>.ErrorResponse("Database exception during payment request updation", ExceptionMapper(dbEx), 500);
|
||||
}
|
||||
|
||||
// Handle bill attachment updates: add new attachments and delete deactivated ones
|
||||
if (model.BillAttachments?.Any() == true && !statusCheck)
|
||||
{
|
||||
var newBillAttachments = model.BillAttachments.Where(ba => ba.DocumentId == null && ba.IsActive).ToList();
|
||||
if (newBillAttachments.Any())
|
||||
{
|
||||
_logger.LogInfo("Processing {AttachmentCount} new attachments for PaymentRequest Id: {PaymentRequestId}", newBillAttachments.Count, paymentRequest.Id);
|
||||
|
||||
// Pre-validate base64 data before upload
|
||||
foreach (var attachment in newBillAttachments)
|
||||
{
|
||||
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();
|
||||
|
||||
var processingTasks = newBillAttachments.Select(attachment =>
|
||||
ProcessSinglePaymentRequestAttachmentAsync(attachment, paymentRequest, loggedInEmployee.Id, tenantId, batchId)).ToList();
|
||||
|
||||
var results = await Task.WhenAll(processingTasks);
|
||||
|
||||
foreach (var (document, paymentRequestAttachment) in results)
|
||||
{
|
||||
_context.Documents.Add(document);
|
||||
_context.PaymentRequestAttachments.Add(paymentRequestAttachment);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
_logger.LogInfo("Added {Count} new attachments for PaymentRequest {PaymentRequestId} by Employee {EmployeeId}", newBillAttachments.Count, paymentRequest.Id, loggedInEmployee.Id);
|
||||
}
|
||||
catch (DbUpdateException dbEx)
|
||||
{
|
||||
_logger.LogError(dbEx, "Database Exception while adding attachments during PaymentRequest update");
|
||||
return ApiResponse<object>.ErrorResponse("Database exception during attachment addition.", ExceptionMapper(dbEx), 500);
|
||||
}
|
||||
}
|
||||
|
||||
var deleteBillAttachments = model.BillAttachments.Where(ba => ba.DocumentId != null && !ba.IsActive).ToList();
|
||||
if (deleteBillAttachments.Any())
|
||||
{
|
||||
var documentIds = deleteBillAttachments.Select(d => d.DocumentId!.Value).ToList();
|
||||
|
||||
try
|
||||
{
|
||||
await DeletePaymentRequestAttachemnts(documentIds);
|
||||
_logger.LogInfo("Deleted {Count} attachments for PaymentRequest {PaymentRequestId} by Employee {EmployeeId}", deleteBillAttachments.Count, paymentRequest.Id, loggedInEmployee.Id);
|
||||
}
|
||||
catch (DbUpdateException dbEx)
|
||||
{
|
||||
_logger.LogError(dbEx, "Database Exception while deleting attachments during PaymentRequest update");
|
||||
return ApiResponse<object>.ErrorResponse("Database exception during attachment deletion.", ExceptionMapper(dbEx), 500);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Unexpected exception while deleting attachments during PaymentRequest update");
|
||||
return ApiResponse<object>.ErrorResponse("Error occurred while deleting attachments.", ExceptionMapper(ex), 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log the update audit trail
|
||||
await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
|
||||
{
|
||||
EntityId = paymentRequest.Id.ToString(),
|
||||
UpdatedById = loggedInEmployee.Id.ToString(),
|
||||
OldObject = existingEntityBson,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
}, "PaymentRequestModificationLog");
|
||||
|
||||
// Prepare response view model with updated details
|
||||
var response = _mapper.Map<PaymentRequestVM>(paymentRequest);
|
||||
response.PaymentRequestUID = paymentRequestUID;
|
||||
response.Currency = currency;
|
||||
response.ExpenseCategory = _mapper.Map<ExpenseCategoryMasterVM>(expenseCategory);
|
||||
response.Project = _mapper.Map<BasicProjectVM>(project);
|
||||
|
||||
return ApiResponse<object>.SuccessResponse(response, "Payment Request updated successfully.", 200);
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
_logger.LogError(ex, "Argument error in EditPaymentRequestAsync: {Message}", ex.Message);
|
||||
return ApiResponse<object>.ErrorResponse(ex.Message, "Invalid data.", 400);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Unexpected error in EditPaymentRequestAsync: {Message}", ex.Message);
|
||||
return ApiResponse<object>.ErrorResponse("An error occurred while updating the payment request.", ex.Message, 500);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_logger.LogInfo("End EditPaymentRequestAsync for PaymentRequestId: {PaymentRequestId}", id);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region =================================================================== Payment Request Functions ===================================================================
|
||||
|
||||
@ -25,6 +25,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
|
||||
Task<ApiResponse<object>> CreatePaymentRequestAsync(PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId);
|
||||
Task<ApiResponse<object>> ChangePaymentRequestStatusAsync(PaymentRequestRecordDto model, Employee loggedInEmployee, Guid tenantId);
|
||||
Task<ApiResponse<object>> ChangeToExpanseFromPaymentRequestAsync(ExpenseConversionDto model, Employee loggedInEmployee, Guid tenantId);
|
||||
Task<ApiResponse<object>> EditPaymentRequestAsync(Guid id, PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId);
|
||||
#endregion
|
||||
#region =================================================================== Payment Request Functions ===================================================================
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user