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 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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,6 +46,14 @@ namespace Marco.Pms.Services.Controllers
|
||||
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?}")]
|
||||
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;
|
||||
|
||||
string op = advanceFilter.Opration.ToLower().Trim();
|
||||
string op = advanceFilter.Operation.ToLower().Trim();
|
||||
string predicate = "";
|
||||
|
||||
// Map your custom strings to Dynamic LINQ operators
|
||||
switch (op)
|
||||
{
|
||||
case "greater than": predicate = $"{advanceFilter.Column} > @0"; break;
|
||||
case "less than": predicate = $"{advanceFilter.Column} < @0"; break;
|
||||
case "equal to": predicate = $"{advanceFilter.Column} == @0"; break;
|
||||
case "not equal": predicate = $"{advanceFilter.Column} != @0"; break;
|
||||
case "greater or equal": predicate = $"{advanceFilter.Column} >= @0"; break;
|
||||
case "smaller or equal": predicate = $"{advanceFilter.Column} <= @0"; break;
|
||||
default: continue;
|
||||
// --- Equality ---
|
||||
case "eq":
|
||||
case "equal to":
|
||||
predicate = $"{advanceFilter.Column} == @0";
|
||||
break;
|
||||
|
||||
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))
|
||||
|
||||
@ -21,6 +21,7 @@ using Marco.Pms.Model.ViewModels.Expenses;
|
||||
using Marco.Pms.Model.ViewModels.Expenses.Masters;
|
||||
using Marco.Pms.Model.ViewModels.Master;
|
||||
using Marco.Pms.Model.ViewModels.Projects;
|
||||
using Marco.Pms.Services.Extensions;
|
||||
using Marco.Pms.Services.Helpers;
|
||||
using Marco.Pms.Services.Service.ServiceInterfaces;
|
||||
using MarcoBMS.Services.Service;
|
||||
@ -38,7 +39,6 @@ namespace Marco.Pms.Services.Service
|
||||
private readonly ILoggingService _logger;
|
||||
private readonly S3UploadService _s3Service;
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
private readonly UtilityMongoDBHelper _updateLogHelper;
|
||||
private readonly CacheUpdateHelper _cache;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
@ -65,7 +65,6 @@ namespace Marco.Pms.Services.Service
|
||||
IDbContextFactory<ApplicationDbContext> dbContextFactory,
|
||||
ApplicationDbContext context,
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
UtilityMongoDBHelper updateLogHelper,
|
||||
CacheUpdateHelper cache,
|
||||
ILoggingService logger,
|
||||
S3UploadService s3Service,
|
||||
@ -76,7 +75,6 @@ namespace Marco.Pms.Services.Service
|
||||
_logger = logger;
|
||||
_cache = cache;
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
_updateLogHelper = updateLogHelper;
|
||||
_s3Service = s3Service;
|
||||
_mapper = mapper;
|
||||
}
|
||||
@ -321,6 +319,219 @@ namespace Marco.Pms.Services.Service
|
||||
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)
|
||||
{
|
||||
try
|
||||
@ -733,6 +944,7 @@ namespace Marco.Pms.Services.Service
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _firebase = scope.ServiceProvider.GetRequiredService<IFirebaseService>();
|
||||
var _updateLogHelper = scope.ServiceProvider.GetRequiredService<UtilityMongoDBHelper>();
|
||||
|
||||
// 1. Fetch Existing Expense with Related Entities (Single Query)
|
||||
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.",
|
||||
"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
|
||||
var hasManagePermission = await HasPermissionAsync(PermissionsMaster.ExpenseManage, loggedInEmployee.Id);
|
||||
@ -1294,6 +1508,9 @@ namespace Marco.Pms.Services.Service
|
||||
.Select(ba => ba.DocumentId)
|
||||
.ToListAsync();
|
||||
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _updateLogHelper = scope.ServiceProvider.GetRequiredService<UtilityMongoDBHelper>();
|
||||
|
||||
var existingEntityBson = _updateLogHelper.EntityToBsonDocument(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);
|
||||
}
|
||||
|
||||
var _updateLogHelper = scope.ServiceProvider.GetRequiredService<UtilityMongoDBHelper>();
|
||||
|
||||
// 5. Prepare for update (Audit snapshot)
|
||||
var paymentRequestStateBeforeChange = _updateLogHelper.EntityToBsonDocument(paymentRequest);
|
||||
|
||||
@ -2599,6 +2818,9 @@ namespace Marco.Pms.Services.Service
|
||||
|
||||
bool isVariableRecurring = paymentRequest.RecurringPayment?.IsVariable ?? false;
|
||||
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _updateLogHelper = scope.ServiceProvider.GetRequiredService<UtilityMongoDBHelper>();
|
||||
|
||||
// Capture existing state for auditing
|
||||
var existingEntityBson = _updateLogHelper.EntityToBsonDocument(paymentRequest);
|
||||
|
||||
@ -2778,6 +3000,9 @@ namespace Marco.Pms.Services.Service
|
||||
.Select(ba => ba.DocumentId)
|
||||
.ToListAsync();
|
||||
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _updateLogHelper = scope.ServiceProvider.GetRequiredService<UtilityMongoDBHelper>();
|
||||
|
||||
var existingEntityBson = _updateLogHelper.EntityToBsonDocument(paymentRequest);
|
||||
paymentRequest.IsActive = false;
|
||||
|
||||
@ -3765,6 +3990,46 @@ namespace Marco.Pms.Services.Service
|
||||
#endregion
|
||||
|
||||
#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)
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
@ -4135,6 +4400,9 @@ namespace Marco.Pms.Services.Service
|
||||
|
||||
private async Task DeleteAttachemnts(List<Guid> documentIds)
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _updateLogHelper = scope.ServiceProvider.GetRequiredService<UtilityMongoDBHelper>();
|
||||
|
||||
var attachmentTask = Task.Run(async () =>
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
@ -4176,6 +4444,9 @@ namespace Marco.Pms.Services.Service
|
||||
}
|
||||
private async Task DeletePaymentRequestAttachemnts(List<Guid> documentIds)
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _updateLogHelper = scope.ServiceProvider.GetRequiredService<UtilityMongoDBHelper>();
|
||||
|
||||
var attachmentTask = Task.Run(async () =>
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
|
||||
@ -8,6 +8,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
|
||||
{
|
||||
#region =================================================================== Expenses Functions ===================================================================
|
||||
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>> GetSupplerNameListAsync(Employee loggedInEmployee, Guid tenantId);
|
||||
Task<ApiResponse<object>> GetFilterObjectAsync(Employee loggedInEmployee, Guid tenantId);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user