Added totalPages , totalEntites and Currect page in response of get expenses list
This commit is contained in:
parent
2449d2a518
commit
5be154a9f1
@ -395,7 +395,7 @@ namespace Marco.Pms.DataAccess.Data
|
||||
Name = "Draft",
|
||||
DisplayName = "Draft",
|
||||
Description = "Expense has been created but not yet submitted.",
|
||||
Color = "#212529",
|
||||
Color = "#6c757d",
|
||||
IsSystem = true,
|
||||
IsActive = true,
|
||||
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||
|
@ -1934,7 +1934,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
new
|
||||
{
|
||||
Id = new Guid("297e0d8f-f668-41b5-bfea-e03b354251c8"),
|
||||
Color = "#212529",
|
||||
Color = "#6c757d",
|
||||
Description = "Expense has been created but not yet submitted.",
|
||||
DisplayName = "Draft",
|
||||
IsActive = true,
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
@ -373,7 +372,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
columns: new[] { "Id", "Color", "Description", "DisplayName", "IsActive", "IsSystem", "Name", "TenantId" },
|
||||
values: new object[,]
|
||||
{
|
||||
{ new Guid("297e0d8f-f668-41b5-bfea-e03b354251c8"), "#212529", "Expense has been created but not yet submitted.", "Draft", true, true, "Draft", new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
|
||||
{ new Guid("297e0d8f-f668-41b5-bfea-e03b354251c8"), "#6c757d", "Expense has been created but not yet submitted.", "Draft", true, true, "Draft", new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
|
||||
{ new Guid("4068007f-c92f-4f37-a907-bc15fe57d4d8"), "#0dcaf0", "Review is completed, waiting for action of approver.", "Approve", true, true, "Approval Pending", new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
|
||||
{ new Guid("61578360-3a49-4c34-8604-7b35a3787b95"), "#198754", "Expense has been settled.", "Paid", true, true, "Processed", new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
|
||||
{ new Guid("6537018f-f4e9-4cb3-a210-6c3b2da999d7"), "#0d6efd", "Reviewer is currently reviewing the expense.", "Review", true, true, "Review Pending", new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
|
||||
|
@ -1931,7 +1931,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
new
|
||||
{
|
||||
Id = new Guid("297e0d8f-f668-41b5-bfea-e03b354251c8"),
|
||||
Color = "#212529",
|
||||
Color = "#6c757d",
|
||||
Description = "Expense has been created but not yet submitted.",
|
||||
DisplayName = "Draft",
|
||||
IsActive = true,
|
||||
|
@ -155,6 +155,9 @@ namespace Marco.Pms.Services.Service
|
||||
|
||||
// 4. --- Apply Ordering and Pagination ---
|
||||
// This should be the last step before executing the query.
|
||||
|
||||
var totalEntites = await expensesQuery.CountAsync();
|
||||
|
||||
var paginatedQuery = expensesQuery
|
||||
.OrderByDescending(e => e.CreatedAt)
|
||||
.Skip((pageNumber - 1) * pageSize)
|
||||
@ -169,7 +172,7 @@ namespace Marco.Pms.Services.Service
|
||||
return ApiResponse<object>.SuccessResponse(new List<ExpenseList>(), "No expenses found for the given criteria.", 200);
|
||||
}
|
||||
|
||||
var response = _mapper.Map<List<ExpenseList>>(expensesList);
|
||||
var expenseVM = _mapper.Map<List<ExpenseList>>(expensesList);
|
||||
|
||||
// 6. --- Efficiently Fetch and Append 'Next Status' Information ---
|
||||
var statusIds = expensesList.Select(e => e.StatusId).Distinct().ToList();
|
||||
@ -182,7 +185,7 @@ namespace Marco.Pms.Services.Service
|
||||
// Use a Lookup for efficient O(1) mapping. This is much better than repeated `.Where()` in a loop.
|
||||
var statusMapLookup = statusMappings.ToLookup(sm => sm.StatusId);
|
||||
|
||||
foreach (var expense in response)
|
||||
foreach (var expense in expenseVM)
|
||||
{
|
||||
if (expense.Status?.Id != null && statusMapLookup.Contains(expense.Status.Id))
|
||||
{
|
||||
@ -197,8 +200,17 @@ namespace Marco.Pms.Services.Service
|
||||
}
|
||||
|
||||
// 7. --- Return Final Success Response ---
|
||||
var message = $"{response.Count} expense records fetched successfully.";
|
||||
var message = $"{expenseVM.Count} expense records fetched successfully.";
|
||||
_logger.LogInfo(message);
|
||||
var totalPages = (int)Math.Ceiling((double)totalEntites / pageSize);
|
||||
var response = new
|
||||
{
|
||||
CurrentPage = pageNumber,
|
||||
TotalPages = totalPages,
|
||||
TotalEntites = totalEntites,
|
||||
Data = expenseVM,
|
||||
};
|
||||
|
||||
return ApiResponse<object>.SuccessResponse(response, message, 200);
|
||||
}
|
||||
catch (DbUpdateException dbEx)
|
||||
@ -415,152 +427,6 @@ namespace Marco.Pms.Services.Service
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<object>> ChangeStatus(ExpenseRecordDto model, Employee loggedInEmployee, Guid tenantId)
|
||||
{
|
||||
|
||||
var existingExpense = await _context.Expenses
|
||||
.Include(e => e.ExpensesType)
|
||||
.Include(e => e.Project)
|
||||
.Include(e => e.PaidBy)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.Include(e => e.PaymentMode)
|
||||
.Include(e => e.Status)
|
||||
.Include(e => e.CreatedBy)
|
||||
.FirstOrDefaultAsync(e => e.Id == model.ExpenseId && e.StatusId != model.StatusId && e.TenantId == tenantId);
|
||||
|
||||
if (existingExpense == null)
|
||||
{
|
||||
return ApiResponse<object>.ErrorResponse("Expense not found", "Expense not found", 404);
|
||||
}
|
||||
|
||||
var statusMappingTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.ExpensesStatusMapping
|
||||
.Include(s => s.NextStatus)
|
||||
.FirstOrDefaultAsync(s => s.StatusId == existingExpense.StatusId && s.NextStatus != null && s.NextStatusId == model.StatusId && s.TenantId == tenantId);
|
||||
});
|
||||
var statusPermissionMappingTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.StatusPermissionMapping.Where(sp => sp.StatusId == model.StatusId).ToListAsync();
|
||||
});
|
||||
|
||||
// Await all prerequisite checks at once.
|
||||
await Task.WhenAll(statusMappingTask, statusPermissionMappingTask);
|
||||
|
||||
var statusMapping = await statusMappingTask;
|
||||
var statusPermissions = await statusPermissionMappingTask;
|
||||
if (statusMapping == null)
|
||||
{
|
||||
return ApiResponse<object>.ErrorResponse("There is no follow-up status for currect status");
|
||||
}
|
||||
if (statusPermissions.Any())
|
||||
{
|
||||
foreach (var sp in statusPermissions)
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
|
||||
|
||||
if (!await permissionService.HasPermission(sp.PermissionId, loggedInEmployee.Id))
|
||||
{
|
||||
_logger.LogWarning("Access DENIED for employee {EmployeeId} attempting to change status of expense from status {StatusId} to {NextStatusId}",
|
||||
loggedInEmployee.Id, statusMapping.StatusId, statusMapping.NextStatusId);
|
||||
return ApiResponse<object>.ErrorResponse("You do not have permission", "Access Denied", 403);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (existingExpense.CreatedById != loggedInEmployee.Id)
|
||||
{
|
||||
_logger.LogWarning("Access DENIED for employee {EmployeeId} attempting to change status of expense from status {StatusId} to {NextStatusId}",
|
||||
loggedInEmployee.Id, statusMapping.StatusId, statusMapping.NextStatusId);
|
||||
return ApiResponse<object>.ErrorResponse("You do not have permission", "Access Denied", 403);
|
||||
}
|
||||
var existingEntity = _updateLogHelper.EntityToBsonDocument(existingExpense);
|
||||
|
||||
existingExpense.StatusId = statusMapping.NextStatusId;
|
||||
existingExpense.Status = statusMapping.NextStatus;
|
||||
|
||||
_context.ExpenseLogs.Add(new ExpenseLog
|
||||
{
|
||||
ExpenseId = existingExpense.Id,
|
||||
Action = statusMapping.NextStatus!.Name,
|
||||
UpdatedById = loggedInEmployee.Id,
|
||||
Comment = model.Comment
|
||||
});
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateConcurrencyException dbEx)
|
||||
{
|
||||
_logger.LogError(dbEx, "Error occured while update status of expanse.");
|
||||
return ApiResponse<object>.ErrorResponse("Error occured while update status of expanse.", new
|
||||
{
|
||||
Message = dbEx.Message,
|
||||
StackTrace = dbEx.StackTrace,
|
||||
Source = dbEx.Source,
|
||||
innerexcption = new
|
||||
{
|
||||
Message = dbEx.InnerException?.Message,
|
||||
StackTrace = dbEx.InnerException?.StackTrace,
|
||||
Source = dbEx.InnerException?.Source,
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
try
|
||||
{
|
||||
var updateLog = new UpdateLogsObject
|
||||
{
|
||||
EntityId = existingExpense.Id.ToString(),
|
||||
UpdatedById = loggedInEmployee.Id.ToString(),
|
||||
OldObject = existingEntity,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
};
|
||||
var mongoDBTask = Task.Run(async () =>
|
||||
{
|
||||
await _updateLogHelper.PushToUpdateLogsAsync(updateLog, Collection);
|
||||
});
|
||||
|
||||
var getNextStatusTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.ExpensesStatusMapping
|
||||
.Include(s => s.NextStatus)
|
||||
.FirstOrDefaultAsync(s => s.StatusId == existingExpense.StatusId && s.NextStatus != null && s.TenantId == tenantId);
|
||||
});
|
||||
|
||||
await Task.WhenAll(mongoDBTask, getNextStatusTask);
|
||||
|
||||
var getNextStatus = await getNextStatusTask;
|
||||
|
||||
var response = _mapper.Map<ExpenseList>(existingExpense);
|
||||
if (getNextStatus != null)
|
||||
{
|
||||
response.NextStatus = _mapper.Map<List<ExpensesStatusMasterVM>>(getNextStatus.NextStatus);
|
||||
}
|
||||
|
||||
return ApiResponse<object>.SuccessResponse(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error occured while Saving old entity in mongoDb");
|
||||
return ApiResponse<object>.ErrorResponse("Error occured while update status of expanse.", new
|
||||
{
|
||||
Message = ex.Message,
|
||||
StackTrace = ex.StackTrace,
|
||||
Source = ex.Source,
|
||||
innerexcption = new
|
||||
{
|
||||
Message = ex.InnerException?.Message,
|
||||
StackTrace = ex.InnerException?.StackTrace,
|
||||
Source = ex.InnerException?.Source,
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Changes the status of an expense record, performing validation, permission checks, and logging.
|
||||
/// </summary>
|
||||
|
Loading…
x
Reference in New Issue
Block a user