Added an new API to filter expense by advance filter
This commit is contained in:
parent
a0a65fc08c
commit
82c9f249ef
@ -3,7 +3,7 @@
|
|||||||
public class AdvanceItem
|
public class AdvanceItem
|
||||||
{
|
{
|
||||||
public string Column { get; set; } = string.Empty;
|
public string Column { get; set; } = string.Empty;
|
||||||
public string Opration { get; set; } = string.Empty; // "greater than", "equal to", etc.
|
public string Operation { get; set; } = string.Empty; // "greater than", "equal to", etc.
|
||||||
public string Value { get; set; } = string.Empty;
|
public string Value { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,6 +46,14 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
return StatusCode(response.StatusCode, response);
|
return StatusCode(response.StatusCode, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("list-dynamic")]
|
||||||
|
public async Task<IActionResult> GetExpensesListDynamic([FromQuery] string? searchString, [FromQuery] string? filter, [FromQuery] int pageSize = 20, [FromQuery] int pageNumber = 1)
|
||||||
|
{
|
||||||
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
|
var response = await _expensesService.GetExpensesListDynamicAsync(loggedInEmployee, tenantId, searchString, filter, pageSize, pageNumber);
|
||||||
|
return StatusCode(response.StatusCode, response);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet("details/{id?}")]
|
[HttpGet("details/{id?}")]
|
||||||
public async Task<IActionResult> GetExpenseDetails(Guid? id, [FromQuery] string? expenseUId)
|
public async Task<IActionResult> GetExpenseDetails(Guid? id, [FromQuery] string? expenseUId)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -18,19 +18,62 @@ namespace Marco.Pms.Services.Extensions
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(advanceFilter.Column)) continue;
|
if (string.IsNullOrWhiteSpace(advanceFilter.Column)) continue;
|
||||||
|
|
||||||
string op = advanceFilter.Opration.ToLower().Trim();
|
string op = advanceFilter.Operation.ToLower().Trim();
|
||||||
string predicate = "";
|
string predicate = "";
|
||||||
|
|
||||||
// Map your custom strings to Dynamic LINQ operators
|
// Map your custom strings to Dynamic LINQ operators
|
||||||
switch (op)
|
switch (op)
|
||||||
{
|
{
|
||||||
case "greater than": predicate = $"{advanceFilter.Column} > @0"; break;
|
// --- Equality ---
|
||||||
case "less than": predicate = $"{advanceFilter.Column} < @0"; break;
|
case "eq":
|
||||||
case "equal to": predicate = $"{advanceFilter.Column} == @0"; break;
|
case "equal to":
|
||||||
case "not equal": predicate = $"{advanceFilter.Column} != @0"; break;
|
predicate = $"{advanceFilter.Column} == @0";
|
||||||
case "greater or equal": predicate = $"{advanceFilter.Column} >= @0"; break;
|
break;
|
||||||
case "smaller or equal": predicate = $"{advanceFilter.Column} <= @0"; break;
|
|
||||||
default: continue;
|
case "neq":
|
||||||
|
case "not equal":
|
||||||
|
predicate = $"{advanceFilter.Column} != @0";
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --- Numeric / Date Comparison ---
|
||||||
|
case "gt":
|
||||||
|
case "greater than":
|
||||||
|
case "after": // Date specific
|
||||||
|
predicate = $"{advanceFilter.Column} > @0";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "gte":
|
||||||
|
case "greater or equal":
|
||||||
|
predicate = $"{advanceFilter.Column} >= @0";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "lt": // Added for consistency
|
||||||
|
case "less than":
|
||||||
|
case "before": // Date specific
|
||||||
|
predicate = $"{advanceFilter.Column} < @0";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "lte":
|
||||||
|
case "less or equal":
|
||||||
|
case "smaller or equal":
|
||||||
|
predicate = $"{advanceFilter.Column} <= @0";
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --- Text Specific ---
|
||||||
|
case "contains":
|
||||||
|
predicate = $"{advanceFilter.Column}.Contains(@0)";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "starts":
|
||||||
|
predicate = $"{advanceFilter.Column}.StartsWith(@0)";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "ends":
|
||||||
|
predicate = $"{advanceFilter.Column}.EndsWith(@0)";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(predicate))
|
if (!string.IsNullOrEmpty(predicate))
|
||||||
|
|||||||
@ -21,6 +21,7 @@ using Marco.Pms.Model.ViewModels.Expenses;
|
|||||||
using Marco.Pms.Model.ViewModels.Expenses.Masters;
|
using Marco.Pms.Model.ViewModels.Expenses.Masters;
|
||||||
using Marco.Pms.Model.ViewModels.Master;
|
using Marco.Pms.Model.ViewModels.Master;
|
||||||
using Marco.Pms.Model.ViewModels.Projects;
|
using Marco.Pms.Model.ViewModels.Projects;
|
||||||
|
using Marco.Pms.Services.Extensions;
|
||||||
using Marco.Pms.Services.Helpers;
|
using Marco.Pms.Services.Helpers;
|
||||||
using Marco.Pms.Services.Service.ServiceInterfaces;
|
using Marco.Pms.Services.Service.ServiceInterfaces;
|
||||||
using MarcoBMS.Services.Service;
|
using MarcoBMS.Services.Service;
|
||||||
@ -38,7 +39,6 @@ namespace Marco.Pms.Services.Service
|
|||||||
private readonly ILoggingService _logger;
|
private readonly ILoggingService _logger;
|
||||||
private readonly S3UploadService _s3Service;
|
private readonly S3UploadService _s3Service;
|
||||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||||
private readonly UtilityMongoDBHelper _updateLogHelper;
|
|
||||||
private readonly CacheUpdateHelper _cache;
|
private readonly CacheUpdateHelper _cache;
|
||||||
private readonly IMapper _mapper;
|
private readonly IMapper _mapper;
|
||||||
|
|
||||||
@ -65,7 +65,6 @@ namespace Marco.Pms.Services.Service
|
|||||||
IDbContextFactory<ApplicationDbContext> dbContextFactory,
|
IDbContextFactory<ApplicationDbContext> dbContextFactory,
|
||||||
ApplicationDbContext context,
|
ApplicationDbContext context,
|
||||||
IServiceScopeFactory serviceScopeFactory,
|
IServiceScopeFactory serviceScopeFactory,
|
||||||
UtilityMongoDBHelper updateLogHelper,
|
|
||||||
CacheUpdateHelper cache,
|
CacheUpdateHelper cache,
|
||||||
ILoggingService logger,
|
ILoggingService logger,
|
||||||
S3UploadService s3Service,
|
S3UploadService s3Service,
|
||||||
@ -76,7 +75,6 @@ namespace Marco.Pms.Services.Service
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
_cache = cache;
|
_cache = cache;
|
||||||
_serviceScopeFactory = serviceScopeFactory;
|
_serviceScopeFactory = serviceScopeFactory;
|
||||||
_updateLogHelper = updateLogHelper;
|
|
||||||
_s3Service = s3Service;
|
_s3Service = s3Service;
|
||||||
_mapper = mapper;
|
_mapper = mapper;
|
||||||
}
|
}
|
||||||
@ -321,6 +319,219 @@ namespace Marco.Pms.Services.Service
|
|||||||
return ApiResponse<object>.ErrorResponse("Error Occured", ExceptionMapper(ex), 500);
|
return ApiResponse<object>.ErrorResponse("Error Occured", ExceptionMapper(ex), 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<ApiResponse<object>> GetExpensesListDynamicAsync(Employee loggedInEmployee, Guid tenantId, string? searchString, string? filter, int pageSize, int pageNumber)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogInfo(
|
||||||
|
"Attempting to fetch expenses list for PageNumber: {PageNumber}, PageSize: {PageSize} with Filter: {Filter}",
|
||||||
|
pageNumber, pageSize, filter ?? "");
|
||||||
|
|
||||||
|
// 1. --- Get User Permissions ---
|
||||||
|
if (loggedInEmployee == null)
|
||||||
|
{
|
||||||
|
// This is an authentication/authorization issue. The user should be logged in.
|
||||||
|
_logger.LogWarning("Could not find an employee for the current logged-in user.");
|
||||||
|
return ApiResponse<object>.ErrorResponse("User not found or not authenticated.", 403);
|
||||||
|
}
|
||||||
|
Guid loggedInEmployeeId = loggedInEmployee.Id;
|
||||||
|
List<ExpenseList> expenseVM = new List<ExpenseList>();
|
||||||
|
var totalEntites = 0;
|
||||||
|
|
||||||
|
var hasViewSelfPermissionTask = HasPermissionAsync(PermissionsMaster.ExpenseViewSelf, loggedInEmployee.Id);
|
||||||
|
var hasViewAllPermissionTask = HasPermissionAsync(PermissionsMaster.ExpenseViewAll, loggedInEmployee.Id);
|
||||||
|
|
||||||
|
await Task.WhenAll(hasViewSelfPermissionTask, hasViewAllPermissionTask);
|
||||||
|
|
||||||
|
var hasViewAllPermission = hasViewAllPermissionTask.Result;
|
||||||
|
var hasViewSelfPermission = hasViewSelfPermissionTask.Result;
|
||||||
|
|
||||||
|
if (!hasViewAllPermission && !hasViewSelfPermission)
|
||||||
|
{
|
||||||
|
// User has neither required permission. Deny access.
|
||||||
|
_logger.LogWarning("Access DENIED for employee {EmployeeId} attempting to get expenses list.", loggedInEmployeeId);
|
||||||
|
return ApiResponse<object>.SuccessResponse(new List<ExpenseList>(), "You do not have permission to view any expenses.", 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 2. --- Deserialize Filter and Apply ---
|
||||||
|
AdvanceFilter? advanceFilter = TryDeserializeAdvanceFilter(filter);
|
||||||
|
|
||||||
|
//var (totalPages, totalCount, cacheList) = await _cache.GetExpenseListAsync(tenantId, loggedInEmployeeId, hasViewAllPermissionTask.Result, hasViewSelfPermissionTask.Result,
|
||||||
|
// pageNumber, pageSize, expenseFilter, searchString);
|
||||||
|
|
||||||
|
List<ExpenseDetailsMongoDB>? cacheList = null;
|
||||||
|
var totalPages = 0;
|
||||||
|
var totalCount = 0;
|
||||||
|
|
||||||
|
// 3. --- Build Base Query and Apply Permissions ---
|
||||||
|
// Start with a base IQueryable. Filters will be chained onto this.
|
||||||
|
var expensesQuery = _context.Expenses
|
||||||
|
.Include(e => e.PaidBy)
|
||||||
|
.Include(e => e.CreatedBy)
|
||||||
|
.Include(e => e.ProcessedBy)
|
||||||
|
.Include(e => e.ApprovedBy)
|
||||||
|
.Include(e => e.ReviewedBy)
|
||||||
|
.Include(e => e.PaymentMode)
|
||||||
|
.Include(e => e.PaymentMode)
|
||||||
|
.Include(e => e.ExpenseCategory)
|
||||||
|
.Include(e => e.PaymentRequest)
|
||||||
|
.Include(e => e.Status)
|
||||||
|
.Include(e => e.Currency)
|
||||||
|
.Where(e => e.TenantId == tenantId); // Always filter by TenantId first.
|
||||||
|
|
||||||
|
if (cacheList == null)
|
||||||
|
{
|
||||||
|
//await _cache.AddExpensesListToCache(expenses: await expensesQuery.ToListAsync(), tenantId);
|
||||||
|
|
||||||
|
// Apply permission-based filtering BEFORE any other filters or pagination.
|
||||||
|
if (hasViewAllPermission)
|
||||||
|
{
|
||||||
|
expensesQuery = expensesQuery.Where(e => e.CreatedById == loggedInEmployeeId || e.StatusId != Draft);
|
||||||
|
}
|
||||||
|
else if (hasViewSelfPermission)
|
||||||
|
{
|
||||||
|
// User only has 'View Self' permission, so restrict the query to their own expenses.
|
||||||
|
_logger.LogInfo("User {EmployeeId} has 'View Self' permission. Restricting query to their expenses.", loggedInEmployeeId);
|
||||||
|
expensesQuery = expensesQuery.Where(e => e.CreatedById == loggedInEmployeeId);
|
||||||
|
}
|
||||||
|
expensesQuery = expensesQuery.ApplyCustomFilters(advanceFilter, "CreatedAt");
|
||||||
|
if (advanceFilter != null)
|
||||||
|
{
|
||||||
|
if (advanceFilter.Filters != null)
|
||||||
|
{
|
||||||
|
expensesQuery = expensesQuery.ApplyListFilters(advanceFilter.Filters);
|
||||||
|
}
|
||||||
|
if (advanceFilter.DateFilter != null)
|
||||||
|
{
|
||||||
|
expensesQuery = expensesQuery.ApplyDateFilter(advanceFilter.DateFilter);
|
||||||
|
}
|
||||||
|
if (advanceFilter.SearchFilters != null)
|
||||||
|
{
|
||||||
|
var invoiceSearchFilter = advanceFilter.SearchFilters.Where(f => f.Column != "ProjectName").ToList();
|
||||||
|
if (invoiceSearchFilter.Any())
|
||||||
|
{
|
||||||
|
expensesQuery = expensesQuery.ApplySearchFilters(invoiceSearchFilter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(advanceFilter.GroupByColumn))
|
||||||
|
{
|
||||||
|
expensesQuery = expensesQuery.ApplyGroupByFilters(advanceFilter.GroupByColumn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) ||
|
||||||
|
(e.UIDPrefix + "/" + e.UIDPostfix.ToString().PadLeft(5, '0')).Contains(searchString));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. --- Apply Ordering and Pagination ---
|
||||||
|
// This should be the last step before executing the query.
|
||||||
|
|
||||||
|
totalEntites = await expensesQuery.CountAsync();
|
||||||
|
|
||||||
|
// 5. --- Execute Query and Map Results ---
|
||||||
|
var expensesList = await expensesQuery
|
||||||
|
//.OrderByDescending(e => e.CreatedAt)
|
||||||
|
.Skip((pageNumber - 1) * pageSize)
|
||||||
|
.Take(pageSize).ToListAsync();
|
||||||
|
|
||||||
|
if (!expensesList.Any())
|
||||||
|
{
|
||||||
|
_logger.LogInfo("No expenses found matching the criteria for employee {EmployeeId}.", loggedInEmployeeId);
|
||||||
|
return ApiResponse<object>.SuccessResponse(new List<ExpenseList>(), "No expenses found for the given criteria.", 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
var projectIds = expensesList.Select(e => e.ProjectId).ToList();
|
||||||
|
|
||||||
|
var infraProjectTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await context.Projects.Where(p => projectIds.Contains(p.Id) && p.TenantId == tenantId).Select(p => _mapper.Map<BasicProjectVM>(p)).ToListAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
var serviceProjectTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await context.ServiceProjects.Where(sp => projectIds.Contains(sp.Id) && sp.TenantId == tenantId).Select(sp => _mapper.Map<BasicProjectVM>(sp)).ToListAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
await Task.WhenAll(infraProjectTask, serviceProjectTask);
|
||||||
|
|
||||||
|
var projects = infraProjectTask.Result;
|
||||||
|
projects.AddRange(serviceProjectTask.Result);
|
||||||
|
|
||||||
|
//expenseVM = await GetAllExpnesRelatedTables(expensesList, tenantId);
|
||||||
|
expenseVM = expensesList.Select(e =>
|
||||||
|
{
|
||||||
|
var result = _mapper.Map<ExpenseList>(e);
|
||||||
|
result.ExpenseUId = $"{e.UIDPrefix}/{e.UIDPostfix:D5}";
|
||||||
|
if (e.PaymentRequest != null)
|
||||||
|
result.PaymentRequestUID = $"{e.PaymentRequest.UIDPrefix}/{e.PaymentRequest.UIDPostfix:D5}";
|
||||||
|
result.Project = projects.FirstOrDefault(p => p.Id == e.ProjectId);
|
||||||
|
return result;
|
||||||
|
}).ToList();
|
||||||
|
totalPages = (int)Math.Ceiling((double)totalEntites / pageSize);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
// 7. --- Return Final Success Response ---
|
||||||
|
var message = $"{expenseVM.Count} expense records fetched successfully.";
|
||||||
|
_logger.LogInfo(message);
|
||||||
|
|
||||||
|
|
||||||
|
var response = new
|
||||||
|
{
|
||||||
|
CurrentPage = pageNumber,
|
||||||
|
TotalPages = totalPages,
|
||||||
|
TotalEntites = totalEntites,
|
||||||
|
Data = expenseVM,
|
||||||
|
};
|
||||||
|
return ApiResponse<object>.SuccessResponse(response, message, 200);
|
||||||
|
}
|
||||||
|
catch (DbUpdateException dbEx)
|
||||||
|
{
|
||||||
|
_logger.LogError(dbEx, "Databsae Exception occured while fetching list expenses");
|
||||||
|
return ApiResponse<object>.ErrorResponse("Databsae Exception", ExceptionMapper(dbEx), 500);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error occured while fetching list expenses");
|
||||||
|
return ApiResponse<object>.ErrorResponse("Error Occured", ExceptionMapper(ex), 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<ApiResponse<object>> GetExpenseDetailsAsync(Guid? id, string? expenseUId, Employee loggedInEmployee, Guid tenantId)
|
public async Task<ApiResponse<object>> GetExpenseDetailsAsync(Guid? id, string? expenseUId, Employee loggedInEmployee, Guid tenantId)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -733,6 +944,7 @@ namespace Marco.Pms.Services.Service
|
|||||||
{
|
{
|
||||||
using var scope = _serviceScopeFactory.CreateScope();
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
var _firebase = scope.ServiceProvider.GetRequiredService<IFirebaseService>();
|
var _firebase = scope.ServiceProvider.GetRequiredService<IFirebaseService>();
|
||||||
|
var _updateLogHelper = scope.ServiceProvider.GetRequiredService<UtilityMongoDBHelper>();
|
||||||
|
|
||||||
// 1. Fetch Existing Expense with Related Entities (Single Query)
|
// 1. Fetch Existing Expense with Related Entities (Single Query)
|
||||||
var expense = await _context.Expenses
|
var expense = await _context.Expenses
|
||||||
@ -1061,6 +1273,8 @@ namespace Marco.Pms.Services.Service
|
|||||||
return ApiResponse<object>.ErrorResponse("The employee Id in the path does not match the Id in the request body.",
|
return ApiResponse<object>.ErrorResponse("The employee Id in the path does not match the Id in the request body.",
|
||||||
"The employee Id in the path does not match the Id in the request body.", 400);
|
"The employee Id in the path does not match the Id in the request body.", 400);
|
||||||
}
|
}
|
||||||
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
|
var _updateLogHelper = scope.ServiceProvider.GetRequiredService<UtilityMongoDBHelper>();
|
||||||
|
|
||||||
// Check if the employee has the required permission
|
// Check if the employee has the required permission
|
||||||
var hasManagePermission = await HasPermissionAsync(PermissionsMaster.ExpenseManage, loggedInEmployee.Id);
|
var hasManagePermission = await HasPermissionAsync(PermissionsMaster.ExpenseManage, loggedInEmployee.Id);
|
||||||
@ -1294,6 +1508,9 @@ namespace Marco.Pms.Services.Service
|
|||||||
.Select(ba => ba.DocumentId)
|
.Select(ba => ba.DocumentId)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
|
var _updateLogHelper = scope.ServiceProvider.GetRequiredService<UtilityMongoDBHelper>();
|
||||||
|
|
||||||
var existingEntityBson = _updateLogHelper.EntityToBsonDocument(existingExpense);
|
var existingEntityBson = _updateLogHelper.EntityToBsonDocument(existingExpense);
|
||||||
|
|
||||||
_context.Expenses.Remove(existingExpense);
|
_context.Expenses.Remove(existingExpense);
|
||||||
@ -2130,6 +2347,8 @@ namespace Marco.Pms.Services.Service
|
|||||||
return ApiResponse<object>.ErrorResponse("You do not have permission for this action.", "Access Denied", 403);
|
return ApiResponse<object>.ErrorResponse("You do not have permission for this action.", "Access Denied", 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _updateLogHelper = scope.ServiceProvider.GetRequiredService<UtilityMongoDBHelper>();
|
||||||
|
|
||||||
// 5. Prepare for update (Audit snapshot)
|
// 5. Prepare for update (Audit snapshot)
|
||||||
var paymentRequestStateBeforeChange = _updateLogHelper.EntityToBsonDocument(paymentRequest);
|
var paymentRequestStateBeforeChange = _updateLogHelper.EntityToBsonDocument(paymentRequest);
|
||||||
|
|
||||||
@ -2599,6 +2818,9 @@ namespace Marco.Pms.Services.Service
|
|||||||
|
|
||||||
bool isVariableRecurring = paymentRequest.RecurringPayment?.IsVariable ?? false;
|
bool isVariableRecurring = paymentRequest.RecurringPayment?.IsVariable ?? false;
|
||||||
|
|
||||||
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
|
var _updateLogHelper = scope.ServiceProvider.GetRequiredService<UtilityMongoDBHelper>();
|
||||||
|
|
||||||
// Capture existing state for auditing
|
// Capture existing state for auditing
|
||||||
var existingEntityBson = _updateLogHelper.EntityToBsonDocument(paymentRequest);
|
var existingEntityBson = _updateLogHelper.EntityToBsonDocument(paymentRequest);
|
||||||
|
|
||||||
@ -2778,6 +3000,9 @@ namespace Marco.Pms.Services.Service
|
|||||||
.Select(ba => ba.DocumentId)
|
.Select(ba => ba.DocumentId)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
|
var _updateLogHelper = scope.ServiceProvider.GetRequiredService<UtilityMongoDBHelper>();
|
||||||
|
|
||||||
var existingEntityBson = _updateLogHelper.EntityToBsonDocument(paymentRequest);
|
var existingEntityBson = _updateLogHelper.EntityToBsonDocument(paymentRequest);
|
||||||
paymentRequest.IsActive = false;
|
paymentRequest.IsActive = false;
|
||||||
|
|
||||||
@ -3765,6 +3990,46 @@ namespace Marco.Pms.Services.Service
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region =================================================================== Helper Functions ===================================================================
|
#region =================================================================== Helper Functions ===================================================================
|
||||||
|
|
||||||
|
private AdvanceFilter? TryDeserializeAdvanceFilter(string? filter)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(filter))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
||||||
|
AdvanceFilter? advanceFilter = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// First, try to deserialize directly. This is the expected case (e.g., from a web client).
|
||||||
|
advanceFilter = JsonSerializer.Deserialize<AdvanceFilter>(filter, options);
|
||||||
|
}
|
||||||
|
catch (JsonException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "[{MethodName}] Failed to directly deserialize filter. Attempting to unescape and re-parse. Filter: {Filter}", nameof(TryDeserializeFilter), filter);
|
||||||
|
|
||||||
|
// If direct deserialization fails, it might be an escaped string (common with tools like Postman or some mobile clients).
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Unescape the string first, then deserialize the result.
|
||||||
|
string unescapedJsonString = JsonSerializer.Deserialize<string>(filter, options) ?? "";
|
||||||
|
if (!string.IsNullOrWhiteSpace(unescapedJsonString))
|
||||||
|
{
|
||||||
|
advanceFilter = JsonSerializer.Deserialize<AdvanceFilter>(unescapedJsonString, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (JsonException ex1)
|
||||||
|
{
|
||||||
|
// If both attempts fail, log the final error and return null.
|
||||||
|
_logger.LogError(ex1, "[{MethodName}] All attempts to deserialize the filter failed. Filter will be ignored. Filter: {Filter}", nameof(TryDeserializeFilter), filter);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return advanceFilter;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<bool> HasPermissionAsync(Guid permission, Guid employeeId)
|
private async Task<bool> HasPermissionAsync(Guid permission, Guid employeeId)
|
||||||
{
|
{
|
||||||
using var scope = _serviceScopeFactory.CreateScope();
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
@ -4135,6 +4400,9 @@ namespace Marco.Pms.Services.Service
|
|||||||
|
|
||||||
private async Task DeleteAttachemnts(List<Guid> documentIds)
|
private async Task DeleteAttachemnts(List<Guid> documentIds)
|
||||||
{
|
{
|
||||||
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
|
var _updateLogHelper = scope.ServiceProvider.GetRequiredService<UtilityMongoDBHelper>();
|
||||||
|
|
||||||
var attachmentTask = Task.Run(async () =>
|
var attachmentTask = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
@ -4176,6 +4444,9 @@ namespace Marco.Pms.Services.Service
|
|||||||
}
|
}
|
||||||
private async Task DeletePaymentRequestAttachemnts(List<Guid> documentIds)
|
private async Task DeletePaymentRequestAttachemnts(List<Guid> documentIds)
|
||||||
{
|
{
|
||||||
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
|
var _updateLogHelper = scope.ServiceProvider.GetRequiredService<UtilityMongoDBHelper>();
|
||||||
|
|
||||||
var attachmentTask = Task.Run(async () =>
|
var attachmentTask = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
|||||||
@ -8,6 +8,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
|
|||||||
{
|
{
|
||||||
#region =================================================================== Expenses Functions ===================================================================
|
#region =================================================================== Expenses Functions ===================================================================
|
||||||
Task<ApiResponse<object>> GetExpensesListAsync(Employee loggedInEmployee, Guid tenantId, string? searchString, 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>> GetExpensesListDynamicAsync(Employee loggedInEmployee, Guid tenantId, string? searchString, string? filter, int pageSize, int pageNumber);
|
||||||
Task<ApiResponse<object>> GetExpenseDetailsAsync(Guid? id, string? expenseUId, Employee loggedInEmployee, Guid tenantId);
|
Task<ApiResponse<object>> GetExpenseDetailsAsync(Guid? id, string? expenseUId, 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>> GetFilterObjectAsync(Employee loggedInEmployee, Guid tenantId);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user