diff --git a/Marco.Pms.Services/MappingProfiles/MappingProfile.cs b/Marco.Pms.Services/MappingProfiles/MappingProfile.cs index a7aee31..ef0873a 100644 --- a/Marco.Pms.Services/MappingProfiles/MappingProfile.cs +++ b/Marco.Pms.Services/MappingProfiles/MappingProfile.cs @@ -248,6 +248,7 @@ namespace Marco.Pms.Services.MappingProfiles dest => dest.Id, opt => opt.MapFrom(src => Guid.Parse(src.Id))); + CreateMap(); CreateMap() .ForMember( dest => dest.Id, diff --git a/Marco.Pms.Services/Service/ExpensesService.cs b/Marco.Pms.Services/Service/ExpensesService.cs index 09a4682..c04ec48 100644 --- a/Marco.Pms.Services/Service/ExpensesService.cs +++ b/Marco.Pms.Services/Service/ExpensesService.cs @@ -129,6 +129,16 @@ namespace Marco.Pms.Services.Service // 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.Project) + .Include(e => e.PaymentMode) + .Include(e => e.ExpensesType) + .Include(e => e.Status) .Where(e => e.TenantId == tenantId); // Always filter by TenantId first. if (cacheList == null) @@ -213,7 +223,8 @@ namespace Marco.Pms.Services.Service return ApiResponse.SuccessResponse(new List(), "No expenses found for the given criteria.", 200); } - expenseVM = await GetAllExpnesRelatedTables(expensesList, tenantId); + //expenseVM = await GetAllExpnesRelatedTables(expensesList, tenantId); + expenseVM = _mapper.Map>(expensesList); totalPages = (int)Math.Ceiling((double)totalEntites / pageSize); } @@ -276,7 +287,18 @@ namespace Marco.Pms.Services.Service ExpenseDetailsMongoDB? expenseDetails = null; if (expenseDetails == null) { - var expense = await _context.Expenses.AsNoTracking().FirstOrDefaultAsync(e => e.Id == id && e.TenantId == tenantId); + var expense = await _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.Project) + .Include(e => e.PaymentMode) + .Include(e => e.ExpensesType) + .Include(e => e.Status) + .AsNoTracking().FirstOrDefaultAsync(e => e.Id == id && e.TenantId == tenantId); if (expense == null) { @@ -458,13 +480,6 @@ namespace Marco.Pms.Services.Service return await permissionService.HasPermission(PermissionsMaster.ExpenseUpload, loggedInEmployee.Id); }); - var hasProjectPermissionTask = Task.Run(async () => - { - using var scope = _serviceScopeFactory.CreateScope(); - var permissionService = scope.ServiceProvider.GetRequiredService(); - return await permissionService.HasProjectPermission(loggedInEmployee, dto.ProjectId); - }); - // VALIDATION CHECKS: Use IDbContextFactory for thread-safe, parallel database queries. // Each task gets its own DbContext instance. var projectTask = Task.Run(async () => @@ -507,12 +522,12 @@ namespace Marco.Pms.Services.Service // Await all prerequisite checks at once. await Task.WhenAll( - hasUploadPermissionTask, hasProjectPermissionTask, + hasUploadPermissionTask, projectTask, expenseTypeTask, paymentModeTask, statusMappingTask, paidByTask ); // 2. Aggregate and Check Results - if (!await hasUploadPermissionTask || !await hasProjectPermissionTask) + if (!await hasUploadPermissionTask) { _logger.LogWarning("Access DENIED for employee {EmployeeId} on project {ProjectId}.", loggedInEmployee.Id, dto.ProjectId); return ApiResponse.ErrorResponse("Access Denied.", "You do not have permission to upload expenses for this project.", 403); @@ -1217,46 +1232,6 @@ namespace Marco.Pms.Services.Service private async Task GetAllExpnesRelatedTablesForSingle(Expenses model, Guid tenantId) { - var projectTask = Task.Run(async () => - { - await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); - return await dbContext.Projects.AsNoTracking().FirstOrDefaultAsync(p => p.Id == model.ProjectId && p.TenantId == tenantId); - }); - var paidByTask = Task.Run(async () => - { - await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); - return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.PaidById && e.TenantId == tenantId); - }); - var createdByTask = Task.Run(async () => - { - await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); - return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.CreatedById && e.TenantId == tenantId); - }); - var reviewedByTask = Task.Run(async () => - { - await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); - return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.ReviewedById && e.TenantId == tenantId); - }); - var approvedByTask = Task.Run(async () => - { - await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); - return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.ApprovedById && e.TenantId == tenantId); - }); - var processedByTask = Task.Run(async () => - { - await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); - return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.ProcessedById && e.TenantId == tenantId); - }); - var expenseTypeTask = Task.Run(async () => - { - await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); - return await dbContext.ExpensesTypeMaster.AsNoTracking().FirstOrDefaultAsync(et => et.Id == model.ExpensesTypeId && et.TenantId == tenantId); - }); - var paymentModeTask = Task.Run(async () => - { - await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); - return await dbContext.PaymentModeMatser.AsNoTracking().FirstOrDefaultAsync(pm => pm.Id == model.PaymentModeId && pm.TenantId == tenantId); - }); var statusMappingTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); @@ -1304,29 +1279,20 @@ namespace Marco.Pms.Services.Service }); // Await all prerequisite checks at once. - await Task.WhenAll(projectTask, expenseTypeTask, paymentModeTask, statusMappingTask, paidByTask, createdByTask, reviewedByTask, approvedByTask, - processedByTask, statusTask, billAttachmentsTask); + await Task.WhenAll(statusTask, billAttachmentsTask); - var project = projectTask.Result; - var expenseType = expenseTypeTask.Result; - var paymentMode = paymentModeTask.Result; var statusMapping = statusMappingTask.Result; - var paidBy = paidByTask.Result; - var createdBy = createdByTask.Result; - var reviewedBy = reviewedByTask.Result; - var approvedBy = approvedByTask.Result; - var processedBy = processedByTask.Result; var billAttachment = billAttachmentsTask.Result; var response = _mapper.Map(model); - response.Project = _mapper.Map(project); - response.PaidBy = _mapper.Map(paidBy); - response.CreatedBy = _mapper.Map(createdBy); - response.ReviewedBy = _mapper.Map(reviewedBy); - response.ApprovedBy = _mapper.Map(approvedBy); - response.ProcessedBy = _mapper.Map(processedBy); + response.Project = _mapper.Map(model.Project); + response.PaidBy = _mapper.Map(model.PaidBy); + response.CreatedBy = _mapper.Map(model.CreatedBy); + response.ReviewedBy = _mapper.Map(model.ReviewedBy); + response.ApprovedBy = _mapper.Map(model.ApprovedBy); + response.ProcessedBy = _mapper.Map(model.ProcessedBy); if (statusMapping != null) { response.Status = _mapper.Map(statusMapping.Status); @@ -1337,8 +1303,8 @@ namespace Marco.Pms.Services.Service var status = statusTask.Result; response.Status = _mapper.Map(status); } - response.PaymentMode = _mapper.Map(paymentMode); - response.ExpensesType = _mapper.Map(expenseType); + response.PaymentMode = _mapper.Map(model.PaymentMode); + response.ExpensesType = _mapper.Map(model.ExpensesType); if (billAttachment != null) response.Documents = billAttachment.Documents; return response; diff --git a/Marco.Pms.Services/appsettings.Development.json b/Marco.Pms.Services/appsettings.Development.json index e7fdcee..7acc146 100644 --- a/Marco.Pms.Services/appsettings.Development.json +++ b/Marco.Pms.Services/appsettings.Development.json @@ -48,7 +48,7 @@ }, "MongoDB": { "SerilogDatabaseUrl": "mongodb://localhost:27017/DotNetLogs", - "ConnectionString": "mongodb://localhost:27017/MarcoBMS_Caches?socketTimeoutMS=500&serverSelectionTimeoutMS=500&connectTimeoutMS=500", - "ModificationConnectionString": "mongodb://devuser:DevPass123@147.93.98.152:27017/MarcoBMSLocalDev?authSource=admin&eplicaSet=rs01&directConnection=true" + "ConnectionString": "mongodb://devuser:DevPass123@147.93.98.152:27017/MarcoBMSCacheLocalDev?authSource=admin&replicaSet=rs01", + "ModificationConnectionString": "mongodb://devuser:DevPass123@147.93.98.152:27017/MarcoBMSLocalDev?authSource=admin&replicaSet=rs01&directConnection=true" } }