Validating the null objects while saving the object in cache
This commit is contained in:
parent
0be021448d
commit
555bb87779
@ -1146,9 +1146,9 @@ namespace Marco.Pms.Services.Helpers
|
|||||||
response.Project = projects.Where(p => p.Id == m.ProjectId).Select(p => _mapper.Map<ProjectBasicMongoDB>(p)).FirstOrDefault() ?? new ProjectBasicMongoDB();
|
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.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.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.ReviewedBy = reviewedBys.Where(e => e.Id == m.CreatedById).Select(e => _mapper.Map<BasicEmployeeMongoDB>(e)).FirstOrDefault();
|
||||||
response.ApprovedBy = approvedBys.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();
|
||||||
response.ProcessedBy = processedBy.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();
|
||||||
response.Status = statusMappings.Where(s => s.StatusId == m.StatusId).Select(s => _mapper.Map<ExpensesStatusMasterMongoDB>(s.Status)).FirstOrDefault() ?? new ExpensesStatusMasterMongoDB();
|
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)
|
if (response.Status.Id == string.Empty)
|
||||||
{
|
{
|
||||||
|
@ -112,7 +112,7 @@ namespace Marco.Pms.Services.MappingProfiles
|
|||||||
CreateMap<BasicEmployeeMongoDB, BasicEmployeeVM>()
|
CreateMap<BasicEmployeeMongoDB, BasicEmployeeVM>()
|
||||||
.ForMember(
|
.ForMember(
|
||||||
dest => dest.Id,
|
dest => dest.Id,
|
||||||
opt => opt.MapFrom(src => Guid.Parse(src.Id)))
|
opt => opt.MapFrom(src => string.IsNullOrWhiteSpace(src.Id) ? Guid.Empty : Guid.Parse(src.Id)))
|
||||||
.ForMember(
|
.ForMember(
|
||||||
dest => dest.JobRoleId,
|
dest => dest.JobRoleId,
|
||||||
opt => opt.MapFrom(src => Guid.Parse(src.JobRoleId ?? "")));
|
opt => opt.MapFrom(src => Guid.Parse(src.JobRoleId ?? "")));
|
||||||
|
@ -5,6 +5,11 @@ 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;
|
||||||
|
using Marco.Pms.Model.MongoDBModels.Employees;
|
||||||
|
using Marco.Pms.Model.MongoDBModels.Expenses;
|
||||||
|
using Marco.Pms.Model.MongoDBModels.Masters;
|
||||||
|
using Marco.Pms.Model.MongoDBModels.Project;
|
||||||
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;
|
||||||
@ -117,14 +122,13 @@ namespace Marco.Pms.Services.Service
|
|||||||
var (totalPages, totalCount, cacheList) = 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, searchString);
|
pageNumber, pageSize, expenseFilter, searchString);
|
||||||
|
|
||||||
if (cacheList == null)
|
|
||||||
{
|
|
||||||
|
|
||||||
// 3. --- Build Base Query and Apply Permissions ---
|
// 3. --- Build Base Query and Apply Permissions ---
|
||||||
// Start with a base IQueryable. Filters will be chained onto this.
|
// Start with a base IQueryable. Filters will be chained onto this.
|
||||||
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.
|
||||||
|
|
||||||
|
if (cacheList == null)
|
||||||
|
{
|
||||||
await _cache.AddExpensesListToCache(expenses: await expensesQuery.ToListAsync(), tenantId);
|
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.
|
||||||
@ -264,12 +268,14 @@ namespace Marco.Pms.Services.Service
|
|||||||
var expenseDetails = await _cache.GetExpenseDetailsById(id, tenantId);
|
var expenseDetails = await _cache.GetExpenseDetailsById(id, tenantId);
|
||||||
if (expenseDetails == null)
|
if (expenseDetails == null)
|
||||||
{
|
{
|
||||||
expenseDetails = await _cache.AddExpenseByIdAsync(id, tenantId);
|
var expense = await _context.Expenses.AsNoTracking().FirstOrDefaultAsync(e => e.Id == id && e.TenantId == tenantId);
|
||||||
if (expenseDetails == null)
|
|
||||||
|
if (expense == null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("User attempted to fetch expense details with ID {ExpenseId}, but not found in both database and cache", id);
|
_logger.LogWarning("User attempted to fetch expense details with ID {ExpenseId}, but not found in both database and cache", id);
|
||||||
return ApiResponse<object>.ErrorResponse("Expense Not Found", "Expense Not Found", 404);
|
return ApiResponse<object>.ErrorResponse("Expense Not Found", "Expense Not Found", 404);
|
||||||
}
|
}
|
||||||
|
expenseDetails = await GetAllExpnesRelatedTablesForSingle(expense, expense.TenantId);
|
||||||
}
|
}
|
||||||
var vm = _mapper.Map<ExpenseDetailsVM>(expenseDetails);
|
var vm = _mapper.Map<ExpenseDetailsVM>(expenseDetails);
|
||||||
|
|
||||||
@ -1182,6 +1188,136 @@ namespace Marco.Pms.Services.Service
|
|||||||
return expenseList;
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <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).
|
||||||
|
Loading…
x
Reference in New Issue
Block a user