diff --git a/Marco.Pms.Services/Controllers/ExpenseController.cs b/Marco.Pms.Services/Controllers/ExpenseController.cs index 60d9e56..2731bc2 100644 --- a/Marco.Pms.Services/Controllers/ExpenseController.cs +++ b/Marco.Pms.Services/Controllers/ExpenseController.cs @@ -124,5 +124,23 @@ namespace Marco.Pms.Services.Controllers #endregion + #region =================================================================== Payment Request Functions =================================================================== + + [HttpGet("get/payment-requests/list")] + public async Task GetPaymentRequestList([FromQuery] string? searchString, [FromQuery] string? filter, [FromQuery] bool isActive = true, [FromQuery] int pageSize = 20, [FromQuery] int pageNumber = 1) + { + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + var response = await _expensesService.GetPaymentRequestListAsync(searchString, filter, isActive, pageSize, pageNumber, loggedInEmployee, tenantId); + return StatusCode(response.StatusCode, response); + } + #endregion + #region =================================================================== Payment Request Functions =================================================================== + + + #endregion + #region =================================================================== Payment Request Functions =================================================================== + + + #endregion } } diff --git a/Marco.Pms.Services/Service/ExpensesService.cs b/Marco.Pms.Services/Service/ExpensesService.cs index 7f37a98..aa74004 100644 --- a/Marco.Pms.Services/Service/ExpensesService.cs +++ b/Marco.Pms.Services/Service/ExpensesService.cs @@ -1209,6 +1209,170 @@ namespace Marco.Pms.Services.Service #endregion + #endregion + + #region =================================================================== Payment Request Functions =================================================================== + public async Task> GetPaymentRequestListAsync(string? searchString, string? filter, bool isActive, int pageSize, int pageNumber, Employee loggedInEmployee, Guid tenantId) + { + _logger.LogInfo("Start GetPaymentRequestListAsync: TenantId={TenantId}, PageNumber={PageNumber}, PageSize={PageSize}, EmployeeId={EmployeeId}", + tenantId, pageNumber, pageSize, loggedInEmployee.Id); + + try + { + var hasViewSelfPermissionTask = Task.Run(async () => + { + using var scope = _serviceScopeFactory.CreateScope(); + var permissionService = scope.ServiceProvider.GetRequiredService(); + return await permissionService.HasPermission(PermissionsMaster.ExpenseViewSelf, loggedInEmployee.Id); + }); + + var hasViewAllPermissionTask = Task.Run(async () => + { + using var scope = _serviceScopeFactory.CreateScope(); + var permissionService = scope.ServiceProvider.GetRequiredService(); + return await permissionService.HasPermission(PermissionsMaster.ExpenseViewAll, loggedInEmployee.Id); + }); + + await Task.WhenAll(hasViewSelfPermissionTask, hasViewAllPermissionTask); + + if (!hasViewAllPermissionTask.Result && !hasViewSelfPermissionTask.Result) + { + // User has neither required permission. Deny access. + _logger.LogWarning("Access DENIED for employee {EmployeeId} attempting to get payment request list.", loggedInEmployee.Id); + return ApiResponse.SuccessResponse(new List(), "You do not have permission to view any payment request.", 200); + } + + // Initial query including the necessary navigation properties and basic multi-tenant/security constraints + var paymentRequestQuery = _context.PaymentRequests + .Include(pr => pr.Currency) + .Include(pr => pr.Project) + .Include(pr => pr.RecurringPayment) + .Include(pr => pr.ExpenseCategory) + .Include(pr => pr.ExpenseStatus) + .Include(pr => pr.CreatedBy) + .ThenInclude(e => e!.JobRole) + .Where(pr => pr.TenantId == tenantId && + pr.IsActive == isActive && + pr.Currency != null && + pr.ExpenseCategory != null && + pr.ExpenseStatus != null && + pr.CreatedBy != null && + pr.CreatedBy.JobRole != null); + + if (hasViewSelfPermissionTask.Result && !hasViewAllPermissionTask.Result) + { + paymentRequestQuery = paymentRequestQuery.Where(pr => pr.CreatedById == loggedInEmployee.Id); + } + + paymentRequestQuery = paymentRequestQuery.Where(pr => pr.ExpenseStatusId != Draft || pr.CreatedById == loggedInEmployee.Id); + + // Deserialize and apply advanced filter if provided + PaymentRequestFilter? paymentRequestFilter = TryDeserializePaymentRequestFilter(filter); + + if (paymentRequestFilter != null) + { + if (paymentRequestFilter.ProjectIds?.Any() ?? false) + { + paymentRequestQuery = paymentRequestQuery + .Where(pr => pr.ProjectId.HasValue && paymentRequestFilter.ProjectIds.Contains(pr.ProjectId.Value)); + } + if (paymentRequestFilter.StatusIds?.Any() ?? false) + { + paymentRequestQuery = paymentRequestQuery + .Where(pr => paymentRequestFilter.StatusIds.Contains(pr.ExpenseStatusId)); + } + if (paymentRequestFilter.CreatedByIds?.Any() ?? false) + { + paymentRequestQuery = paymentRequestQuery + .Where(pr => paymentRequestFilter.CreatedByIds.Contains(pr.CreatedById)); + } + if (paymentRequestFilter.CurrencyIds?.Any() ?? false) + { + paymentRequestQuery = paymentRequestQuery + .Where(pr => paymentRequestFilter.CurrencyIds.Contains(pr.CurrencyId)); + } + if (paymentRequestFilter.ExpenseCategoryIds?.Any() ?? false) + { + paymentRequestQuery = paymentRequestQuery + .Where(pr => pr.ExpenseCategoryId.HasValue && paymentRequestFilter.ExpenseCategoryIds.Contains(pr.ExpenseCategoryId.Value)); + } + if (paymentRequestFilter.Payees?.Any() ?? false) + { + paymentRequestQuery = paymentRequestQuery + .Where(pr => paymentRequestFilter.Payees.Contains(pr.Payee)); + } + if (paymentRequestFilter.StartDate.HasValue && paymentRequestFilter.EndDate.HasValue) + { + DateTime startDate = paymentRequestFilter.StartDate.Value.Date; + DateTime endDate = paymentRequestFilter.EndDate.Value.Date; + + paymentRequestQuery = paymentRequestQuery + .Where(pr => pr.CreatedAt.Date >= startDate && pr.CreatedAt.Date <= endDate); + } + } + + // Full-text search by payee, title, or UID + if (!string.IsNullOrWhiteSpace(searchString)) + { + paymentRequestQuery = paymentRequestQuery + .Where(pr => + pr.Payee.Contains(searchString) || + pr.Title.Contains(searchString) || + (pr.UIDPrefix + "/" + pr.UIDPostfix.ToString().PadLeft(5, '0')).Contains(searchString) + ); + } + + // Get total count for pagination + var totalEntities = await paymentRequestQuery.CountAsync(); + + // Fetch paginated result set + var paymentRequests = await paymentRequestQuery + .OrderByDescending(e => e.CreatedAt) + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize) + .ToListAsync(); + + var totalPages = (int)Math.Ceiling((double)totalEntities / pageSize); + + var results = paymentRequests.Select(pr => + { + var result = _mapper.Map(pr); + result.PaymentRequestUID = $"{pr.UIDPrefix}/{pr.UIDPostfix:D5}"; + return result; + }).ToList(); + + var response = new + { + CurrentPage = pageNumber, + TotalPages = totalPages, + TotalEntities = totalEntities, + Data = results, + }; + + _logger.LogInfo("GetPaymentRequestListAsync: {ResultCount} payment requests fetched successfully for TenantId={TenantId} Page={PageNumber}/{TotalPages}", + results.Count, tenantId, pageNumber, totalPages); + + return ApiResponse.SuccessResponse(response, $"{results.Count} payment requests fetched successfully.", 200); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error occurred in GetPaymentRequestListAsync for TenantId={TenantId}, EmployeeId={EmployeeId}: {Message}", tenantId, loggedInEmployee.Id, ex.Message); + return ApiResponse.ErrorResponse("An error occurred while fetching payment requests.", ex.Message, 500); + } + finally + { + _logger.LogInfo("End GetPaymentRequestListAsync for TenantId={TenantId}, EmployeeId={EmployeeId}", tenantId, loggedInEmployee.Id); + } + } + + #endregion + #region =================================================================== Payment Request Functions =================================================================== + + + #endregion + #region =================================================================== Payment Request Functions =================================================================== + + #endregion diff --git a/Marco.Pms.Services/Service/ServiceInterfaces/IExpensesService.cs b/Marco.Pms.Services/Service/ServiceInterfaces/IExpensesService.cs index 70c339c..4f2d033 100644 --- a/Marco.Pms.Services/Service/ServiceInterfaces/IExpensesService.cs +++ b/Marco.Pms.Services/Service/ServiceInterfaces/IExpensesService.cs @@ -17,7 +17,17 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces Task> DeleteExpanseAsync(Guid id, Employee loggedInEmployee, Guid tenantId); #endregion + #region =================================================================== Payment Request Functions =================================================================== + Task> GetPaymentRequestListAsync(string? searchString, string? filter, bool isActive, int pageSize, int pageNumber, Employee loggedInEmployee, Guid tenantId); + #endregion + #region =================================================================== Payment Request Functions =================================================================== + #endregion + #region =================================================================== Payment Request Functions =================================================================== + + + #endregion + } }