diff --git a/Marco.Pms.Services/Controllers/DashboardController.cs b/Marco.Pms.Services/Controllers/DashboardController.cs index 38a7499..d508d87 100644 --- a/Marco.Pms.Services/Controllers/DashboardController.cs +++ b/Marco.Pms.Services/Controllers/DashboardController.cs @@ -774,16 +774,16 @@ namespace Marco.Pms.Services.Controllers } [HttpGet("expense/pendings")] - public async Task GetPendingExpenseListAsync() + public async Task GetPendingExpenseListAsync([FromQuery] Guid? projectId) { // Start log with correlation fields _logger.LogDebug( - "GetPendingExpenseListAsync started. TenantId={TenantId}", tenantId); // [Start Log] [memory:4][memory:1] + "GetPendingExpenseListAsync started. Project={ProjectId} TenantId={TenantId}", projectId ?? Guid.Empty, tenantId); // [Start Log] try { // Resolve current employee once; avoid using scoped services inside Task.Run - var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); // [User Context] [memory:1] + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); // [User Context] // Resolve permission service from current scope once using var scope = _serviceScopeFactory.CreateScope(); @@ -816,12 +816,12 @@ namespace Marco.Pms.Services.Controllers _logger.LogInfo( "Permissions resolved: Review={Review}, Approve={Approve}, Process={Process}", - hasReviewPermission, hasApprovePermission, hasProcessPermission); // [Permissions Log] [memory:4] + hasReviewPermission, hasApprovePermission, hasProcessPermission); // [Permissions Log] // 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] [memory:7] + .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 @@ -841,9 +841,11 @@ namespace Marco.Pms.Services.Controllers (e.StatusId == Draft && e.CreatedById == loggedInEmployee.Id) || (hasReviewPermission && e.StatusId == Review) || (hasApprovePermission && e.StatusId == Approve) - || (hasProcessPermission && e.StatusId == ProcessPending)); // [Correct Precedence] [memory:7] + || (hasProcessPermission && e.StatusId == ProcessPending)); // [Correct Precedence] // Project to DTO in SQL to avoid heavy Include graph. + if (projectId.HasValue) + pendingQuery = pendingQuery.Where(e => e.ProjectId == projectId); // Prefer ProjectTo when profiles exist; otherwise project minimal fields var response = await pendingQuery @@ -865,24 +867,24 @@ namespace Marco.Pms.Services.Controllers PaidByName = e.PaidBy != null ? $"{e.PaidBy.FirstName} {e.PaidBy.LastName}" : null }) .OrderByDescending(x => x.TransactionDate) - .ToListAsync(); // Single round-trip; no Include needed for this shape [memory:7] + .ToListAsync(); // Single round-trip; no Include needed for this shape _logger.LogInfo( "GetPendingExpenseListAsync completed. TenantId={TenantId}, Count={Count}", - tenantId, response.Count); // [Completion Log] [memory:4] + tenantId, response.Count); // [Completion Log] - return Ok(ApiResponse.SuccessResponse(response, "Pending Expenses fetched successfully", 200)); // [Success Response] [memory:1] + return Ok(ApiResponse.SuccessResponse(response, "Pending Expenses fetched successfully", 200)); // [Success Response] } catch (OperationCanceledException) { - _logger.LogWarning("GetPendingExpenseListAsync canceled by client. TenantId={TenantId}", tenantId); // [Cancel Log] [memory:4] - return StatusCode(499, ApiResponse.ErrorResponse("Client has canceled the opration", "Client has canceled the opration", 499)); // [Cancel Response] [memory:1] + _logger.LogWarning("GetPendingExpenseListAsync canceled by client. TenantId={TenantId}", tenantId); // [Cancel Log] + return StatusCode(499, ApiResponse.ErrorResponse("Client has canceled the opration", "Client has canceled the opration", 499)); // [Cancel Response] } catch (Exception ex) { - _logger.LogError(ex, "GetPendingExpenseListAsync failed. TenantId={TenantId}", tenantId); // [Error Log] [memory:4] + _logger.LogError(ex, "GetPendingExpenseListAsync failed. TenantId={TenantId}", tenantId); // [Error Log] return StatusCode(500, - ApiResponse.ErrorResponse("An error occurred while fetching pending expenses.", "An error occurred while fetching pending expenses.", 500)); // [Error Response] [memory:1] + ApiResponse.ErrorResponse("An error occurred while fetching pending expenses.", "An error occurred while fetching pending expenses.", 500)); // [Error Response] } }