Organization_Management #142

Merged
ashutosh.nehete merged 92 commits from Organization_Management into main 2025-09-30 09:05:14 +00:00
Showing only changes of commit fef3db297c - Show all commits

View File

@ -125,38 +125,43 @@ namespace Marco.Pms.Services.Controllers
public async Task<IActionResult> CreateOrganizationAsync([FromBody] CreateOrganizationDto model) public async Task<IActionResult> CreateOrganizationAsync([FromBody] CreateOrganizationDto model)
{ {
await using var _context = await _dbContextFactory.CreateDbContextAsync(); await using var _context = await _dbContextFactory.CreateDbContextAsync();
using var scope = _serviceScope.CreateScope(); using var scope = _serviceScope.CreateScope(); // Create scope for scoped services
await using var transaction = await _context.Database.BeginTransactionAsync(); await using var transaction = await _context.Database.BeginTransactionAsync();
try try
{ {
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
// Concurrent permission check and organization existence check
var hasPermissionTask = Task.Run(async () => var hasPermissionTask = Task.Run(async () =>
{ {
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>(); var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.AddOrganization, loggedInEmployee.Id); return await permissionService.HasPermission(PermissionsMaster.AddOrganization, loggedInEmployee.Id);
}); });
var IsPrimaryOrganizationTask = Task.Run(async () => var isPrimaryOrganizationTask = Task.Run(async () =>
{ {
await using var context = await _dbContextFactory.CreateDbContextAsync(); await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.Tenants.AnyAsync(t => t.OrganizationId == loggedInEmployee.OrganizationId); return await context.Tenants.AnyAsync(t => t.OrganizationId == loggedInEmployee.OrganizationId);
}); });
await Task.WhenAll(hasPermissionTask, IsPrimaryOrganizationTask); await Task.WhenAll(hasPermissionTask, isPrimaryOrganizationTask);
var hasPermission = hasPermissionTask.Result; bool hasPermission = hasPermissionTask.Result;
var IsPrimaryOrganization = IsPrimaryOrganizationTask.Result; bool isPrimaryOrganization = isPrimaryOrganizationTask.Result;
if (!hasPermission && !IsPrimaryOrganization) // Check user access permission
if (!hasPermission && !isPrimaryOrganization)
{ {
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access Denied", "You do not have permission to create new service provider.", 403)); _logger.LogWarning("User {EmployeeId} attempted to create a new organization without permission", loggedInEmployee.Id);
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access Denied", "You do not have permission to create new organization.", 403));
} }
// Get last SPRID and increment for new organization
var lastOrganization = await _context.Organizations.OrderByDescending(sp => sp.SPRID).FirstOrDefaultAsync(); var lastOrganization = await _context.Organizations.OrderByDescending(sp => sp.SPRID).FirstOrDefaultAsync();
double lastSPRID = lastOrganization?.SPRID ?? 5400;
var lastSPRID = lastOrganization != null ? lastOrganization.SPRID : 5400; // Map DTO to entity and set defaults
Organization organization = _mapper.Map<Organization>(model); Organization organization = _mapper.Map<Organization>(model);
organization.SPRID = lastSPRID + 1; organization.SPRID = lastSPRID + 1;
organization.CreatedAt = DateTime.UtcNow; organization.CreatedAt = DateTime.UtcNow;
@ -165,8 +170,19 @@ namespace Marco.Pms.Services.Controllers
_context.Organizations.Add(organization); _context.Organizations.Add(organization);
await _context.SaveChangesAsync(); // Create mapping for organization tenant
var newOrganizationTenantMapping = new TenantOrgMapping
{
OrganizationId = organization.Id,
SPRID = organization.SPRID,
AssignedDate = DateTime.UtcNow,
IsActive = true,
AssignedById = loggedInEmployee.Id,
TenantId = tenantId
};
_context.TenantOrgMappings.Add(newOrganizationTenantMapping);
// Prepare user creation for identity
var user = new ApplicationUser var user = new ApplicationUser
{ {
UserName = model.Email, UserName = model.Email,
@ -174,25 +190,29 @@ namespace Marco.Pms.Services.Controllers
EmailConfirmed = true EmailConfirmed = true
}; };
var _configuration = scope.ServiceProvider.GetRequiredService<IConfiguration>(); var configuration = scope.ServiceProvider.GetRequiredService<IConfiguration>();
var _emailSender = scope.ServiceProvider.GetRequiredService<IEmailSender>(); var emailSender = scope.ServiceProvider.GetRequiredService<IEmailSender>();
var _userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>(); var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
// Create Identity User // Create Identity user with a default password (recommend to improve password handling)
var result = await _userManager.CreateAsync(user, "User@123"); var result = await userManager.CreateAsync(user, "User@123");
if (!result.Succeeded) if (!result.Succeeded)
{
_logger.LogWarning("Failed to create identity user for email {Email}: {Errors}", model.Email, result.Errors);
return BadRequest(ApiResponse<object>.ErrorResponse("Failed to create user", result.Errors, 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Failed to create user", result.Errors, 400));
}
var jobRole = await _context.JobRoles.FirstOrDefaultAsync(jr => jr.Name == "Admin" && jr.TenantId == tenantId); // Get admin job role or fallback role of the tenant
if (jobRole == null) var jobRole = await _context.JobRoles.FirstOrDefaultAsync(jr => jr.Name == "Admin" && jr.TenantId == tenantId)
jobRole = await _context.JobRoles.FirstOrDefaultAsync(jr => jr.TenantId == tenantId); ?? await _context.JobRoles.FirstOrDefaultAsync(jr => jr.TenantId == tenantId);
var fullName = model.ContactPerson.Split(" "); // Parse full name safely (consider improving split logic for multi-part names)
var fullName = model.ContactPerson?.Split(' ', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
Employee newEmployee = new Employee Employee newEmployee = new Employee
{ {
FirstName = fullName[0], FirstName = fullName.Length > 0 ? fullName[0] : string.Empty,
LastName = fullName[fullName.Length - 1], LastName = fullName.Length > 1 ? fullName[^1] : string.Empty,
Email = model.Email, Email = model.Email,
PermanentAddress = model.Address, PermanentAddress = model.Address,
CurrentAddress = model.Address, CurrentAddress = model.Address,
@ -202,54 +222,54 @@ namespace Marco.Pms.Services.Controllers
IsActive = true, IsActive = true,
IsSystem = false, IsSystem = false,
IsPrimary = true, IsPrimary = true,
OrganizationId = organization.Id OrganizationId = organization.Id,
HasApplicationAccess = user.Id != null
}; };
if (newEmployee.ApplicationUserId != null)
{
newEmployee.HasApplicationAccess = true;
}
_context.Employees.Add(newEmployee); _context.Employees.Add(newEmployee);
var serviceOrgMapping = model.ServiceIds.Select(s => new OrgServiceMapping // Map organization services
var serviceOrgMappings = model.ServiceIds.Select(s => new OrgServiceMapping
{ {
ServiceId = s, ServiceId = s,
OrganizationId = organization.Id OrganizationId = organization.Id
}).ToList(); }).ToList();
_context.OrgServiceMappings.AddRange(serviceOrgMapping); _context.OrgServiceMappings.AddRange(serviceOrgMappings);
// Persist all changes
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
await transaction.CommitAsync(); await transaction.CommitAsync();
/* SEND USER REGISTRATION MAIL*/ // Send user registration email with password reset link
var token = await _userManager.GeneratePasswordResetTokenAsync(user); var token = await userManager.GeneratePasswordResetTokenAsync(user);
var resetLink = $"{_configuration["AppSettings:WebFrontendUrl"]}/reset-password?token={WebUtility.UrlEncode(token)}"; var resetLink = $"{configuration["AppSettings:WebFrontendUrl"]}/reset-password?token={WebUtility.UrlEncode(token)}";
if (newEmployee.FirstName != null) if (!string.IsNullOrEmpty(newEmployee.FirstName))
{ {
await _emailSender.SendResetPasswordEmailOnRegister(user.Email, newEmployee.FirstName, resetLink); await emailSender.SendResetPasswordEmailOnRegister(user.Email, newEmployee.FirstName, resetLink);
} }
// Prepare response DTO
var response = _mapper.Map<OrganizationVM>(organization); var response = _mapper.Map<OrganizationVM>(organization);
response.CreatedBy = _mapper.Map<BasicEmployeeVM>(loggedInEmployee); response.CreatedBy = _mapper.Map<BasicEmployeeVM>(loggedInEmployee);
return Ok(ApiResponse<object>.SuccessResponse(response, "Successfully created the service provider", 200)); return Ok(ApiResponse<object>.SuccessResponse(response, "Successfully created the organization", 200));
} }
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
{ {
await transaction.RollbackAsync(); await transaction.RollbackAsync();
_logger.LogError(dbEx, "Database exception occurred while creating organization");
_logger.LogError(dbEx, "Database Exception has been occured"); return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal error", "A database exception occurred", 500));
return StatusCode(500, 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"); _logger.LogError(ex, "Unexpected exception occurred while creating organization");
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal error", "An internal exception has been occured", 500)); return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal error", "An unexpected error occurred", 500));
} }
} }
[HttpPost("assign/project")] [HttpPost("assign/project")]
public async Task<IActionResult> AssignOrganizationToProjectAsync([FromBody] AssignOrganizationDto model) public async Task<IActionResult> AssignOrganizationToProjectAsync([FromBody] AssignOrganizationDto model)
{ {
@ -468,7 +488,7 @@ namespace Marco.Pms.Services.Controllers
{ {
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var serviceProviderTenantMappingTask = Task.Run(async () => var organizationTenantMappingTask = Task.Run(async () =>
{ {
await using var context = await _dbContextFactory.CreateDbContextAsync(); await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.TenantOrgMappings return await context.TenantOrgMappings
@ -482,9 +502,9 @@ namespace Marco.Pms.Services.Controllers
.FirstOrDefaultAsync(o => o.Id == organizationId); .FirstOrDefaultAsync(o => o.Id == organizationId);
}); });
await Task.WhenAll(serviceProviderTenantMappingTask, organizationTask); await Task.WhenAll(organizationTenantMappingTask, organizationTask);
var serviceProviderTenantMapping = serviceProviderTenantMappingTask.Result; var organizationTenantMapping = organizationTenantMappingTask.Result;
var organization = organizationTask.Result; var organization = organizationTask.Result;
if (organization == null) if (organization == null)
@ -492,9 +512,9 @@ namespace Marco.Pms.Services.Controllers
return NotFound(ApiResponse<object>.ErrorResponse("Organization not found", "Organization not found in database", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Organization not found", "Organization not found in database", 404));
} }
if (serviceProviderTenantMapping == null) if (organizationTenantMapping == null)
{ {
var newServiceProviderTenantMapping = new TenantOrgMapping var newOrganizationTenantMapping = new TenantOrgMapping
{ {
OrganizationId = organization.Id, OrganizationId = organization.Id,
SPRID = organization.SPRID, SPRID = organization.SPRID,
@ -503,7 +523,7 @@ namespace Marco.Pms.Services.Controllers
AssignedById = loggedInEmployee.Id, AssignedById = loggedInEmployee.Id,
TenantId = tenantId TenantId = tenantId
}; };
_context.TenantOrgMappings.Add(newServiceProviderTenantMapping); _context.TenantOrgMappings.Add(newOrganizationTenantMapping);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
await transaction.CommitAsync(); await transaction.CommitAsync();
} }