|
|
|
@ -2165,255 +2165,323 @@ namespace Marco.Pms.Services.Service
|
|
|
|
|
|
|
|
|
|
#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)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning("Project not found. ProjectId: {ProjectId}, TenantId: {TenantId}", projectId, tenantId);
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning("Access DENIED for user {UserId} attempting to update project {ProjectId}.", loggedInEmployee.Id, projectId);
|
|
|
|
|
return ApiResponse<object>.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<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)
|
|
|
|
|
{
|
|
|
|
|
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<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)
|
|
|
|
|
{
|
|
|
|
|
//await transaction.RollbackAsync();
|
|
|
|
|
|
|
|
|
|
_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);
|
|
|
|
|
_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);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogError(ex, "Exception has been occured, While deassigning the sevice to the project");
|
|
|
|
|
return ApiResponse<object>.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<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)
|
|
|
|
|
{
|
|
|
|
|
// 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<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)
|
|
|
|
|
{
|
|
|
|
|
_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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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<object>.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<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 () =>
|
|
|
|
|
{
|
|
|
|
|
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<BasicProjectVM>(project),
|
|
|
|
|
Service = _mapper.Map<ServiceMasterVM>(s),
|
|
|
|
|
PlannedStartDate = model.PlannedStartDate,
|
|
|
|
|
PlannedEndDate = model.PlannedEndDate,
|
|
|
|
|
ActualStartDate = DateTime.UtcNow
|
|
|
|
|
});
|
|
|
|
|
return ApiResponse<object>.SuccessResponse(response, "Services has been assigned to the project", 200);
|
|
|
|
|
ActualStartDate = currentUtc
|
|
|
|
|
}).ToList();
|
|
|
|
|
|
|
|
|
|
return ApiResponse<object>.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<object>.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<object>.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<object>.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<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)
|
|
|
|
|
{
|
|
|
|
|
await using var transaction = await _context.Database.BeginTransactionAsync();
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
_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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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<object>.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<object>.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<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;
|
|
|
|
|
return ps;
|
|
|
|
|
}).ToList();
|
|
|
|
|
ps.ActualEndDate = currentUtc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_context.ProjectServiceMappings.UpdateRange(projectServices);
|
|
|
|
|
await _context.SaveChangesAsync();
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
await transaction.RollbackAsync();
|
|
|
|
|
|
|
|
|
|
_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);
|
|
|
|
|
_logger.LogError(dbEx, "Database exception occurred while deassigning services from project {ProjectId} by user {UserId}.", model.ProjectId, loggedInEmployee.Id);
|
|
|
|
|
return ApiResponse<object>.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<object>.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<object>.ErrorResponse("Internal error", "An unexpected internal exception has occurred", 500);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region =================================================================== Assign Organization APIs ===================================================================
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
{
|
|
|
|
|
// Create a scoped PermissionServices instance for permission checks
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning("Project not found. ProjectId: {ProjectId}, TenantId: {TenantId}", projectId, tenantId);
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning("Access DENIED for user {UserId} attempting to update project {ProjectId}.", loggedInEmployee.Id, projectId);
|
|
|
|
|
return ApiResponse<object>.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<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)
|
|
|
|
|
.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
|
|
|
|
|
// 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,
|
|
|
|
@ -2425,21 +2493,22 @@ namespace Marco.Pms.Services.Service
|
|
|
|
|
Service = _mapper.Map<ServiceMasterVM>(po.ProjectService!.Service),
|
|
|
|
|
AssignedDate = po.AssignedDate,
|
|
|
|
|
CompletionDate = po.CompletionDate
|
|
|
|
|
}).ToList();
|
|
|
|
|
})
|
|
|
|
|
.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)
|
|
|
|
|
{
|
|
|
|
|
//await transaction.RollbackAsync();
|
|
|
|
|
|
|
|
|
|
_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);
|
|
|
|
|
_logger.LogError(dbEx, "Database exception while fetching assigned organizations for ProjectId: {ProjectId}", projectId);
|
|
|
|
|
return ApiResponse<object>.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<object>.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<object>.ErrorResponse("Internal error", "An internal exception occurred", 500);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|