diff --git a/Marco.Pms.Model/Dtos/Expenses/PaymentRequestRecordDto.cs b/Marco.Pms.Model/Dtos/Expenses/PaymentRequestRecordDto.cs index 3a3c37e..24fd763 100644 --- a/Marco.Pms.Model/Dtos/Expenses/PaymentRequestRecordDto.cs +++ b/Marco.Pms.Model/Dtos/Expenses/PaymentRequestRecordDto.cs @@ -1,4 +1,6 @@ -namespace Marco.Pms.Model.Dtos.Expenses +using Marco.Pms.Model.Utilities; + +namespace Marco.Pms.Model.Dtos.Expenses { public class PaymentRequestRecordDto { @@ -11,5 +13,9 @@ public double? TaxAmount { get; set; } public DateTime? PaidAt { get; set; } public Guid? PaidById { get; set; } + public Guid? PaymentModeId { get; set; } + public string? Location { get; set; } + public string? GSTNumber { get; set; } + public List? BillAttachments { get; set; } } } diff --git a/Marco.Pms.Services/Controllers/ExpenseController.cs b/Marco.Pms.Services/Controllers/ExpenseController.cs index 7c45839..4a24160 100644 --- a/Marco.Pms.Services/Controllers/ExpenseController.cs +++ b/Marco.Pms.Services/Controllers/ExpenseController.cs @@ -184,19 +184,6 @@ namespace Marco.Pms.Services.Controllers return StatusCode(response.StatusCode, response); } - [HttpPost("payment-request/expense/create")] - public async Task ChangeToExpanseFromPaymentRequest(ExpenseConversionDto model) - { - var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); - var response = await _expensesService.ChangeToExpanseFromPaymentRequestAsync(model, loggedInEmployee, tenantId); - if (response.Success) - { - var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Expanse", Response = response.Data }; - await _signalR.SendNotificationAsync(notification); - } - return StatusCode(response.StatusCode, response); - } - [HttpPut("payment-request/edit/{id}")] public async Task EditPaymentRequest(Guid id, [FromBody] PaymentRequestDto model) { diff --git a/Marco.Pms.Services/Service/ExpensesService.cs b/Marco.Pms.Services/Service/ExpensesService.cs index 086291b..552897f 100644 --- a/Marco.Pms.Services/Service/ExpensesService.cs +++ b/Marco.Pms.Services/Service/ExpensesService.cs @@ -1836,6 +1836,7 @@ namespace Marco.Pms.Services.Service .Include(pr => pr.ExpenseCategory) .Include(pr => pr.ExpenseStatus) .Include(pr => pr.CreatedBy).ThenInclude(e => e!.JobRole) + .Include(pr => pr.UpdatedBy).ThenInclude(e => e!.JobRole) .FirstOrDefaultAsync(pr => pr.Id == model.PaymentRequestId && pr.ExpenseStatusId != model.StatusId && @@ -1920,7 +1921,7 @@ namespace Marco.Pms.Services.Service paymentRequest.ExpenseStatus = statusTransition.NextStatus; - // 7. Add Reimbursement if applicable + // 7. Add Payment details if applicable if (model.StatusId == Processed) { var totalAmount = model.BaseAmount + model.TaxAmount; @@ -1967,6 +1968,38 @@ namespace Marco.Pms.Services.Service }); } + try + { + await _context.SaveChangesAsync(); + _logger.LogInfo("ChangeStatus: Status updated successfully. PaymentRequestId={PaymentRequestId} NewStatus={NewStatusId}", paymentRequest.Id, paymentRequest.ExpenseStatusId); + } + catch (DbUpdateConcurrencyException ex) + { + _logger.LogError(ex, "ChangeStatus: Concurrency error. PaymentRequestId={PaymentRequestId}", paymentRequest.Id); + return ApiResponse.ErrorResponse("Payment Request was modified by another user. Please refresh and try again.", "Concurrency Error", 409); + } + + if (model.StatusId == Done) + { + if (!model.PaymentModeId.HasValue) + { + return ApiResponse.ErrorResponse("Payment mode Id is missing from payload", "Payment mode Id is missing from payload", 400); + } + var expenseConversion = new ExpenseConversionDto + { + PaymentModeId = model.PaymentModeId.Value, + PaymentRequestId = model.PaymentRequestId, + Location = model.Location, + GSTNumber = model.GSTNumber, + BillAttachments = model.BillAttachments + }; + var response = await ChangeToExpanseFromPaymentRequestAsync(expenseConversion, paymentRequest, loggedInEmployee, tenantId); + if (!response.Success) + { + return response; + } + } + // 8. Add paymentRequest Log Entry _context.StatusUpdateLogs.Add(new StatusUpdateLog { @@ -2027,7 +2060,7 @@ namespace Marco.Pms.Services.Service return ApiResponse.SuccessResponse(responseDto, "Status updated, but audit logging or cache update failed."); } } - public async Task> ChangeToExpanseFromPaymentRequestAsync(ExpenseConversionDto model, Employee loggedInEmployee, Guid tenantId) + public async Task> ChangeToExpanseFromPaymentRequestAsync(ExpenseConversionDto model, PaymentRequest paymentRequest, Employee loggedInEmployee, Guid tenantId) { _logger.LogInfo("Start ChangeToExpanseFromPaymentRequestAsync called by EmployeeId: {EmployeeId} for TenantId: {TenantId} PaymentRequestId: {PaymentRequestId}", loggedInEmployee.Id, tenantId, model.PaymentRequestId); @@ -2036,28 +2069,24 @@ namespace Marco.Pms.Services.Service try { - // Retrieve payment request with required navigation property and validation - var paymentRequest = await _context.PaymentRequests - .Include(pr => pr.ExpenseCategory) - .FirstOrDefaultAsync(pr => - pr.Id == model.PaymentRequestId && - !pr.IsAdvancePayment && - pr.ProjectId.HasValue && - pr.ExpenseCategoryId.HasValue && - pr.PaidById.HasValue && - pr.PaidAt.HasValue && - pr.ExpenseCategory != null && - pr.TenantId == tenantId && - pr.IsActive); + var ispaymentRequestApplicable = (paymentRequest.Id == model.PaymentRequestId && + !paymentRequest.IsAdvancePayment && + paymentRequest.ProjectId.HasValue && + paymentRequest.ExpenseCategoryId.HasValue && + paymentRequest.PaidById.HasValue && + paymentRequest.PaidAt.HasValue && + paymentRequest.ExpenseCategory != null && + paymentRequest.TenantId == tenantId && + paymentRequest.IsActive); - if (paymentRequest == null) + if (!ispaymentRequestApplicable) { _logger.LogWarning("Payment request not found for Id: {PaymentRequestId}, TenantId: {TenantId}", model.PaymentRequestId, tenantId); return ApiResponse.ErrorResponse("Payment request not found.", "Payment request not found.", 404); } // Check payment request status for eligibility to convert - if (paymentRequest.ExpenseStatusId != Processed) + if (paymentRequest.ExpenseStatusId != Done) { _logger.LogWarning("Payment request {PaymentRequestId} status is not processed. Current status: {StatusId}", paymentRequest.Id, paymentRequest.ExpenseStatusId); return ApiResponse.ErrorResponse("Payment is not processed.", "Payment is not processed.", 400); diff --git a/Marco.Pms.Services/Service/ServiceInterfaces/IExpensesService.cs b/Marco.Pms.Services/Service/ServiceInterfaces/IExpensesService.cs index 3334054..547862b 100644 --- a/Marco.Pms.Services/Service/ServiceInterfaces/IExpensesService.cs +++ b/Marco.Pms.Services/Service/ServiceInterfaces/IExpensesService.cs @@ -24,7 +24,6 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces Task> GetPaymentRequestFilterObjectAsync(Employee loggedInEmployee, Guid tenantId); Task> CreatePaymentRequestAsync(PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId); Task> ChangePaymentRequestStatusAsync(PaymentRequestRecordDto model, Employee loggedInEmployee, Guid tenantId); - Task> ChangeToExpanseFromPaymentRequestAsync(ExpenseConversionDto model, Employee loggedInEmployee, Guid tenantId); Task> EditPaymentRequestAsync(Guid id, PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId); Task> DeletePaymentRequestAsync(Guid id, Employee loggedInEmployee, Guid tenantId); #endregion