Removed Project ForignKey From PaymentRequest Table

This commit is contained in:
ashutosh.nehete 2025-11-21 11:33:57 +05:30
parent 1b94592026
commit c7a73e78fb
5 changed files with 9173 additions and 201 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,38 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Removed_Project_ForignKey_From_RecurringPayment_Table : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_RecurringPayments_Projects_ProjectId",
table: "RecurringPayments");
migrationBuilder.DropIndex(
name: "IX_RecurringPayments_ProjectId",
table: "RecurringPayments");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateIndex(
name: "IX_RecurringPayments_ProjectId",
table: "RecurringPayments",
column: "ProjectId");
migrationBuilder.AddForeignKey(
name: "FK_RecurringPayments_Projects_ProjectId",
table: "RecurringPayments",
column: "ProjectId",
principalTable: "Projects",
principalColumn: "Id");
}
}
}

View File

@ -2922,8 +2922,6 @@ namespace Marco.Pms.DataAccess.Migrations
b.HasIndex("ExpenseCategoryId"); b.HasIndex("ExpenseCategoryId");
b.HasIndex("ProjectId");
b.HasIndex("StatusId"); b.HasIndex("StatusId");
b.HasIndex("TenantId"); b.HasIndex("TenantId");
@ -7612,10 +7610,6 @@ namespace Marco.Pms.DataAccess.Migrations
.WithMany() .WithMany()
.HasForeignKey("ExpenseCategoryId"); .HasForeignKey("ExpenseCategoryId");
b.HasOne("Marco.Pms.Model.Projects.Project", "Project")
.WithMany()
.HasForeignKey("ProjectId");
b.HasOne("Marco.Pms.Model.Expenses.Masters.RecurringPaymentStatus", "Status") b.HasOne("Marco.Pms.Model.Expenses.Masters.RecurringPaymentStatus", "Status")
.WithMany() .WithMany()
.HasForeignKey("StatusId") .HasForeignKey("StatusId")
@ -7638,8 +7632,6 @@ namespace Marco.Pms.DataAccess.Migrations
b.Navigation("ExpenseCategory"); b.Navigation("ExpenseCategory");
b.Navigation("Project");
b.Navigation("Status"); b.Navigation("Status");
b.Navigation("Tenant"); b.Navigation("Tenant");

View File

@ -1,7 +1,6 @@
using Marco.Pms.Model.Employees; using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Expenses.Masters; using Marco.Pms.Model.Expenses.Masters;
using Marco.Pms.Model.Master; using Marco.Pms.Model.Master;
using Marco.Pms.Model.Projects;
using Marco.Pms.Model.TenantModels; using Marco.Pms.Model.TenantModels;
using Marco.Pms.Model.Utilities; using Marco.Pms.Model.Utilities;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
@ -29,10 +28,6 @@ namespace Marco.Pms.Model.Expenses
public DateTime? LatestPRGeneratedAt { get; set; } public DateTime? LatestPRGeneratedAt { get; set; }
public DateTime? NextStrikeDate { get; set; } public DateTime? NextStrikeDate { get; set; }
public Guid? ProjectId { get; set; } public Guid? ProjectId { get; set; }
[ValidateNever]
[ForeignKey("ProjectId")]
public Project? Project { get; set; }
public int PaymentBufferDays { get; set; } public int PaymentBufferDays { get; set; }
public Guid? ExpenseCategoryId { get; set; } public Guid? ExpenseCategoryId { get; set; }

View File

@ -110,23 +110,16 @@ namespace Marco.Pms.Services.Service
Guid loggedInEmployeeId = loggedInEmployee.Id; Guid loggedInEmployeeId = loggedInEmployee.Id;
List<ExpenseList> expenseVM = new List<ExpenseList>(); List<ExpenseList> expenseVM = new List<ExpenseList>();
var totalEntites = 0; var totalEntites = 0;
var hasViewSelfPermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseViewSelf, loggedInEmployeeId);
});
var hasViewAllPermissionTask = Task.Run(async () => var hasViewSelfPermissionTask = HasPermissionAsync(PermissionsMaster.ExpenseViewSelf, loggedInEmployee.Id);
{ var hasViewAllPermissionTask = HasPermissionAsync(PermissionsMaster.ExpenseViewAll, loggedInEmployee.Id);
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseViewAll, loggedInEmployeeId);
});
await Task.WhenAll(hasViewSelfPermissionTask, hasViewAllPermissionTask); await Task.WhenAll(hasViewSelfPermissionTask, hasViewAllPermissionTask);
if (!hasViewAllPermissionTask.Result && !hasViewSelfPermissionTask.Result) var hasViewAllPermission = hasViewAllPermissionTask.Result;
var hasViewSelfPermission = hasViewSelfPermissionTask.Result;
if (!hasViewAllPermission && !hasViewSelfPermission)
{ {
// User has neither required permission. Deny access. // User has neither required permission. Deny access.
_logger.LogWarning("Access DENIED for employee {EmployeeId} attempting to get expenses list.", loggedInEmployeeId); _logger.LogWarning("Access DENIED for employee {EmployeeId} attempting to get expenses list.", loggedInEmployeeId);
@ -165,11 +158,11 @@ namespace Marco.Pms.Services.Service
//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.
if (hasViewAllPermissionTask.Result) if (hasViewAllPermission)
{ {
expensesQuery = expensesQuery.Where(e => e.CreatedById == loggedInEmployeeId || e.StatusId != Draft); expensesQuery = expensesQuery.Where(e => e.CreatedById == loggedInEmployeeId || e.StatusId != Draft);
} }
else if (hasViewSelfPermissionTask.Result) else if (hasViewSelfPermission)
{ {
// User only has 'View Self' permission, so restrict the query to their own expenses. // 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); _logger.LogInfo("User {EmployeeId} has 'View Self' permission. Restricting query to their expenses.", loggedInEmployeeId);
@ -367,11 +360,7 @@ 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);
} }
using var scope = _serviceScopeFactory.CreateScope(); expenseDetails = await GetAllExpnesRelatedTablesForSingle(expense, loggedInEmployee.Id, expense.TenantId);
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
var hasManagePermission = await permissionService.HasPermission(PermissionsMaster.ExpenseManage, loggedInEmployee.Id);
expenseDetails = await GetAllExpnesRelatedTablesForSingle(expense, hasManagePermission, loggedInEmployee.Id, expense.TenantId);
} }
var vm = _mapper.Map<ExpenseDetailsVM>(expenseDetails); var vm = _mapper.Map<ExpenseDetailsVM>(expenseDetails);
@ -452,10 +441,13 @@ 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) public async Task<ApiResponse<object>> GetFilterObjectAsync(Employee loggedInEmployee, Guid tenantId)
{ {
try try
{ {
_logger.LogInfo("Fetching expenses for tenant: {TenantId}", tenantId);
var expenses = await _context.Expenses var expenses = await _context.Expenses
.Include(e => e.PaidBy) .Include(e => e.PaidBy)
.Include(e => e.CreatedBy) .Include(e => e.CreatedBy)
@ -464,6 +456,8 @@ namespace Marco.Pms.Services.Service
.Where(e => e.TenantId == tenantId) .Where(e => e.TenantId == tenantId)
.ToListAsync(); .ToListAsync();
_logger.LogInfo("Fetched {ExpenseCount} expenses", expenses.Count);
var projectIds = expenses.Select(e => e.ProjectId).ToList(); var projectIds = expenses.Select(e => e.ProjectId).ToList();
var infraProjectTask = Task.Run(async () => var infraProjectTask = Task.Run(async () =>
@ -490,6 +484,8 @@ namespace Marco.Pms.Services.Service
var projects = infraProjectTask.Result; var projects = infraProjectTask.Result;
projects.AddRange(serviceProjectTask.Result); projects.AddRange(serviceProjectTask.Result);
_logger.LogInfo("Fetched {ProjectCount} projects", projects.Count);
// Construct the final object from the results of the completed tasks. // Construct the final object from the results of the completed tasks.
var response = new var response = new
{ {
@ -499,12 +495,15 @@ namespace Marco.Pms.Services.Service
Status = expenses.Where(e => e.Status != null).Select(e => new { Id = e.Status!.Id, Name = e.Status.Name }).Distinct().ToList(), Status = expenses.Where(e => e.Status != null).Select(e => new { Id = e.Status!.Id, Name = e.Status.Name }).Distinct().ToList(),
ExpenseCategory = expenses.Where(e => e.ExpenseCategory != null).Select(e => new { Id = e.ExpenseCategory!.Id, Name = e.ExpenseCategory.Name }).Distinct().ToList() ExpenseCategory = expenses.Where(e => e.ExpenseCategory != null).Select(e => new { Id = e.ExpenseCategory!.Id, Name = e.ExpenseCategory.Name }).Distinct().ToList()
}; };
_logger.LogInfo("Successfully fetched the filter list");
return ApiResponse<object>.SuccessResponse(response, "Successfully fetched the filter list", 200); return ApiResponse<object>.SuccessResponse(response, "Successfully fetched the filter list", 200);
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Exception occured while fetching the list filters for expenses"); _logger.LogError(ex, "Exception occurred while fetching the list filters for expenses");
return ApiResponse<object>.ErrorResponse("Internal Exception Occured", ExceptionMapper(ex), 500); return ApiResponse<object>.ErrorResponse("Internal Exception Occurred", ExceptionMapper(ex), 500);
} }
} }
@ -529,13 +528,8 @@ namespace Marco.Pms.Services.Service
{ {
// 1. Authorization & Validation: Run all I/O-bound checks concurrently using factories for safety. // 1. Authorization & Validation: Run all I/O-bound checks concurrently using factories for safety.
// PERMISSION CHECKS: Use IServiceScopeFactory for thread-safe access to scoped services. // PERMISSION CHECKS
var hasUploadPermissionTask = Task.Run(async () => // Task.Run is acceptable here to create a new scope, but let's do it cleaner. var hasUploadPermissionTask = HasPermissionAsync(PermissionsMaster.ExpenseUpload, loggedInEmployee.Id);
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseUpload, loggedInEmployee.Id);
});
// VALIDATION CHECKS: Use IDbContextFactory for thread-safe, parallel database queries. // VALIDATION CHECKS: Use IDbContextFactory for thread-safe, parallel database queries.
// Each task gets its own DbContext instance. // Each task gets its own DbContext instance.
@ -798,10 +792,9 @@ namespace Marco.Pms.Services.Service
} }
else if (requiredPermissions.Any()) else if (requiredPermissions.Any())
{ {
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
foreach (var permission in requiredPermissions) foreach (var permission in requiredPermissions)
{ {
if (await permissionService.HasPermission(permission.PermissionId, loggedInEmployee.Id) && model.StatusId != Review) if (await HasPermissionAsync(permission.PermissionId, loggedInEmployee.Id) && model.StatusId != Review)
{ {
hasPermission = true; hasPermission = true;
break; break;
@ -1046,10 +1039,8 @@ namespace Marco.Pms.Services.Service
"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(); // Check if the employee has the required permission
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>(); var hasManagePermission = await HasPermissionAsync(PermissionsMaster.ExpenseManage, loggedInEmployee.Id);
var hasManagePermission = await permissionService.HasPermission(PermissionsMaster.ExpenseManage, loggedInEmployee.Id);
var existingExpense = await _context.Expenses var existingExpense = await _context.Expenses
.Include(e => e.ExpenseCategory) .Include(e => e.ExpenseCategory)
@ -1242,12 +1233,7 @@ namespace Marco.Pms.Services.Service
return await context.Expenses.Where(e => e.Id == id && e.StatusId == Draft && e.TenantId == tenantId).FirstOrDefaultAsync(); return await context.Expenses.Where(e => e.Id == id && e.StatusId == Draft && e.TenantId == tenantId).FirstOrDefaultAsync();
}); });
var hasAprrovePermissionTask = Task.Run(async () => var hasAprrovePermissionTask = HasPermissionAsync(PermissionsMaster.ExpenseApprove, loggedInEmployee.Id);
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseApprove, loggedInEmployee.Id);
});
await Task.WhenAll(expenseTask, hasAprrovePermissionTask); await Task.WhenAll(expenseTask, hasAprrovePermissionTask);
@ -1344,23 +1330,16 @@ namespace Marco.Pms.Services.Service
try try
{ {
var hasViewSelfPermissionTask = Task.Run(async () => // Check if the user has the required permission
{ var hasViewSelfPermissionTask = HasPermissionAsync(PermissionsMaster.ExpenseViewSelf, loggedInEmployee.Id);
using var scope = _serviceScopeFactory.CreateScope(); var hasViewAllPermissionTask = HasPermissionAsync(PermissionsMaster.ExpenseViewAll, loggedInEmployee.Id);
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseViewSelf, loggedInEmployee.Id);
});
var hasViewAllPermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseViewAll, loggedInEmployee.Id);
});
await Task.WhenAll(hasViewSelfPermissionTask, hasViewAllPermissionTask); await Task.WhenAll(hasViewSelfPermissionTask, hasViewAllPermissionTask);
if (!hasViewAllPermissionTask.Result && !hasViewSelfPermissionTask.Result) var hasViewAllPermission = hasViewAllPermissionTask.Result;
var hasViewSelfPermission = hasViewSelfPermissionTask.Result;
if (!hasViewAllPermission && !hasViewSelfPermission)
{ {
// User has neither required permission. Deny access. // User has neither required permission. Deny access.
_logger.LogWarning("Access DENIED for employee {EmployeeId} attempting to get payment request list.", loggedInEmployee.Id); _logger.LogWarning("Access DENIED for employee {EmployeeId} attempting to get payment request list.", loggedInEmployee.Id);
@ -1383,7 +1362,8 @@ namespace Marco.Pms.Services.Service
pr.CreatedBy != null && pr.CreatedBy != null &&
pr.CreatedBy.JobRole != null); pr.CreatedBy.JobRole != null);
if (hasViewSelfPermissionTask.Result && !hasViewAllPermissionTask.Result) // Filter the query based on the user's permissions
if (hasViewSelfPermission && !hasViewAllPermission)
{ {
paymentRequestQuery = paymentRequestQuery.Where(pr => pr.CreatedById == loggedInEmployee.Id); paymentRequestQuery = paymentRequestQuery.Where(pr => pr.CreatedById == loggedInEmployee.Id);
} }
@ -1393,6 +1373,7 @@ namespace Marco.Pms.Services.Service
// Deserialize and apply advanced filter if provided // Deserialize and apply advanced filter if provided
PaymentRequestFilter? paymentRequestFilter = TryDeserializePaymentRequestFilter(filter); PaymentRequestFilter? paymentRequestFilter = TryDeserializePaymentRequestFilter(filter);
// filter the query based on the advanced filter
if (paymentRequestFilter != null) if (paymentRequestFilter != null)
{ {
if (paymentRequestFilter.ProjectIds?.Any() ?? false) if (paymentRequestFilter.ProjectIds?.Any() ?? false)
@ -1456,10 +1437,11 @@ namespace Marco.Pms.Services.Service
.Take(pageSize) .Take(pageSize)
.ToListAsync(); .ToListAsync();
// Calculate total pages
var totalPages = (int)Math.Ceiling((double)totalEntities / pageSize); var totalPages = (int)Math.Ceiling((double)totalEntities / pageSize);
// Fetch projects for each payment request
var projectIds = paymentRequests.Select(pr => pr.ProjectId).ToList(); var projectIds = paymentRequests.Select(pr => pr.ProjectId).ToList();
var infraProjectTask = Task.Run(async () => var infraProjectTask = Task.Run(async () =>
{ {
await using var context = await _dbContextFactory.CreateDbContextAsync(); await using var context = await _dbContextFactory.CreateDbContextAsync();
@ -1477,6 +1459,7 @@ namespace Marco.Pms.Services.Service
var projects = infraProjectTask.Result; var projects = infraProjectTask.Result;
projects.AddRange(serviceProjectTask.Result); projects.AddRange(serviceProjectTask.Result);
// mapping to view model
var results = paymentRequests.Select(pr => var results = paymentRequests.Select(pr =>
{ {
var result = _mapper.Map<PaymentRequestVM>(pr); var result = _mapper.Map<PaymentRequestVM>(pr);
@ -1523,26 +1506,9 @@ namespace Marco.Pms.Services.Service
} }
// Check user permissions concurrently // Check user permissions concurrently
var hasViewSelfPermissionTask = Task.Run(async () => var hasViewSelfPermissionTask = HasPermissionAsync(PermissionsMaster.ExpenseViewSelf, loggedInEmployee.Id);
{ var hasViewAllPermissionTask = HasPermissionAsync(PermissionsMaster.ExpenseViewAll, loggedInEmployee.Id);
using var scope = _serviceScopeFactory.CreateScope(); var hasReviewPermissionTask = HasPermissionAsync(PermissionsMaster.ExpenseReview, loggedInEmployee.Id);
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseViewSelf, loggedInEmployee.Id);
});
var hasViewAllPermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseViewAll, loggedInEmployee.Id);
});
var hasReviewPermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseReview, loggedInEmployee.Id);
});
await Task.WhenAll(hasViewSelfPermissionTask, hasViewAllPermissionTask, hasReviewPermissionTask); await Task.WhenAll(hasViewSelfPermissionTask, hasViewAllPermissionTask, hasReviewPermissionTask);
@ -1550,26 +1516,9 @@ namespace Marco.Pms.Services.Service
bool hasViewAllPermission = hasViewAllPermissionTask.Result; bool hasViewAllPermission = hasViewAllPermissionTask.Result;
bool hasReviewPermission = hasReviewPermissionTask.Result; bool hasReviewPermission = hasReviewPermissionTask.Result;
var hasApprovePermissionTask = Task.Run(async () => var hasApprovePermissionTask = HasPermissionAsync(PermissionsMaster.ExpenseApprove, loggedInEmployee.Id);
{ var hasProcessPermissionTask = HasPermissionAsync(PermissionsMaster.ExpenseProcess, loggedInEmployee.Id);
using var scope = _serviceScopeFactory.CreateScope(); var hasManagePermissionTask = HasPermissionAsync(PermissionsMaster.ExpenseManage, loggedInEmployee.Id);
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseApprove, loggedInEmployee.Id);
});
var hasProcessPermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseProcess, loggedInEmployee.Id);
});
var hasManagePermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseManage, loggedInEmployee.Id);
});
await Task.WhenAll(hasApprovePermissionTask, hasProcessPermissionTask, hasManagePermissionTask); await Task.WhenAll(hasApprovePermissionTask, hasProcessPermissionTask, hasManagePermissionTask);
@ -1581,7 +1530,7 @@ namespace Marco.Pms.Services.Service
if (!hasViewSelfPermission && !hasViewAllPermission && !hasReviewPermission && !hasApprovePermission && !hasProcessPermission) if (!hasViewSelfPermission && !hasViewAllPermission && !hasReviewPermission && !hasApprovePermission && !hasProcessPermission)
{ {
_logger.LogWarning("Access DENIED: Employee {EmployeeId} has no permission to view payment requests.", loggedInEmployee.Id); _logger.LogWarning("Access DENIED: Employee {EmployeeId} has no permission to view payment requests.", loggedInEmployee.Id);
return ApiResponse<object>.SuccessResponse(new { }, "You do not have permission to view any payment request.", 200); return ApiResponse<object>.ErrorResponse("You do not have permission to view payment requests.", "You do not have permission to view any payment request.", 403);
} }
// Query payment request with all necessary navigation properties and validation constraints // Query payment request with all necessary navigation properties and validation constraints
@ -1864,9 +1813,7 @@ namespace Marco.Pms.Services.Service
try try
{ {
using var scope = _serviceScopeFactory.CreateScope(); var hasUploadPermission = await HasPermissionAsync(PermissionsMaster.ExpenseUpload, loggedInEmployee.Id);
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
var hasUploadPermission = await permissionService.HasPermission(PermissionsMaster.ExpenseUpload, loggedInEmployee.Id);
if (!hasUploadPermission) if (!hasUploadPermission)
{ {
@ -1890,13 +1837,8 @@ namespace Marco.Pms.Services.Service
await using var context = await _dbContextFactory.CreateDbContextAsync(); await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.ExpensesStatusMaster.FirstOrDefaultAsync(es => es.Id == Draft && es.IsActive); return await context.ExpensesStatusMaster.FirstOrDefaultAsync(es => es.Id == Draft && es.IsActive);
}); });
var projectTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.Projects.FirstOrDefaultAsync(P => model.ProjectId.HasValue && P.Id == model.ProjectId.Value);
});
await Task.WhenAll(expenseCategoryTask, currencyTask, expenseStatusTask, projectTask); await Task.WhenAll(expenseCategoryTask, currencyTask, expenseStatusTask);
var expenseCategory = await expenseCategoryTask; var expenseCategory = await expenseCategoryTask;
if (expenseCategory == null) if (expenseCategory == null)
@ -1919,8 +1861,42 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("Expense Status (Draft) not found.", "Expense Status not found.", 404); return ApiResponse<object>.ErrorResponse("Expense Status (Draft) not found.", "Expense Status not found.", 404);
} }
var project = await projectTask; BasicProjectVM? project = null;
// Project is optional so no error if not found
if (model.ProjectId.HasValue && model.ProjectId.Value != Guid.Empty)
{
var infraProjectTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.Projects
.AsNoTracking()
.Where(p => p.Id == model.ProjectId && p.TenantId == tenantId)
.Select(p => _mapper.Map<BasicProjectVM>(p))
.FirstOrDefaultAsync();
});
var serviceProjectTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.ServiceProjects
.AsNoTracking()
.Where(sp => sp.Id == model.ProjectId && sp.TenantId == tenantId)
.Select(sp => _mapper.Map<BasicProjectVM>(sp))
.FirstOrDefaultAsync();
});
await Task.WhenAll(infraProjectTask, serviceProjectTask);
var infraProject = infraProjectTask.Result;
var serviceProject = serviceProjectTask.Result;
if (infraProject == null && serviceProject == null)
{
_logger.LogWarning("Cannot proceed: Both infrastructure and service projects are not found.");
return ApiResponse<object>.ErrorResponse("Cannot proceed: Both infrastructure and service projects are not found.",
"Cannot proceed: Both infrastructure and service projects are not found.", 404);
}
project = infraProject ?? serviceProject;
}
// Generate unique UID postfix based on existing requests for the current prefix // Generate unique UID postfix based on existing requests for the current prefix
var lastPR = await _context.PaymentRequests.Where(pr => pr.UIDPrefix == uIDPrefix) var lastPR = await _context.PaymentRequests.Where(pr => pr.UIDPrefix == uIDPrefix)
@ -1999,7 +1975,7 @@ namespace Marco.Pms.Services.Service
response.Currency = currency; response.Currency = currency;
response.ExpenseCategory = _mapper.Map<ExpenseCategoryMasterVM>(expenseCategory); response.ExpenseCategory = _mapper.Map<ExpenseCategoryMasterVM>(expenseCategory);
response.ExpenseStatus = _mapper.Map<ExpensesStatusMasterVM>(expenseStatus); response.ExpenseStatus = _mapper.Map<ExpensesStatusMasterVM>(expenseStatus);
response.Project = _mapper.Map<BasicProjectVM>(project); response.Project = project;
response.CreatedBy = _mapper.Map<BasicEmployeeVM>(loggedInEmployee); response.CreatedBy = _mapper.Map<BasicEmployeeVM>(loggedInEmployee);
_logger.LogInfo("Payment request created successfully with UID: {PaymentRequestUID}", response.PaymentRequestUID); _logger.LogInfo("Payment request created successfully with UID: {PaymentRequestUID}", response.PaymentRequestUID);
@ -2517,12 +2493,7 @@ namespace Marco.Pms.Services.Service
await using var context = await _dbContextFactory.CreateDbContextAsync(); await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.CurrencyMaster.FirstOrDefaultAsync(c => c.Id == model.CurrencyId); return await context.CurrencyMaster.FirstOrDefaultAsync(c => c.Id == model.CurrencyId);
}); });
var hasManagePermissionTask = Task.Run(async () => var hasManagePermissionTask = HasPermissionAsync(PermissionsMaster.ExpenseManage, loggedInEmployee.Id);
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseManage, loggedInEmployee.Id);
});
await Task.WhenAll(expenseCategoryTask, currencyTask, hasManagePermissionTask); await Task.WhenAll(expenseCategoryTask, currencyTask, hasManagePermissionTask);
@ -2542,7 +2513,7 @@ namespace Marco.Pms.Services.Service
BasicProjectVM? project = null; BasicProjectVM? project = null;
if (model.ProjectId.HasValue) if (model.ProjectId.HasValue && model.ProjectId.Value != Guid.Empty)
{ {
var infraProjectTask = Task.Run(async () => var infraProjectTask = Task.Run(async () =>
{ {
@ -2746,12 +2717,7 @@ namespace Marco.Pms.Services.Service
return await context.PaymentRequests.AsNoTracking().Where(e => e.Id == id && e.ExpenseStatusId == Draft && e.TenantId == tenantId).FirstOrDefaultAsync(); return await context.PaymentRequests.AsNoTracking().Where(e => e.Id == id && e.ExpenseStatusId == Draft && e.TenantId == tenantId).FirstOrDefaultAsync();
}); });
var hasAprrovePermissionTask = Task.Run(async () => var hasAprrovePermissionTask = HasPermissionAsync(PermissionsMaster.ExpenseApprove, loggedInEmployee.Id);
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseApprove, loggedInEmployee.Id);
});
await Task.WhenAll(paymentRequestTask, hasAprrovePermissionTask); await Task.WhenAll(paymentRequestTask, hasAprrovePermissionTask);
@ -2849,19 +2815,8 @@ namespace Marco.Pms.Services.Service
try try
{ {
// Check permissions concurrently: view self and view all recurring payments // Check permissions concurrently: view self and view all recurring payments
var hasViewSelfPermissionTask = Task.Run(async () => var hasViewSelfPermissionTask = HasPermissionAsync(PermissionsMaster.ViewSelfRecurring, loggedInEmployee.Id);
{ var hasViewAllPermissionTask = HasPermissionAsync(PermissionsMaster.ViewAllRecurring, loggedInEmployee.Id);
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ViewSelfRecurring, loggedInEmployee.Id);
});
var hasViewAllPermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ViewAllRecurring, loggedInEmployee.Id);
});
await Task.WhenAll(hasViewSelfPermissionTask, hasViewAllPermissionTask); await Task.WhenAll(hasViewSelfPermissionTask, hasViewAllPermissionTask);
@ -2880,7 +2835,6 @@ namespace Marco.Pms.Services.Service
.Include(rp => rp.Currency) .Include(rp => rp.Currency)
.Include(rp => rp.ExpenseCategory) .Include(rp => rp.ExpenseCategory)
.Include(rp => rp.Status) .Include(rp => rp.Status)
.Include(rp => rp.Project)
.Include(rp => rp.CreatedBy).ThenInclude(e => e!.JobRole) .Include(rp => rp.CreatedBy).ThenInclude(e => e!.JobRole)
.Where(rp => rp.TenantId == tenantId && rp.IsActive == isActive); .Where(rp => rp.TenantId == tenantId && rp.IsActive == isActive);
@ -2959,11 +2913,31 @@ namespace Marco.Pms.Services.Service
.Take(pageSize) .Take(pageSize)
.ToListAsync(); .ToListAsync();
var projectIds = recurringPayments.Select(pr => pr.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);
// Map entities to view models and set recurring payment UID // Map entities to view models and set recurring payment UID
var results = recurringPayments.Select(rp => var results = recurringPayments.Select(rp =>
{ {
var vm = _mapper.Map<RecurringPaymentVM>(rp); var vm = _mapper.Map<RecurringPaymentVM>(rp);
vm.RecurringPaymentUId = $"{rp.UIDPrefix}/{rp.UIDPostfix:D5}"; vm.RecurringPaymentUId = $"{rp.UIDPrefix}/{rp.UIDPostfix:D5}";
vm.Project = projects.FirstOrDefault(p => p.Id == rp.ProjectId);
return vm; return vm;
}).ToList(); }).ToList();
@ -3005,26 +2979,9 @@ namespace Marco.Pms.Services.Service
} }
// Concurrent permission checks for view-self, view-all, and manage recurring payments // Concurrent permission checks for view-self, view-all, and manage recurring payments
var hasViewSelfPermissionTask = Task.Run(async () => var hasViewSelfPermissionTask = HasPermissionAsync(PermissionsMaster.ViewSelfRecurring, loggedInEmployee.Id);
{ var hasViewAllPermissionTask = HasPermissionAsync(PermissionsMaster.ViewAllRecurring, loggedInEmployee.Id);
using var scope = _serviceScopeFactory.CreateScope(); var hasManagePermissionTask = HasPermissionAsync(PermissionsMaster.ManageRecurring, loggedInEmployee.Id);
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ViewSelfRecurring, loggedInEmployee.Id);
});
var hasViewAllPermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ViewAllRecurring, loggedInEmployee.Id);
});
var hasManagePermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ManageRecurring, loggedInEmployee.Id);
});
await Task.WhenAll(hasViewSelfPermissionTask, hasViewAllPermissionTask, hasManagePermissionTask); await Task.WhenAll(hasViewSelfPermissionTask, hasViewAllPermissionTask, hasManagePermissionTask);
@ -3042,7 +2999,6 @@ namespace Marco.Pms.Services.Service
// Query recurring payment by Id or UID with navigation and tenant checks // Query recurring payment by Id or UID with navigation and tenant checks
var recurringPayment = await _context.RecurringPayments var recurringPayment = await _context.RecurringPayments
.Include(rp => rp.Currency) .Include(rp => rp.Currency)
.Include(rp => rp.Project)
.Include(rp => rp.ExpenseCategory) .Include(rp => rp.ExpenseCategory)
.Include(rp => rp.Status) .Include(rp => rp.Status)
.Include(rp => rp.CreatedBy).ThenInclude(e => e!.JobRole) .Include(rp => rp.CreatedBy).ThenInclude(e => e!.JobRole)
@ -3100,9 +3056,34 @@ namespace Marco.Pms.Services.Service
var employees = employeeTask.Result; var employees = employeeTask.Result;
var paymentRequests = paymentRequestTask.Result; var paymentRequests = paymentRequestTask.Result;
var infraProjectTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.Projects
.AsNoTracking()
.Where(p => p.Id == recurringPayment.ProjectId && p.TenantId == tenantId)
.Select(p => _mapper.Map<BasicProjectVM>(p))
.FirstOrDefaultAsync();
});
var serviceProjectTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.ServiceProjects
.AsNoTracking()
.Where(sp => sp.Id == recurringPayment.ProjectId && sp.TenantId == tenantId)
.Select(sp => _mapper.Map<BasicProjectVM>(sp))
.FirstOrDefaultAsync();
});
await Task.WhenAll(infraProjectTask, serviceProjectTask);
var infraProject = infraProjectTask.Result;
var serviceProject = serviceProjectTask.Result;
// Map main response DTO and enrich with notification employees and payment requests // Map main response DTO and enrich with notification employees and payment requests
var response = _mapper.Map<RecurringPaymentDetailsVM>(recurringPayment); var response = _mapper.Map<RecurringPaymentDetailsVM>(recurringPayment);
response.NotifyTo = _mapper.Map<List<BasicEmployeeVM>>(employees); response.NotifyTo = _mapper.Map<List<BasicEmployeeVM>>(employees);
response.Project = infraProject ?? serviceProject;
response.PaymentRequests = paymentRequests; response.PaymentRequests = paymentRequests;
_logger.LogInfo("Recurring payment details fetched successfully for RecurringPaymentId: {RecurringPaymentId} by EmployeeId: {EmployeeId}", _logger.LogInfo("Recurring payment details fetched successfully for RecurringPaymentId: {RecurringPaymentId} by EmployeeId: {EmployeeId}",
@ -3135,9 +3116,7 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("End date cannot be earlier than today.", "End date cannot be earlier than today.", 400); return ApiResponse<object>.ErrorResponse("End date cannot be earlier than today.", "End date cannot be earlier than today.", 400);
} }
// Check if user has permission to create recurring payment templates // Check if user has permission to create recurring payment templates
using var scope = _serviceScopeFactory.CreateScope(); var hasPermission = await HasPermissionAsync(PermissionsMaster.ManageRecurring, loggedInEmployee.Id);
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
var hasPermission = await permissionService.HasPermission(PermissionsMaster.ManageRecurring, loggedInEmployee.Id);
if (!hasPermission) if (!hasPermission)
{ {
@ -3164,13 +3143,7 @@ namespace Marco.Pms.Services.Service
return await context.CurrencyMaster.FirstOrDefaultAsync(c => c.Id == model.CurrencyId); return await context.CurrencyMaster.FirstOrDefaultAsync(c => c.Id == model.CurrencyId);
}); });
var projectTask = Task.Run(async () => await Task.WhenAll(expenseCategoryTask, recurringStatusTask, currencyTask);
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return model.ProjectId.HasValue ? await context.Projects.FirstOrDefaultAsync(p => p.Id == model.ProjectId.Value) : null;
});
await Task.WhenAll(expenseCategoryTask, recurringStatusTask, currencyTask, projectTask);
var expenseCategory = await expenseCategoryTask; var expenseCategory = await expenseCategoryTask;
if (expenseCategory == null) if (expenseCategory == null)
@ -3193,7 +3166,42 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("Currency not found.", "Currency not found.", 404); return ApiResponse<object>.ErrorResponse("Currency not found.", "Currency not found.", 404);
} }
var project = await projectTask; // Optional, can be null BasicProjectVM? project = null;
if (model.ProjectId.HasValue && model.ProjectId.Value != Guid.Empty)
{
var infraProjectTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.Projects
.AsNoTracking()
.Where(p => p.Id == model.ProjectId && p.TenantId == tenantId)
.Select(p => _mapper.Map<BasicProjectVM>(p))
.FirstOrDefaultAsync();
});
var serviceProjectTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.ServiceProjects
.AsNoTracking()
.Where(sp => sp.Id == model.ProjectId && sp.TenantId == tenantId)
.Select(sp => _mapper.Map<BasicProjectVM>(sp))
.FirstOrDefaultAsync();
});
await Task.WhenAll(infraProjectTask, serviceProjectTask);
var infraProject = infraProjectTask.Result;
var serviceProject = serviceProjectTask.Result;
if (infraProject == null && serviceProject == null)
{
_logger.LogWarning("Cannot proceed: Both infrastructure and service projects are not found.");
return ApiResponse<object>.ErrorResponse("Cannot proceed: Both infrastructure and service projects are not found.",
"Cannot proceed: Both infrastructure and service projects are not found.", 404);
}
project = infraProject ?? serviceProject;
}
// Generate unique UID prefix and postfix per current month/year // Generate unique UID prefix and postfix per current month/year
string uIDPrefix = $"RP/{DateTime.Now:MMyy}"; string uIDPrefix = $"RP/{DateTime.Now:MMyy}";
@ -3224,7 +3232,7 @@ namespace Marco.Pms.Services.Service
response.ExpenseCategory = _mapper.Map<ExpenseCategoryMasterVM>(expenseCategory); response.ExpenseCategory = _mapper.Map<ExpenseCategoryMasterVM>(expenseCategory);
response.Status = recurringStatus; response.Status = recurringStatus;
response.Currency = currency; response.Currency = currency;
response.Project = _mapper.Map<BasicProjectVM>(project); response.Project = project;
_logger.LogInfo("Recurring Payment Template created successfully with UID: {RecurringPaymentUId} by EmployeeId: {EmployeeId}", response.RecurringPaymentUId, loggedInEmployee.Id); _logger.LogInfo("Recurring Payment Template created successfully with UID: {RecurringPaymentUId} by EmployeeId: {EmployeeId}", response.RecurringPaymentUId, loggedInEmployee.Id);
@ -3387,7 +3395,6 @@ namespace Marco.Pms.Services.Service
.Include(rp => rp.Currency) .Include(rp => rp.Currency)
.Include(rp => rp.ExpenseCategory) .Include(rp => rp.ExpenseCategory)
.Include(rp => rp.Status) .Include(rp => rp.Status)
.Include(rp => rp.Project)
.AsNoTracking() .AsNoTracking()
.Where(rp => newRecurringTemplateIds.Contains(rp.Id)) .Where(rp => newRecurringTemplateIds.Contains(rp.Id))
.ToListAsync(); .ToListAsync();
@ -3395,11 +3402,35 @@ namespace Marco.Pms.Services.Service
_logger.LogInfo("{Count} payment requests created successfully from recurring payments by EmployeeId: {EmployeeId} for TenantId: {TenantId}", _logger.LogInfo("{Count} payment requests created successfully from recurring payments by EmployeeId: {EmployeeId} for TenantId: {TenantId}",
paymentRequests.Count, loggedInEmployee.Id, tenantId); paymentRequests.Count, loggedInEmployee.Id, tenantId);
var response = newRecurringPayments.Select(rp => new var projectIds = recurringPayments.Select(pr => pr.ProjectId).ToList();
var infraProjectTask = Task.Run(async () =>
{ {
RecurringPayment = _mapper.Map<RecurringPaymentVM>(rp), 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);
var response = newRecurringPayments.Select(rp =>
{
var result = _mapper.Map<RecurringPaymentVM>(rp);
result.Project = projects.FirstOrDefault(p => p.Id == rp.ProjectId);
return new
{
RecurringPayment = result,
DueDate = DateTime.UtcNow.AddDays(rp.PaymentBufferDays), DueDate = DateTime.UtcNow.AddDays(rp.PaymentBufferDays),
Emails = rp.NotifyTo.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) Emails = rp.NotifyTo.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
};
}).ToList(); }).ToList();
return ApiResponse<object>.SuccessResponse(response, $"{paymentRequests.Count} conversion(s) to payment request completed successfully.", 201); return ApiResponse<object>.SuccessResponse(response, $"{paymentRequests.Count} conversion(s) to payment request completed successfully.", 201);
@ -3450,9 +3481,7 @@ namespace Marco.Pms.Services.Service
} }
// Permission check for managing recurring payments // Permission check for managing recurring payments
using var scope = _serviceScopeFactory.CreateScope(); var hasPermission = await HasPermissionAsync(PermissionsMaster.ManageRecurring, loggedInEmployee.Id);
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
var hasPermission = await permissionService.HasPermission(PermissionsMaster.ManageRecurring, loggedInEmployee.Id);
if (!hasPermission) if (!hasPermission)
{ {
@ -3479,13 +3508,7 @@ namespace Marco.Pms.Services.Service
return await context.CurrencyMaster.FirstOrDefaultAsync(c => c.Id == model.CurrencyId); return await context.CurrencyMaster.FirstOrDefaultAsync(c => c.Id == model.CurrencyId);
}); });
var projectTask = Task.Run(async () => await Task.WhenAll(expenseCategoryTask, recurringStatusTask, currencyTask);
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return model.ProjectId.HasValue ? await context.Projects.FirstOrDefaultAsync(p => p.Id == model.ProjectId.Value) : null;
});
await Task.WhenAll(expenseCategoryTask, recurringStatusTask, currencyTask, projectTask);
var expenseCategory = await expenseCategoryTask; var expenseCategory = await expenseCategoryTask;
if (expenseCategory == null) if (expenseCategory == null)
@ -3508,7 +3531,42 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("Currency not found.", "Currency not found.", 404); return ApiResponse<object>.ErrorResponse("Currency not found.", "Currency not found.", 404);
} }
var project = await projectTask; // Optional BasicProjectVM? project = null;
if (model.ProjectId.HasValue && model.ProjectId.Value != Guid.Empty)
{
var infraProjectTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.Projects
.AsNoTracking()
.Where(p => p.Id == model.ProjectId && p.TenantId == tenantId)
.Select(p => _mapper.Map<BasicProjectVM>(p))
.FirstOrDefaultAsync();
});
var serviceProjectTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.ServiceProjects
.AsNoTracking()
.Where(sp => sp.Id == model.ProjectId && sp.TenantId == tenantId)
.Select(sp => _mapper.Map<BasicProjectVM>(sp))
.FirstOrDefaultAsync();
});
await Task.WhenAll(infraProjectTask, serviceProjectTask);
var infraProject = infraProjectTask.Result;
var serviceProject = serviceProjectTask.Result;
if (infraProject == null && serviceProject == null)
{
_logger.LogWarning("Cannot proceed: Both infrastructure and service projects are not found.");
return ApiResponse<object>.ErrorResponse("Cannot proceed: Both infrastructure and service projects are not found.",
"Cannot proceed: Both infrastructure and service projects are not found.", 404);
}
project = infraProject ?? serviceProject;
}
// Retrieve the existing recurring payment record for update // Retrieve the existing recurring payment record for update
var recurringPayment = await _context.RecurringPayments var recurringPayment = await _context.RecurringPayments
@ -3535,7 +3593,7 @@ namespace Marco.Pms.Services.Service
response.ExpenseCategory = _mapper.Map<ExpenseCategoryMasterVM>(expenseCategory); response.ExpenseCategory = _mapper.Map<ExpenseCategoryMasterVM>(expenseCategory);
response.Status = recurringStatus; response.Status = recurringStatus;
response.Currency = currency; response.Currency = currency;
response.Project = _mapper.Map<BasicProjectVM>(project); response.Project = project;
_logger.LogInfo("Recurring Payment Template updated successfully with UID: {RecurringPaymentUId} by EmployeeId: {EmployeeId}", response.RecurringPaymentUId, loggedInEmployee.Id); _logger.LogInfo("Recurring Payment Template updated successfully with UID: {RecurringPaymentUId} by EmployeeId: {EmployeeId}", response.RecurringPaymentUId, loggedInEmployee.Id);
@ -3665,6 +3723,12 @@ namespace Marco.Pms.Services.Service
#endregion #endregion
#region =================================================================== Helper Functions =================================================================== #region =================================================================== Helper Functions ===================================================================
private async Task<bool> HasPermissionAsync(Guid permission, Guid employeeId)
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(permission, employeeId);
}
private static object ExceptionMapper(Exception ex) private static object ExceptionMapper(Exception ex)
{ {
return new return new
@ -3680,7 +3744,7 @@ namespace Marco.Pms.Services.Service
} }
}; };
} }
private async Task<ExpenseDetailsMongoDB> GetAllExpnesRelatedTablesForSingle(Expenses model, bool hasManagePermission, Guid loggedInEmployeeId, Guid tenantId) private async Task<ExpenseDetailsMongoDB> GetAllExpnesRelatedTablesForSingle(Expenses model, Guid loggedInEmployeeId, Guid tenantId)
{ {
var statusMappingTask = Task.Run(async () => var statusMappingTask = Task.Run(async () =>
{ {