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",
|
Name = "Draft",
|
||||||
DisplayName = "Draft",
|
DisplayName = "Draft",
|
||||||
Description = "Expense has been created but not yet submitted.",
|
Description = "Expense has been created but not yet submitted.",
|
||||||
Color = "#212529",
|
Color = "#6c757d",
|
||||||
IsSystem = true,
|
IsSystem = true,
|
||||||
IsActive = true,
|
IsActive = true,
|
||||||
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
|
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||||
|
@ -1934,7 +1934,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
|||||||
new
|
new
|
||||||
{
|
{
|
||||||
Id = new Guid("297e0d8f-f668-41b5-bfea-e03b354251c8"),
|
Id = new Guid("297e0d8f-f668-41b5-bfea-e03b354251c8"),
|
||||||
Color = "#212529",
|
Color = "#6c757d",
|
||||||
Description = "Expense has been created but not yet submitted.",
|
Description = "Expense has been created but not yet submitted.",
|
||||||
DisplayName = "Draft",
|
DisplayName = "Draft",
|
||||||
IsActive = true,
|
IsActive = true,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
@ -373,7 +372,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
|||||||
columns: new[] { "Id", "Color", "Description", "DisplayName", "IsActive", "IsSystem", "Name", "TenantId" },
|
columns: new[] { "Id", "Color", "Description", "DisplayName", "IsActive", "IsSystem", "Name", "TenantId" },
|
||||||
values: new object[,]
|
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("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("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") },
|
{ 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
|
new
|
||||||
{
|
{
|
||||||
Id = new Guid("297e0d8f-f668-41b5-bfea-e03b354251c8"),
|
Id = new Guid("297e0d8f-f668-41b5-bfea-e03b354251c8"),
|
||||||
Color = "#212529",
|
Color = "#6c757d",
|
||||||
Description = "Expense has been created but not yet submitted.",
|
Description = "Expense has been created but not yet submitted.",
|
||||||
DisplayName = "Draft",
|
DisplayName = "Draft",
|
||||||
IsActive = true,
|
IsActive = true,
|
||||||
|
@ -155,6 +155,9 @@ namespace Marco.Pms.Services.Service
|
|||||||
|
|
||||||
// 4. --- Apply Ordering and Pagination ---
|
// 4. --- Apply Ordering and Pagination ---
|
||||||
// This should be the last step before executing the query.
|
// This should be the last step before executing the query.
|
||||||
|
|
||||||
|
var totalEntites = await expensesQuery.CountAsync();
|
||||||
|
|
||||||
var paginatedQuery = expensesQuery
|
var paginatedQuery = expensesQuery
|
||||||
.OrderByDescending(e => e.CreatedAt)
|
.OrderByDescending(e => e.CreatedAt)
|
||||||
.Skip((pageNumber - 1) * pageSize)
|
.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);
|
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 ---
|
// 6. --- Efficiently Fetch and Append 'Next Status' Information ---
|
||||||
var statusIds = expensesList.Select(e => e.StatusId).Distinct().ToList();
|
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.
|
// 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);
|
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))
|
if (expense.Status?.Id != null && statusMapLookup.Contains(expense.Status.Id))
|
||||||
{
|
{
|
||||||
@ -197,8 +200,17 @@ namespace Marco.Pms.Services.Service
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 7. --- Return Final Success Response ---
|
// 7. --- Return Final Success Response ---
|
||||||
var message = $"{response.Count} expense records fetched successfully.";
|
var message = $"{expenseVM.Count} expense records fetched successfully.";
|
||||||
_logger.LogInfo(message);
|
_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);
|
return ApiResponse<object>.SuccessResponse(response, message, 200);
|
||||||
}
|
}
|
||||||
catch (DbUpdateException dbEx)
|
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>
|
/// <summary>
|
||||||
/// Changes the status of an expense record, performing validation, permission checks, and logging.
|
/// Changes the status of an expense record, performing validation, permission checks, and logging.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user