Organization_Management #142

Merged
ashutosh.nehete merged 92 commits from Organization_Management into main 2025-09-30 09:05:14 +00:00
6 changed files with 222 additions and 128 deletions
Showing only changes of commit 18d590ccbe - Show all commits

View File

@ -74,14 +74,20 @@ namespace Marco.Pms.Helpers
Id = promotor.Id.ToString(), Id = promotor.Id.ToString(),
Name = promotor.Name, Name = promotor.Name,
ContactPerson = promotor.ContactPerson, ContactPerson = promotor.ContactPerson,
Email = promotor.Email Email = promotor.Email,
Address = promotor.Address,
ContactNumber = promotor.ContactNumber,
SPRID = promotor.SPRID
}), }),
Builders<ProjectMongoDB>.Update.Set(r => r.PMC, new OrganizationMongoDB Builders<ProjectMongoDB>.Update.Set(r => r.PMC, new OrganizationMongoDB
{ {
Id = pmc.Id.ToString(), Id = pmc.Id.ToString(),
Name = pmc.Name, Name = pmc.Name,
ContactPerson = pmc.ContactPerson, ContactPerson = pmc.ContactPerson,
Email = pmc.Email Email = pmc.Email,
Address = promotor.Address,
ContactNumber = promotor.ContactNumber,
SPRID = promotor.SPRID
}), }),
Builders<ProjectMongoDB>.Update.Set(r => r.StartDate, project.StartDate), Builders<ProjectMongoDB>.Update.Set(r => r.StartDate, project.StartDate),
Builders<ProjectMongoDB>.Update.Set(r => r.EndDate, project.EndDate), Builders<ProjectMongoDB>.Update.Set(r => r.EndDate, project.EndDate),

View File

@ -3,8 +3,11 @@
public class OrganizationMongoDB public class OrganizationMongoDB
{ {
public string Id { get; set; } = string.Empty; public string Id { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty; public string? Name { get; set; }
public string Email { get; set; } = string.Empty; public string? Email { get; set; }
public string ContactPerson { get; set; } = string.Empty; public string? ContactPerson { get; set; }
public string? Address { get; set; }
public string? ContactNumber { get; set; }
public double SPRID { get; set; }
} }
} }

View File

@ -511,6 +511,7 @@ namespace MarcoBMS.Services.Controllers
return StatusCode(response.StatusCode, response); return StatusCode(response.StatusCode, response);
} }
[HttpPost("assign/service")] [HttpPost("assign/service")]
public async Task<IActionResult> AssignServiceToProject([FromBody] AssignServiceDto model) public async Task<IActionResult> AssignServiceToProject([FromBody] AssignServiceDto model)
{ {

View File

@ -84,7 +84,10 @@ namespace Marco.Pms.Services.Helpers
Id = o.Id.ToString(), Id = o.Id.ToString(),
Name = o.Name, Name = o.Name,
ContactPerson = o.ContactPerson, ContactPerson = o.ContactPerson,
Email = o.Email Email = o.Email,
Address = o.Address,
ContactNumber = o.ContactNumber,
SPRID = o.SPRID
}) // Projection }) // Projection
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
}); });
@ -100,7 +103,10 @@ namespace Marco.Pms.Services.Helpers
Id = o.Id.ToString(), Id = o.Id.ToString(),
Name = o.Name, Name = o.Name,
ContactPerson = o.ContactPerson, ContactPerson = o.ContactPerson,
Email = o.Email Email = o.Email,
Address = o.Address,
ContactNumber = o.ContactNumber,
SPRID = o.SPRID
}) // Projection }) // Projection
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
}); });
@ -300,7 +306,10 @@ namespace Marco.Pms.Services.Helpers
Id = o.Id.ToString(), Id = o.Id.ToString(),
Name = o.Name, Name = o.Name,
ContactPerson = o.ContactPerson, ContactPerson = o.ContactPerson,
Email = o.Email Email = o.Email,
Address = o.Address,
ContactNumber = o.ContactNumber,
SPRID = o.SPRID
}) // Projection }) // Projection
.ToListAsync(); .ToListAsync();
}); });

View File

@ -47,6 +47,12 @@ namespace Marco.Pms.Services.MappingProfiles
CreateMap<CreateOrganizationDto, Organization>(); CreateMap<CreateOrganizationDto, Organization>();
CreateMap<Organization, OrganizationVM>(); CreateMap<Organization, OrganizationVM>();
CreateMap<Organization, BasicOrganizationVm>(); CreateMap<Organization, BasicOrganizationVm>();
CreateMap<OrganizationMongoDB, BasicOrganizationVm>()
.ForMember(
dest => dest.Id,
// Explicitly and safely convert string Id to Guid Id
opt => opt.MapFrom(src => new Guid(src.Id))
);
#endregion #endregion

View File

@ -2165,281 +2165,350 @@ namespace Marco.Pms.Services.Service
#region =================================================================== Assign Service APIs =================================================================== #region =================================================================== Assign Service APIs ===================================================================
/// <summary>
/// Retrieves the list of services assigned to a specific project based on the logged-in employee's organization and permissions.
/// </summary>
/// <param name="projectId">The unique identifier of the project.</param>
/// <param name="tenantId">The tenant identifier for multi-tenant data isolation.</param>
/// <param name="loggedInEmployee">The employee making the request, whose permissions are checked.</param>
/// <returns>An ApiResponse containing the list of assigned services or an error response.</returns>
public async Task<ApiResponse<object>> GetAssignedServiceToProjectAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee) public async Task<ApiResponse<object>> GetAssignedServiceToProjectAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee)
{ {
try try
{ {
using var scope = _serviceScopeFactory.CreateScope(); using var scope = _serviceScopeFactory.CreateScope();
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>(); var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
// Fetch the project to ensure it exists in the given tenant scope
var project = await _context.Projects
.AsNoTracking() // No changes are made, so use NoTracking for performance
.FirstOrDefaultAsync(p => p.Id == projectId && p.TenantId == tenantId);
var project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == projectId && p.TenantId == tenantId);
if (project == null) if (project == null)
{ {
_logger.LogWarning("Project not found. ProjectId: {ProjectId}, TenantId: {TenantId}", projectId, tenantId);
return ApiResponse<object>.ErrorResponse("Project not found", "Project not found in database", 404); return ApiResponse<object>.ErrorResponse("Project not found", "Project not found in database", 404);
} }
var hasPermission = await _permission.HasProjectPermission(loggedInEmployee, projectId); // Verify logged-in employee has permission on the project
var hasPermission = await permissionService.HasProjectPermission(loggedInEmployee, projectId);
if (!hasPermission) if (!hasPermission)
{ {
_logger.LogWarning("Access DENIED for user {UserId} attempting to update project {ProjectId}.", loggedInEmployee.Id, projectId); _logger.LogWarning("Access DENIED for user {UserId} attempting to access project {ProjectId}.", loggedInEmployee.Id, projectId);
return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to modify this project.", 403); return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to access this project.", 403);
} }
List<ServiceMaster> services = new List<ServiceMaster>(); List<ServiceMaster> assignedServices;
// Check if the logged-in employee's organization matches both Promoter and PMC of the project
if (project.PromoterId == loggedInEmployee.OrganizationId && project.PMCId == loggedInEmployee.OrganizationId) if (project.PromoterId == loggedInEmployee.OrganizationId && project.PMCId == loggedInEmployee.OrganizationId)
{ {
var projectServices = await _context.ProjectServiceMappings // Get all active services assigned directly to the project within the tenant
assignedServices = await _context.ProjectServiceMappings
.AsNoTracking()
.Include(ps => ps.Service) .Include(ps => ps.Service)
.Where(ps => ps.ProjectId == projectId && ps.Service != null && ps.TenantId == tenantId && ps.IsActive) .Where(ps => ps.ProjectId == projectId && ps.IsActive && ps.TenantId == tenantId && ps.Service != null)
.Select(ps => ps.Service!)
.Distinct()
.ToListAsync(); .ToListAsync();
services = projectServices.Select(ps => ps.Service!).Distinct().ToList();
_logger.LogInfo("User {UserId} requested all services for project {ProjectId} as Promoter and PMC.", loggedInEmployee.Id, projectId);
} }
else else
{ {
var orgProjectMapping = await _context.ProjectOrgMappings // Get the active project services mapped to the employee's organization for this project
assignedServices = await _context.ProjectOrgMappings
.AsNoTracking()
.Include(po => po.ProjectService) .Include(po => po.ProjectService)
.ThenInclude(ps => ps!.Service) .ThenInclude(ps => ps!.Service)
.Where(po => po.OrganizationId == loggedInEmployee.OrganizationId && po.ProjectService != null .Where(po =>
&& po.ProjectService.IsActive && po.ProjectService.ProjectId == projectId && po.ProjectService.Service != null) po.OrganizationId == loggedInEmployee.OrganizationId &&
po.ProjectService != null &&
po.ProjectService.IsActive &&
po.ProjectService.ProjectId == projectId &&
po.ProjectService.Service != null)
.Select(po => po.ProjectService!.Service!)
.Distinct()
.ToListAsync(); .ToListAsync();
services = orgProjectMapping.Select(po => po.ProjectService!.Service!).Distinct().ToList(); _logger.LogInfo("User {UserId} requested services for project {ProjectId} via organization mapping.", loggedInEmployee.Id, projectId);
} }
var response = _mapper.Map<List<ServiceMasterVM>>(services);
return ApiResponse<object>.SuccessResponse(response, "Successfully fetched the services for this project", 200); // Map entities to view models
var serviceViewModels = _mapper.Map<List<ServiceMasterVM>>(assignedServices);
return ApiResponse<object>.SuccessResponse(serviceViewModels, "Successfully fetched the services for this project", 200);
} }
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
{ {
//await transaction.RollbackAsync(); _logger.LogError(dbEx, "Database exception occurred while fetching assigned services to project {ProjectId} for tenant {TenantId}.", projectId, tenantId);
return ApiResponse<object>.ErrorResponse("Internal error", "A database exception occurred", 500);
_logger.LogError(dbEx, "Database Exception has been occured, While deassigning the sevice to the project");
return ApiResponse<object>.ErrorResponse("Internal error", "An database exception has been occured", 500);
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Exception has been occured, While deassigning the sevice to the project"); _logger.LogError(ex, "Unexpected exception occurred while fetching assigned services to project {ProjectId} for tenant {TenantId}.", projectId, tenantId);
return ApiResponse<object>.ErrorResponse("Internal error", "An internal exception has been occured", 500); return ApiResponse<object>.ErrorResponse("Internal error", "An unexpected internal exception occurred", 500);
} }
} }
/// <summary>
/// Assigns one or multiple services to a project with specified planned and actual dates.
/// Checks for project existence and employee permissions before assignment.
/// </summary>
/// <param name="model">Data transfer object containing project ID, list of service IDs, and planned dates.</param>
/// <param name="tenantId">Tenant identifier for proper multi-tenant separation.</param>
/// <param name="loggedInEmployee">The employee requesting the service assignment, used for permission checks.</param>
/// <returns>ApiResponse with assigned services info or error details.</returns>
public async Task<ApiResponse<object>> AssignServiceToProjectAsync(AssignServiceDto model, Guid tenantId, Employee loggedInEmployee) public async Task<ApiResponse<object>> AssignServiceToProjectAsync(AssignServiceDto model, Guid tenantId, Employee loggedInEmployee)
{ {
// Begin a transaction to ensure atomicity of assignments
await using var transaction = await _context.Database.BeginTransactionAsync(); await using var transaction = await _context.Database.BeginTransactionAsync();
try try
{ {
using var scope = _serviceScopeFactory.CreateScope(); using var scope = _serviceScopeFactory.CreateScope();
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>(); var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
// Validate project exists within the tenant
var project = await _context.Projects
.AsNoTracking()
.FirstOrDefaultAsync(p => p.Id == model.ProjectId && p.TenantId == tenantId);
var project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == model.ProjectId && p.TenantId == tenantId);
if (project == null) if (project == null)
{ {
_logger.LogWarning("Project not found. ProjectId: {ProjectId}, TenantId: {TenantId}", model.ProjectId, tenantId);
return ApiResponse<object>.ErrorResponse("Project not found", "Project not found in database", 404); return ApiResponse<object>.ErrorResponse("Project not found", "Project not found in database", 404);
} }
var hasPermission = await _permission.HasProjectPermission(loggedInEmployee, model.ProjectId); // Validate permission for logged-in employee to assign services to project
var hasPermission = await permissionService.HasProjectPermission(loggedInEmployee, model.ProjectId);
if (!hasPermission) if (!hasPermission)
{ {
_logger.LogWarning("Access DENIED for user {UserId} attempting to update project {ProjectId}.", loggedInEmployee.Id, model.ProjectId); _logger.LogWarning("Access DENIED for user {UserId} attempting to assign services to project {ProjectId}.", loggedInEmployee.Id, model.ProjectId);
return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to modify this project.", 403); return ApiResponse<object>.ErrorResponse("Access Denied", "You do not have permission to modify this project.", 403);
} }
var todaysDate = DateTime.UtcNow.Date; // Fetch existing active project service mappings for the requested service IDs, within the same tenant
var existingProjectServices = await _context.ProjectServiceMappings
.Where(sp => model.ServiceIds.Contains(sp.ServiceId) && sp.ProjectId == model.ProjectId && sp.IsActive)
.ToListAsync();
var projectServicesTask = Task.Run(async () => // Fetch service details for the provided service IDs within the tenant scope
{ var services = await _context.ServiceMasters
await using var context = await _dbContextFactory.CreateDbContextAsync(); .Where(s => model.ServiceIds.Contains(s.Id) && s.TenantId == tenantId)
return await context.ProjectServiceMappings .ToListAsync();
.Where(sp => model.ServiceIds.Contains(sp.ServiceId) && sp.ProjectId == model.ProjectId && sp.IsActive).ToListAsync();
});
var serviceTask = Task.Run(async () => // Current UTC timestamp for actual start date
{ var currentUtc = DateTime.UtcNow;
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.ServiceMasters.Where(s => model.ServiceIds.Contains(s.Id) && s.TenantId == tenantId).ToListAsync();
});
await Task.WhenAll(projectServicesTask, serviceTask);
var projectServices = projectServicesTask.Result;
var services = serviceTask.Result;
// Add new project service mappings if not already present
foreach (var serviceId in model.ServiceIds) foreach (var serviceId in model.ServiceIds)
{ {
if (!existingProjectServices.Any(ps => ps.ServiceId == serviceId))
var projectService = projectServices.FirstOrDefault(ps => ps.ServiceId == serviceId);
if (projectService == null)
{ {
projectService = new ProjectServiceMapping var newMapping = new ProjectServiceMapping
{ {
ProjectId = project.Id, ProjectId = project.Id,
ServiceId = serviceId, ServiceId = serviceId,
TenantId = project.TenantId, TenantId = tenantId,
PlannedStartDate = model.PlannedStartDate, PlannedStartDate = model.PlannedStartDate,
PlannedEndDate = model.PlannedEndDate, PlannedEndDate = model.PlannedEndDate,
ActualStartDate = DateTime.UtcNow, ActualStartDate = currentUtc,
IsActive = true IsActive = true
}; };
_context.ProjectServiceMappings.Add(projectService); _context.ProjectServiceMappings.Add(newMapping);
_logger.LogInfo("Assigned service {ServiceId} to project {ProjectId} by user {UserId}.", serviceId, model.ProjectId, loggedInEmployee.Id);
}
else
{
_logger.LogInfo("Service {ServiceId} is already assigned and active for project {ProjectId}.", serviceId, model.ProjectId);
} }
} }
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
await transaction.CommitAsync(); await transaction.CommitAsync();
// Prepare response combining project and service data mapped to view models
var response = services.Select(s => new ProjectServiceVM var response = services.Select(s => new ProjectServiceVM
{ {
Project = _mapper.Map<BasicProjectVM>(project), Project = _mapper.Map<BasicProjectVM>(project),
Service = _mapper.Map<ServiceMasterVM>(s), Service = _mapper.Map<ServiceMasterVM>(s),
PlannedStartDate = model.PlannedStartDate, PlannedStartDate = model.PlannedStartDate,
PlannedEndDate = model.PlannedEndDate, PlannedEndDate = model.PlannedEndDate,
ActualStartDate = DateTime.UtcNow ActualStartDate = currentUtc
}); }).ToList();
return ApiResponse<object>.SuccessResponse(response, "Services has been assigned to the project", 200);
return ApiResponse<object>.SuccessResponse(response, "Services have been assigned to the project successfully", 200);
} }
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
{ {
await transaction.RollbackAsync(); await transaction.RollbackAsync();
_logger.LogError(dbEx, "Database exception while assigning services to project {ProjectId} for tenant {TenantId} by user {UserId}.", model.ProjectId, tenantId, loggedInEmployee.Id);
_logger.LogError(dbEx, "Database Exception has been occured, While assigning the sevice to the project"); return ApiResponse<object>.ErrorResponse("Internal error", "A database exception has occurred", 500);
return ApiResponse<object>.ErrorResponse("Internal error", "An database exception has been occured", 500);
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Exception has been occured, While assigning the sevice to the project"); await transaction.RollbackAsync();
return ApiResponse<object>.ErrorResponse("Internal error", "An internal exception has been occured", 500); _logger.LogError(ex, "Unexpected exception while assigning services to project {ProjectId} for tenant {TenantId} by user {UserId}.", model.ProjectId, tenantId, loggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("Internal error", "An unexpected internal exception has occurred", 500);
} }
} }
/// <summary>
/// Deassigns specified services from a project by marking them inactive and setting actual end date.
/// Validates project existence and employee permission before making updates.
/// </summary>
/// <param name="model">Contains ProjectId and list of ServiceIds to deassign.</param>
/// <param name="tenantId">Tenant context for multi-tenant data isolation.</param>
/// <param name="loggedInEmployee">Employee executing the operation, used for permission checks.</param>
/// <returns>ApiResponse indicating success or failure.</returns>
public async Task<ApiResponse<object>> DeassignServiceToProjectAsync(DeassignServiceDto model, Guid tenantId, Employee loggedInEmployee) public async Task<ApiResponse<object>> DeassignServiceToProjectAsync(DeassignServiceDto model, Guid tenantId, Employee loggedInEmployee)
{ {
await using var transaction = await _context.Database.BeginTransactionAsync(); await using var transaction = await _context.Database.BeginTransactionAsync();
try try
{ {
using var scope = _serviceScopeFactory.CreateScope(); using var scope = _serviceScopeFactory.CreateScope();
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>(); var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
// Validate that project exists for given tenant
var project = await _context.Projects
.AsNoTracking()
.FirstOrDefaultAsync(p => p.Id == model.ProjectId && p.TenantId == tenantId);
var project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == model.ProjectId && p.TenantId == tenantId);
if (project == null) if (project == null)
{ {
_logger.LogWarning("Project not found. ProjectId: {ProjectId}, TenantId: {TenantId}", model.ProjectId, tenantId);
return ApiResponse<object>.ErrorResponse("Project not found", "Project not found in database", 404); return ApiResponse<object>.ErrorResponse("Project not found", "Project not found in database", 404);
} }
var hasPermission = await _permission.HasProjectPermission(loggedInEmployee, model.ProjectId); // Verify permission to update project
var hasPermission = await permissionService.HasProjectPermission(loggedInEmployee, model.ProjectId);
if (!hasPermission) if (!hasPermission)
{ {
_logger.LogWarning("Access DENIED for user {UserId} attempting to update project {ProjectId}.", loggedInEmployee.Id, model.ProjectId); _logger.LogWarning("Access DENIED for user {UserId} trying to deassign services from project {ProjectId}.", loggedInEmployee.Id, model.ProjectId);
return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to modify this project.", 403); return ApiResponse<object>.ErrorResponse("Access Denied", "You do not have permission to modify this project.", 403);
} }
var todaysDate = DateTime.UtcNow.Date;
var projectServicesTask = Task.Run(async () => // Fetch active project service mappings matching provided service IDs
{ var projectServices = await _context.ProjectServiceMappings
await using var context = await _dbContextFactory.CreateDbContextAsync(); .Where(ps => model.ServiceIds.Contains(ps.ServiceId) && ps.ProjectId == model.ProjectId && ps.IsActive)
return await context.ProjectServiceMappings .ToListAsync();
.AsNoTracking()
.Where(sp => model.ServiceIds.Contains(sp.ServiceId) && sp.ProjectId == model.ProjectId && sp.IsActive).ToListAsync();
});
var serviceTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.ServiceMasters.Where(s => model.ServiceIds.Contains(s.Id) && s.TenantId == tenantId).ToListAsync();
});
await Task.WhenAll(projectServicesTask, serviceTask);
var projectServices = projectServicesTask.Result;
var services = serviceTask.Result;
if (!projectServices.Any()) if (!projectServices.Any())
{ {
return ApiResponse<object>.ErrorResponse("Project Service mapping not found", "Project Service mapping not found in database", 404); _logger.LogWarning("No matching active project service mappings found for deassignment. ProjectId: {ProjectId}, ServiceIds: {ServiceIds}",
model.ProjectId, string.Join(",", model.ServiceIds));
return ApiResponse<object>.ErrorResponse("Project Service mapping not found", "No active service mappings found in database", 404);
} }
projectServices = projectServices.Select(ps => var currentUtc = DateTime.UtcNow;
// Mark mappings as inactive and set actual end date to now
foreach (var ps in projectServices)
{ {
ps.IsActive = false; ps.IsActive = false;
return ps; ps.ActualEndDate = currentUtc;
}).ToList(); }
_context.ProjectServiceMappings.UpdateRange(projectServices); _context.ProjectServiceMappings.UpdateRange(projectServices);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
await transaction.CommitAsync(); await transaction.CommitAsync();
return ApiResponse<object>.SuccessResponse(new { }, "Services has been deassigned to the project", 200); _logger.LogInfo("User {UserId} deassigned {Count} services from project {ProjectId}.", loggedInEmployee.Id, projectServices.Count, model.ProjectId);
return ApiResponse<object>.SuccessResponse(new { }, "Services have been deassigned from the project successfully", 200);
} }
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
{ {
await transaction.RollbackAsync(); await transaction.RollbackAsync();
_logger.LogError(dbEx, "Database exception occurred while deassigning services from project {ProjectId} by user {UserId}.", model.ProjectId, loggedInEmployee.Id);
_logger.LogError(dbEx, "Database Exception has been occured, While deassigning the sevice to the project"); return ApiResponse<object>.ErrorResponse("Internal error", "A database exception has occurred", 500);
return ApiResponse<object>.ErrorResponse("Internal error", "An database exception has been occured", 500);
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Exception has been occured, While deassigning the sevice to the project"); await transaction.RollbackAsync();
return ApiResponse<object>.ErrorResponse("Internal error", "An internal exception has been occured", 500); _logger.LogError(ex, "Unexpected exception occurred while deassigning services from project {ProjectId} by user {UserId}.", model.ProjectId, loggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("Internal error", "An unexpected internal exception has occurred", 500);
} }
} }
#endregion #endregion
#region =================================================================== Assign Organization APIs =================================================================== #region =================================================================== Assign Organization APIs ===================================================================
public async Task<ApiResponse<object>> GetAssignedOrganizationsToProjectAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee) public async Task<ApiResponse<object>> GetAssignedOrganizationsToProjectAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee)
{ {
_logger.LogDebug("Started fetching assigned organizations for ProjectId: {ProjectId} and TenantId: {TenantId} by user {UserId}",
projectId, tenantId, loggedInEmployee.Id);
try try
{ {
// Create a scoped PermissionServices instance for permission checks
using var scope = _serviceScopeFactory.CreateScope(); using var scope = _serviceScopeFactory.CreateScope();
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>(); var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
// Retrieve the project by projectId and tenantId
var project = await _context.Projects
.AsNoTracking()
.FirstOrDefaultAsync(p => p.Id == projectId && p.TenantId == tenantId);
var project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == projectId && p.TenantId == tenantId);
if (project == null) if (project == null)
{ {
_logger.LogWarning("Project not found. ProjectId: {ProjectId}, TenantId: {TenantId}", projectId, tenantId);
return ApiResponse<object>.ErrorResponse("Project not found", "Project not found in database", 404); return ApiResponse<object>.ErrorResponse("Project not found", "Project not found in database", 404);
} }
var hasPermission = await _permission.HasProjectPermission(loggedInEmployee, projectId); // Check if the logged in employee has permission to access the project
var hasPermission = await permissionService.HasProjectPermission(loggedInEmployee, projectId);
if (!hasPermission) if (!hasPermission)
{ {
_logger.LogWarning("Access DENIED for user {UserId} attempting to update project {ProjectId}.", loggedInEmployee.Id, projectId); _logger.LogWarning("Access denied for user {UserId} on project {ProjectId}", loggedInEmployee.Id, projectId);
return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to modify this project.", 403); return ApiResponse<object>.ErrorResponse("Access Denied", "You do not have permission to access this project.", 403);
} }
var projectOrgMapping = await _context.ProjectOrgMappings // Fetch all project-organization mappings with related service and organization data
var projectOrgMappings = await _context.ProjectOrgMappings
.AsNoTracking()
.Include(po => po.ProjectService) .Include(po => po.ProjectService)
.ThenInclude(ps => ps!.Service) .ThenInclude(ps => ps!.Service)
.Include(po => po.Organization) .Include(po => po.Organization)
.Where(po => po.ProjectService != null && po.ProjectService.ProjectId == projectId && po.TenantId == tenantId) .Where(po => po.ProjectService != null
&& po.ProjectService.ProjectId == projectId
&& po.TenantId == tenantId)
.Distinct() .Distinct()
.ToListAsync(); .ToListAsync();
var response = projectOrgMapping.Where(po => po.Organization != null).Select(po => new ProjectOrganizationVM // Filter and map the data to the desired view model
{ var response = projectOrgMappings
Id = po.Organization!.Id, .Where(po => po.Organization != null)
Name = po.Organization.Name, .Select(po => new ProjectOrganizationVM
Email = po.Organization.Email, {
ContactPerson = po.Organization.ContactPerson, Id = po.Organization!.Id,
SPRID = po.Organization.SPRID, Name = po.Organization.Name,
logoImage = po.Organization.logoImage, Email = po.Organization.Email,
AssignedBy = _mapper.Map<BasicEmployeeVM>(po.AssignedBy), ContactPerson = po.Organization.ContactPerson,
Service = _mapper.Map<ServiceMasterVM>(po.ProjectService!.Service), SPRID = po.Organization.SPRID,
AssignedDate = po.AssignedDate, logoImage = po.Organization.logoImage,
CompletionDate = po.CompletionDate AssignedBy = _mapper.Map<BasicEmployeeVM>(po.AssignedBy),
}).ToList(); Service = _mapper.Map<ServiceMasterVM>(po.ProjectService!.Service),
AssignedDate = po.AssignedDate,
CompletionDate = po.CompletionDate
})
.ToList();
return ApiResponse<object>.SuccessResponse(response, "Successfully fetched the list of organization assigned to the project", 200); _logger.LogInfo("Fetched {Count} assigned organizations for ProjectId: {ProjectId}", response.Count, projectId);
return ApiResponse<object>.SuccessResponse(response, "Successfully fetched the list of organizations assigned to the project", 200);
} }
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
{ {
//await transaction.RollbackAsync(); _logger.LogError(dbEx, "Database exception while fetching assigned organizations for ProjectId: {ProjectId}", projectId);
return ApiResponse<object>.ErrorResponse("Internal error", "A database exception occurred", 500);
_logger.LogError(dbEx, "Database Exception has been occured, While deassigning the sevice to the project");
return ApiResponse<object>.ErrorResponse("Internal error", "An database exception has been occured", 500);
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Exception has been occured, While deassigning the sevice to the project"); _logger.LogError(ex, "Unhandled exception while fetching assigned organizations for ProjectId: {ProjectId}", projectId);
return ApiResponse<object>.ErrorResponse("Internal error", "An internal exception has been occured", 500); return ApiResponse<object>.ErrorResponse("Internal error", "An internal exception occurred", 500);
} }
} }