Added the projectId in get expense pending list API

This commit is contained in:
ashutosh.nehete 2025-10-03 11:08:15 +05:30
parent 7e15c517ac
commit 91f4305995

View File

@ -774,16 +774,16 @@ namespace Marco.Pms.Services.Controllers
} }
[HttpGet("expense/pendings")] [HttpGet("expense/pendings")]
public async Task<IActionResult> GetPendingExpenseListAsync() public async Task<IActionResult> GetPendingExpenseListAsync([FromQuery] Guid? projectId)
{ {
// Start log with correlation fields // Start log with correlation fields
_logger.LogDebug( _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 try
{ {
// Resolve current employee once; avoid using scoped services inside Task.Run // 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 // Resolve permission service from current scope once
using var scope = _serviceScopeFactory.CreateScope(); using var scope = _serviceScopeFactory.CreateScope();
@ -816,12 +816,12 @@ namespace Marco.Pms.Services.Controllers
_logger.LogInfo( _logger.LogInfo(
"Permissions resolved: Review={Review}, Approve={Approve}, Process={Process}", "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 // Build base query: read-only, tenant-scoped
var baseQuery = _context.Expenses var baseQuery = _context.Expenses
.AsNoTracking() // Reduce tracking overhead for read-only list .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 // Important: fix operator precedence by grouping OR conditions
// Pending means Draft always, plus role-gated statuses // Pending means Draft always, plus role-gated statuses
@ -841,9 +841,11 @@ namespace Marco.Pms.Services.Controllers
(e.StatusId == Draft && e.CreatedById == loggedInEmployee.Id) (e.StatusId == Draft && e.CreatedById == loggedInEmployee.Id)
|| (hasReviewPermission && e.StatusId == Review) || (hasReviewPermission && e.StatusId == Review)
|| (hasApprovePermission && e.StatusId == Approve) || (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. // 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 // Prefer ProjectTo when profiles exist; otherwise project minimal fields
var response = await pendingQuery var response = await pendingQuery
@ -865,24 +867,24 @@ namespace Marco.Pms.Services.Controllers
PaidByName = e.PaidBy != null ? $"{e.PaidBy.FirstName} {e.PaidBy.LastName}" : null PaidByName = e.PaidBy != null ? $"{e.PaidBy.FirstName} {e.PaidBy.LastName}" : null
}) })
.OrderByDescending(x => x.TransactionDate) .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( _logger.LogInfo(
"GetPendingExpenseListAsync completed. TenantId={TenantId}, Count={Count}", "GetPendingExpenseListAsync completed. TenantId={TenantId}, Count={Count}",
tenantId, response.Count); // [Completion Log] [memory:4] tenantId, response.Count); // [Completion Log]
return Ok(ApiResponse<object>.SuccessResponse(response, "Pending Expenses fetched successfully", 200)); // [Success Response] [memory:1] return Ok(ApiResponse<object>.SuccessResponse(response, "Pending Expenses fetched successfully", 200)); // [Success Response]
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
_logger.LogWarning("GetPendingExpenseListAsync canceled by client. TenantId={TenantId}", tenantId); // [Cancel Log] [memory:4] _logger.LogWarning("GetPendingExpenseListAsync canceled by client. TenantId={TenantId}", tenantId); // [Cancel Log]
return StatusCode(499, ApiResponse<object>.ErrorResponse("Client has canceled the opration", "Client has canceled the opration", 499)); // [Cancel Response] [memory:1] return StatusCode(499, ApiResponse<object>.ErrorResponse("Client has canceled the opration", "Client has canceled the opration", 499)); // [Cancel Response]
} }
catch (Exception ex) 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, return StatusCode(500,
ApiResponse<object>.ErrorResponse("An error occurred while fetching pending expenses.", "An error occurred while fetching pending expenses.", 500)); // [Error Response] [memory:1] ApiResponse<object>.ErrorResponse("An error occurred while fetching pending expenses.", "An error occurred while fetching pending expenses.", 500)); // [Error Response]
} }
} }