Added the promotor and PMC in project VM
This commit is contained in:
parent
4c6070fee5
commit
18d590ccbe
@ -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),
|
||||||
|
|||||||
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user