Removed the project forign key and able to create expense for both infra and service project
This commit is contained in:
parent
df0e9f7b46
commit
9c95b12a8f
8899
Marco.Pms.DataAccess/Migrations/20251120111120_Removed_Project_ForignKey_From_Expenses_Table.Designer.cs
generated
Normal file
8899
Marco.Pms.DataAccess/Migrations/20251120111120_Removed_Project_ForignKey_From_Expenses_Table.Designer.cs
generated
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1,39 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Removed_Project_ForignKey_From_Expenses_Table : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Expenses_Projects_ProjectId",
|
||||
table: "Expenses");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Expenses_ProjectId",
|
||||
table: "Expenses");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Expenses_ProjectId",
|
||||
table: "Expenses",
|
||||
column: "ProjectId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Expenses_Projects_ProjectId",
|
||||
table: "Expenses",
|
||||
column: "ProjectId",
|
||||
principalTable: "Projects",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2329,8 +2329,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasIndex("ProcessedById");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.HasIndex("ReviewedById");
|
||||
|
||||
b.HasIndex("StatusId");
|
||||
@ -7378,12 +7376,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.WithMany()
|
||||
.HasForeignKey("ProcessedById");
|
||||
|
||||
b.HasOne("Marco.Pms.Model.Projects.Project", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Marco.Pms.Model.Employees.Employee", "ReviewedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("ReviewedById");
|
||||
@ -7416,8 +7408,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.Navigation("ProcessedBy");
|
||||
|
||||
b.Navigation("Project");
|
||||
|
||||
b.Navigation("ReviewedBy");
|
||||
|
||||
b.Navigation("Status");
|
||||
|
||||
12
Marco.Pms.Model/Dtos/ServiceProject/TalkingPointDto.cs
Normal file
12
Marco.Pms.Model/Dtos/ServiceProject/TalkingPointDto.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Marco.Pms.Model.Utilities;
|
||||
|
||||
namespace Marco.Pms.Model.Dtos.ServiceProject
|
||||
{
|
||||
public class TalkingPointDto
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
public required Guid ServiceProjectId { get; set; }
|
||||
public required string Comment { get; set; }
|
||||
public List<FileUploadModel>? Attachments { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
using Marco.Pms.Model.Employees;
|
||||
using Marco.Pms.Model.Expenses.Masters;
|
||||
using Marco.Pms.Model.Master;
|
||||
using Marco.Pms.Model.Projects;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
@ -14,10 +13,6 @@ namespace Marco.Pms.Model.Expenses
|
||||
public string UIDPrefix { get; set; } = default!;
|
||||
public int UIDPostfix { get; set; }
|
||||
public Guid ProjectId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("ProjectId")]
|
||||
public Project? Project { get; set; }
|
||||
public Guid ExpensesTypeId { get; set; }
|
||||
|
||||
//[ValidateNever]
|
||||
|
||||
31
Marco.Pms.Model/ServiceProject/TalkingPoint.cs
Normal file
31
Marco.Pms.Model/ServiceProject/TalkingPoint.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using Marco.Pms.Model.Employees;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Marco.Pms.Model.ServiceProject
|
||||
{
|
||||
public class TalkingPoint : TenantRelation
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid ServiceProjectId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("ServiceProjectId")]
|
||||
public ServiceProject? ServiceProject { get; set; }
|
||||
public string Comment { get; set; } = string.Empty;
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public Guid CreatedById { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("CreatedById")]
|
||||
public Employee? CreatedBy { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public Guid? UpdatedById { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("UpdatedById")]
|
||||
public Employee? UpdatedBy { get; set; }
|
||||
}
|
||||
}
|
||||
22
Marco.Pms.Model/ServiceProject/TalkingPointAttachment.cs
Normal file
22
Marco.Pms.Model/ServiceProject/TalkingPointAttachment.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using Marco.Pms.Model.DocumentManager;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Marco.Pms.Model.ServiceProject
|
||||
{
|
||||
public class TalkingPointAttachment : TenantRelation
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid DocumentId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("DocumentId")]
|
||||
public Document? Document { get; set; }
|
||||
public Guid? TalkingPointId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("TalkingPointId")]
|
||||
public TalkingPoint? TalkingPoint { get; set; }
|
||||
}
|
||||
}
|
||||
@ -10,7 +10,7 @@ namespace Marco.Pms.Model.ViewModels.Expenses
|
||||
public class ExpenseDetailsVM
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public ProjectInfoVM? Project { get; set; }
|
||||
public BasicProjectVM? Project { get; set; }
|
||||
public ExpenseCategoryMasterVM? ExpenseCategory { get; set; }
|
||||
public PaymentModeMatserVM? PaymentMode { get; set; }
|
||||
public BasicEmployeeVM? PaidBy { get; set; }
|
||||
|
||||
@ -9,7 +9,7 @@ namespace Marco.Pms.Model.ViewModels.Expanses
|
||||
public class ExpenseList
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public ProjectInfoVM? Project { get; set; }
|
||||
public BasicProjectVM? Project { get; set; }
|
||||
public ExpenseCategoryMasterVM? ExpenseCategory { get; set; }
|
||||
public PaymentModeMatserVM? PaymentMode { get; set; }
|
||||
public BasicEmployeeVM? PaidBy { get; set; }
|
||||
|
||||
18
Marco.Pms.Model/ViewModels/ServiceProject/TalkingPointVM.cs
Normal file
18
Marco.Pms.Model/ViewModels/ServiceProject/TalkingPointVM.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using Marco.Pms.Model.ViewModels.Activities;
|
||||
using Marco.Pms.Model.ViewModels.DocumentManager;
|
||||
|
||||
namespace Marco.Pms.Model.ViewModels.ServiceProject
|
||||
{
|
||||
public class TalkingPointVM
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public BasicServiceProjectVM? ServiceProject { get; set; }
|
||||
public string? Comment { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public BasicEmployeeVM? CreatedBy { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public BasicEmployeeVM? UpdatedBy { get; set; }
|
||||
public List<DocumentVM>? Attachments { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1162,35 +1162,23 @@ namespace Marco.Pms.Services.Helpers
|
||||
var processedByIds = model.Select(m => m.ProcessedById).ToList();
|
||||
var paidByIds = model.Select(m => m.PaidById).ToList();
|
||||
|
||||
var projectTask = Task.Run(async () =>
|
||||
var infraProjectTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Projects.AsNoTracking().Where(p => projectIds.Contains(p.Id) && p.TenantId == tenantId).ToListAsync();
|
||||
return await dbContext.Projects
|
||||
.AsNoTracking()
|
||||
.Where(p => projectIds.Contains(p.Id) && p.TenantId == tenantId)
|
||||
.Select(p => _mapper.Map<ProjectBasicMongoDB>(p))
|
||||
.ToListAsync();
|
||||
});
|
||||
var paidByTask = Task.Run(async () =>
|
||||
var serviceProjectTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => paidByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
var createdByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => createdByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
var reviewedByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => reviewedByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
var approvedByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => approvedByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
var processedByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => processedByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
return await dbContext.ServiceProjects
|
||||
.AsNoTracking()
|
||||
.Where(sp => projectIds.Contains(sp.Id) && sp.TenantId == tenantId)
|
||||
.Select(sp => _mapper.Map<ProjectBasicMongoDB>(sp))
|
||||
.ToListAsync();
|
||||
});
|
||||
var expenseCategoryTask = Task.Run(async () =>
|
||||
{
|
||||
@ -1202,6 +1190,15 @@ namespace Marco.Pms.Services.Helpers
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.PaymentModeMatser.AsNoTracking().Where(pm => paymentModeIds.Contains(pm.Id)).ToListAsync();
|
||||
});
|
||||
|
||||
await Task.WhenAll(infraProjectTask, serviceProjectTask, expenseCategoryTask, paymentModeTask);
|
||||
|
||||
var projects = infraProjectTask.Result;
|
||||
projects.AddRange(serviceProjectTask.Result);
|
||||
|
||||
var expenseCategories = expenseCategoryTask.Result;
|
||||
var paymentModes = paymentModeTask.Result;
|
||||
|
||||
var statusMappingTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
@ -1218,6 +1215,43 @@ namespace Marco.Pms.Services.Helpers
|
||||
NextStatus = g.Select(s => s.NextStatus).OrderBy(s => s!.Name).ToList()
|
||||
}).ToListAsync();
|
||||
});
|
||||
var paidByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => paidByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
var createdByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => createdByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
|
||||
await Task.WhenAll(statusMappingTask, paidByTask, createdByTask);
|
||||
var statusMappings = statusMappingTask.Result;
|
||||
var paidBys = paidByTask.Result;
|
||||
var createdBys = createdByTask.Result;
|
||||
|
||||
var reviewedByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => reviewedByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
var approvedByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => approvedByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
var processedByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => processedByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
|
||||
await Task.WhenAll(reviewedByTask, approvedByTask, processedByTask);
|
||||
var reviewedBys = reviewedByTask.Result;
|
||||
var approvedBys = approvedByTask.Result;
|
||||
var processedBy = processedByTask.Result;
|
||||
|
||||
var statusTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
@ -1249,26 +1283,17 @@ namespace Marco.Pms.Services.Helpers
|
||||
.ToListAsync();
|
||||
});
|
||||
|
||||
// Await all prerequisite checks at once.
|
||||
await Task.WhenAll(projectTask, expenseCategoryTask, paymentModeTask, statusMappingTask, paidByTask, createdByTask, reviewedByTask, approvedByTask,
|
||||
processedByTask, statusTask, billAttachmentsTask);
|
||||
|
||||
var projects = projectTask.Result;
|
||||
var expenseCategories = expenseCategoryTask.Result;
|
||||
var paymentModes = paymentModeTask.Result;
|
||||
var statusMappings = statusMappingTask.Result;
|
||||
var paidBys = paidByTask.Result;
|
||||
var createdBys = createdByTask.Result;
|
||||
var reviewedBys = reviewedByTask.Result;
|
||||
var approvedBys = approvedByTask.Result;
|
||||
var processedBy = processedByTask.Result;
|
||||
|
||||
// Await all prerequisite checks at once.
|
||||
await Task.WhenAll(statusTask, billAttachmentsTask);
|
||||
var billAttachments = billAttachmentsTask.Result;
|
||||
|
||||
expenseList = model.Select(m =>
|
||||
{
|
||||
var response = _mapper.Map<ExpenseDetailsMongoDB>(m);
|
||||
|
||||
response.Project = projects.Where(p => p.Id == m.ProjectId).Select(p => _mapper.Map<ProjectBasicMongoDB>(p)).FirstOrDefault() ?? new ProjectBasicMongoDB();
|
||||
response.Project = projects.Where(p => Guid.Parse(p.Id) == m.ProjectId).FirstOrDefault() ?? new ProjectBasicMongoDB();
|
||||
response.PaidBy = paidBys.Where(p => p.Id == m.PaidById).Select(p => _mapper.Map<BasicEmployeeMongoDB>(p)).FirstOrDefault() ?? new BasicEmployeeMongoDB();
|
||||
response.CreatedBy = createdBys.Where(e => e.Id == m.CreatedById).Select(e => _mapper.Map<BasicEmployeeMongoDB>(e)).FirstOrDefault() ?? new BasicEmployeeMongoDB();
|
||||
response.ReviewedBy = reviewedBys.Where(e => e.Id == m.CreatedById).Select(e => _mapper.Map<BasicEmployeeMongoDB>(e)).FirstOrDefault();
|
||||
@ -1292,35 +1317,23 @@ namespace Marco.Pms.Services.Helpers
|
||||
}
|
||||
private async Task<ExpenseDetailsMongoDB> GetAllExpnesRelatedTablesForSingle(Expenses model, Guid tenantId)
|
||||
{
|
||||
var projectTask = Task.Run(async () =>
|
||||
var infraProjectTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Projects.AsNoTracking().FirstOrDefaultAsync(p => p.Id == model.ProjectId && p.TenantId == tenantId);
|
||||
return await dbContext.Projects
|
||||
.AsNoTracking()
|
||||
.Where(p => p.Id == model.ProjectId && p.TenantId == tenantId)
|
||||
.Select(p => _mapper.Map<ProjectBasicMongoDB>(p))
|
||||
.FirstOrDefaultAsync();
|
||||
});
|
||||
var paidByTask = Task.Run(async () =>
|
||||
var serviceProjectTask = 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);
|
||||
return await dbContext.ServiceProjects
|
||||
.AsNoTracking()
|
||||
.Where(sp => sp.Id == model.ProjectId && sp.TenantId == tenantId)
|
||||
.Select(sp => _mapper.Map<ProjectBasicMongoDB>(sp))
|
||||
.FirstOrDefaultAsync();
|
||||
});
|
||||
var expenseCategoryTask = Task.Run(async () =>
|
||||
{
|
||||
@ -1332,6 +1345,12 @@ namespace Marco.Pms.Services.Helpers
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.PaymentModeMatser.AsNoTracking().FirstOrDefaultAsync(pm => pm.Id == model.PaymentModeId);
|
||||
});
|
||||
|
||||
await Task.WhenAll(infraProjectTask, serviceProjectTask, expenseCategoryTask, paymentModeTask);
|
||||
var project = infraProjectTask.Result ?? serviceProjectTask.Result ?? new ProjectBasicMongoDB();
|
||||
var expenseCategory = expenseCategoryTask.Result;
|
||||
var paymentMode = paymentModeTask.Result;
|
||||
|
||||
var statusMappingTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
@ -1348,6 +1367,43 @@ namespace Marco.Pms.Services.Helpers
|
||||
NextStatus = g.Select(s => s.NextStatus).OrderBy(s => s!.Name).ToList()
|
||||
}).FirstOrDefaultAsync();
|
||||
});
|
||||
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);
|
||||
});
|
||||
|
||||
await Task.WhenAll(statusMappingTask, paidByTask, createdByTask);
|
||||
var statusMapping = statusMappingTask.Result;
|
||||
var paidBy = paidByTask.Result;
|
||||
var createdBy = createdByTask.Result;
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
await Task.WhenAll(reviewedByTask, approvedByTask, processedByTask);
|
||||
var reviewedBy = reviewedByTask.Result;
|
||||
var approvedBy = approvedByTask.Result;
|
||||
var processedBy = processedByTask.Result;
|
||||
|
||||
var statusTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
@ -1378,25 +1434,13 @@ namespace Marco.Pms.Services.Helpers
|
||||
.FirstOrDefaultAsync();
|
||||
});
|
||||
|
||||
// Await all prerequisite checks at once.
|
||||
await Task.WhenAll(projectTask, expenseCategoryTask, paymentModeTask, statusMappingTask, paidByTask, createdByTask, reviewedByTask, approvedByTask,
|
||||
processedByTask, statusTask, billAttachmentsTask);
|
||||
|
||||
var project = projectTask.Result;
|
||||
var expenseCategory = expenseCategoryTask.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;
|
||||
await Task.WhenAll(statusTask, billAttachmentsTask);
|
||||
var billAttachment = billAttachmentsTask.Result;
|
||||
|
||||
|
||||
var response = _mapper.Map<ExpenseDetailsMongoDB>(model);
|
||||
|
||||
response.Project = _mapper.Map<ProjectBasicMongoDB>(project);
|
||||
response.Project = project;
|
||||
response.PaidBy = _mapper.Map<BasicEmployeeMongoDB>(paidBy);
|
||||
response.CreatedBy = _mapper.Map<BasicEmployeeMongoDB>(createdBy);
|
||||
response.ReviewedBy = _mapper.Map<BasicEmployeeMongoDB>(reviewedBy);
|
||||
|
||||
@ -162,6 +162,10 @@ namespace Marco.Pms.Services.MappingProfiles
|
||||
dest => dest.ProjectStatusId,
|
||||
opt => opt.MapFrom(src => Guid.Empty)
|
||||
);
|
||||
CreateMap<ProjectBasicMongoDB, BasicProjectVM>()
|
||||
.ForMember(
|
||||
dest => dest.Id,
|
||||
opt => opt.MapFrom(src => new Guid(src.Id)));
|
||||
|
||||
CreateMap<ProjectMongoDB, Project>()
|
||||
.ForMember(
|
||||
@ -197,6 +201,7 @@ namespace Marco.Pms.Services.MappingProfiles
|
||||
CreateMap<ServiceProject, BasicProjectVM>();
|
||||
CreateMap<ServiceProject, ServiceProjectVM>();
|
||||
CreateMap<ServiceProject, BasicServiceProjectVM>();
|
||||
CreateMap<ServiceProject, ProjectBasicMongoDB>();
|
||||
CreateMap<ServiceProject, ServiceProjectDetailsVM>();
|
||||
CreateMap<ServiceProjectAllocation, ServiceProjectAllocationVM>();
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
using AutoMapper;
|
||||
using Marco.Pms.DataAccess.Data;
|
||||
using Marco.Pms.Helpers.Utility;
|
||||
using Marco.Pms.Model.DocumentManager;
|
||||
using Marco.Pms.Model.Dtos.Expenses;
|
||||
using Marco.Pms.Model.Employees;
|
||||
using Marco.Pms.Model.Entitlements;
|
||||
@ -25,8 +24,10 @@ using Marco.Pms.Model.ViewModels.Projects;
|
||||
using Marco.Pms.Services.Helpers;
|
||||
using Marco.Pms.Services.Service.ServiceInterfaces;
|
||||
using MarcoBMS.Services.Service;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Text.Json;
|
||||
using Document = Marco.Pms.Model.DocumentManager.Document;
|
||||
|
||||
namespace Marco.Pms.Services.Service
|
||||
{
|
||||
@ -152,7 +153,6 @@ namespace Marco.Pms.Services.Service
|
||||
.Include(e => e.ApprovedBy)
|
||||
.Include(e => e.ReviewedBy)
|
||||
.Include(e => e.PaymentMode)
|
||||
.Include(e => e.Project)
|
||||
.Include(e => e.PaymentMode)
|
||||
.Include(e => e.ExpenseCategory)
|
||||
.Include(e => e.PaymentRequest)
|
||||
@ -245,6 +245,25 @@ namespace Marco.Pms.Services.Service
|
||||
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 =>
|
||||
{
|
||||
@ -252,6 +271,7 @@ namespace Marco.Pms.Services.Service
|
||||
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);
|
||||
@ -327,7 +347,6 @@ namespace Marco.Pms.Services.Service
|
||||
.Include(e => e.ApprovedBy)
|
||||
.Include(e => e.ReviewedBy)
|
||||
.Include(e => e.PaymentMode)
|
||||
.Include(e => e.Project)
|
||||
.Include(e => e.PaymentMode)
|
||||
.Include(e => e.ExpenseCategory)
|
||||
.Include(e => e.Status)
|
||||
@ -439,17 +458,42 @@ namespace Marco.Pms.Services.Service
|
||||
{
|
||||
var expenses = await _context.Expenses
|
||||
.Include(e => e.PaidBy)
|
||||
.Include(e => e.Project)
|
||||
.Include(e => e.CreatedBy)
|
||||
.Include(e => e.Status)
|
||||
.Include(e => e.ExpenseCategory)
|
||||
.Where(e => e.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
|
||||
var projectIds = expenses.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 => new { Id = p.Id, Name = p.Name })
|
||||
.Distinct()
|
||||
.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 => new { Id = sp.Id, Name = sp.Name }).Distinct()
|
||||
.ToListAsync();
|
||||
});
|
||||
|
||||
await Task.WhenAll(infraProjectTask, serviceProjectTask);
|
||||
|
||||
var projects = infraProjectTask.Result;
|
||||
projects.AddRange(serviceProjectTask.Result);
|
||||
|
||||
// Construct the final object from the results of the completed tasks.
|
||||
var response = new
|
||||
{
|
||||
Projects = expenses.Where(e => e.Project != null).Select(e => new { Id = e.Project!.Id, Name = e.Project.Name }).Distinct().ToList(),
|
||||
Projects = projects,
|
||||
PaidBy = expenses.Where(e => e.PaidBy != null).Select(e => new { Id = e.PaidBy!.Id, Name = $"{e.PaidBy.FirstName} {e.PaidBy.LastName}" }).Distinct().ToList(),
|
||||
CreatedBy = expenses.Where(e => e.CreatedBy != null).Select(e => new { Id = e.CreatedBy!.Id, Name = $"{e.CreatedBy.FirstName} {e.CreatedBy.LastName}" }).Distinct().ToList(),
|
||||
Status = expenses.Where(e => e.Status != null).Select(e => new { Id = e.Status!.Id, Name = e.Status.Name }).Distinct().ToList(),
|
||||
@ -495,10 +539,23 @@ namespace Marco.Pms.Services.Service
|
||||
|
||||
// VALIDATION CHECKS: Use IDbContextFactory for thread-safe, parallel database queries.
|
||||
// Each task gets its own DbContext instance.
|
||||
var projectTask = Task.Run(async () =>
|
||||
var infraProjectTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Projects.AsNoTracking().FirstOrDefaultAsync(p => p.Id == dto.ProjectId);
|
||||
return await dbContext.Projects
|
||||
.AsNoTracking()
|
||||
.Where(p => p.Id == dto.ProjectId && p.TenantId == tenantId)
|
||||
.Select(p => _mapper.Map<BasicProjectVM>(p))
|
||||
.FirstOrDefaultAsync();
|
||||
});
|
||||
var serviceProjectTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.ServiceProjects
|
||||
.AsNoTracking()
|
||||
.Where(sp => sp.Id == dto.ProjectId && sp.TenantId == tenantId)
|
||||
.Select(sp => _mapper.Map<BasicProjectVM>(sp))
|
||||
.FirstOrDefaultAsync();
|
||||
});
|
||||
var paidByTask = Task.Run(async () =>
|
||||
{
|
||||
@ -534,7 +591,7 @@ namespace Marco.Pms.Services.Service
|
||||
|
||||
|
||||
// Await all prerequisite checks at once.
|
||||
await Task.WhenAll(hasUploadPermissionTask, projectTask, expenseCategoriesTask, paymentModeTask, statusMappingTask, paidByTask);
|
||||
await Task.WhenAll(hasUploadPermissionTask, infraProjectTask, serviceProjectTask);
|
||||
|
||||
// 2. Aggregate and Check Results
|
||||
if (!await hasUploadPermissionTask)
|
||||
@ -544,7 +601,11 @@ namespace Marco.Pms.Services.Service
|
||||
}
|
||||
|
||||
var validationErrors = new List<string>();
|
||||
var project = await projectTask;
|
||||
var infraProject = infraProjectTask.Result;
|
||||
var serviceProject = serviceProjectTask.Result;
|
||||
var project = infraProject ?? serviceProject;
|
||||
|
||||
await Task.WhenAll(expenseCategoriesTask, paymentModeTask, statusMappingTask, paidByTask);
|
||||
var expenseCategory = await expenseCategoriesTask;
|
||||
var paymentMode = await paymentModeTask;
|
||||
var statusMapping = await statusMappingTask;
|
||||
@ -590,7 +651,8 @@ namespace Marco.Pms.Services.Service
|
||||
// 4. Process Attachments
|
||||
if (dto.BillAttachments?.Any() ?? false)
|
||||
{
|
||||
await ProcessAndUploadAttachmentsAsync(dto.BillAttachments, expense, loggedInEmployee.Id, tenantId);
|
||||
bool isServiceProject = serviceProject != null;
|
||||
await ProcessAndUploadAttachmentsAsync(dto.BillAttachments, expense, isServiceProject, loggedInEmployee.Id, tenantId);
|
||||
}
|
||||
|
||||
var expenseLog = new ExpenseLog
|
||||
@ -616,7 +678,7 @@ namespace Marco.Pms.Services.Service
|
||||
var response = _mapper.Map<ExpenseList>(expense);
|
||||
response.ExpenseUId = $"{expense.UIDPrefix}/{expense.UIDPostfix:D5}";
|
||||
response.PaidBy = _mapper.Map<BasicEmployeeVM>(paidBy);
|
||||
response.Project = _mapper.Map<ProjectInfoVM>(project);
|
||||
response.Project = project;
|
||||
response.Status = _mapper.Map<ExpensesStatusMasterVM>(statusMapping!.Status);
|
||||
response.NextStatus = _mapper.Map<List<ExpensesStatusMasterVM>>(statusMapping.NextStatus);
|
||||
response.PaymentMode = _mapper.Map<PaymentModeMatserVM>(paymentMode);
|
||||
@ -658,7 +720,6 @@ namespace Marco.Pms.Services.Service
|
||||
// 1. Fetch Existing Expense with Related Entities (Single Query)
|
||||
var expense = await _context.Expenses
|
||||
.Include(e => e.ExpenseCategory)
|
||||
.Include(e => e.Project)
|
||||
.Include(e => e.PaidBy).ThenInclude(e => e!.JobRole)
|
||||
.Include(e => e.PaymentMode)
|
||||
.Include(e => e.Status)
|
||||
@ -934,6 +995,29 @@ namespace Marco.Pms.Services.Service
|
||||
if (nextPossibleStatuses is { Count: > 0 })
|
||||
responseDto.NextStatus = _mapper.Map<List<ExpensesStatusMasterVM>>(nextPossibleStatuses);
|
||||
|
||||
var infraProjectTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Projects
|
||||
.AsNoTracking()
|
||||
.Where(p => p.Id == expense.ProjectId && p.TenantId == tenantId)
|
||||
.Select(p => _mapper.Map<BasicProjectVM>(p))
|
||||
.FirstOrDefaultAsync();
|
||||
});
|
||||
var serviceProjectTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.ServiceProjects
|
||||
.AsNoTracking()
|
||||
.Where(sp => sp.Id == expense.ProjectId && sp.TenantId == tenantId)
|
||||
.Select(sp => _mapper.Map<BasicProjectVM>(sp))
|
||||
.FirstOrDefaultAsync();
|
||||
});
|
||||
|
||||
await Task.WhenAll(infraProjectTask, serviceProjectTask);
|
||||
|
||||
responseDto.Project = infraProjectTask.Result ?? serviceProjectTask.Result;
|
||||
|
||||
return ApiResponse<object>.SuccessResponse(responseDto);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -969,7 +1053,6 @@ namespace Marco.Pms.Services.Service
|
||||
|
||||
var existingExpense = await _context.Expenses
|
||||
.Include(e => e.ExpenseCategory)
|
||||
.Include(e => e.Project)
|
||||
.Include(e => e.PaidBy)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.Include(e => e.PaymentMode)
|
||||
@ -1017,12 +1100,37 @@ namespace Marco.Pms.Services.Service
|
||||
return ApiResponse<object>.ErrorResponse("Conflict occurred.", "This project has been modified by someone else. Please refresh and try again.", 409);
|
||||
}
|
||||
|
||||
var infraProjectTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Projects
|
||||
.AsNoTracking()
|
||||
.Where(p => p.Id == existingExpense.ProjectId && p.TenantId == tenantId)
|
||||
.Select(p => _mapper.Map<BasicProjectVM>(p))
|
||||
.FirstOrDefaultAsync();
|
||||
});
|
||||
var serviceProjectTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.ServiceProjects
|
||||
.AsNoTracking()
|
||||
.Where(sp => sp.Id == existingExpense.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 (model.BillAttachments?.Any() ?? false)
|
||||
{
|
||||
var newBillAttachments = model.BillAttachments.Where(ba => ba.DocumentId == null && ba.IsActive).ToList();
|
||||
if (newBillAttachments.Any())
|
||||
{
|
||||
await ProcessAndUploadAttachmentsAsync(newBillAttachments, existingExpense, loggedInEmployee.Id, tenantId);
|
||||
bool isServiceProject = serviceProject != null;
|
||||
await ProcessAndUploadAttachmentsAsync(newBillAttachments, existingExpense, isServiceProject, loggedInEmployee.Id, tenantId);
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
@ -1102,6 +1210,9 @@ namespace Marco.Pms.Services.Service
|
||||
response.NextStatus = _mapper.Map<List<ExpensesStatusMasterVM>>(nextPossibleStatuses);
|
||||
}
|
||||
|
||||
|
||||
response.Project = serviceProject ?? infraProject;
|
||||
|
||||
return ApiResponse<object>.SuccessResponse(response, "Expense Updated Successfully", 200);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -1883,9 +1994,31 @@ namespace Marco.Pms.Services.Service
|
||||
.ToListAsync();
|
||||
});
|
||||
|
||||
await Task.WhenAll(statusTransitionTask, targetStatusPermissionsTask);
|
||||
var statusTransition = await statusTransitionTask;
|
||||
var requiredPermissions = await targetStatusPermissionsTask;
|
||||
var infraProjectTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Projects
|
||||
.AsNoTracking()
|
||||
.Where(p => p.Id == paymentRequest.ProjectId && p.TenantId == tenantId)
|
||||
.Select(p => _mapper.Map<BasicProjectVM>(p))
|
||||
.FirstOrDefaultAsync();
|
||||
});
|
||||
var serviceProjectTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.ServiceProjects
|
||||
.AsNoTracking()
|
||||
.Where(sp => sp.Id == paymentRequest.ProjectId && sp.TenantId == tenantId)
|
||||
.Select(sp => _mapper.Map<BasicProjectVM>(sp))
|
||||
.FirstOrDefaultAsync();
|
||||
});
|
||||
|
||||
await Task.WhenAll(infraProjectTask, serviceProjectTask, statusTransitionTask, targetStatusPermissionsTask);
|
||||
|
||||
var statusTransition = statusTransitionTask.Result;
|
||||
var requiredPermissions = targetStatusPermissionsTask.Result;
|
||||
var infraProject = infraProjectTask.Result;
|
||||
var serviceProject = serviceProjectTask.Result;
|
||||
|
||||
// 3. Validate Transition and Required Fields
|
||||
if (statusTransition == null)
|
||||
@ -2055,7 +2188,10 @@ namespace Marco.Pms.Services.Service
|
||||
GSTNumber = model.GSTNumber,
|
||||
BillAttachments = model.BillAttachments
|
||||
};
|
||||
var response = await ChangeToExpanseFromPaymentRequestAsync(expenseConversion, paymentRequest, loggedInEmployee, tenantId);
|
||||
|
||||
bool isServiceProject = serviceProject != null;
|
||||
|
||||
var response = await ChangeToExpanseFromPaymentRequestAsync(expenseConversion, paymentRequest, isServiceProject, loggedInEmployee, tenantId);
|
||||
if (!response.Success)
|
||||
{
|
||||
return response;
|
||||
@ -2122,7 +2258,8 @@ namespace Marco.Pms.Services.Service
|
||||
return ApiResponse<object>.SuccessResponse(responseDto, "Status updated, but audit logging or cache update failed.");
|
||||
}
|
||||
}
|
||||
public async Task<ApiResponse<object>> ChangeToExpanseFromPaymentRequestAsync(ExpenseConversionDto model, PaymentRequest paymentRequest, Employee loggedInEmployee, Guid tenantId)
|
||||
public async Task<ApiResponse<object>> ChangeToExpanseFromPaymentRequestAsync(ExpenseConversionDto model, PaymentRequest paymentRequest, bool isServiceProject,
|
||||
Employee loggedInEmployee, Guid tenantId)
|
||||
{
|
||||
_logger.LogInfo("Start ChangeToExpanseFromPaymentRequestAsync called by EmployeeId: {EmployeeId} for TenantId: {TenantId} PaymentRequestId: {PaymentRequestId}",
|
||||
loggedInEmployee.Id, tenantId, model.PaymentRequestId);
|
||||
@ -2259,7 +2396,7 @@ namespace Marco.Pms.Services.Service
|
||||
// Process and upload bill attachments if present
|
||||
if (hasAttachments)
|
||||
{
|
||||
await ProcessAndUploadAttachmentsAsync(model.BillAttachments!, expense, loggedInEmployee.Id, tenantId);
|
||||
await ProcessAndUploadAttachmentsAsync(model.BillAttachments!, expense, isServiceProject, loggedInEmployee.Id, tenantId);
|
||||
}
|
||||
|
||||
// Mark the payment request as converted to expense to prevent duplicates
|
||||
@ -3490,19 +3627,31 @@ namespace Marco.Pms.Services.Service
|
||||
.FirstOrDefaultAsync();
|
||||
});
|
||||
|
||||
var infraProjectTask = Task.Run(async () =>
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await context.Projects.Where(p => p.Id == model.ProjectId && p.TenantId == tenantId).Select(p => _mapper.Map<ProjectBasicMongoDB>(p)).FirstOrDefaultAsync();
|
||||
});
|
||||
|
||||
var serviceProjectTask = Task.Run(async () =>
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await context.ServiceProjects.Where(sp => sp.Id == model.ProjectId && sp.TenantId == tenantId).Select(sp => _mapper.Map<ProjectBasicMongoDB>(sp)).FirstOrDefaultAsync();
|
||||
});
|
||||
|
||||
// Await all prerequisite checks at once.
|
||||
await Task.WhenAll(statusTask, billAttachmentsTask);
|
||||
await Task.WhenAll(statusTask, billAttachmentsTask, infraProjectTask, serviceProjectTask);
|
||||
|
||||
var statusMapping = statusMappingTask.Result;
|
||||
var billAttachment = billAttachmentsTask.Result;
|
||||
|
||||
var project = infraProjectTask.Result ?? serviceProjectTask.Result ?? new ProjectBasicMongoDB();
|
||||
|
||||
var response = _mapper.Map<ExpenseDetailsMongoDB>(model);
|
||||
|
||||
response.ExpenseUId = $"{model.UIDPrefix}/{model.UIDPostfix:D5}";
|
||||
if (model.PaymentRequest != null)
|
||||
response.PaymentRequestUID = $"{model.PaymentRequest.UIDPrefix}/{model.PaymentRequest.UIDPostfix:D5}";
|
||||
response.Project = _mapper.Map<ProjectBasicMongoDB>(model.Project);
|
||||
response.Project = project;
|
||||
response.PaidBy = _mapper.Map<BasicEmployeeMongoDB>(model.PaidBy);
|
||||
response.CreatedBy = _mapper.Map<BasicEmployeeMongoDB>(model.CreatedBy);
|
||||
response.ReviewedBy = _mapper.Map<BasicEmployeeMongoDB>(model.ReviewedBy);
|
||||
@ -3653,7 +3802,7 @@ namespace Marco.Pms.Services.Service
|
||||
/// <summary>
|
||||
/// Processes and uploads attachments concurrently, then adds the resulting entities to the main DbContext.
|
||||
/// </summary>
|
||||
private async Task ProcessAndUploadAttachmentsAsync(IEnumerable<FileUploadModel> attachments, Expenses expense, Guid employeeId, Guid tenantId)
|
||||
private async Task ProcessAndUploadAttachmentsAsync(IEnumerable<FileUploadModel> attachments, Expenses expense, bool isServiceProject, Guid employeeId, Guid tenantId)
|
||||
{
|
||||
// Pre-validate all attachments to fail fast before any uploads.
|
||||
foreach (var attachment in attachments)
|
||||
@ -3668,7 +3817,7 @@ namespace Marco.Pms.Services.Service
|
||||
|
||||
// Create a list of tasks to be executed concurrently.
|
||||
var processingTasks = attachments.Select(attachment =>
|
||||
ProcessSingleExpenseAttachmentAsync(attachment, expense, employeeId, tenantId, batchId)
|
||||
ProcessSingleExpenseAttachmentAsync(attachment, expense, employeeId, isServiceProject, tenantId, batchId)
|
||||
).ToList();
|
||||
|
||||
var results = await Task.WhenAll(processingTasks);
|
||||
@ -3686,12 +3835,17 @@ namespace Marco.Pms.Services.Service
|
||||
/// Handles the logic for a single attachment: upload to S3 and create corresponding entities.
|
||||
/// </summary>
|
||||
private async Task<(Document document, BillAttachments billAttachment)> ProcessSingleExpenseAttachmentAsync(
|
||||
FileUploadModel attachment, Expenses expense, Guid employeeId, Guid tenantId, Guid batchId)
|
||||
FileUploadModel attachment, Expenses expense, Guid employeeId, bool isServiceProject, Guid tenantId, Guid batchId)
|
||||
{
|
||||
var base64Data = attachment.Base64Data!.Contains(',') ? attachment.Base64Data[(attachment.Base64Data.IndexOf(",") + 1)..] : attachment.Base64Data;
|
||||
var fileType = _s3Service.GetContentTypeFromBase64(base64Data);
|
||||
var fileName = _s3Service.GenerateFileName(fileType, expense.Id, "Expense");
|
||||
var objectKey = $"tenant-{tenantId}/project-{expense.ProjectId}/Expenses/{fileName}";
|
||||
var objectKey = $"tenant-{tenantId}/Project/{expense.ProjectId}/Expenses/{fileName}";
|
||||
|
||||
if (isServiceProject)
|
||||
{
|
||||
objectKey = $"tenant-{tenantId}/ServiceProject/{expense.ProjectId}/Expenses/{fileName}";
|
||||
}
|
||||
|
||||
// Await the I/O-bound upload operation directly.
|
||||
await _s3Service.UploadFileAsync(base64Data, fileType, objectKey);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user