diff --git a/Marco.Pms.Helpers/CacheHelper/ProjectCache.cs b/Marco.Pms.Helpers/CacheHelper/ProjectCache.cs index cc972ee..6f11b07 100644 --- a/Marco.Pms.Helpers/CacheHelper/ProjectCache.cs +++ b/Marco.Pms.Helpers/CacheHelper/ProjectCache.cs @@ -74,14 +74,20 @@ namespace Marco.Pms.Helpers Id = promotor.Id.ToString(), Name = promotor.Name, ContactPerson = promotor.ContactPerson, - Email = promotor.Email + Email = promotor.Email, + Address = promotor.Address, + ContactNumber = promotor.ContactNumber, + SPRID = promotor.SPRID }), Builders.Update.Set(r => r.PMC, new OrganizationMongoDB { Id = pmc.Id.ToString(), Name = pmc.Name, ContactPerson = pmc.ContactPerson, - Email = pmc.Email + Email = pmc.Email, + Address = promotor.Address, + ContactNumber = promotor.ContactNumber, + SPRID = promotor.SPRID }), Builders.Update.Set(r => r.StartDate, project.StartDate), Builders.Update.Set(r => r.EndDate, project.EndDate), diff --git a/Marco.Pms.Model/MongoDBModels/OrganizationMongoDB.cs b/Marco.Pms.Model/MongoDBModels/OrganizationMongoDB.cs index d18f0e9..d6e6ef1 100644 --- a/Marco.Pms.Model/MongoDBModels/OrganizationMongoDB.cs +++ b/Marco.Pms.Model/MongoDBModels/OrganizationMongoDB.cs @@ -3,8 +3,11 @@ public class OrganizationMongoDB { public string Id { get; set; } = string.Empty; - public string Name { get; set; } = string.Empty; - public string Email { get; set; } = string.Empty; - public string ContactPerson { get; set; } = string.Empty; + public string? Name { get; set; } + public string? Email { get; set; } + public string? ContactPerson { get; set; } + public string? Address { get; set; } + public string? ContactNumber { get; set; } + public double SPRID { get; set; } } } diff --git a/Marco.Pms.Services/Controllers/ProjectController.cs b/Marco.Pms.Services/Controllers/ProjectController.cs index dfa0361..deadf09 100644 --- a/Marco.Pms.Services/Controllers/ProjectController.cs +++ b/Marco.Pms.Services/Controllers/ProjectController.cs @@ -511,6 +511,7 @@ namespace MarcoBMS.Services.Controllers return StatusCode(response.StatusCode, response); } + [HttpPost("assign/service")] public async Task AssignServiceToProject([FromBody] AssignServiceDto model) { diff --git a/Marco.Pms.Services/Helpers/CacheUpdateHelper.cs b/Marco.Pms.Services/Helpers/CacheUpdateHelper.cs index f95f06e..fd56f41 100644 --- a/Marco.Pms.Services/Helpers/CacheUpdateHelper.cs +++ b/Marco.Pms.Services/Helpers/CacheUpdateHelper.cs @@ -84,7 +84,10 @@ namespace Marco.Pms.Services.Helpers Id = o.Id.ToString(), Name = o.Name, ContactPerson = o.ContactPerson, - Email = o.Email + Email = o.Email, + Address = o.Address, + ContactNumber = o.ContactNumber, + SPRID = o.SPRID }) // Projection .FirstOrDefaultAsync(); }); @@ -100,7 +103,10 @@ namespace Marco.Pms.Services.Helpers Id = o.Id.ToString(), Name = o.Name, ContactPerson = o.ContactPerson, - Email = o.Email + Email = o.Email, + Address = o.Address, + ContactNumber = o.ContactNumber, + SPRID = o.SPRID }) // Projection .FirstOrDefaultAsync(); }); @@ -300,7 +306,10 @@ namespace Marco.Pms.Services.Helpers Id = o.Id.ToString(), Name = o.Name, ContactPerson = o.ContactPerson, - Email = o.Email + Email = o.Email, + Address = o.Address, + ContactNumber = o.ContactNumber, + SPRID = o.SPRID }) // Projection .ToListAsync(); }); diff --git a/Marco.Pms.Services/MappingProfiles/MappingProfile.cs b/Marco.Pms.Services/MappingProfiles/MappingProfile.cs index 647d0bd..2dad4ba 100644 --- a/Marco.Pms.Services/MappingProfiles/MappingProfile.cs +++ b/Marco.Pms.Services/MappingProfiles/MappingProfile.cs @@ -47,6 +47,12 @@ namespace Marco.Pms.Services.MappingProfiles CreateMap(); CreateMap(); CreateMap(); + CreateMap() + .ForMember( + dest => dest.Id, + // Explicitly and safely convert string Id to Guid Id + opt => opt.MapFrom(src => new Guid(src.Id)) + ); #endregion diff --git a/Marco.Pms.Services/Service/ProjectServices.cs b/Marco.Pms.Services/Service/ProjectServices.cs index 4744089..9e2a1b0 100644 --- a/Marco.Pms.Services/Service/ProjectServices.cs +++ b/Marco.Pms.Services/Service/ProjectServices.cs @@ -2165,281 +2165,350 @@ namespace Marco.Pms.Services.Service #region =================================================================== Assign Service APIs =================================================================== + /// + /// Retrieves the list of services assigned to a specific project based on the logged-in employee's organization and permissions. + /// + /// The unique identifier of the project. + /// The tenant identifier for multi-tenant data isolation. + /// The employee making the request, whose permissions are checked. + /// An ApiResponse containing the list of assigned services or an error response. public async Task> GetAssignedServiceToProjectAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee) { try { using var scope = _serviceScopeFactory.CreateScope(); - var _permission = scope.ServiceProvider.GetRequiredService(); + var permissionService = scope.ServiceProvider.GetRequiredService(); + + // 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) { + _logger.LogWarning("Project not found. ProjectId: {ProjectId}, TenantId: {TenantId}", projectId, tenantId); return ApiResponse.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) { - _logger.LogWarning("Access DENIED for user {UserId} attempting to update project {ProjectId}.", loggedInEmployee.Id, projectId); - return ApiResponse.ErrorResponse("Access Denied.", "You do not have permission to modify this project.", 403); + _logger.LogWarning("Access DENIED for user {UserId} attempting to access project {ProjectId}.", loggedInEmployee.Id, projectId); + return ApiResponse.ErrorResponse("Access Denied.", "You do not have permission to access this project.", 403); } - List services = new List(); + List 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) { - 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) - .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(); - 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 { - 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) .ThenInclude(ps => ps!.Service) - .Where(po => po.OrganizationId == loggedInEmployee.OrganizationId && po.ProjectService != null - && po.ProjectService.IsActive && po.ProjectService.ProjectId == projectId && po.ProjectService.Service != null) + .Where(po => + 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(); - 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>(services); - return ApiResponse.SuccessResponse(response, "Successfully fetched the services for this project", 200); + // Map entities to view models + var serviceViewModels = _mapper.Map>(assignedServices); + + return ApiResponse.SuccessResponse(serviceViewModels, "Successfully fetched the services for this project", 200); } catch (DbUpdateException dbEx) { - //await transaction.RollbackAsync(); - - _logger.LogError(dbEx, "Database Exception has been occured, While deassigning the sevice to the project"); - return ApiResponse.ErrorResponse("Internal error", "An database exception has been occured", 500); + _logger.LogError(dbEx, "Database exception occurred while fetching assigned services to project {ProjectId} for tenant {TenantId}.", projectId, tenantId); + return ApiResponse.ErrorResponse("Internal error", "A database exception occurred", 500); } catch (Exception ex) { - _logger.LogError(ex, "Exception has been occured, While deassigning the sevice to the project"); - return ApiResponse.ErrorResponse("Internal error", "An internal exception has been occured", 500); + _logger.LogError(ex, "Unexpected exception occurred while fetching assigned services to project {ProjectId} for tenant {TenantId}.", projectId, tenantId); + return ApiResponse.ErrorResponse("Internal error", "An unexpected internal exception occurred", 500); } } + + /// + /// Assigns one or multiple services to a project with specified planned and actual dates. + /// Checks for project existence and employee permissions before assignment. + /// + /// Data transfer object containing project ID, list of service IDs, and planned dates. + /// Tenant identifier for proper multi-tenant separation. + /// The employee requesting the service assignment, used for permission checks. + /// ApiResponse with assigned services info or error details. public async Task> AssignServiceToProjectAsync(AssignServiceDto model, Guid tenantId, Employee loggedInEmployee) { + // Begin a transaction to ensure atomicity of assignments await using var transaction = await _context.Database.BeginTransactionAsync(); + try { using var scope = _serviceScopeFactory.CreateScope(); - var _permission = scope.ServiceProvider.GetRequiredService(); + var permissionService = scope.ServiceProvider.GetRequiredService(); + + // 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) { + _logger.LogWarning("Project not found. ProjectId: {ProjectId}, TenantId: {TenantId}", model.ProjectId, tenantId); return ApiResponse.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) { - _logger.LogWarning("Access DENIED for user {UserId} attempting to update project {ProjectId}.", loggedInEmployee.Id, model.ProjectId); - return ApiResponse.ErrorResponse("Access Denied.", "You do not have permission to modify this project.", 403); + _logger.LogWarning("Access DENIED for user {UserId} attempting to assign services to project {ProjectId}.", loggedInEmployee.Id, model.ProjectId); + return ApiResponse.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 () => - { - await using var context = await _dbContextFactory.CreateDbContextAsync(); - return await context.ProjectServiceMappings - .Where(sp => model.ServiceIds.Contains(sp.ServiceId) && sp.ProjectId == model.ProjectId && sp.IsActive).ToListAsync(); - }); + // Fetch service details for the provided service IDs within the tenant scope + var services = await _context.ServiceMasters + .Where(s => model.ServiceIds.Contains(s.Id) && s.TenantId == tenantId) + .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; + // Current UTC timestamp for actual start date + var currentUtc = DateTime.UtcNow; + // Add new project service mappings if not already present foreach (var serviceId in model.ServiceIds) { - - var projectService = projectServices.FirstOrDefault(ps => ps.ServiceId == serviceId); - if (projectService == null) + if (!existingProjectServices.Any(ps => ps.ServiceId == serviceId)) { - projectService = new ProjectServiceMapping + var newMapping = new ProjectServiceMapping { ProjectId = project.Id, ServiceId = serviceId, - TenantId = project.TenantId, + TenantId = tenantId, PlannedStartDate = model.PlannedStartDate, PlannedEndDate = model.PlannedEndDate, - ActualStartDate = DateTime.UtcNow, + ActualStartDate = currentUtc, 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 transaction.CommitAsync(); + // Prepare response combining project and service data mapped to view models var response = services.Select(s => new ProjectServiceVM { Project = _mapper.Map(project), Service = _mapper.Map(s), PlannedStartDate = model.PlannedStartDate, PlannedEndDate = model.PlannedEndDate, - ActualStartDate = DateTime.UtcNow - }); - return ApiResponse.SuccessResponse(response, "Services has been assigned to the project", 200); + ActualStartDate = currentUtc + }).ToList(); + + return ApiResponse.SuccessResponse(response, "Services have been assigned to the project successfully", 200); } catch (DbUpdateException dbEx) { await transaction.RollbackAsync(); - - _logger.LogError(dbEx, "Database Exception has been occured, While assigning the sevice to the project"); - return ApiResponse.ErrorResponse("Internal error", "An database exception has been occured", 500); + _logger.LogError(dbEx, "Database exception while assigning services to project {ProjectId} for tenant {TenantId} by user {UserId}.", model.ProjectId, tenantId, loggedInEmployee.Id); + return ApiResponse.ErrorResponse("Internal error", "A database exception has occurred", 500); } catch (Exception ex) { - _logger.LogError(ex, "Exception has been occured, While assigning the sevice to the project"); - return ApiResponse.ErrorResponse("Internal error", "An internal exception has been occured", 500); + await transaction.RollbackAsync(); + _logger.LogError(ex, "Unexpected exception while assigning services to project {ProjectId} for tenant {TenantId} by user {UserId}.", model.ProjectId, tenantId, loggedInEmployee.Id); + return ApiResponse.ErrorResponse("Internal error", "An unexpected internal exception has occurred", 500); } } + + /// + /// Deassigns specified services from a project by marking them inactive and setting actual end date. + /// Validates project existence and employee permission before making updates. + /// + /// Contains ProjectId and list of ServiceIds to deassign. + /// Tenant context for multi-tenant data isolation. + /// Employee executing the operation, used for permission checks. + /// ApiResponse indicating success or failure. public async Task> DeassignServiceToProjectAsync(DeassignServiceDto model, Guid tenantId, Employee loggedInEmployee) { await using var transaction = await _context.Database.BeginTransactionAsync(); + try { using var scope = _serviceScopeFactory.CreateScope(); - var _permission = scope.ServiceProvider.GetRequiredService(); + var permissionService = scope.ServiceProvider.GetRequiredService(); + + // 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) { + _logger.LogWarning("Project not found. ProjectId: {ProjectId}, TenantId: {TenantId}", model.ProjectId, tenantId); return ApiResponse.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) { - _logger.LogWarning("Access DENIED for user {UserId} attempting to update project {ProjectId}.", loggedInEmployee.Id, model.ProjectId); - return ApiResponse.ErrorResponse("Access Denied.", "You do not have permission to modify this project.", 403); + _logger.LogWarning("Access DENIED for user {UserId} trying to deassign services from project {ProjectId}.", loggedInEmployee.Id, model.ProjectId); + return ApiResponse.ErrorResponse("Access Denied", "You do not have permission to modify this project.", 403); } - var todaysDate = DateTime.UtcNow.Date; - var projectServicesTask = Task.Run(async () => - { - await using var context = await _dbContextFactory.CreateDbContextAsync(); - return await context.ProjectServiceMappings - .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; + // Fetch active project service mappings matching provided service IDs + var projectServices = await _context.ProjectServiceMappings + .Where(ps => model.ServiceIds.Contains(ps.ServiceId) && ps.ProjectId == model.ProjectId && ps.IsActive) + .ToListAsync(); if (!projectServices.Any()) { - return ApiResponse.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.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; - return ps; - }).ToList(); + ps.ActualEndDate = currentUtc; + } _context.ProjectServiceMappings.UpdateRange(projectServices); await _context.SaveChangesAsync(); await transaction.CommitAsync(); - return ApiResponse.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.SuccessResponse(new { }, "Services have been deassigned from the project successfully", 200); } catch (DbUpdateException dbEx) { await transaction.RollbackAsync(); - - _logger.LogError(dbEx, "Database Exception has been occured, While deassigning the sevice to the project"); - return ApiResponse.ErrorResponse("Internal error", "An database exception has been occured", 500); + _logger.LogError(dbEx, "Database exception occurred while deassigning services from project {ProjectId} by user {UserId}.", model.ProjectId, loggedInEmployee.Id); + return ApiResponse.ErrorResponse("Internal error", "A database exception has occurred", 500); } catch (Exception ex) { - _logger.LogError(ex, "Exception has been occured, While deassigning the sevice to the project"); - return ApiResponse.ErrorResponse("Internal error", "An internal exception has been occured", 500); + await transaction.RollbackAsync(); + _logger.LogError(ex, "Unexpected exception occurred while deassigning services from project {ProjectId} by user {UserId}.", model.ProjectId, loggedInEmployee.Id); + return ApiResponse.ErrorResponse("Internal error", "An unexpected internal exception has occurred", 500); } } - #endregion #region =================================================================== Assign Organization APIs =================================================================== public async Task> 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 { + // Create a scoped PermissionServices instance for permission checks using var scope = _serviceScopeFactory.CreateScope(); - var _permission = scope.ServiceProvider.GetRequiredService(); + var permissionService = scope.ServiceProvider.GetRequiredService(); + + // 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) { + _logger.LogWarning("Project not found. ProjectId: {ProjectId}, TenantId: {TenantId}", projectId, tenantId); return ApiResponse.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) { - _logger.LogWarning("Access DENIED for user {UserId} attempting to update project {ProjectId}.", loggedInEmployee.Id, projectId); - return ApiResponse.ErrorResponse("Access Denied.", "You do not have permission to modify this project.", 403); + _logger.LogWarning("Access denied for user {UserId} on project {ProjectId}", loggedInEmployee.Id, projectId); + return ApiResponse.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) .ThenInclude(ps => ps!.Service) .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() .ToListAsync(); - var response = projectOrgMapping.Where(po => po.Organization != null).Select(po => new ProjectOrganizationVM - { - Id = po.Organization!.Id, - Name = po.Organization.Name, - Email = po.Organization.Email, - ContactPerson = po.Organization.ContactPerson, - SPRID = po.Organization.SPRID, - logoImage = po.Organization.logoImage, - AssignedBy = _mapper.Map(po.AssignedBy), - Service = _mapper.Map(po.ProjectService!.Service), - AssignedDate = po.AssignedDate, - CompletionDate = po.CompletionDate - }).ToList(); + // Filter and map the data to the desired view model + var response = projectOrgMappings + .Where(po => po.Organization != null) + .Select(po => new ProjectOrganizationVM + { + Id = po.Organization!.Id, + Name = po.Organization.Name, + Email = po.Organization.Email, + ContactPerson = po.Organization.ContactPerson, + SPRID = po.Organization.SPRID, + logoImage = po.Organization.logoImage, + AssignedBy = _mapper.Map(po.AssignedBy), + Service = _mapper.Map(po.ProjectService!.Service), + AssignedDate = po.AssignedDate, + CompletionDate = po.CompletionDate + }) + .ToList(); - return ApiResponse.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.SuccessResponse(response, "Successfully fetched the list of organizations assigned to the project", 200); } catch (DbUpdateException dbEx) { - //await transaction.RollbackAsync(); - - _logger.LogError(dbEx, "Database Exception has been occured, While deassigning the sevice to the project"); - return ApiResponse.ErrorResponse("Internal error", "An database exception has been occured", 500); + _logger.LogError(dbEx, "Database exception while fetching assigned organizations for ProjectId: {ProjectId}", projectId); + return ApiResponse.ErrorResponse("Internal error", "A database exception occurred", 500); } catch (Exception ex) { - _logger.LogError(ex, "Exception has been occured, While deassigning the sevice to the project"); - return ApiResponse.ErrorResponse("Internal error", "An internal exception has been occured", 500); + _logger.LogError(ex, "Unhandled exception while fetching assigned organizations for ProjectId: {ProjectId}", projectId); + return ApiResponse.ErrorResponse("Internal error", "An internal exception occurred", 500); } }