Removed Project ForignKey From ProjectContactMapping Table

This commit is contained in:
ashutosh.nehete 2025-11-21 12:04:35 +05:30
parent c7a73e78fb
commit 64aab7d712
6 changed files with 8978 additions and 37 deletions

File diff suppressed because one or more lines are too long

View File

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

View File

@ -898,8 +898,6 @@ namespace Marco.Pms.DataAccess.Migrations
b.HasIndex("ContactId"); b.HasIndex("ContactId");
b.HasIndex("ProjectId");
b.HasIndex("TenantId"); b.HasIndex("TenantId");
b.ToTable("ContactProjectMappings"); b.ToTable("ContactProjectMappings");
@ -6865,12 +6863,6 @@ namespace Marco.Pms.DataAccess.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.HasOne("Marco.Pms.Model.Projects.Project", "Project")
.WithMany()
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant") b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany() .WithMany()
.HasForeignKey("TenantId") .HasForeignKey("TenantId")
@ -6879,8 +6871,6 @@ namespace Marco.Pms.DataAccess.Migrations
b.Navigation("Contact"); b.Navigation("Contact");
b.Navigation("Project");
b.Navigation("Tenant"); b.Navigation("Tenant");
}); });

View File

@ -1,7 +1,6 @@
using System.ComponentModel.DataAnnotations.Schema; using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Utilities;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations.Schema;
namespace Marco.Pms.Model.Directory namespace Marco.Pms.Model.Directory
{ {
@ -9,9 +8,6 @@ namespace Marco.Pms.Model.Directory
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public Guid ProjectId { get; set; } public Guid ProjectId { get; set; }
[ValidateNever]
[ForeignKey("ProjectId")]
public Project? Project { get; set; }
public Guid ContactId { get; set; } public Guid ContactId { get; set; }
[ValidateNever] [ValidateNever]
[ForeignKey("ContactId")] [ForeignKey("ContactId")]

View File

@ -474,6 +474,12 @@ namespace Marco.Pms.Services.Service
return await _dbContext.ContactsEmails.Where(e => contactIds.Contains(e.ContactId)).ToListAsync(); return await _dbContext.ContactsEmails.Where(e => contactIds.Contains(e.ContactId)).ToListAsync();
}); });
await Task.WhenAll(contactTask, phoneTask, emailTask);
var contacts = contactTask.Result;
var phones = phoneTask.Result;
var emails = emailTask.Result;
var tagTask = Task.Run(async () => var tagTask = Task.Run(async () =>
{ {
await using var _dbContext = await _dbContextFactory.CreateDbContextAsync(); await using var _dbContext = await _dbContextFactory.CreateDbContextAsync();
@ -493,11 +499,8 @@ namespace Marco.Pms.Services.Service
return await _dbContext.ContactBucketMappings.Where(cb => contactIds.Contains(cb.ContactId)).ToListAsync(); return await _dbContext.ContactBucketMappings.Where(cb => contactIds.Contains(cb.ContactId)).ToListAsync();
}); });
await Task.WhenAll(contactTask); await Task.WhenAll(tagTask, contactProjectTask, contactBucketTask);
var contacts = contactTask.Result;
var phones = phoneTask.Result;
var emails = emailTask.Result;
var tags = tagTask.Result; var tags = tagTask.Result;
var contactProjects = contactProjectTask.Result; var contactProjects = contactProjectTask.Result;
var contactBuckets = contactBucketTask.Result; var contactBuckets = contactBucketTask.Result;
@ -587,6 +590,13 @@ namespace Marco.Pms.Services.Service
_logger.LogWarning("Employee with ID {LoggedInEmployeeId} tries to update contact with ID {ContactId} is not found in database", loggedInEmployeeId); _logger.LogWarning("Employee with ID {LoggedInEmployeeId} tries to update contact with ID {ContactId} is not found in database", loggedInEmployeeId);
return ApiResponse<object>.ErrorResponse("Contact not found", "Contact not found", 404); return ApiResponse<object>.ErrorResponse("Contact not found", "Contact not found", 404);
} }
var projectIds = await dbContext.ContactProjectMappings
.AsNoTracking()
.Where(cp => cp.ContactId == contact.Id && cp.TenantId == tenantId)
.Select(cp => cp.ProjectId)
.ToListAsync();
ContactProfileVM contactVM = _mapper.Map<ContactProfileVM>(contact); ContactProfileVM contactVM = _mapper.Map<ContactProfileVM>(contact);
var phonesTask = Task.Run(async () => var phonesTask = Task.Run(async () =>
@ -609,21 +619,26 @@ namespace Marco.Pms.Services.Service
.ToListAsync(); .ToListAsync();
}); });
var contactProjectsTask = Task.Run(async () => var infraProjectTask = Task.Run(async () =>
{ {
await using var taskDbContext = await _dbContextFactory.CreateDbContextAsync(); await using var context = await _dbContextFactory.CreateDbContextAsync();
return await taskDbContext.ContactProjectMappings return await context.Projects.Where(p => projectIds.Contains(p.Id) && p.TenantId == tenantId).Select(p => _mapper.Map<BasicProjectVM>(p)).ToListAsync();
.AsNoTracking()
.Include(cp => cp.Project)
.Where(cp => cp.ContactId == contact.Id && cp.Project != null && cp.Project.TenantId == tenantId)
.Select(cp => new BasicProjectVM
{
Id = cp.Project!.Id,
Name = cp.Project.Name
})
.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(phonesTask, emailsTask, infraProjectTask, serviceProjectTask);
contactVM.ContactPhones = phonesTask.Result;
contactVM.ContactEmails = emailsTask.Result;
var projects = infraProjectTask.Result;
projects.AddRange(serviceProjectTask.Result);
contactVM.Projects = projects;
var contactBucketsTask = Task.Run(async () => var contactBucketsTask = Task.Run(async () =>
{ {
await using var taskDbContext = await _dbContextFactory.CreateDbContextAsync(); await using var taskDbContext = await _dbContextFactory.CreateDbContextAsync();
@ -679,12 +694,10 @@ namespace Marco.Pms.Services.Service
.ToListAsync(); .ToListAsync();
}); });
await Task.WhenAll(phonesTask, emailsTask, contactProjectsTask, contactBucketsTask, contactTagsTask, contactNotesTask); await Task.WhenAll(contactBucketsTask, contactTagsTask, contactNotesTask);
contactVM.ContactPhones = phonesTask.Result;
contactVM.ContactEmails = emailsTask.Result;
contactVM.Tags = contactTagsTask.Result; contactVM.Tags = contactTagsTask.Result;
contactVM.Buckets = contactBucketsTask.Result; contactVM.Buckets = contactBucketsTask.Result;
contactVM.Projects = contactProjectsTask.Result;
contactVM.Notes = contactNotesTask.Result; contactVM.Notes = contactNotesTask.Result;
_logger.LogInfo("Employee ID {EmployeeId} fetched profile of contact {ContactId}", loggedInEmployeeId, contact.Id); _logger.LogInfo("Employee ID {EmployeeId} fetched profile of contact {ContactId}", loggedInEmployeeId, contact.Id);
return ApiResponse<object>.SuccessResponse(contactVM, "Contact profile fetched successfully"); return ApiResponse<object>.SuccessResponse(contactVM, "Contact profile fetched successfully");
@ -3289,11 +3302,18 @@ namespace Marco.Pms.Services.Service
{ {
if (!(dto.ProjectIds?.Any() ?? false)) return new List<ContactProjectMapping>(); if (!(dto.ProjectIds?.Any() ?? false)) return new List<ContactProjectMapping>();
var validProjectIds = await _context.Projects var infraProjectIds = await _context.Projects
.Where(p => dto.ProjectIds.Contains(p.Id) && p.TenantId == tenantId) .Where(p => dto.ProjectIds.Contains(p.Id) && p.TenantId == tenantId)
.Select(p => p.Id) .Select(p => p.Id)
.ToListAsync(); .ToListAsync();
var serviceProjectIds = await _context.ServiceProjects
.Where(sp => dto.ProjectIds.Contains(sp.Id) && sp.IsActive && sp.TenantId == tenantId)
.Select(sp => sp.Id)
.ToListAsync();
var validProjectIds = infraProjectIds.Concat(serviceProjectIds).Distinct().Where(p => p != Guid.Empty).ToList();
var mappings = validProjectIds.Select(projectId => new ContactProjectMapping var mappings = validProjectIds.Select(projectId => new ContactProjectMapping
{ {
ProjectId = projectId, ProjectId = projectId,

View File

@ -442,12 +442,20 @@ namespace Marco.Pms.Services.Service
} }
} }
/// <summary>
/// Retrieves the filter object for expenses.
/// </summary>
/// <param name="loggedInEmployee">The logged-in employee.</param>
/// <param name="tenantId">The tenant ID.</param>
/// <returns>The API response containing the filter object.</returns>
public async Task<ApiResponse<object>> GetFilterObjectAsync(Employee loggedInEmployee, Guid tenantId) public async Task<ApiResponse<object>> GetFilterObjectAsync(Employee loggedInEmployee, Guid tenantId)
{ {
try try
{ {
// Log the start of the operation
_logger.LogInfo("Fetching expenses for tenant: {TenantId}", tenantId); _logger.LogInfo("Fetching expenses for tenant: {TenantId}", tenantId);
// Fetch expenses with related entities
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)
@ -456,10 +464,13 @@ namespace Marco.Pms.Services.Service
.Where(e => e.TenantId == tenantId) .Where(e => e.TenantId == tenantId)
.ToListAsync(); .ToListAsync();
// Log the number of fetched expenses
_logger.LogInfo("Fetched {ExpenseCount} expenses", expenses.Count); _logger.LogInfo("Fetched {ExpenseCount} expenses", expenses.Count);
// Get project IDs from fetched expenses
var projectIds = expenses.Select(e => e.ProjectId).ToList(); var projectIds = expenses.Select(e => e.ProjectId).ToList();
// Fetch projects from infrastructure and service layers in parallel
var infraProjectTask = Task.Run(async () => var infraProjectTask = Task.Run(async () =>
{ {
await using var context = await _dbContextFactory.CreateDbContextAsync(); await using var context = await _dbContextFactory.CreateDbContextAsync();
@ -479,30 +490,42 @@ namespace Marco.Pms.Services.Service
.ToListAsync(); .ToListAsync();
}); });
// Wait for both tasks to complete
await Task.WhenAll(infraProjectTask, serviceProjectTask); await Task.WhenAll(infraProjectTask, serviceProjectTask);
// Combine the results of both tasks
var projects = infraProjectTask.Result; var projects = infraProjectTask.Result;
projects.AddRange(serviceProjectTask.Result); projects.AddRange(serviceProjectTask.Result);
// Log the number of fetched projects
_logger.LogInfo("Fetched {ProjectCount} projects", projects.Count); _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
{ {
Projects = projects, Projects = projects,
// Fetch distinct paid by entities from fetched expenses
PaidBy = expenses.Where(e => e.PaidBy != null).Select(e => new { Id = e.PaidBy!.Id, Name = $"{e.PaidBy.FirstName} {e.PaidBy.LastName}" }).Distinct().ToList(), PaidBy = expenses.Where(e => e.PaidBy != null).Select(e => new { Id = e.PaidBy!.Id, Name = $"{e.PaidBy.FirstName} {e.PaidBy.LastName}" }).Distinct().ToList(),
// Fetch distinct created by entities from fetched expenses
CreatedBy = expenses.Where(e => e.CreatedBy != null).Select(e => new { Id = e.CreatedBy!.Id, Name = $"{e.CreatedBy.FirstName} {e.CreatedBy.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(),
// Fetch distinct status entities from fetched expenses
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(),
// Fetch distinct expense category entities from fetched expenses
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()
}; };
// Log the successful completion of the operation
_logger.LogInfo("Successfully fetched the filter list"); _logger.LogInfo("Successfully fetched the filter list");
// Return the success response
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)
{ {
// Log the exception
_logger.LogError(ex, "Exception occurred while fetching the list filters for expenses"); _logger.LogError(ex, "Exception occurred while fetching the list filters for expenses");
// Return the error response
return ApiResponse<object>.ErrorResponse("Internal Exception Occurred", ExceptionMapper(ex), 500); return ApiResponse<object>.ErrorResponse("Internal Exception Occurred", ExceptionMapper(ex), 500);
} }
} }