Organization_Management #142
@ -269,19 +269,25 @@ namespace Marco.Pms.Services.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("assign/project")]
|
||||
public async Task<IActionResult> AssignOrganizationToProjectAsync([FromBody] AssignOrganizationDto model)
|
||||
{
|
||||
_logger.LogDebug("Started assigning organization {OrganizationId} to project {ProjectId} with service IDs {@ServiceIds}",
|
||||
model.OrganizationId, model.ProjectId, model.ServiceIds);
|
||||
|
||||
// Create DbContext for the method scope
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScope.CreateScope();
|
||||
|
||||
// Begin a database transaction
|
||||
await using var transaction = await _context.Database.BeginTransactionAsync();
|
||||
|
||||
try
|
||||
{
|
||||
// Get currently logged in employee
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var todaysDate = DateTime.UtcNow.Date;
|
||||
var today = DateTime.UtcNow.Date;
|
||||
|
||||
// Fetch all needed entities concurrently using the single context
|
||||
var projectServicesTask = Task.Run(async () =>
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
@ -289,7 +295,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
.Where(sp => model.ServiceIds.Contains(sp.ServiceId) && sp.ProjectId == model.ProjectId && sp.IsActive).ToListAsync();
|
||||
});
|
||||
|
||||
var projectOrganizationsTask = Task.Run(async () =>
|
||||
var projectOrgMappingsTask = Task.Run(async () =>
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await context.ProjectOrgMappings
|
||||
@ -303,7 +309,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
return await context.ServiceMasters.Where(s => model.ServiceIds.Contains(s.Id) && s.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
|
||||
var organizationTypeTask = Task.Run(async () =>
|
||||
var orgTypeTask = Task.Run(async () =>
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await context.OrgTypeMasters.FirstOrDefaultAsync(o => o.Id == model.OrganizationId);
|
||||
@ -315,7 +321,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
return await context.Organizations.FirstOrDefaultAsync(o => o.Id == model.OrganizationId);
|
||||
});
|
||||
|
||||
var parentorganizationTask = Task.Run(async () =>
|
||||
var parentOrgTask = Task.Run(async () =>
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await context.Organizations.FirstOrDefaultAsync(o => o.Id == model.ParentOrganizationId);
|
||||
@ -340,68 +346,82 @@ namespace Marco.Pms.Services.Controllers
|
||||
&& p.OrganizationTypeId == ServiceProvider);
|
||||
});
|
||||
|
||||
await Task.WhenAll(organizationTask, parentorganizationTask, projectTask, projectServicesTask, isPMCTask, isServiceProviderTask,
|
||||
serviceTask, organizationTypeTask, projectOrganizationsTask);
|
||||
await Task.WhenAll(projectTask, organizationTask, parentOrgTask, serviceTask, orgTypeTask, projectServicesTask, projectOrgMappingsTask, isPMCTask, isServiceProviderTask);
|
||||
|
||||
var services = serviceTask.Result;
|
||||
var organizationType = organizationTypeTask.Result;
|
||||
var organization = organizationTask.Result;
|
||||
var projectServices = projectServicesTask.Result;
|
||||
var parentorganization = parentorganizationTask.Result;
|
||||
var projectOrganizations = projectOrganizationsTask.Result;
|
||||
var project = projectTask.Result;
|
||||
var organization = organizationTask.Result;
|
||||
var parentOrganization = parentOrgTask.Result;
|
||||
var services = serviceTask.Result;
|
||||
var organizationType = orgTypeTask.Result;
|
||||
var projectServices = projectServicesTask.Result;
|
||||
var projectOrganizations = projectOrgMappingsTask.Result;
|
||||
var isPMC = isPMCTask.Result;
|
||||
var isServiceProvider = isServiceProviderTask.Result;
|
||||
|
||||
// Validation checks
|
||||
if (organization == null)
|
||||
{
|
||||
_logger.LogWarning("Organization with ID {OrganizationId} not found.", model.OrganizationId);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Organization not found", "Organization not found in database", 404));
|
||||
}
|
||||
if (project == null)
|
||||
{
|
||||
_logger.LogWarning("Project with ID {ProjectId} not found.", model.ProjectId);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Project not found", "Project not found in database", 404));
|
||||
}
|
||||
if (projectServices == null)
|
||||
if (services == null || !services.Any())
|
||||
{
|
||||
_logger.LogWarning("No services found for Service IDs {@ServiceIds}.", model.ServiceIds);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Project Service not found", "Project Service not found in database", 404));
|
||||
}
|
||||
|
||||
// Check whether mapping exists between service provider organization and tenant
|
||||
var serviceProviderTenantMapping = await _context.TenantOrgMappings
|
||||
.FirstOrDefaultAsync(spt => spt.OrganizationId == model.OrganizationId && spt.TenantId == project.TenantId && spt.IsActive);
|
||||
|
||||
if (serviceProviderTenantMapping == null)
|
||||
{
|
||||
var newServiceProviderTenantMapping = new TenantOrgMapping
|
||||
var newMapping = new TenantOrgMapping
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
SPRID = organization.SPRID,
|
||||
AssignedDate = DateTime.UtcNow,
|
||||
AssignedDate = today,
|
||||
IsActive = true,
|
||||
AssignedById = loggedInEmployee.Id,
|
||||
TenantId = project.TenantId
|
||||
};
|
||||
_context.TenantOrgMappings.Add(newServiceProviderTenantMapping);
|
||||
_context.TenantOrgMappings.Add(newMapping);
|
||||
_logger.LogInfo("Created new TenantOrgMapping for OrganizationId {OrganizationId} and TenantId {TenantId}",
|
||||
organization.Id, project.TenantId);
|
||||
}
|
||||
|
||||
List<ProjectOrgMapping> projectOrgMappings = new List<ProjectOrgMapping>();
|
||||
List<ProjectServiceMapping> projectServiceMappings = new List<ProjectServiceMapping>();
|
||||
|
||||
// Access control validations
|
||||
if (isPMC && model.OrganizationTypeId != ServiceProvider && model.OrganizationTypeId != SubContractorProvider)
|
||||
{
|
||||
_logger.LogWarning("PMCs cannot assign organization type {OrganizationTypeId}. UserId: {UserId}",
|
||||
model.OrganizationTypeId, loggedInEmployee.Id);
|
||||
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access Denied", "You don't have access to assign this type of organization", 403));
|
||||
}
|
||||
if (isServiceProvider && model.OrganizationTypeId == ServiceProvider)
|
||||
{
|
||||
_logger.LogWarning("Service providers cannot assign organization type {OrganizationTypeId}. UserId: {UserId}",
|
||||
model.OrganizationTypeId, loggedInEmployee.Id);
|
||||
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access Denied", "You don't have access to assign this type of organization", 403));
|
||||
}
|
||||
|
||||
var newProjectOrgMappings = new List<ProjectOrgMapping>();
|
||||
var newProjectServiceMappings = new List<ProjectServiceMapping>();
|
||||
|
||||
// Loop through each service to create mappings
|
||||
foreach (var serviceId in model.ServiceIds)
|
||||
{
|
||||
var service = await _context.ServiceMasters.FirstOrDefaultAsync(s => s.Id == serviceId);
|
||||
var service = services.FirstOrDefault(s => s.Id == serviceId);
|
||||
if (service == null)
|
||||
{
|
||||
_logger.LogWarning("Service with ID {ServiceId} not found.", serviceId);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Service not found", "Service not found in database", 404));
|
||||
}
|
||||
|
||||
var projectService = projectServices.FirstOrDefault(ps => ps.ServiceId == serviceId);
|
||||
if (projectService == null)
|
||||
{
|
||||
@ -411,54 +431,69 @@ namespace Marco.Pms.Services.Controllers
|
||||
ProjectId = project.Id,
|
||||
ServiceId = serviceId,
|
||||
TenantId = project.TenantId,
|
||||
PlannedStartDate = project.StartDate ?? DateTime.UtcNow,
|
||||
PlannedEndDate = project.EndDate ?? DateTime.UtcNow,
|
||||
ActualStartDate = DateTime.UtcNow,
|
||||
PlannedStartDate = project.StartDate ?? today,
|
||||
PlannedEndDate = project.EndDate ?? today,
|
||||
ActualStartDate = today,
|
||||
IsActive = true
|
||||
};
|
||||
projectServiceMappings.Add(projectService);
|
||||
newProjectServiceMappings.Add(projectService);
|
||||
}
|
||||
|
||||
// Check if the organization is already assigned for this service
|
||||
var existingAssignment = projectOrganizations.FirstOrDefault(po => po.ProjectService != null
|
||||
&& po.ProjectService.ProjectId == project.Id
|
||||
&& po.ProjectService.ServiceId == serviceId
|
||||
&& po.OrganizationId == model.OrganizationId);
|
||||
|
||||
if (existingAssignment != null)
|
||||
{
|
||||
_logger.LogWarning("Organization {OrganizationId} is already assigned to project {ProjectId} for service {ServiceId}.",
|
||||
model.OrganizationId, project.Id, serviceId);
|
||||
return Conflict(ApiResponse<object>.ErrorResponse("Organization already assigned", "Organization is already assigned to this project and service", 409));
|
||||
}
|
||||
|
||||
// Prepare new project-org mapping
|
||||
var projectOrgMapping = new ProjectOrgMapping
|
||||
{
|
||||
ProjectServiceId = projectService.Id,
|
||||
OrganizationId = model.OrganizationId,
|
||||
OrganizationTypeId = model.OrganizationTypeId,
|
||||
ParentOrganizationId = model.ParentOrganizationId ?? loggedInEmployee.OrganizationId,
|
||||
AssignedDate = DateTime.UtcNow,
|
||||
AssignedDate = today,
|
||||
AssignedById = loggedInEmployee.Id,
|
||||
TenantId = project.TenantId
|
||||
};
|
||||
var projectOrganization = projectOrganizations
|
||||
.FirstOrDefault(po => po.ProjectService != null && po.ProjectService.ProjectId == project.Id && po.ProjectService.ServiceId == serviceId
|
||||
&& po.OrganizationId == model.OrganizationId);
|
||||
if (projectOrganization != null)
|
||||
{
|
||||
return StatusCode(409, ApiResponse<object>.ErrorResponse("Organization is already assigned to this project", "Organization is already assigned to this project", 409));
|
||||
}
|
||||
projectOrgMappings.Add(projectOrgMapping);
|
||||
newProjectOrgMappings.Add(projectOrgMapping);
|
||||
}
|
||||
|
||||
if (projectServiceMappings.Any())
|
||||
// Save new project service mappings if any
|
||||
if (newProjectServiceMappings.Any())
|
||||
{
|
||||
_context.ProjectServiceMappings.AddRange(projectServiceMappings);
|
||||
_context.ProjectServiceMappings.AddRange(newProjectServiceMappings);
|
||||
await _context.SaveChangesAsync();
|
||||
_logger.LogInfo("Added {Count} new ProjectServiceMappings for ProjectId {ProjectId}.", newProjectServiceMappings.Count, project.Id);
|
||||
}
|
||||
|
||||
_context.ProjectOrgMappings.AddRange(projectOrgMappings);
|
||||
|
||||
// Save new project organization mappings
|
||||
_context.ProjectOrgMappings.AddRange(newProjectOrgMappings);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// Commit transaction
|
||||
await transaction.CommitAsync();
|
||||
|
||||
_logger.LogInfo("Assigned organization {OrganizationId} to project {ProjectId} successfully.", model.OrganizationId, model.ProjectId);
|
||||
|
||||
// Prepare response view models
|
||||
var organizationVm = _mapper.Map<BasicOrganizationVm>(organization);
|
||||
var parentorganizationVm = _mapper.Map<BasicOrganizationVm>(parentorganization);
|
||||
var projectvm = _mapper.Map<BasicProjectVM>(project);
|
||||
var parentOrganizationVm = _mapper.Map<BasicOrganizationVm>(parentOrganization);
|
||||
var projectVm = _mapper.Map<BasicProjectVM>(project);
|
||||
|
||||
var response = services.Select(s => new AssignOrganizationVm
|
||||
{
|
||||
Project = projectvm,
|
||||
Project = projectVm,
|
||||
OrganizationType = organizationType,
|
||||
Organization = organizationVm,
|
||||
ParentOrganization = parentorganizationVm,
|
||||
ParentOrganization = parentOrganizationVm,
|
||||
Service = _mapper.Map<ServiceMasterVM>(s)
|
||||
}).ToList();
|
||||
|
||||
@ -467,54 +502,53 @@ namespace Marco.Pms.Services.Controllers
|
||||
catch (DbUpdateException dbEx)
|
||||
{
|
||||
await transaction.RollbackAsync();
|
||||
|
||||
_logger.LogError(dbEx, "Database Exception has been occured, While assigning the organization to project");
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal error", "An database exception has been occured", 500));
|
||||
_logger.LogError(dbEx, "Database exception occurred while assigning organization {OrganizationId} to project {ProjectId}",
|
||||
model.OrganizationId, model.ProjectId);
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal error", "A database exception occurred", 500));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Exception has been occured, While assigned the organizatio to project");
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal error", "An internal exception has been occured", 500));
|
||||
await transaction.RollbackAsync();
|
||||
_logger.LogError(ex, "Unhandled exception occurred while assigning organization {OrganizationId} to project {ProjectId}",
|
||||
model.OrganizationId, model.ProjectId);
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal error", "An internal exception occurred", 500));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("assign/tenant/{organizationId}")]
|
||||
public async Task<IActionResult> AssignOrganizationToTenantAsync(Guid organizationId)
|
||||
{
|
||||
_logger.LogInfo("Started assigning organization {OrganizationId} to tenant {TenantId}", organizationId, tenantId);
|
||||
|
||||
// Create a DbContext instance for this method scope
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScope.CreateScope();
|
||||
|
||||
// Begin a database transaction
|
||||
await using var transaction = await _context.Database.BeginTransactionAsync();
|
||||
|
||||
try
|
||||
{
|
||||
// Get currently logged in employee for auditing purposes
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
var organizationTenantMappingTask = Task.Run(async () =>
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await context.TenantOrgMappings
|
||||
.FirstOrDefaultAsync(spt => spt.OrganizationId == organizationId && spt.TenantId == tenantId && spt.IsActive);
|
||||
});
|
||||
// Fetch existing tenant-organization mapping if any
|
||||
var organizationTenantMapping = await _context.TenantOrgMappings
|
||||
.FirstOrDefaultAsync(spt => spt.OrganizationId == organizationId && spt.TenantId == tenantId && spt.IsActive);
|
||||
|
||||
var organizationTask = Task.Run(async () =>
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await context.Organizations
|
||||
.FirstOrDefaultAsync(o => o.Id == organizationId);
|
||||
});
|
||||
|
||||
await Task.WhenAll(organizationTenantMappingTask, organizationTask);
|
||||
|
||||
var organizationTenantMapping = organizationTenantMappingTask.Result;
|
||||
var organization = organizationTask.Result;
|
||||
// Fetch the organization details
|
||||
var organization = await _context.Organizations.FirstOrDefaultAsync(o => o.Id == organizationId);
|
||||
|
||||
// Validate organization existence
|
||||
if (organization == null)
|
||||
{
|
||||
_logger.LogWarning("Organization with ID {OrganizationId} not found.", organizationId);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Organization not found", "Organization not found in database", 404));
|
||||
}
|
||||
|
||||
if (organizationTenantMapping == null)
|
||||
{
|
||||
var newOrganizationTenantMapping = new TenantOrgMapping
|
||||
// Create new tenant-organization mapping if none exists
|
||||
var newMapping = new TenantOrgMapping
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
SPRID = organization.SPRID,
|
||||
@ -523,26 +557,38 @@ namespace Marco.Pms.Services.Controllers
|
||||
AssignedById = loggedInEmployee.Id,
|
||||
TenantId = tenantId
|
||||
};
|
||||
_context.TenantOrgMappings.Add(newOrganizationTenantMapping);
|
||||
_context.TenantOrgMappings.Add(newMapping);
|
||||
await _context.SaveChangesAsync();
|
||||
await transaction.CommitAsync();
|
||||
|
||||
_logger.LogInfo("Assigned organization {OrganizationId} to tenant {TenantId} successfully.", organizationId, tenantId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInfo("Organization {OrganizationId} is already assigned to tenant {TenantId}. No action taken.", organizationId, tenantId);
|
||||
// Commit transaction anyway to complete scope cleanly (optional)
|
||||
await transaction.CommitAsync();
|
||||
}
|
||||
|
||||
// Prepare response view model
|
||||
var response = _mapper.Map<BasicOrganizationVm>(organization);
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Organization has been assigned to tenant", 200));
|
||||
}
|
||||
catch (DbUpdateException dbEx)
|
||||
{
|
||||
await transaction.RollbackAsync();
|
||||
|
||||
_logger.LogError(dbEx, "Database Exception has been occured, While assigning the organization to project");
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal error", "An database exception has been occured", 500));
|
||||
_logger.LogError(dbEx, "Database exception occurred while assigning organization {OrganizationId} to tenant {TenantId}.", organizationId, tenantId);
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal error", "A database exception has occurred", 500));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Exception has been occured, While assigned the organizatio to project");
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal error", "An internal exception has been occured", 500));
|
||||
await transaction.RollbackAsync();
|
||||
_logger.LogError(ex, "Unhandled exception occurred while assigning organization {OrganizationId} to tenant {TenantId}.", organizationId, tenantId);
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal error", "An internal exception has occurred", 500));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region =================================================================== Put Functions ===================================================================
|
||||
|
Loading…
x
Reference in New Issue
Block a user