From 590476a8aa0750bdd0fd218071886260cee7a2e3 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Fri, 3 Oct 2025 14:32:59 +0530 Subject: [PATCH] Added the APi to get count of pending expenses --- .../Controllers/DashboardController.cs | 130 +++++++++++------- 1 file changed, 84 insertions(+), 46 deletions(-) diff --git a/Marco.Pms.Services/Controllers/DashboardController.cs b/Marco.Pms.Services/Controllers/DashboardController.cs index 4447b68..5056bb1 100644 --- a/Marco.Pms.Services/Controllers/DashboardController.cs +++ b/Marco.Pms.Services/Controllers/DashboardController.cs @@ -801,11 +801,18 @@ namespace Marco.Pms.Services.Controllers return await _permission.HasPermission(PermissionsMaster.ExpenseProcess, loggedInEmployee.Id); }); - await Task.WhenAll(hasReviewPermissionTask, hasApprovePermissionTask, hasProcessPermissionTask); // [Parallel Await] + var hasManagePermissionTask = Task.Run(async () => + { + var _permission = scope.ServiceProvider.GetRequiredService(); + return await _permission.HasPermission(PermissionsMaster.ExpenseManage, loggedInEmployee.Id); + }); + + await Task.WhenAll(hasReviewPermissionTask, hasApprovePermissionTask, hasProcessPermissionTask, hasManagePermissionTask); // [Parallel Await] var hasReviewPermission = hasReviewPermissionTask.Result; var hasApprovePermission = hasApprovePermissionTask.Result; var hasProcessPermission = hasProcessPermissionTask.Result; + var hasManagePermission = hasManagePermissionTask.Result; _logger.LogInfo( "Permissions resolved: Review={Review}, Approve={Approve}, Process={Process}", @@ -813,60 +820,91 @@ namespace Marco.Pms.Services.Controllers // Build base query: read-only, tenant-scoped var baseQuery = _context.Expenses - .AsNoTracking() // Reduce tracking overhead for read-only list - .Where(e => e.IsActive && e.TenantId == tenantId); // [Base Filter] - - // Important: fix operator precedence by grouping OR conditions - // Pending means Draft always, plus role-gated statuses - var pendingQuery = baseQuery - .Include(e => e.PaidBy) - .Include(e => e.CreatedBy) - .Include(e => e.ProcessedBy) - .Include(e => e.ApprovedBy) - .Include(e => e.ReviewedBy) - .Include(e => e.PaymentMode) - .Include(e => e.Project) - .Include(e => e.PaymentMode) - .Include(e => e.ExpensesType) .Include(e => e.Status) - .AsNoTracking() - .Where(e => - (e.StatusId == Draft && e.CreatedById == loggedInEmployee.Id) - || (hasReviewPermission && e.StatusId == Review) - || (hasApprovePermission && e.StatusId == Approve) - || (hasProcessPermission && e.StatusId == ProcessPending)); // [Correct Precedence] + .AsNoTracking() // Reduce tracking overhead for read-only list + .Where(e => e.IsActive && e.TenantId == tenantId && e.StatusId != Processed && e.Status != null); // [Base Filter] // Project to DTO in SQL to avoid heavy Include graph. if (projectId.HasValue) - pendingQuery = pendingQuery.Where(e => e.ProjectId == projectId); + baseQuery = baseQuery.Where(e => e.ProjectId == projectId); // Prefer ProjectTo when profiles exist; otherwise project minimal fields - var response = await pendingQuery - .Where(e => e.Status != null && e.ExpensesType != null && e.PaymentMode != null && e.Project != null && e.CreatedBy != null) - .Select(e => new - { - Id = e.Id, - Amount = e.Amount, - TransactionDate = e.TransactionDate, - StatusId = e.StatusId, - StatusName = e.Status!.Name, - ExpenseTypeName = e.ExpensesType!.Name, - PaymentModeName = e.PaymentMode!.Name, - ProjectName = e.Project!.Name, - CreatedByName = $"{e.CreatedBy!.FirstName} {e.CreatedBy.LastName}", - ReviewedByName = e.ReviewedBy != null ? $"{e.ReviewedBy.FirstName} {e.ReviewedBy.LastName}" : null, - ApprovedByName = e.ApprovedBy != null ? $"{e.ApprovedBy.FirstName} {e.ApprovedBy.LastName}" : null, - ProcessedByName = e.ProcessedBy != null ? $"{e.ProcessedBy.FirstName} {e.ProcessedBy.LastName}" : null, - PaidByName = e.PaidBy != null ? $"{e.PaidBy.FirstName} {e.PaidBy.LastName}" : null - }) - .OrderByDescending(x => x.TransactionDate) + var expenses = await baseQuery .ToListAsync(); // Single round-trip; no Include needed for this shape - _logger.LogInfo( - "GetPendingExpenseListAsync completed. TenantId={TenantId}, Count={Count}", - tenantId, response.Count); // [Completion Log] + var draftExpenses = expenses.Where(e => e.StatusId == Draft && e.CreatedById == loggedInEmployee.Id).ToList(); + var reviewExpenses = expenses.Where(e => (hasReviewPermission || e.CreatedById == loggedInEmployee.Id) && e.StatusId == Review).ToList(); + var approveExpenses = expenses.Where(e => (hasApprovePermission || e.CreatedById == loggedInEmployee.Id) && e.StatusId == Approve).ToList(); + var processPendingExpenses = expenses.Where(e => (hasProcessPermission || e.CreatedById == loggedInEmployee.Id) && e.StatusId == ProcessPending).ToList(); + var submitedExpenses = expenses.Where(e => e.StatusId != Draft && e.CreatedById == loggedInEmployee.Id).ToList(); - return Ok(ApiResponse.SuccessResponse(response, "Pending Expenses fetched successfully", 200)); // [Success Response] + if (hasManagePermission) + { + var response = new + { + Draft = new + { + Count = draftExpenses.Count, + TotalAmount = draftExpenses.Sum(e => e.Amount) + }, + ReviewPending = new + { + Count = reviewExpenses.Count, + TotalAmount = reviewExpenses.Sum(e => e.Amount) + }, + ApprovePending = new + { + Count = approveExpenses.Count, + TotalAmount = approveExpenses.Sum(e => e.Amount) + }, + ProcessPending = new + { + Count = processPendingExpenses.Count, + TotalAmount = processPendingExpenses.Sum(e => e.Amount) + }, + Submited = new + { + Count = submitedExpenses.Count, + TotalAmount = submitedExpenses.Sum(e => e.Amount) + } + }; + _logger.LogInfo( + "GetPendingExpenseListAsync completed. TenantId={TenantId}", + tenantId); // [Completion Log] + + return Ok(ApiResponse.SuccessResponse(response, "Pending Expenses fetched successfully", 200)); // [Success Response] + } + else + { + var response = new + { + Draft = new + { + Count = draftExpenses.Count + }, + ReviewPending = new + { + Count = reviewExpenses.Count + }, + ApprovePending = new + { + Count = approveExpenses.Count + }, + ProcessPending = new + { + Count = processPendingExpenses.Count + }, + Submited = new + { + Count = submitedExpenses.Count + } + }; + _logger.LogInfo( + "GetPendingExpenseListAsync completed. TenantId={TenantId}", + tenantId); // [Completion Log] + + return Ok(ApiResponse.SuccessResponse(response, "Pending Expenses fetched successfully", 200)); // [Success Response] + } } catch (OperationCanceledException) {