Added the search funcationality abd chnaged the cache object
This commit is contained in:
parent
1a0641162c
commit
1c9008ca62
@ -1,6 +1,7 @@
|
|||||||
using Marco.Pms.Model.MongoDBModels.Expenses;
|
using Marco.Pms.Model.MongoDBModels.Expenses;
|
||||||
using Marco.Pms.Model.Utilities;
|
using Marco.Pms.Model.Utilities;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using MongoDB.Bson;
|
||||||
using MongoDB.Driver;
|
using MongoDB.Driver;
|
||||||
|
|
||||||
namespace Marco.Pms.Helpers.CacheHelper
|
namespace Marco.Pms.Helpers.CacheHelper
|
||||||
@ -36,7 +37,7 @@ namespace Marco.Pms.Helpers.CacheHelper
|
|||||||
await InitializeCollectionAsync();
|
await InitializeCollectionAsync();
|
||||||
}
|
}
|
||||||
public async Task<(int totalPages, long totalCount, List<ExpenseDetailsMongoDB> expenseList)> GetExpenseListFromCacheAsync(Guid tenantId, Guid loggedInEmployeeId, bool viewAll,
|
public async Task<(int totalPages, long totalCount, List<ExpenseDetailsMongoDB> expenseList)> GetExpenseListFromCacheAsync(Guid tenantId, Guid loggedInEmployeeId, bool viewAll,
|
||||||
bool viewSelf, int pageNumber, int pageSize, ExpensesFilter? expenseFilter)
|
bool viewSelf, int pageNumber, int pageSize, ExpensesFilter? expenseFilter, string? searchString)
|
||||||
{
|
{
|
||||||
var filterBuilder = Builders<ExpenseDetailsMongoDB>.Filter;
|
var filterBuilder = Builders<ExpenseDetailsMongoDB>.Filter;
|
||||||
var filter = filterBuilder.Empty;
|
var filter = filterBuilder.Empty;
|
||||||
@ -44,10 +45,11 @@ namespace Marco.Pms.Helpers.CacheHelper
|
|||||||
// Permission-based filter
|
// Permission-based filter
|
||||||
if (!viewAll && viewSelf)
|
if (!viewAll && viewSelf)
|
||||||
{
|
{
|
||||||
filter &= filterBuilder.Eq(e => e.CreatedById, loggedInEmployeeId.ToString());
|
filter &= filterBuilder.Eq(e => e.CreatedBy.Id, loggedInEmployeeId.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply filters
|
// Apply filters
|
||||||
|
|
||||||
if (expenseFilter != null)
|
if (expenseFilter != null)
|
||||||
{
|
{
|
||||||
if (expenseFilter.StartDate.HasValue && expenseFilter.EndDate.HasValue)
|
if (expenseFilter.StartDate.HasValue && expenseFilter.EndDate.HasValue)
|
||||||
@ -58,25 +60,62 @@ namespace Marco.Pms.Helpers.CacheHelper
|
|||||||
|
|
||||||
if (expenseFilter.ProjectIds?.Any() == true)
|
if (expenseFilter.ProjectIds?.Any() == true)
|
||||||
{
|
{
|
||||||
filter &= filterBuilder.In(e => e.ProjectId, expenseFilter.ProjectIds.Select(p => p.ToString()).ToList());
|
filter &= filterBuilder.In(e => e.Project.Id, expenseFilter.ProjectIds.Select(p => p.ToString()).ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expenseFilter.StatusIds?.Any() == true)
|
if (expenseFilter.StatusIds?.Any() == true)
|
||||||
{
|
{
|
||||||
filter &= filterBuilder.In(e => e.StatusId, expenseFilter.StatusIds.Select(p => p.ToString()).ToList());
|
filter &= filterBuilder.In(e => e.Status.Id, expenseFilter.StatusIds.Select(p => p.ToString()).ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expenseFilter.PaidById?.Any() == true)
|
if (expenseFilter.PaidById?.Any() == true)
|
||||||
{
|
{
|
||||||
filter &= filterBuilder.In(e => e.PaidById, expenseFilter.PaidById.Select(p => p.ToString()).ToList());
|
filter &= filterBuilder.In(e => e.PaidBy.Id, expenseFilter.PaidById.Select(p => p.ToString()).ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expenseFilter.CreatedByIds?.Any() == true && viewAll)
|
if (expenseFilter.CreatedByIds?.Any() == true && viewAll)
|
||||||
{
|
{
|
||||||
filter &= filterBuilder.In(e => e.CreatedById, expenseFilter.CreatedByIds.Select(p => p.ToString()).ToList());
|
filter &= filterBuilder.In(e => e.CreatedBy.Id, expenseFilter.CreatedByIds.Select(p => p.ToString()).ToList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(searchString))
|
||||||
|
{
|
||||||
|
var searchPattern = new BsonRegularExpression(searchString, "i");
|
||||||
|
|
||||||
|
// The base text searches remain the same
|
||||||
|
var searchClauses = new List<FilterDefinition<ExpenseDetailsMongoDB>>
|
||||||
|
{
|
||||||
|
filterBuilder.Regex(e => e.Description, searchPattern),
|
||||||
|
filterBuilder.Regex(e => e.TransactionId, searchPattern)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build the complex filter for PaidBy.FullName
|
||||||
|
var paidByFilter = new BsonDocument("$expr",
|
||||||
|
new BsonDocument("$regexMatch", new BsonDocument
|
||||||
|
{
|
||||||
|
{ "input", new BsonDocument("$concat", new BsonArray { "$PaidBy.FirstName", " ", "$PaidBy.LastName" }) },
|
||||||
|
{ "regex", searchString }, // BsonRegularExpression can't be used here, pass the string
|
||||||
|
{ "options", "i" } // Case-insensitivity option
|
||||||
|
})
|
||||||
|
);
|
||||||
|
searchClauses.Add(paidByFilter);
|
||||||
|
|
||||||
|
// Build the complex filter for CreatedBy.FullName
|
||||||
|
var createdByFilter = new BsonDocument("$expr",
|
||||||
|
new BsonDocument("$regexMatch", new BsonDocument
|
||||||
|
{
|
||||||
|
{ "input", new BsonDocument("$concat", new BsonArray { "$CreatedBy.FirstName", " ", "$CreatedBy.LastName" }) },
|
||||||
|
{ "regex", searchString },
|
||||||
|
{ "options", "i" }
|
||||||
|
})
|
||||||
|
);
|
||||||
|
searchClauses.Add(createdByFilter);
|
||||||
|
|
||||||
|
// Combine all clauses with an OR
|
||||||
|
filter &= filterBuilder.Or(searchClauses);
|
||||||
|
}
|
||||||
|
|
||||||
// Total count
|
// Total count
|
||||||
var totalCount = await _collection.CountDocumentsAsync(filter);
|
var totalCount = await _collection.CountDocumentsAsync(filter);
|
||||||
var totalPages = (int)Math.Ceiling((double)totalCount / pageSize);
|
var totalPages = (int)Math.Ceiling((double)totalCount / pageSize);
|
||||||
|
@ -1,22 +1,27 @@
|
|||||||
namespace Marco.Pms.Model.MongoDBModels.Expenses
|
using Marco.Pms.Model.MongoDBModels.Employees;
|
||||||
|
using Marco.Pms.Model.MongoDBModels.Masters;
|
||||||
|
using Marco.Pms.Model.MongoDBModels.Project;
|
||||||
|
|
||||||
|
namespace Marco.Pms.Model.MongoDBModels.Expenses
|
||||||
{
|
{
|
||||||
public class ExpenseDetailsMongoDB
|
public class ExpenseDetailsMongoDB
|
||||||
{
|
{
|
||||||
public string Id { get; set; } = string.Empty;
|
public string Id { get; set; } = string.Empty;
|
||||||
public string ProjectId { get; set; } = string.Empty;
|
public ProjectBasicMongoDB Project { get; set; } = new ProjectBasicMongoDB();
|
||||||
public string ExpensesTypeId { get; set; } = string.Empty;
|
public ExpensesTypeMasterMongoDB ExpensesType { get; set; } = new ExpensesTypeMasterMongoDB();
|
||||||
public string PaymentModeId { get; set; } = string.Empty;
|
public PaymentModeMatserMongoDB PaymentMode { get; set; } = new PaymentModeMatserMongoDB();
|
||||||
public string PaidById { get; set; } = string.Empty;
|
public BasicEmployeeMongoDB PaidBy { get; set; } = new BasicEmployeeMongoDB();
|
||||||
public string CreatedById { get; set; } = string.Empty;
|
public BasicEmployeeMongoDB CreatedBy { get; set; } = new BasicEmployeeMongoDB();
|
||||||
public string? ReviewedById { get; set; }
|
public BasicEmployeeMongoDB? ReviewedBy { get; set; }
|
||||||
public string? ApprovedById { get; set; }
|
public BasicEmployeeMongoDB? ApprovedBy { get; set; }
|
||||||
public string? ProcessedById { get; set; }
|
public BasicEmployeeMongoDB? ProcessedBy { get; set; }
|
||||||
public DateTime TransactionDate { get; set; }
|
public DateTime TransactionDate { get; set; }
|
||||||
public DateTime CreatedAt { get; set; }
|
public DateTime CreatedAt { get; set; }
|
||||||
public DateTime ExpireAt { get; set; } = DateTime.UtcNow.Date.AddDays(1);
|
public DateTime ExpireAt { get; set; } = DateTime.UtcNow.Date.AddDays(1);
|
||||||
public string SupplerName { get; set; } = string.Empty;
|
public string SupplerName { get; set; } = string.Empty;
|
||||||
public double Amount { get; set; }
|
public double Amount { get; set; }
|
||||||
public string StatusId { get; set; } = string.Empty;
|
public ExpensesStatusMasterMongoDB Status { get; set; } = new ExpensesStatusMasterMongoDB();
|
||||||
|
public List<ExpensesStatusMasterMongoDB> NextStatus { get; set; } = new List<ExpensesStatusMasterMongoDB>();
|
||||||
public bool PreApproved { get; set; } = false;
|
public bool PreApproved { get; set; } = false;
|
||||||
public string? TransactionId { get; set; }
|
public string? TransactionId { get; set; }
|
||||||
public string Description { get; set; } = string.Empty;
|
public string Description { get; set; } = string.Empty;
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
public string DisplayName { get; set; } = string.Empty;
|
public string DisplayName { get; set; } = string.Empty;
|
||||||
public string Description { get; set; } = string.Empty;
|
public string Description { get; set; } = string.Empty;
|
||||||
|
//public List<string> PermissionIds { get; set; } = new List<string>();
|
||||||
public string? Color { get; set; }
|
public string? Color { get; set; }
|
||||||
public bool IsSystem { get; set; } = false;
|
public bool IsSystem { get; set; } = false;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@ namespace Marco.Pms.Model.ViewModels.Expanses
|
|||||||
public DateTime TransactionDate { get; set; }
|
public DateTime TransactionDate { get; set; }
|
||||||
public DateTime CreatedAt { get; set; }
|
public DateTime CreatedAt { get; set; }
|
||||||
public string SupplerName { get; set; } = string.Empty;
|
public string SupplerName { get; set; } = string.Empty;
|
||||||
|
public string Description { get; set; } = string.Empty;
|
||||||
|
public string TransactionId { get; set; } = string.Empty;
|
||||||
public double Amount { get; set; }
|
public double Amount { get; set; }
|
||||||
public ExpensesStatusMasterVM? Status { get; set; }
|
public ExpensesStatusMasterVM? Status { get; set; }
|
||||||
public List<ExpensesStatusMasterVM>? NextStatus { get; set; }
|
public List<ExpensesStatusMasterVM>? NextStatus { get; set; }
|
||||||
|
@ -39,10 +39,10 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
/// <returns>A paginated list of expenses.</returns>
|
/// <returns>A paginated list of expenses.</returns>
|
||||||
|
|
||||||
[HttpGet("list")]
|
[HttpGet("list")]
|
||||||
public async Task<IActionResult> GetExpensesList(string? filter, int pageSize = 20, int pageNumber = 1)
|
public async Task<IActionResult> GetExpensesList([FromQuery] string? searchString, [FromQuery] string? filter, [FromQuery] int pageSize = 20, [FromQuery] int pageNumber = 1)
|
||||||
{
|
{
|
||||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
var response = await _expensesService.GetExpensesListAsync(loggedInEmployee, tenantId, filter, pageSize, pageNumber);
|
var response = await _expensesService.GetExpensesListAsync(loggedInEmployee, tenantId, searchString, filter, pageSize, pageNumber);
|
||||||
return StatusCode(response.StatusCode, response);
|
return StatusCode(response.StatusCode, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,11 +62,24 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
return StatusCode(response.StatusCode, response);
|
return StatusCode(response.StatusCode, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("filter")]
|
||||||
|
public async Task<IActionResult> GetFilterObject()
|
||||||
|
{
|
||||||
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
|
var response = await _expensesService.GetFilterObjectAsync(loggedInEmployee, tenantId);
|
||||||
|
return StatusCode(response.StatusCode, response);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("create")]
|
[HttpPost("create")]
|
||||||
public async Task<IActionResult> CreateExpense([FromBody] CreateExpensesDto model)
|
public async Task<IActionResult> CreateExpense([FromBody] CreateExpensesDto model)
|
||||||
{
|
{
|
||||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
var response = await _expensesService.CreateExpenseAsync(model, loggedInEmployee, tenantId);
|
var response = await _expensesService.CreateExpenseAsync(model, loggedInEmployee, tenantId);
|
||||||
|
if (response.Success)
|
||||||
|
{
|
||||||
|
var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Expanse", Response = response.Data };
|
||||||
|
await _signalR.SendNotificationAsync(notification);
|
||||||
|
}
|
||||||
return StatusCode(response.StatusCode, response);
|
return StatusCode(response.StatusCode, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,6 +114,11 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
{
|
{
|
||||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
var response = await _expensesService.DeleteExpanseAsync(id, loggedInEmployee, tenantId);
|
var response = await _expensesService.DeleteExpanseAsync(id, loggedInEmployee, tenantId);
|
||||||
|
if (response.Success)
|
||||||
|
{
|
||||||
|
var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Expanse", Response = response.Data };
|
||||||
|
await _signalR.SendNotificationAsync(notification);
|
||||||
|
}
|
||||||
return StatusCode(response.StatusCode, response);
|
return StatusCode(response.StatusCode, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ using Marco.Pms.Helpers.CacheHelper;
|
|||||||
using Marco.Pms.Model.Expenses;
|
using Marco.Pms.Model.Expenses;
|
||||||
using Marco.Pms.Model.Master;
|
using Marco.Pms.Model.Master;
|
||||||
using Marco.Pms.Model.MongoDBModels;
|
using Marco.Pms.Model.MongoDBModels;
|
||||||
|
using Marco.Pms.Model.MongoDBModels.Employees;
|
||||||
using Marco.Pms.Model.MongoDBModels.Expenses;
|
using Marco.Pms.Model.MongoDBModels.Expenses;
|
||||||
using Marco.Pms.Model.MongoDBModels.Masters;
|
using Marco.Pms.Model.MongoDBModels.Masters;
|
||||||
using Marco.Pms.Model.MongoDBModels.Project;
|
using Marco.Pms.Model.MongoDBModels.Project;
|
||||||
@ -869,36 +870,8 @@ namespace Marco.Pms.Services.Helpers
|
|||||||
#region ======================================================= Expenses Cache =======================================================
|
#region ======================================================= Expenses Cache =======================================================
|
||||||
public async Task AddExpenseByObjectAsync(Expenses expense)
|
public async Task AddExpenseByObjectAsync(Expenses expense)
|
||||||
{
|
{
|
||||||
var expenseCache = _mapper.Map<ExpenseDetailsMongoDB>(expense);
|
var expenseCache = await GetAllExpnesRelatedTablesForSingle(expense, expense.TenantId);
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var billAttachment = await _context.BillAttachments
|
|
||||||
.Include(ba => ba.Document)
|
|
||||||
.AsNoTracking()
|
|
||||||
.Where(ba => ba.ExpensesId == expense.Id && ba.Document != null)
|
|
||||||
.GroupBy(ba => ba.ExpensesId)
|
|
||||||
.Select(g => new
|
|
||||||
{
|
|
||||||
Documents = g.Select(ba => new DocumentMongoDB
|
|
||||||
{
|
|
||||||
DocumentId = ba.Document!.Id.ToString(),
|
|
||||||
FileName = ba.Document.FileName,
|
|
||||||
ContentType = ba.Document.ContentType,
|
|
||||||
S3Key = ba.Document.S3Key,
|
|
||||||
ThumbS3Key = ba.Document.ThumbS3Key ?? ba.Document.S3Key
|
|
||||||
}).ToList()
|
|
||||||
})
|
|
||||||
.FirstOrDefaultAsync(); ;
|
|
||||||
if (billAttachment != null)
|
|
||||||
{
|
|
||||||
expenseCache.Documents = billAttachment.Documents;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error occurd while fetched expense related tables to save in cahce");
|
|
||||||
}
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _expenseCache.AddExpenseToCacheAsync(expenseCache);
|
await _expenseCache.AddExpenseToCacheAsync(expenseCache);
|
||||||
@ -914,40 +887,13 @@ namespace Marco.Pms.Services.Helpers
|
|||||||
public async Task<ExpenseDetailsMongoDB?> AddExpenseByIdAsync(Guid Id, Guid tenantId)
|
public async Task<ExpenseDetailsMongoDB?> AddExpenseByIdAsync(Guid Id, Guid tenantId)
|
||||||
{
|
{
|
||||||
var expense = await _context.Expenses.AsNoTracking().FirstOrDefaultAsync(e => e.Id == Id && e.TenantId == tenantId);
|
var expense = await _context.Expenses.AsNoTracking().FirstOrDefaultAsync(e => e.Id == Id && e.TenantId == tenantId);
|
||||||
var expenseCache = _mapper.Map<ExpenseDetailsMongoDB>(expense);
|
|
||||||
if (expense == null)
|
if (expense == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try
|
var expenseCache = await GetAllExpnesRelatedTablesForSingle(expense, expense.TenantId);
|
||||||
{
|
|
||||||
var billAttachments = await _context.BillAttachments
|
|
||||||
.Include(ba => ba.Document)
|
|
||||||
.AsNoTracking()
|
|
||||||
.Where(ba => ba.ExpensesId == expense.Id && ba.Document != null)
|
|
||||||
.GroupBy(ba => ba.ExpensesId)
|
|
||||||
.Select(g => new
|
|
||||||
{
|
|
||||||
Documents = g.Select(ba => new DocumentMongoDB
|
|
||||||
{
|
|
||||||
DocumentId = ba.Document!.Id.ToString(),
|
|
||||||
FileName = ba.Document.FileName,
|
|
||||||
ContentType = ba.Document.ContentType,
|
|
||||||
S3Key = ba.Document.S3Key,
|
|
||||||
ThumbS3Key = ba.Document.ThumbS3Key ?? ba.Document.S3Key
|
|
||||||
}).ToList()
|
|
||||||
})
|
|
||||||
.FirstOrDefaultAsync();
|
|
||||||
if (billAttachments != null)
|
|
||||||
{
|
|
||||||
expenseCache.Documents = billAttachments.Documents;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error occurd while fetched expense related tables to save in cahce");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _expenseCache.AddExpenseToCacheAsync(expenseCache);
|
await _expenseCache.AddExpenseToCacheAsync(expenseCache);
|
||||||
@ -962,39 +908,9 @@ namespace Marco.Pms.Services.Helpers
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
public async Task AddExpensesListToCache(List<Expenses> expenses)
|
public async Task AddExpensesListToCache(List<Expenses> expenses, Guid tenantId)
|
||||||
{
|
{
|
||||||
var expensesCache = _mapper.Map<List<ExpenseDetailsMongoDB>>(expenses);
|
var expensesCache = await GetAllExpnesRelatedTablesForList(expenses, tenantId);
|
||||||
var expenseIds = expenses.Select(e => e.Id).ToList();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var billAttachments = await _context.BillAttachments
|
|
||||||
.Include(ba => ba.Document)
|
|
||||||
.AsNoTracking()
|
|
||||||
.Where(ba => expenseIds.Contains(ba.ExpensesId) && ba.Document != null)
|
|
||||||
.GroupBy(ba => ba.ExpensesId)
|
|
||||||
.Select(g => new
|
|
||||||
{
|
|
||||||
ExpensesId = g.Key,
|
|
||||||
Documents = g.Select(ba => new DocumentMongoDB
|
|
||||||
{
|
|
||||||
DocumentId = ba.Document!.Id.ToString(),
|
|
||||||
FileName = ba.Document.FileName,
|
|
||||||
ContentType = ba.Document.ContentType,
|
|
||||||
S3Key = ba.Document.S3Key,
|
|
||||||
ThumbS3Key = ba.Document.ThumbS3Key ?? ba.Document.S3Key
|
|
||||||
}).ToList()
|
|
||||||
})
|
|
||||||
.ToListAsync();
|
|
||||||
foreach (var expenseCache in expensesCache)
|
|
||||||
{
|
|
||||||
expenseCache.Documents = billAttachments.Where(ba => ba.ExpensesId == Guid.Parse(expenseCache.Id)).Select(ba => ba.Documents).FirstOrDefault() ?? new List<DocumentMongoDB>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error occurd while fetched expense related tables to save in cahce");
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -1007,15 +923,18 @@ namespace Marco.Pms.Services.Helpers
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(int totalPages, long totalCount, List<ExpenseDetailsMongoDB>? expenseList)> GetExpenseListAsync(Guid tenantId, Guid loggedInEmployeeId, bool viewAll,
|
public async Task<(int totalPages, long totalCount, List<ExpenseDetailsMongoDB>? expenseList)> GetExpenseListAsync(Guid tenantId, Guid loggedInEmployeeId, bool viewAll,
|
||||||
bool viewSelf, int pageNumber, int pageSize, ExpensesFilter? filter)
|
bool viewSelf, int pageNumber, int pageSize, ExpensesFilter? filter, string? searchString)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var (totalPages, totalCount, expenseList) = await _expenseCache.GetExpenseListFromCacheAsync(tenantId, loggedInEmployeeId, viewAll, viewSelf, pageNumber, pageSize, filter);
|
var (totalPages, totalCount, expenseList) = await _expenseCache.GetExpenseListFromCacheAsync(tenantId, loggedInEmployeeId, viewAll, viewSelf, pageNumber, pageSize, filter, searchString);
|
||||||
if (expenseList.Any())
|
if (expenseList.Any())
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
return (totalPages, totalCount, expenseList);
|
return (totalPages, totalCount, expenseList);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -1101,5 +1020,281 @@ namespace Marco.Pms.Services.Helpers
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region ======================================================= Helper Functions =======================================================
|
||||||
|
private async Task<List<ExpenseDetailsMongoDB>> GetAllExpnesRelatedTablesForList(List<Expenses> model, Guid tenantId)
|
||||||
|
{
|
||||||
|
List<ExpenseDetailsMongoDB> expenseList = new List<ExpenseDetailsMongoDB>();
|
||||||
|
var expenseIds = model.Select(m => m.Id).ToList();
|
||||||
|
var projectIds = model.Select(m => m.ProjectId).ToList();
|
||||||
|
var statusIds = model.Select(m => m.StatusId).ToList();
|
||||||
|
var expensesTypeIds = model.Select(m => m.ExpensesTypeId).ToList();
|
||||||
|
var paymentModeIds = model.Select(m => m.PaymentModeId).ToList();
|
||||||
|
var createdByIds = model.Select(m => m.CreatedById).ToList();
|
||||||
|
var reviewedByIds = model.Select(m => m.ReviewedById).ToList();
|
||||||
|
var approvedByIds = model.Select(m => m.ApprovedById).ToList();
|
||||||
|
var processedByIds = model.Select(m => m.ProcessedById).ToList();
|
||||||
|
var paidByIds = model.Select(m => m.PaidById).ToList();
|
||||||
|
|
||||||
|
var projectTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Projects.AsNoTracking().Where(p => projectIds.Contains(p.Id) && p.TenantId == tenantId).ToListAsync();
|
||||||
|
});
|
||||||
|
var paidByTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => paidByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||||
|
});
|
||||||
|
var createdByTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => createdByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||||
|
});
|
||||||
|
var reviewedByTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => reviewedByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||||
|
});
|
||||||
|
var approvedByTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => approvedByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||||
|
});
|
||||||
|
var processedByTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => processedByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||||
|
});
|
||||||
|
var expenseTypeTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.ExpensesTypeMaster.AsNoTracking().Where(et => expensesTypeIds.Contains(et.Id) && et.TenantId == tenantId).ToListAsync();
|
||||||
|
});
|
||||||
|
var paymentModeTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.PaymentModeMatser.AsNoTracking().Where(pm => paymentModeIds.Contains(pm.Id) && pm.TenantId == tenantId).ToListAsync();
|
||||||
|
});
|
||||||
|
var statusMappingTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.ExpensesStatusMapping
|
||||||
|
.Include(s => s.Status)
|
||||||
|
.Include(s => s.NextStatus)
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(es => statusIds.Contains(es.StatusId) && es.Status != null)
|
||||||
|
.GroupBy(s => s.StatusId)
|
||||||
|
.Select(g => new
|
||||||
|
{
|
||||||
|
StatusId = g.Key,
|
||||||
|
Status = g.Select(s => s.Status).FirstOrDefault(),
|
||||||
|
NextStatus = g.Select(s => s.NextStatus).ToList()
|
||||||
|
}).ToListAsync();
|
||||||
|
});
|
||||||
|
var statusTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.ExpensesStatusMaster
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(es => statusIds.Contains(es.Id))
|
||||||
|
.ToListAsync();
|
||||||
|
});
|
||||||
|
var billAttachmentsTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.BillAttachments
|
||||||
|
.Include(ba => ba.Document)
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(ba => expenseIds.Contains(ba.ExpensesId) && ba.Document != null)
|
||||||
|
.GroupBy(ba => ba.ExpensesId)
|
||||||
|
.Select(g => new
|
||||||
|
{
|
||||||
|
ExpensesId = g.Key,
|
||||||
|
Documents = g.Select(ba => new DocumentMongoDB
|
||||||
|
{
|
||||||
|
DocumentId = ba.Document!.Id.ToString(),
|
||||||
|
FileName = ba.Document.FileName,
|
||||||
|
ContentType = ba.Document.ContentType,
|
||||||
|
S3Key = ba.Document.S3Key,
|
||||||
|
ThumbS3Key = ba.Document.ThumbS3Key ?? ba.Document.S3Key
|
||||||
|
}).ToList()
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Await all prerequisite checks at once.
|
||||||
|
await Task.WhenAll(projectTask, expenseTypeTask, paymentModeTask, statusMappingTask, paidByTask, createdByTask, reviewedByTask, approvedByTask,
|
||||||
|
processedByTask, statusTask, billAttachmentsTask);
|
||||||
|
|
||||||
|
var projects = projectTask.Result;
|
||||||
|
var expenseTypes = expenseTypeTask.Result;
|
||||||
|
var paymentModes = paymentModeTask.Result;
|
||||||
|
var statusMappings = statusMappingTask.Result;
|
||||||
|
var paidBys = paidByTask.Result;
|
||||||
|
var createdBys = createdByTask.Result;
|
||||||
|
var reviewedBys = reviewedByTask.Result;
|
||||||
|
var approvedBys = approvedByTask.Result;
|
||||||
|
var processedBy = processedByTask.Result;
|
||||||
|
var billAttachments = billAttachmentsTask.Result;
|
||||||
|
|
||||||
|
expenseList = model.Select(m =>
|
||||||
|
{
|
||||||
|
var response = _mapper.Map<ExpenseDetailsMongoDB>(m);
|
||||||
|
|
||||||
|
response.Project = projects.Where(p => p.Id == m.ProjectId).Select(p => _mapper.Map<ProjectBasicMongoDB>(p)).FirstOrDefault() ?? new ProjectBasicMongoDB();
|
||||||
|
response.PaidBy = paidBys.Where(p => p.Id == m.PaidById).Select(p => _mapper.Map<BasicEmployeeMongoDB>(p)).FirstOrDefault() ?? new BasicEmployeeMongoDB();
|
||||||
|
response.CreatedBy = createdBys.Where(e => e.Id == m.CreatedById).Select(e => _mapper.Map<BasicEmployeeMongoDB>(e)).FirstOrDefault() ?? new BasicEmployeeMongoDB();
|
||||||
|
response.ReviewedBy = reviewedBys.Where(e => e.Id == m.CreatedById).Select(e => _mapper.Map<BasicEmployeeMongoDB>(e)).FirstOrDefault() ?? new BasicEmployeeMongoDB();
|
||||||
|
response.ApprovedBy = approvedBys.Where(e => e.Id == m.CreatedById).Select(e => _mapper.Map<BasicEmployeeMongoDB>(e)).FirstOrDefault() ?? new BasicEmployeeMongoDB();
|
||||||
|
response.ProcessedBy = processedBy.Where(e => e.Id == m.CreatedById).Select(e => _mapper.Map<BasicEmployeeMongoDB>(e)).FirstOrDefault() ?? new BasicEmployeeMongoDB();
|
||||||
|
response.Status = statusMappings.Where(s => s.StatusId == m.StatusId).Select(s => _mapper.Map<ExpensesStatusMasterMongoDB>(s.Status)).FirstOrDefault() ?? new ExpensesStatusMasterMongoDB();
|
||||||
|
if (response.Status.Id == string.Empty)
|
||||||
|
{
|
||||||
|
var status = statusTask.Result;
|
||||||
|
response.Status = status.Where(s => s.Id == m.StatusId).Select(s => _mapper.Map<ExpensesStatusMasterMongoDB>(s)).FirstOrDefault() ?? new ExpensesStatusMasterMongoDB();
|
||||||
|
}
|
||||||
|
|
||||||
|
response.NextStatus = statusMappings.Where(s => s.StatusId == m.StatusId).Select(s => _mapper.Map<List<ExpensesStatusMasterMongoDB>>(s.NextStatus)).FirstOrDefault() ?? new List<ExpensesStatusMasterMongoDB>();
|
||||||
|
response.PaymentMode = paymentModes.Where(pm => pm.Id == m.PaymentModeId).Select(pm => _mapper.Map<PaymentModeMatserMongoDB>(pm)).FirstOrDefault() ?? new PaymentModeMatserMongoDB();
|
||||||
|
response.ExpensesType = expenseTypes.Where(et => et.Id == m.ExpensesTypeId).Select(et => _mapper.Map<ExpensesTypeMasterMongoDB>(et)).FirstOrDefault() ?? new ExpensesTypeMasterMongoDB();
|
||||||
|
response.Documents = billAttachments.Where(ba => ba.ExpensesId == m.Id).Select(ba => ba.Documents).FirstOrDefault() ?? new List<DocumentMongoDB>();
|
||||||
|
return response;
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
return expenseList;
|
||||||
|
}
|
||||||
|
private async Task<ExpenseDetailsMongoDB> GetAllExpnesRelatedTablesForSingle(Expenses model, Guid tenantId)
|
||||||
|
{
|
||||||
|
var projectTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Projects.AsNoTracking().FirstOrDefaultAsync(p => p.Id == model.ProjectId && p.TenantId == tenantId);
|
||||||
|
});
|
||||||
|
var paidByTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.PaidById && e.TenantId == tenantId);
|
||||||
|
});
|
||||||
|
var createdByTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.CreatedById && e.TenantId == tenantId);
|
||||||
|
});
|
||||||
|
var reviewedByTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.ReviewedById && e.TenantId == tenantId);
|
||||||
|
});
|
||||||
|
var approvedByTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.ApprovedById && e.TenantId == tenantId);
|
||||||
|
});
|
||||||
|
var processedByTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.ProcessedById && e.TenantId == tenantId);
|
||||||
|
});
|
||||||
|
var expenseTypeTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.ExpensesTypeMaster.AsNoTracking().FirstOrDefaultAsync(et => et.Id == model.ExpensesTypeId && et.TenantId == tenantId);
|
||||||
|
});
|
||||||
|
var paymentModeTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.PaymentModeMatser.AsNoTracking().FirstOrDefaultAsync(pm => pm.Id == model.PaymentModeId && pm.TenantId == tenantId);
|
||||||
|
});
|
||||||
|
var statusMappingTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.ExpensesStatusMapping
|
||||||
|
.Include(s => s.Status)
|
||||||
|
.Include(s => s.NextStatus)
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(es => es.StatusId == model.StatusId && es.Status != null)
|
||||||
|
.GroupBy(s => s.StatusId)
|
||||||
|
.Select(g => new
|
||||||
|
{
|
||||||
|
StatusId = g.Key,
|
||||||
|
Status = g.Select(s => s.Status).FirstOrDefault(),
|
||||||
|
NextStatus = g.Select(s => s.NextStatus).ToList()
|
||||||
|
}).FirstOrDefaultAsync();
|
||||||
|
});
|
||||||
|
var statusTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.ExpensesStatusMaster
|
||||||
|
.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(es => es.Id == model.StatusId);
|
||||||
|
});
|
||||||
|
var billAttachmentsTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.BillAttachments
|
||||||
|
.Include(ba => ba.Document)
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(ba => ba.ExpensesId == model.Id && ba.Document != null)
|
||||||
|
.GroupBy(ba => ba.ExpensesId)
|
||||||
|
.Select(g => new
|
||||||
|
{
|
||||||
|
ExpensesId = g.Key,
|
||||||
|
Documents = g.Select(ba => new DocumentMongoDB
|
||||||
|
{
|
||||||
|
DocumentId = ba.Document!.Id.ToString(),
|
||||||
|
FileName = ba.Document.FileName,
|
||||||
|
ContentType = ba.Document.ContentType,
|
||||||
|
S3Key = ba.Document.S3Key,
|
||||||
|
ThumbS3Key = ba.Document.ThumbS3Key ?? ba.Document.S3Key
|
||||||
|
}).ToList()
|
||||||
|
})
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Await all prerequisite checks at once.
|
||||||
|
await Task.WhenAll(projectTask, expenseTypeTask, paymentModeTask, statusMappingTask, paidByTask, createdByTask, reviewedByTask, approvedByTask,
|
||||||
|
processedByTask, statusTask, billAttachmentsTask);
|
||||||
|
|
||||||
|
var project = projectTask.Result;
|
||||||
|
var expenseType = expenseTypeTask.Result;
|
||||||
|
var paymentMode = paymentModeTask.Result;
|
||||||
|
var statusMapping = statusMappingTask.Result;
|
||||||
|
var paidBy = paidByTask.Result;
|
||||||
|
var createdBy = createdByTask.Result;
|
||||||
|
var reviewedBy = reviewedByTask.Result;
|
||||||
|
var approvedBy = approvedByTask.Result;
|
||||||
|
var processedBy = processedByTask.Result;
|
||||||
|
var billAttachment = billAttachmentsTask.Result;
|
||||||
|
|
||||||
|
|
||||||
|
var response = _mapper.Map<ExpenseDetailsMongoDB>(model);
|
||||||
|
|
||||||
|
response.Project = _mapper.Map<ProjectBasicMongoDB>(project);
|
||||||
|
response.PaidBy = _mapper.Map<BasicEmployeeMongoDB>(paidBy);
|
||||||
|
response.CreatedBy = _mapper.Map<BasicEmployeeMongoDB>(createdBy);
|
||||||
|
response.ReviewedBy = _mapper.Map<BasicEmployeeMongoDB>(reviewedBy);
|
||||||
|
response.ApprovedBy = _mapper.Map<BasicEmployeeMongoDB>(approvedBy);
|
||||||
|
response.ProcessedBy = _mapper.Map<BasicEmployeeMongoDB>(processedBy);
|
||||||
|
if (statusMapping != null)
|
||||||
|
{
|
||||||
|
response.Status = _mapper.Map<ExpensesStatusMasterMongoDB>(statusMapping.Status);
|
||||||
|
response.NextStatus = _mapper.Map<List<ExpensesStatusMasterMongoDB>>(statusMapping.NextStatus);
|
||||||
|
}
|
||||||
|
if (response.Status == null)
|
||||||
|
{
|
||||||
|
var status = statusTask.Result;
|
||||||
|
response.Status = _mapper.Map<ExpensesStatusMasterMongoDB>(status);
|
||||||
|
}
|
||||||
|
response.PaymentMode = _mapper.Map<PaymentModeMatserMongoDB>(paymentMode);
|
||||||
|
response.ExpensesType = _mapper.Map<ExpensesTypeMasterMongoDB>(expenseType);
|
||||||
|
if (billAttachment != null) response.Documents = billAttachment.Documents;
|
||||||
|
|
||||||
|
return response;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,33 +130,6 @@ namespace Marco.Pms.Services.MappingProfiles
|
|||||||
dest => dest.Id,
|
dest => dest.Id,
|
||||||
opt => opt.MapFrom(src => src.Id.ToString()))
|
opt => opt.MapFrom(src => src.Id.ToString()))
|
||||||
.ForMember(
|
.ForMember(
|
||||||
dest => dest.ProjectId,
|
|
||||||
opt => opt.MapFrom(src => src.ProjectId.ToString()))
|
|
||||||
.ForMember(
|
|
||||||
dest => dest.ExpensesTypeId,
|
|
||||||
opt => opt.MapFrom(src => src.ExpensesTypeId.ToString()))
|
|
||||||
.ForMember(
|
|
||||||
dest => dest.PaymentModeId,
|
|
||||||
opt => opt.MapFrom(src => src.PaymentModeId.ToString()))
|
|
||||||
.ForMember(
|
|
||||||
dest => dest.PaidById,
|
|
||||||
opt => opt.MapFrom(src => src.PaidById.ToString()))
|
|
||||||
.ForMember(
|
|
||||||
dest => dest.CreatedById,
|
|
||||||
opt => opt.MapFrom(src => src.CreatedById.ToString()))
|
|
||||||
.ForMember(
|
|
||||||
dest => dest.ReviewedById,
|
|
||||||
opt => opt.MapFrom(src => src.ReviewedById.ToString()))
|
|
||||||
.ForMember(
|
|
||||||
dest => dest.ApprovedById,
|
|
||||||
opt => opt.MapFrom(src => src.ApprovedById.ToString()))
|
|
||||||
.ForMember(
|
|
||||||
dest => dest.ProcessedById,
|
|
||||||
opt => opt.MapFrom(src => src.ProcessedById.ToString()))
|
|
||||||
.ForMember(
|
|
||||||
dest => dest.StatusId,
|
|
||||||
opt => opt.MapFrom(src => src.StatusId.ToString()))
|
|
||||||
.ForMember(
|
|
||||||
dest => dest.TenantId,
|
dest => dest.TenantId,
|
||||||
opt => opt.MapFrom(src => src.TenantId.ToString()));
|
opt => opt.MapFrom(src => src.TenantId.ToString()));
|
||||||
|
|
||||||
@ -165,33 +138,6 @@ namespace Marco.Pms.Services.MappingProfiles
|
|||||||
dest => dest.Id,
|
dest => dest.Id,
|
||||||
opt => opt.MapFrom(src => Guid.Parse(src.Id)))
|
opt => opt.MapFrom(src => Guid.Parse(src.Id)))
|
||||||
.ForMember(
|
.ForMember(
|
||||||
dest => dest.ProjectId,
|
|
||||||
opt => opt.MapFrom(src => Guid.Parse(src.ProjectId)))
|
|
||||||
.ForMember(
|
|
||||||
dest => dest.ExpensesTypeId,
|
|
||||||
opt => opt.MapFrom(src => Guid.Parse(src.ExpensesTypeId)))
|
|
||||||
.ForMember(
|
|
||||||
dest => dest.PaymentModeId,
|
|
||||||
opt => opt.MapFrom(src => Guid.Parse(src.PaymentModeId)))
|
|
||||||
.ForMember(
|
|
||||||
dest => dest.PaidById,
|
|
||||||
opt => opt.MapFrom(src => Guid.Parse(src.PaidById)))
|
|
||||||
.ForMember(
|
|
||||||
dest => dest.CreatedById,
|
|
||||||
opt => opt.MapFrom(src => Guid.Parse(src.CreatedById)))
|
|
||||||
.ForMember(
|
|
||||||
dest => dest.ReviewedById,
|
|
||||||
opt => opt.MapFrom(src => src.ReviewedById != null ? Guid.Parse(src.ReviewedById) : Guid.Empty))
|
|
||||||
.ForMember(
|
|
||||||
dest => dest.ApprovedById,
|
|
||||||
opt => opt.MapFrom(src => src.ApprovedById != null ? Guid.Parse(src.ApprovedById) : Guid.Empty))
|
|
||||||
.ForMember(
|
|
||||||
dest => dest.ProcessedById,
|
|
||||||
opt => opt.MapFrom(src => src.ProcessedById != null ? Guid.Parse(src.ProcessedById) : Guid.Empty))
|
|
||||||
.ForMember(
|
|
||||||
dest => dest.StatusId,
|
|
||||||
opt => opt.MapFrom(src => Guid.Parse(src.StatusId)))
|
|
||||||
.ForMember(
|
|
||||||
dest => dest.TenantId,
|
dest => dest.TenantId,
|
||||||
opt => opt.MapFrom(src => Guid.Parse(src.TenantId)));
|
opt => opt.MapFrom(src => Guid.Parse(src.TenantId)));
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ using Marco.Pms.Model.Dtos.Expenses;
|
|||||||
using Marco.Pms.Model.Employees;
|
using Marco.Pms.Model.Employees;
|
||||||
using Marco.Pms.Model.Entitlements;
|
using Marco.Pms.Model.Entitlements;
|
||||||
using Marco.Pms.Model.Expenses;
|
using Marco.Pms.Model.Expenses;
|
||||||
using Marco.Pms.Model.MongoDBModels.Expenses;
|
|
||||||
using Marco.Pms.Model.MongoDBModels.Utility;
|
using Marco.Pms.Model.MongoDBModels.Utility;
|
||||||
using Marco.Pms.Model.Utilities;
|
using Marco.Pms.Model.Utilities;
|
||||||
using Marco.Pms.Model.ViewModels.Activities;
|
using Marco.Pms.Model.ViewModels.Activities;
|
||||||
@ -70,7 +69,7 @@ namespace Marco.Pms.Services.Service
|
|||||||
/// <param name="pageSize">The number of records to return per page.</param>
|
/// <param name="pageSize">The number of records to return per page.</param>
|
||||||
/// <param name="pageNumber">The page number to retrieve.</param>
|
/// <param name="pageNumber">The page number to retrieve.</param>
|
||||||
/// <returns>A paginated list of expenses.</returns>
|
/// <returns>A paginated list of expenses.</returns>
|
||||||
public async Task<ApiResponse<object>> GetExpensesListAsync(Employee loggedInEmployee, Guid tenantId, string? filter, int pageSize, int pageNumber)
|
public async Task<ApiResponse<object>> GetExpensesListAsync(Employee loggedInEmployee, Guid tenantId, string? searchString, string? filter, int pageSize, int pageNumber)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -115,10 +114,10 @@ namespace Marco.Pms.Services.Service
|
|||||||
// 2. --- Deserialize Filter and Apply ---
|
// 2. --- Deserialize Filter and Apply ---
|
||||||
ExpensesFilter? expenseFilter = TryDeserializeFilter(filter);
|
ExpensesFilter? expenseFilter = TryDeserializeFilter(filter);
|
||||||
|
|
||||||
var (totalPages, totalCount, expenseList) = await _cache.GetExpenseListAsync(tenantId, loggedInEmployeeId, hasViewAllPermissionTask.Result, hasViewSelfPermissionTask.Result,
|
var (totalPages, totalCount, cacheList) = await _cache.GetExpenseListAsync(tenantId, loggedInEmployeeId, hasViewAllPermissionTask.Result, hasViewSelfPermissionTask.Result,
|
||||||
pageNumber, pageSize, expenseFilter);
|
pageNumber, pageSize, expenseFilter, searchString);
|
||||||
|
|
||||||
if (expenseList == null)
|
if (cacheList == null)
|
||||||
{
|
{
|
||||||
|
|
||||||
// 3. --- Build Base Query and Apply Permissions ---
|
// 3. --- Build Base Query and Apply Permissions ---
|
||||||
@ -126,7 +125,7 @@ namespace Marco.Pms.Services.Service
|
|||||||
var expensesQuery = _context.Expenses
|
var expensesQuery = _context.Expenses
|
||||||
.Where(e => e.TenantId == tenantId); // Always filter by TenantId first.
|
.Where(e => e.TenantId == tenantId); // Always filter by TenantId first.
|
||||||
|
|
||||||
await _cache.AddExpensesListToCache(expenses: await expensesQuery.ToListAsync());
|
await _cache.AddExpensesListToCache(expenses: await expensesQuery.ToListAsync(), tenantId);
|
||||||
|
|
||||||
// Apply permission-based filtering BEFORE any other filters or pagination.
|
// Apply permission-based filtering BEFORE any other filters or pagination.
|
||||||
|
|
||||||
@ -174,6 +173,16 @@ namespace Marco.Pms.Services.Service
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(searchString))
|
||||||
|
{
|
||||||
|
var searchStringLower = searchString.ToLower();
|
||||||
|
expensesQuery = expensesQuery.Include(e => e.PaidBy).Include(e => e.CreatedBy)
|
||||||
|
.Where(e => e.Description.ToLower().Contains(searchStringLower) ||
|
||||||
|
(e.TransactionId != null && e.TransactionId.ToLower().Contains(searchStringLower)) ||
|
||||||
|
(e.PaidBy != null && (e.PaidBy.FirstName + " " + e.PaidBy.LastName).ToLower().Contains(searchStringLower)) ||
|
||||||
|
(e.CreatedBy != null && (e.CreatedBy.FirstName + " " + e.CreatedBy.LastName).ToLower().Contains(searchStringLower)));
|
||||||
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
|
|
||||||
@ -199,20 +208,40 @@ namespace Marco.Pms.Services.Service
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
expenseVM = await GetAllExpnesRelatedTables(_mapper.Map<List<Expenses>>(expenseList), tenantId);
|
var permissionStatusMapping = await _context.StatusPermissionMapping
|
||||||
|
.GroupBy(ps => ps.StatusId)
|
||||||
|
.Select(g => new
|
||||||
|
{
|
||||||
|
StatusId = g.Key,
|
||||||
|
PermissionIds = g.Select(ps => ps.PermissionId).ToList()
|
||||||
|
}).ToListAsync();
|
||||||
|
|
||||||
|
expenseVM = cacheList.Select(m =>
|
||||||
|
{
|
||||||
|
var response = _mapper.Map<ExpenseList>(m);
|
||||||
|
if (response.Status != null && (response.NextStatus?.Any() ?? false))
|
||||||
|
{
|
||||||
|
response.Status.PermissionIds = permissionStatusMapping.Where(ps => ps.StatusId == Guid.Parse(m.Status.Id)).Select(ps => ps.PermissionIds).FirstOrDefault();
|
||||||
|
foreach (var status in response.NextStatus)
|
||||||
|
{
|
||||||
|
status.PermissionIds = permissionStatusMapping.Where(ps => ps.StatusId == status.Id).Select(ps => ps.PermissionIds).FirstOrDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}).ToList();
|
||||||
totalEntites = (int)totalCount;
|
totalEntites = (int)totalCount;
|
||||||
}
|
}
|
||||||
// 7. --- Return Final Success Response ---
|
// 7. --- Return Final Success Response ---
|
||||||
var message = $"{expenseVM.Count} expense records fetched successfully.";
|
var message = $"{expenseVM.Count} expense records fetched successfully.";
|
||||||
_logger.LogInfo(message);
|
_logger.LogInfo(message);
|
||||||
var defaultFilter = await GetObjectForfilter(tenantId);
|
|
||||||
|
|
||||||
var response = new
|
var response = new
|
||||||
{
|
{
|
||||||
CurrentFilter = expenseFilter,
|
CurrentFilter = expenseFilter,
|
||||||
CurrentPage = pageNumber,
|
CurrentPage = pageNumber,
|
||||||
TotalPages = totalPages,
|
TotalPages = totalPages,
|
||||||
TotalEntites = totalEntites,
|
TotalEntites = totalEntites,
|
||||||
DefaultFilter = defaultFilter,
|
|
||||||
Data = expenseVM,
|
Data = expenseVM,
|
||||||
};
|
};
|
||||||
return ApiResponse<object>.SuccessResponse(response, message, 200);
|
return ApiResponse<object>.SuccessResponse(response, message, 200);
|
||||||
@ -242,7 +271,50 @@ namespace Marco.Pms.Services.Service
|
|||||||
return ApiResponse<object>.ErrorResponse("Expense Not Found", "Expense Not Found", 404);
|
return ApiResponse<object>.ErrorResponse("Expense Not Found", "Expense Not Found", 404);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var vm = await GetAllExpnesRelatedTablesFromMongoDB(expenseDetails, tenantId);
|
var vm = _mapper.Map<ExpenseDetailsVM>(expenseDetails);
|
||||||
|
|
||||||
|
var permissionStatusMappingTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.StatusPermissionMapping
|
||||||
|
.GroupBy(ps => ps.StatusId)
|
||||||
|
.Select(g => new
|
||||||
|
{
|
||||||
|
StatusId = g.Key,
|
||||||
|
PermissionIds = g.Select(ps => ps.PermissionId).ToList()
|
||||||
|
}).ToListAsync();
|
||||||
|
});
|
||||||
|
var expenseReimburseTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.ExpensesReimburseMapping
|
||||||
|
.Include(er => er.ExpensesReimburse)
|
||||||
|
.ThenInclude(er => er!.ReimburseBy)
|
||||||
|
.ThenInclude(e => e!.JobRole)
|
||||||
|
.Where(er => er.TenantId == tenantId && er.ExpensesId == vm.Id)
|
||||||
|
.Select(er => er.ExpensesReimburse).FirstOrDefaultAsync();
|
||||||
|
});
|
||||||
|
var permissionStatusMappings = permissionStatusMappingTask.Result;
|
||||||
|
var expensesReimburse = expenseReimburseTask.Result;
|
||||||
|
|
||||||
|
if (vm.Status != null && (vm.NextStatus?.Any() ?? false))
|
||||||
|
{
|
||||||
|
vm.Status.PermissionIds = permissionStatusMappings.Where(ps => ps.StatusId == vm.Status.Id).Select(ps => ps.PermissionIds).FirstOrDefault();
|
||||||
|
foreach (var status in vm.NextStatus)
|
||||||
|
{
|
||||||
|
status.PermissionIds = permissionStatusMappings.Where(ps => ps.StatusId == status.Id).Select(ps => ps.PermissionIds).FirstOrDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vm.ExpensesReimburse = _mapper.Map<ExpensesReimburseVM>(expensesReimburse);
|
||||||
|
|
||||||
|
foreach (var document in expenseDetails.Documents)
|
||||||
|
{
|
||||||
|
var response = vm.Documents.FirstOrDefault(d => d.DocumentId == Guid.Parse(document.DocumentId));
|
||||||
|
|
||||||
|
response!.PreSignedUrl = _s3Service.GeneratePreSignedUrl(document.S3Key);
|
||||||
|
response!.ThumbPreSignedUrl = _s3Service.GeneratePreSignedUrl(document.ThumbS3Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
_logger.LogInfo("Employee {EmployeeId} successfully fetched expense details with ID {ExpenseId}", loggedInEmployee.Id, vm.Id);
|
_logger.LogInfo("Employee {EmployeeId} successfully fetched expense details with ID {ExpenseId}", loggedInEmployee.Id, vm.Id);
|
||||||
return ApiResponse<object>.SuccessResponse(vm, "Successfully fetched the details of expense", 200);
|
return ApiResponse<object>.SuccessResponse(vm, "Successfully fetched the details of expense", 200);
|
||||||
@ -268,6 +340,81 @@ namespace Marco.Pms.Services.Service
|
|||||||
return ApiResponse<object>.ErrorResponse("Databsae Exception", ExceptionMapper(dbEx), 500);
|
return ApiResponse<object>.ErrorResponse("Databsae Exception", ExceptionMapper(dbEx), 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public async Task<ApiResponse<object>> GetFilterObjectAsync(Employee loggedInEmployee, Guid tenantId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
|
var projectHelper = scope.ServiceProvider.GetRequiredService<IProjectServices>();
|
||||||
|
var projectIds = await projectHelper.GetMyProjectIdsAsync(tenantId, loggedInEmployee);
|
||||||
|
|
||||||
|
// Task 1: Get all distinct projects associated with the tenant's expenses.
|
||||||
|
var projectsTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Expenses
|
||||||
|
.Where(e => e.TenantId == tenantId && e.Project != null && projectIds.Contains(e.ProjectId))
|
||||||
|
.Select(e => e.Project!)
|
||||||
|
.Distinct()
|
||||||
|
.Select(p => new { p.Id, Name = $"{p.Name}" })
|
||||||
|
.ToListAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Task 2: Get all distinct users who paid for the tenant's expenses.
|
||||||
|
var paidByTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Expenses
|
||||||
|
.Where(e => e.TenantId == tenantId && e.PaidBy != null)
|
||||||
|
.Select(e => e.PaidBy!)
|
||||||
|
.Distinct()
|
||||||
|
.Select(u => new { u.Id, Name = $"{u.FirstName} {u.LastName}" })
|
||||||
|
.ToListAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Task 3: Get all distinct users who created the tenant's expenses.
|
||||||
|
var createdByTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Expenses
|
||||||
|
.Where(e => e.TenantId == tenantId && e.CreatedBy != null)
|
||||||
|
.Select(e => e.CreatedBy!)
|
||||||
|
.Distinct()
|
||||||
|
.Select(u => new { u.Id, Name = $"{u.FirstName} {u.LastName}" })
|
||||||
|
.ToListAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Task 4: Get all distinct statuses associated with the tenant's expenses.
|
||||||
|
var statusTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Expenses
|
||||||
|
.Where(e => e.TenantId == tenantId && e.Status != null)
|
||||||
|
.Select(e => e.Status!)
|
||||||
|
.Distinct()
|
||||||
|
.Select(s => new { s.Id, s.Name })
|
||||||
|
.ToListAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Execute all four queries concurrently. The total wait time will be determined
|
||||||
|
// by the longest-running query, not the sum of all four.
|
||||||
|
await Task.WhenAll(projectsTask, paidByTask, createdByTask, statusTask);
|
||||||
|
|
||||||
|
// Construct the final object from the results of the completed tasks.
|
||||||
|
return ApiResponse<object>.SuccessResponse(new
|
||||||
|
{
|
||||||
|
Projects = await projectsTask,
|
||||||
|
PaidBy = await paidByTask,
|
||||||
|
CreatedBy = await createdByTask,
|
||||||
|
Status = await statusTask
|
||||||
|
}, "Successfully fetched the filter list", 200);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Exception occured while fetching the list filters for expenses");
|
||||||
|
return ApiResponse<object>.ErrorResponse("Internal Exception Occured", ExceptionMapper(ex), 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -1038,217 +1185,7 @@ namespace Marco.Pms.Services.Service
|
|||||||
|
|
||||||
return expenseList;
|
return expenseList;
|
||||||
}
|
}
|
||||||
private async Task<ExpenseDetailsVM> GetAllExpnesRelatedTablesFromMongoDB(ExpenseDetailsMongoDB model, Guid tenantId)
|
|
||||||
{
|
|
||||||
var reviewedById = model.ReviewedById != null ? Guid.Parse(model.ReviewedById) : Guid.Empty;
|
|
||||||
var approvedById = model.ApprovedById != null ? Guid.Parse(model.ApprovedById) : Guid.Empty;
|
|
||||||
var processedById = model.ProcessedById != null ? Guid.Parse(model.ProcessedById) : Guid.Empty;
|
|
||||||
|
|
||||||
var projectTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
return await dbContext.Projects.AsNoTracking().FirstOrDefaultAsync(p => p.Id == Guid.Parse(model.ProjectId) && p.TenantId == tenantId);
|
|
||||||
});
|
|
||||||
var paidByTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == Guid.Parse(model.PaidById) && e.TenantId == tenantId);
|
|
||||||
});
|
|
||||||
var createdByTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == Guid.Parse(model.CreatedById) && e.TenantId == tenantId);
|
|
||||||
});
|
|
||||||
var reviewedByTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == reviewedById && e.TenantId == tenantId);
|
|
||||||
});
|
|
||||||
var approvedByTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == approvedById && e.TenantId == tenantId);
|
|
||||||
});
|
|
||||||
var processedByTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == processedById && e.TenantId == tenantId);
|
|
||||||
});
|
|
||||||
var expenseTypeTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
return await dbContext.ExpensesTypeMaster.AsNoTracking().FirstOrDefaultAsync(et => et.Id == Guid.Parse(model.ExpensesTypeId) && et.TenantId == tenantId);
|
|
||||||
});
|
|
||||||
var paymentModeTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
return await dbContext.PaymentModeMatser.AsNoTracking().FirstOrDefaultAsync(pm => pm.Id == Guid.Parse(model.PaymentModeId) && pm.TenantId == tenantId);
|
|
||||||
});
|
|
||||||
var statusMappingTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
return await dbContext.ExpensesStatusMapping
|
|
||||||
.Include(s => s.Status)
|
|
||||||
.Include(s => s.NextStatus)
|
|
||||||
.AsNoTracking()
|
|
||||||
.Where(es => es.StatusId == Guid.Parse(model.StatusId) && es.Status != null)
|
|
||||||
.GroupBy(s => s.StatusId)
|
|
||||||
.Select(g => new
|
|
||||||
{
|
|
||||||
Status = g.Select(s => s.Status).FirstOrDefault(),
|
|
||||||
NextStatus = g.Select(s => s.NextStatus).ToList()
|
|
||||||
}).FirstOrDefaultAsync();
|
|
||||||
});
|
|
||||||
var statusTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
return await dbContext.ExpensesStatusMaster
|
|
||||||
.AsNoTracking()
|
|
||||||
.FirstOrDefaultAsync(es => es.Id == Guid.Parse(model.StatusId));
|
|
||||||
});
|
|
||||||
var permissionStatusMappingTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
return await dbContext.StatusPermissionMapping
|
|
||||||
.GroupBy(ps => ps.StatusId)
|
|
||||||
.Select(g => new
|
|
||||||
{
|
|
||||||
StatusId = g.Key,
|
|
||||||
PermissionIds = g.Select(ps => ps.PermissionId).ToList()
|
|
||||||
}).ToListAsync();
|
|
||||||
});
|
|
||||||
var expenseReimburseTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
return await dbContext.ExpensesReimburseMapping
|
|
||||||
.Include(er => er.ExpensesReimburse)
|
|
||||||
.ThenInclude(er => er!.ReimburseBy)
|
|
||||||
.ThenInclude(e => e!.JobRole)
|
|
||||||
.Where(er => er.TenantId == tenantId && er.ExpensesId == Guid.Parse(model.Id))
|
|
||||||
.Select(er => er.ExpensesReimburse).FirstOrDefaultAsync();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Await all prerequisite checks at once.
|
|
||||||
await Task.WhenAll(projectTask, expenseTypeTask, paymentModeTask, statusMappingTask, paidByTask, createdByTask, reviewedByTask, approvedByTask, processedByTask,
|
|
||||||
statusTask, permissionStatusMappingTask, expenseReimburseTask);
|
|
||||||
|
|
||||||
var project = projectTask.Result;
|
|
||||||
var expenseType = expenseTypeTask.Result;
|
|
||||||
var paymentMode = paymentModeTask.Result;
|
|
||||||
var statusMapping = statusMappingTask.Result;
|
|
||||||
var permissionStatusMappings = permissionStatusMappingTask.Result;
|
|
||||||
var paidBy = paidByTask.Result;
|
|
||||||
var createdBy = createdByTask.Result;
|
|
||||||
var reviewedBy = reviewedByTask.Result;
|
|
||||||
var approvedBy = approvedByTask.Result;
|
|
||||||
var processedBy = processedByTask.Result;
|
|
||||||
var expensesReimburse = expenseReimburseTask.Result;
|
|
||||||
|
|
||||||
var response = _mapper.Map<ExpenseDetailsVM>(model);
|
|
||||||
|
|
||||||
response.Project = _mapper.Map<ProjectInfoVM>(project);
|
|
||||||
response.PaidBy = _mapper.Map<BasicEmployeeVM>(paidBy);
|
|
||||||
response.CreatedBy = _mapper.Map<BasicEmployeeVM>(createdBy);
|
|
||||||
if (reviewedBy != null) response.ReviewedBy = _mapper.Map<BasicEmployeeVM>(reviewedBy);
|
|
||||||
if (approvedBy != null) response.ApprovedBy = _mapper.Map<BasicEmployeeVM>(approvedBy);
|
|
||||||
if (processedBy != null) response.ProcessedBy = _mapper.Map<BasicEmployeeVM>(processedBy);
|
|
||||||
response.PaymentMode = _mapper.Map<PaymentModeMatserVM>(paymentMode);
|
|
||||||
response.ExpensesType = _mapper.Map<ExpensesTypeMasterVM>(expenseType);
|
|
||||||
response.ExpensesReimburse = _mapper.Map<ExpensesReimburseVM>(expensesReimburse);
|
|
||||||
if (statusMapping != null)
|
|
||||||
{
|
|
||||||
response.Status = _mapper.Map<ExpensesStatusMasterVM>(statusMapping.Status);
|
|
||||||
|
|
||||||
response.NextStatus = _mapper.Map<List<ExpensesStatusMasterVM>>(statusMapping.NextStatus);
|
|
||||||
if (response.NextStatus != null)
|
|
||||||
{
|
|
||||||
foreach (var status in response.NextStatus)
|
|
||||||
{
|
|
||||||
status.PermissionIds = permissionStatusMappings.Where(ps => ps.StatusId == status.Id).Select(ps => ps.PermissionIds).FirstOrDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (response.Status == null)
|
|
||||||
{
|
|
||||||
var status = statusTask.Result;
|
|
||||||
response.Status = _mapper.Map<ExpensesStatusMasterVM>(status);
|
|
||||||
}
|
|
||||||
response.Status.PermissionIds = permissionStatusMappings.Where(ps => ps.StatusId == Guid.Parse(model.StatusId)).Select(ps => ps.PermissionIds).FirstOrDefault();
|
|
||||||
|
|
||||||
foreach (var document in model.Documents)
|
|
||||||
{
|
|
||||||
var vm = response.Documents.FirstOrDefault(d => d.DocumentId == Guid.Parse(document.DocumentId));
|
|
||||||
|
|
||||||
vm!.PreSignedUrl = _s3Service.GeneratePreSignedUrl(document.S3Key);
|
|
||||||
vm!.ThumbPreSignedUrl = _s3Service.GeneratePreSignedUrl(document.ThumbS3Key);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<object> GetObjectForfilter(Guid tenantId)
|
|
||||||
{
|
|
||||||
// Task 1: Get all distinct projects associated with the tenant's expenses.
|
|
||||||
var projectsTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
return await dbContext.Expenses
|
|
||||||
.Where(e => e.TenantId == tenantId && e.Project != null)
|
|
||||||
.Select(e => e.Project!)
|
|
||||||
.Distinct()
|
|
||||||
.Select(p => new { p.Id, Name = $"{p.Name}" })
|
|
||||||
.ToListAsync();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Task 2: Get all distinct users who paid for the tenant's expenses.
|
|
||||||
var paidByTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
return await dbContext.Expenses
|
|
||||||
.Where(e => e.TenantId == tenantId && e.PaidBy != null)
|
|
||||||
.Select(e => e.PaidBy!)
|
|
||||||
.Distinct()
|
|
||||||
.Select(u => new { u.Id, Name = $"{u.FirstName} {u.LastName}" })
|
|
||||||
.ToListAsync();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Task 3: Get all distinct users who created the tenant's expenses.
|
|
||||||
var createdByTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
return await dbContext.Expenses
|
|
||||||
.Where(e => e.TenantId == tenantId && e.CreatedBy != null)
|
|
||||||
.Select(e => e.CreatedBy!)
|
|
||||||
.Distinct()
|
|
||||||
.Select(u => new { u.Id, Name = $"{u.FirstName} {u.LastName}" })
|
|
||||||
.ToListAsync();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Task 4: Get all distinct statuses associated with the tenant's expenses.
|
|
||||||
var statusTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
return await dbContext.Expenses
|
|
||||||
.Where(e => e.TenantId == tenantId && e.Status != null)
|
|
||||||
.Select(e => e.Status!)
|
|
||||||
.Distinct()
|
|
||||||
.Select(s => new { s.Id, s.Name })
|
|
||||||
.ToListAsync();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Execute all four queries concurrently. The total wait time will be determined
|
|
||||||
// by the longest-running query, not the sum of all four.
|
|
||||||
await Task.WhenAll(projectsTask, paidByTask, createdByTask, statusTask);
|
|
||||||
|
|
||||||
// Construct the final object from the results of the completed tasks.
|
|
||||||
return new
|
|
||||||
{
|
|
||||||
Projects = await projectsTask,
|
|
||||||
PaidBy = await paidByTask,
|
|
||||||
CreatedBy = await createdByTask,
|
|
||||||
Status = await statusTask
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deserializes the filter string, handling multiple potential formats (e.g., direct JSON vs. escaped JSON string).
|
/// Deserializes the filter string, handling multiple potential formats (e.g., direct JSON vs. escaped JSON string).
|
||||||
|
@ -6,9 +6,10 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
|
|||||||
{
|
{
|
||||||
public interface IExpensesService
|
public interface IExpensesService
|
||||||
{
|
{
|
||||||
Task<ApiResponse<object>> GetExpensesListAsync(Employee loggedInEmployee, Guid tenantId, string? filter, int pageSize, int pageNumber);
|
Task<ApiResponse<object>> GetExpensesListAsync(Employee loggedInEmployee, Guid tenantId, string? searchString, string? filter, int pageSize, int pageNumber);
|
||||||
Task<ApiResponse<object>> GetExpenseDetailsAsync(Guid id, Employee loggedInEmployee, Guid tenantId);
|
Task<ApiResponse<object>> GetExpenseDetailsAsync(Guid id, Employee loggedInEmployee, Guid tenantId);
|
||||||
Task<ApiResponse<object>> GetSupplerNameListAsync(Employee loggedInEmployee, Guid tenantId);
|
Task<ApiResponse<object>> GetSupplerNameListAsync(Employee loggedInEmployee, Guid tenantId);
|
||||||
|
Task<ApiResponse<object>> GetFilterObjectAsync(Employee loggedInEmployee, Guid tenantId);
|
||||||
Task<ApiResponse<object>> CreateExpenseAsync(CreateExpensesDto dto, Employee loggedInEmployee, Guid tenantId);
|
Task<ApiResponse<object>> CreateExpenseAsync(CreateExpensesDto dto, Employee loggedInEmployee, Guid tenantId);
|
||||||
Task<ApiResponse<object>> ChangeStatusAsync(ExpenseRecordDto model, Employee loggedInEmployee, Guid tenantId);
|
Task<ApiResponse<object>> ChangeStatusAsync(ExpenseRecordDto model, Employee loggedInEmployee, Guid tenantId);
|
||||||
Task<ApiResponse<object>> UpdateExpanseAsync(Guid id, UpdateExpensesDto model, Employee loggedInEmployee, Guid tenantId);
|
Task<ApiResponse<object>> UpdateExpanseAsync(Guid id, UpdateExpensesDto model, Employee loggedInEmployee, Guid tenantId);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user