From ceddddf6fe38253aaa480b84f8f5d5dd62d9d8d4 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Tue, 1 Apr 2025 10:54:08 +0530 Subject: [PATCH] Perform CRUD oprations on Tenanat --- .../Dtos/Tenant/CreateTenantDto.cs | 26 ++ .../Dtos/Tenant/UpdateTenantDto.cs | 15 ++ Marco.Pms.Model/Mapper/TenantMapper.cs | 38 +++ .../Utilities/RoleConfiguration.cs | 10 + Marco.Pms.Model/ViewModels/Tenant/TenantVM.cs | 15 ++ .../Controllers/TenantController.cs | 239 ++++++++++++++++++ .../Data/RolesCofiguration.json | 6 + Marco.Pms.Services/Marco.Pms.Services.csproj | 1 + Marco.Pms.Services/Service/ILoggingService.cs | 2 +- Marco.Pms.Services/Service/LoggingServices.cs | 3 +- Marco.Pms.Services/appsettings.json | 3 +- 11 files changed, 354 insertions(+), 4 deletions(-) create mode 100644 Marco.Pms.Model/Dtos/Tenant/CreateTenantDto.cs create mode 100644 Marco.Pms.Model/Dtos/Tenant/UpdateTenantDto.cs create mode 100644 Marco.Pms.Model/Mapper/TenantMapper.cs create mode 100644 Marco.Pms.Model/Utilities/RoleConfiguration.cs create mode 100644 Marco.Pms.Model/ViewModels/Tenant/TenantVM.cs create mode 100644 Marco.Pms.Services/Controllers/TenantController.cs create mode 100644 Marco.Pms.Services/Data/RolesCofiguration.json diff --git a/Marco.Pms.Model/Dtos/Tenant/CreateTenantDto.cs b/Marco.Pms.Model/Dtos/Tenant/CreateTenantDto.cs new file mode 100644 index 0000000..6a4107a --- /dev/null +++ b/Marco.Pms.Model/Dtos/Tenant/CreateTenantDto.cs @@ -0,0 +1,26 @@ +using System.ComponentModel.DataAnnotations; + +namespace Marco.Pms.Model.Dtos.Tenant +{ + public class CreateTenantDto + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [MinLength(6)] + [RegularExpression(@"^(?=.*[^a-zA-Z0-9])(?=.*\d)(?=.*[A-Z]).+$", ErrorMessage = "Passwords must have at least one non-alphanumeric character, at least one digit ('0'-'9'), and at least one uppercase ('A'-'Z').")] + public string Password { get; set; } + + + public string OrganizatioinName { get; set; } + public string? About { get; set; } + public string? OragnizationSize { get; set; } + public int IndustryId { get; set; } + public string Website { get; set; } + public string Name { get; set; } + public string ContactNumber { get; set; } + public DateTime OnBoardingDate { get; set; } + } +} diff --git a/Marco.Pms.Model/Dtos/Tenant/UpdateTenantDto.cs b/Marco.Pms.Model/Dtos/Tenant/UpdateTenantDto.cs new file mode 100644 index 0000000..687d4d6 --- /dev/null +++ b/Marco.Pms.Model/Dtos/Tenant/UpdateTenantDto.cs @@ -0,0 +1,15 @@ +namespace Marco.Pms.Model.Dtos.Tenant +{ + public class UpdateTenantDto + { + public string OrganizatioinName { get; set; } + public string About { get; set; } + public string Website { get; set; } + public string Name { get; set; } + public string ContactNumber { get; set; } + + public string? OragnizationSize { get; set; } + public int IndustryId { get; set; } + public DateTime OnBoardingDate { get; set; } + } +} diff --git a/Marco.Pms.Model/Mapper/TenantMapper.cs b/Marco.Pms.Model/Mapper/TenantMapper.cs new file mode 100644 index 0000000..0d8be9b --- /dev/null +++ b/Marco.Pms.Model/Mapper/TenantMapper.cs @@ -0,0 +1,38 @@ +using Marco.Pms.Model.Dtos.Tenant; +using Marco.Pms.Model.Entitlements; +using Marco.Pms.Model.ViewModels.Tenant; + +namespace Marco.Pms.Model.Mapper +{ + public static class TenantMapper + { + public static Tenant CreateDtoToTenant(this CreateTenantDto createTenant) + { + return new Tenant + { + Name = createTenant.OrganizatioinName, + Description = createTenant.About, + DomainName = createTenant.Website, + ContactName = createTenant.Name, + OragnizationSize = createTenant.OragnizationSize, + IndustryId = createTenant.IndustryId, + ContactNumber = createTenant.ContactNumber, + OnBoardingDate = createTenant.OnBoardingDate, + }; + } + public static TenantVM ToTenantVMFromTenant(this Tenant tenant) + { + return new TenantVM + { + OrganizationName = tenant.Name, + About = tenant.Description, + Website = tenant.DomainName, + Name = tenant.ContactName, + OragnizationSize = tenant.OragnizationSize, + IndustryId = tenant.IndustryId, + ContactNumber = tenant.ContactNumber, + OnBoardingDate = tenant.OnBoardingDate, + }; + } + } +} diff --git a/Marco.Pms.Model/Utilities/RoleConfiguration.cs b/Marco.Pms.Model/Utilities/RoleConfiguration.cs new file mode 100644 index 0000000..9b45fe4 --- /dev/null +++ b/Marco.Pms.Model/Utilities/RoleConfiguration.cs @@ -0,0 +1,10 @@ +namespace Marco.Pms.Model.Utilities +{ + public class RoleConfiguration + { + public string JobRoleName { get; set; } + public string JobRoleDescription { get; set; } + public string RoleName { get; set; } + public string RoleDescription { get; set; } + } +} diff --git a/Marco.Pms.Model/ViewModels/Tenant/TenantVM.cs b/Marco.Pms.Model/ViewModels/Tenant/TenantVM.cs new file mode 100644 index 0000000..dd797ec --- /dev/null +++ b/Marco.Pms.Model/ViewModels/Tenant/TenantVM.cs @@ -0,0 +1,15 @@ +namespace Marco.Pms.Model.ViewModels.Tenant +{ + public class TenantVM + { + public string OrganizationName { get; set; } + public string About { get; set; } + public string Website { get; set; } + public string Name { get; set; } + public string ContactNumber { get; set; } + + public string? OragnizationSize { get; set; } + public int? IndustryId { get; set; } + public DateTime OnBoardingDate { get; set; } + } +} diff --git a/Marco.Pms.Services/Controllers/TenantController.cs b/Marco.Pms.Services/Controllers/TenantController.cs new file mode 100644 index 0000000..5693845 --- /dev/null +++ b/Marco.Pms.Services/Controllers/TenantController.cs @@ -0,0 +1,239 @@ +using System.Text.Json; +using Marco.Pms.DataAccess.Data; +using Marco.Pms.Model.Dtos.Tenant; +using Marco.Pms.Model.Employees; +using Marco.Pms.Model.Entitlements; +using Marco.Pms.Model.Mapper; +using Marco.Pms.Model.Utilities; +using Marco.Pms.Model.ViewModels.Tenant; +using MarcoBMS.Services.Service; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace Marco.Pms.Services.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class TenantController : ControllerBase + { + private readonly string jsonString = System.IO.File.ReadAllText("Data/RolesCofiguration.json"); + private readonly ApplicationDbContext _context; + private readonly UserManager _userManager; + private readonly ILoggingService _logger; + public TenantController(ApplicationDbContext context, UserManager userManager, ILoggingService logger) + { + _context = context; + _userManager = userManager; + _logger = logger; + } + [HttpPost] + public async Task CreateTenant([FromForm] CreateTenantDto createTenantDto) + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + var existedUser = await _userManager.FindByEmailAsync(createTenantDto.Email); + var existedTenanat = await _context.Tenants.FirstOrDefaultAsync(t => t.Name == createTenantDto.OrganizatioinName); + if (existedUser == null && existedTenanat == null) + { + var transaction = _context.Database.BeginTransaction(); + Tenant newTenant = createTenantDto.CreateDtoToTenant(); + _context.Tenants.Add(newTenant); + await _context.SaveChangesAsync(); + + //Tenant? tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.Name == newTenant.Name); + + if (newTenant != null) + { + RoleConfiguration settings = JsonSerializer.Deserialize(jsonString); + var TenantId = newTenant.Id; + JobRole jobRole = new JobRole + { + Name = settings.JobRoleName, + Description = settings.JobRoleDescription, + TenantId = TenantId + }; + + ApplicationRole role = new ApplicationRole + { + Role = settings.RoleName, + Description = settings.RoleDescription, + TenantId = TenantId + }; + + _context.JobRoles.Add(jobRole); + _context.ApplicationRoles.Add(role); + await _context.SaveChangesAsync(); + + + List permissions = await _context.FeaturePermissions.AsNoTracking().ToListAsync(); + List rolePermissionMappings = new List(); + + foreach (var permission in permissions) + { + var item = new RolePermissionMappings() { ApplicationRoleId = role.Id, FeaturePermissionId = permission.Id }; + bool assigned = _context.RolePermissionMappings.Any(c => c.ApplicationRoleId == role.Id && c.FeaturePermissionId == permission.Id); + if (permission.IsEnabled && !assigned) + rolePermissionMappings.Add(item); + + } + _context.RolePermissionMappings.AddRange(rolePermissionMappings); + await _context.SaveChangesAsync(); + + var user = new ApplicationUser + { + UserName = createTenantDto.Email, + Email = createTenantDto.Email, + TenantId = TenantId, + IsRootUser = true, + EmailConfirmed = true + }; + + var result = await _userManager.CreateAsync(user, createTenantDto.Password); + if (result.Succeeded) + { + try + { + Employee newEmployee = CreateTenantDtoToEmployee(createTenantDto, TenantId, user.Id, jobRole.Id); + _context.Employees.Add(newEmployee); + await _context.SaveChangesAsync(); + + var employeeRoleMapping = new EmployeeRoleMapping + { + EmployeeId = newEmployee.Id, + RoleId = role.Id, + TenantId = TenantId, + IsEnabled = true + }; + _context.EmployeeRoleMappings.Add(employeeRoleMapping); + await _context.SaveChangesAsync(); + transaction.Commit(); + return Ok(ApiResponse.SuccessResponse(result.Succeeded, "Tenant created successfully.", 200)); + }catch(Exception ex) + { + transaction.Rollback(); + _logger.LogError("{Error}", ex); + } + } + else + { + // Log the errors for debugging + foreach (var error in result.Errors) + { + // Log error.Description + _logger.LogError("{Error}", error.Description); + } + transaction.Rollback(); + return BadRequest("Failed to create the root user."); + } + } + transaction.Rollback(); + return BadRequest("Falied to create Tenant"); + + + } + return BadRequest("Tenant Already Exists"); + + } + + [HttpGet("profile/{tenantId}")] + public async Task GetTenantProfile(int tenantId) + { + if (tenantId <= 0) + { + return BadRequest("Tenant Id is required and must be greater than zero."); + } + var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.Id == tenantId && t.IsActive ==true); + if (tenant == null) + { + return NotFound("Tenant Not Found"); + } + TenantVM tenantVM = tenant.ToTenantVMFromTenant(); + return Ok(ApiResponse.SuccessResponse(tenantVM, "Tenant Profile.", 200)); + } + + [HttpPost("edit/{tenantId}")] + public async Task SuspendTenant(int tenantId, UpdateTenantDto model) + { + if (tenantId <= 0) + { + return BadRequest("Tenant Id is required and must be greater than zero."); + } + var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.Id == tenantId && t.IsActive == true); + //var user = await _context.ApplicationUsers.FirstOrDefaultAsync(u => u.TenantId == tenantId && u.IsRootUser == true); + //var employee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == tenantId && e.ApplicationUserId == user.Id); + if (tenant == null) + { + return NotFound("Tenant Not Found"); + } + tenant.Name = model.OrganizatioinName; + tenant.DomainName = model.Website; + tenant.ContactName = model.Name; + tenant.Description = model.About; + tenant.ContactNumber = model.ContactNumber; + tenant.OnBoardingDate = model.OnBoardingDate; + + await _context.SaveChangesAsync(); + TenantVM tenantVM = tenant.ToTenantVMFromTenant(); + return Ok(ApiResponse.SuccessResponse(tenantVM, "Tenant Profile.", 200)); + } + + [HttpDelete("suspend/{tenantId}")] + public async Task SuspendTenant(int tenantId) + { + if (tenantId <= 0) + { + return BadRequest("Tenant Id is required and must be greater than zero."); + } + var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.Id == tenantId); + var user = await _context.ApplicationUsers.FirstOrDefaultAsync(u => u.TenantId == tenantId && u.IsRootUser == true); + if (tenant != null && user != null) + { + var employee = await _context.Employees.FirstOrDefaultAsync(e => e.TenantId == tenantId && e.ApplicationUserId == user.Id); + tenant.IsActive = false; + user.IsActive = false; + user.IsRootUser = false; + employee.IsActive = false; + + + var result = await _userManager.UpdateAsync(user); + _context.Tenants.Update(tenant); + _context.Employees.Update(employee); + await _context.SaveChangesAsync(); + return Ok("Tenant is Suspended"); + } + return NotFound("Tenant Not Found"); + } + + private static Employee CreateTenantDtoToEmployee(CreateTenantDto model, int TenantId, string? ApplicationUserId, int jobRoleId) + { + return new Employee + { + ApplicationUserId = ApplicationUserId, + FirstName = model.Name, + LastName = "", + Email = model.Email, + TenantId = TenantId, + CurrentAddress = "", + BirthDate = DateTime.UtcNow, + EmergencyPhoneNumber = "", + EmergencyContactPerson = "", + AadharNumber = "", + Gender = "", + MiddleName = "", + PanNumber = "", + PeramnentAddress = "", + PhoneNumber = model.ContactNumber, + Photo = null, // GetFileDetails(model.Photo).Result.FileData, + JobRoleId = jobRoleId, + JoiningDate = DateTime.UtcNow, + + }; + } + + + } + +} diff --git a/Marco.Pms.Services/Data/RolesCofiguration.json b/Marco.Pms.Services/Data/RolesCofiguration.json new file mode 100644 index 0000000..aa9034e --- /dev/null +++ b/Marco.Pms.Services/Data/RolesCofiguration.json @@ -0,0 +1,6 @@ +{ + "JobRoleName": "Admin", + "JobRoleDescription": "Admin", + "RoleName": "SuperUser", + "RoleDescription": "SuperUser" +} \ No newline at end of file diff --git a/Marco.Pms.Services/Marco.Pms.Services.csproj b/Marco.Pms.Services/Marco.Pms.Services.csproj index 4e70e23..0b5889e 100644 --- a/Marco.Pms.Services/Marco.Pms.Services.csproj +++ b/Marco.Pms.Services/Marco.Pms.Services.csproj @@ -44,6 +44,7 @@ + diff --git a/Marco.Pms.Services/Service/ILoggingService.cs b/Marco.Pms.Services/Service/ILoggingService.cs index 744ab19..39dbb00 100644 --- a/Marco.Pms.Services/Service/ILoggingService.cs +++ b/Marco.Pms.Services/Service/ILoggingService.cs @@ -6,7 +6,7 @@ namespace MarcoBMS.Services.Service { void LogInfo(string? message, params object[]? args); void LogWarning(string? message, params object[]? args); - void LogError(Exception? ex, string? message, params object[]? args); + void LogError(string? message, params object[]? args); } } diff --git a/Marco.Pms.Services/Service/LoggingServices.cs b/Marco.Pms.Services/Service/LoggingServices.cs index aec6257..10fc5ab 100644 --- a/Marco.Pms.Services/Service/LoggingServices.cs +++ b/Marco.Pms.Services/Service/LoggingServices.cs @@ -11,9 +11,8 @@ namespace MarcoBMS.Services.Service _logger = logger; } - public void LogError(Exception? ex, string? message, params object[]? args) + public void LogError(string? message, params object[]? args) { - using (LogContext.PushProperty("Error", ex)) using (LogContext.PushProperty("LogLevel", "Error")) _logger.LogError(message, args); } diff --git a/Marco.Pms.Services/appsettings.json b/Marco.Pms.Services/appsettings.json index bb0e7e5..e31eefd 100644 --- a/Marco.Pms.Services/appsettings.json +++ b/Marco.Pms.Services/appsettings.json @@ -65,7 +65,8 @@ "ConnectionStrings": { // "DefaultConnectionString": "Server=103.50.160.45;User ID=marcowvh_admin;Password=Marcoemp@123;Database=marcowvh_empattendanceci", //"DefaultConnectionString": "Server=localhost;port=3333;User ID=root;Password=root;Database=MarcoBMS1", - "DefaultConnectionString": "Server=147.93.98.152;User ID=devuser;Password=AppUser@123$;Database=MarcoBMS1" + "DefaultConnectionString": "Server=localhost;port=3306;User ID=root;Password=Ashutosh;Database=MarcoBMS2" + //"DefaultConnectionString": "Server=147.93.98.152;User ID=devuser;Password=AppUser@123$;Database=MarcoBMS1" }, "AppSettings": { "WebFrontendUrl": "http://localhost:5173",