From 1e66dd20179d3b47e745e4740c4995d694b22602 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Thu, 18 Sep 2025 17:23:50 +0530 Subject: [PATCH] Added the deassiged service from project API --- .../Dtos/Projects/AssignServiceDto.cs | 2 + .../Dtos/Projects/DeassignServiceDto.cs | 8 + .../ViewModels/Projects/ProjectServiceVM.cs | 14 ++ .../Controllers/OrganizationController.cs | 11 +- .../Controllers/ProjectController.cs | 42 ++++- Marco.Pms.Services/Service/ProjectServices.cs | 144 ++++++++++++++++++ .../ServiceInterfaces/IProjectServices.cs | 4 + 7 files changed, 219 insertions(+), 6 deletions(-) create mode 100644 Marco.Pms.Model/Dtos/Projects/DeassignServiceDto.cs create mode 100644 Marco.Pms.Model/ViewModels/Projects/ProjectServiceVM.cs diff --git a/Marco.Pms.Model/Dtos/Projects/AssignServiceDto.cs b/Marco.Pms.Model/Dtos/Projects/AssignServiceDto.cs index 4049ccc..37f8d13 100644 --- a/Marco.Pms.Model/Dtos/Projects/AssignServiceDto.cs +++ b/Marco.Pms.Model/Dtos/Projects/AssignServiceDto.cs @@ -4,5 +4,7 @@ { public required Guid ProjectId { get; set; } public required List ServiceIds { get; set; } + public DateTime PlannedStartDate { get; set; } + public DateTime PlannedEndDate { get; set; } } } diff --git a/Marco.Pms.Model/Dtos/Projects/DeassignServiceDto.cs b/Marco.Pms.Model/Dtos/Projects/DeassignServiceDto.cs new file mode 100644 index 0000000..c987b38 --- /dev/null +++ b/Marco.Pms.Model/Dtos/Projects/DeassignServiceDto.cs @@ -0,0 +1,8 @@ +namespace Marco.Pms.Model.Dtos.Projects +{ + public class DeassignServiceDto + { + public required Guid ProjectId { get; set; } + public required List ServiceIds { get; set; } + } +} diff --git a/Marco.Pms.Model/ViewModels/Projects/ProjectServiceVM.cs b/Marco.Pms.Model/ViewModels/Projects/ProjectServiceVM.cs new file mode 100644 index 0000000..194889c --- /dev/null +++ b/Marco.Pms.Model/ViewModels/Projects/ProjectServiceVM.cs @@ -0,0 +1,14 @@ +using Marco.Pms.Model.ViewModels.Master; + +namespace Marco.Pms.Model.ViewModels.Projects +{ + public class ProjectServiceVM + { + public BasicProjectVM? Project { get; set; } + public ServiceMasterVM? Service { get; set; } + public DateTime PlannedStartDate { get; set; } + public DateTime PlannedEndDate { get; set; } + public DateTime ActualStartDate { get; set; } + public DateTime? ActualEndDate { get; set; } + } +} diff --git a/Marco.Pms.Services/Controllers/OrganizationController.cs b/Marco.Pms.Services/Controllers/OrganizationController.cs index e4a2948..8928983 100644 --- a/Marco.Pms.Services/Controllers/OrganizationController.cs +++ b/Marco.Pms.Services/Controllers/OrganizationController.cs @@ -253,8 +253,7 @@ namespace Marco.Pms.Services.Controllers { await using var context = await _dbContextFactory.CreateDbContextAsync(); return await context.ProjectServiceMappings - .Where(sp => model.ServiceIds.Contains(sp.ServiceId) && sp.ProjectId == model.ProjectId && sp.PlannedEndDate.Date <= todaysDate - && sp.PlannedStartDate.Date >= todaysDate && sp.ActualStartDate.Date >= todaysDate).ToListAsync(); + .Where(sp => model.ServiceIds.Contains(sp.ServiceId) && sp.ProjectId == model.ProjectId && sp.IsActive).ToListAsync(); }); var serviceTask = Task.Run(async () => @@ -361,7 +360,8 @@ namespace Marco.Pms.Services.Controllers TenantId = project.TenantId, PlannedStartDate = project.StartDate ?? DateTime.UtcNow, PlannedEndDate = project.EndDate ?? DateTime.UtcNow, - ActualStartDate = DateTime.UtcNow + ActualStartDate = DateTime.UtcNow, + IsActive = true }; _context.ProjectServiceMappings.Add(projectService); } @@ -380,6 +380,7 @@ namespace Marco.Pms.Services.Controllers _context.ProjectOrgMappings.AddRange(projectOrgMappings); await _context.SaveChangesAsync(); + await transaction.CommitAsync(); var organizationVm = _mapper.Map(organization); var parentorganizationVm = _mapper.Map(parentorganization); @@ -400,12 +401,12 @@ namespace Marco.Pms.Services.Controllers { await transaction.RollbackAsync(); - _logger.LogError(dbEx, "Database Exception has been occured"); + _logger.LogError(dbEx, "Database Exception has been occured, While assigning the organization to project"); return StatusCode(500, ApiResponse.ErrorResponse("Internal error", "An database exception has been occured", 500)); } catch (Exception ex) { - _logger.LogError(ex, "Exception has been occured"); + _logger.LogError(ex, "Exception has been occured, While assigned the organizatio to project"); return StatusCode(500, ApiResponse.ErrorResponse("Internal error", "An internal exception has been occured", 500)); } } diff --git a/Marco.Pms.Services/Controllers/ProjectController.cs b/Marco.Pms.Services/Controllers/ProjectController.cs index f5aa8f8..d786fe5 100644 --- a/Marco.Pms.Services/Controllers/ProjectController.cs +++ b/Marco.Pms.Services/Controllers/ProjectController.cs @@ -1,4 +1,5 @@ using Marco.Pms.Model.Dtos.Project; +using Marco.Pms.Model.Dtos.Projects; using Marco.Pms.Model.Dtos.Util; using Marco.Pms.Model.Employees; using Marco.Pms.Model.Utilities; @@ -455,14 +456,16 @@ namespace MarcoBMS.Services.Controllers #endregion - [HttpPost("assign/project-level-permission")] + #region =================================================================== Project-Level Permission APIs =================================================================== + [HttpPost("assign/project-level-permission")] public async Task ManageProjectLevelPermission([FromBody] ProjctLevelPermissionDto model) { Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var response = await _projectServices.ManageProjectLevelPermissionAsync(model, tenantId, loggedInEmployee); return StatusCode(response.StatusCode, response); } + [HttpGet("get/project-level-permission/employee/{employeeId}/project/{projectId}")] public async Task GetAssignedProjectLevelPermission(Guid employeeId, Guid projectId) { @@ -470,6 +473,7 @@ namespace MarcoBMS.Services.Controllers var response = await _projectServices.GetAssignedProjectLevelPermissionAsync(employeeId, projectId, tenantId, loggedInEmployee); return StatusCode(response.StatusCode, response); } + [HttpGet("get/all/project-level-permission/{projectId}")] public async Task GetAllPermissionFroProject(Guid projectId) { @@ -477,6 +481,7 @@ namespace MarcoBMS.Services.Controllers var response = await _projectServices.GetAllPermissionFroProjectAsync(projectId, loggedInEmployee, tenantId); return StatusCode(response.StatusCode, response); } + [HttpGet("get/proejct-level/modules")] public async Task AssignProjectLevelModules() { @@ -484,6 +489,7 @@ namespace MarcoBMS.Services.Controllers var response = await _projectServices.AssignProjectLevelModulesAsync(tenantId, loggedInEmployee); return StatusCode(response.StatusCode, response); } + [HttpGet("get/proejct-level/employees/{projectId}")] public async Task GetEmployeeToWhomProjectLevelAssigned(Guid projectId) { @@ -492,5 +498,39 @@ namespace MarcoBMS.Services.Controllers return StatusCode(response.StatusCode, response); } + #endregion + + #region =================================================================== Assign Service APIs =================================================================== + + [HttpPost("assign/service")] + public async Task AssignServiceToProject([FromBody] AssignServiceDto model) + { + Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + var response = await _projectServices.AssignServiceToProjectAsync(model, tenantId, loggedInEmployee); + if (response.Success) + { + string message = response.Message; + var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Assigned_Services", data = response.Data, Message = message }; + await _signalR.SendNotificationAsync(notification); + } + + return StatusCode(response.StatusCode, response); + } + + [HttpPost("deassign/service")] + public async Task DeassignServiceToProject([FromBody] DeassignServiceDto model) + { + Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + var response = await _projectServices.DeassignServiceToProjectAsync(model, tenantId, loggedInEmployee); + if (response.Success) + { + string message = response.Message; + var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Deassigned_Services", data = response.Data, Message = message }; + await _signalR.SendNotificationAsync(notification); + } + + return StatusCode(response.StatusCode, response); + } + #endregion } }; \ No newline at end of file diff --git a/Marco.Pms.Services/Service/ProjectServices.cs b/Marco.Pms.Services/Service/ProjectServices.cs index 8fcfb26..2024e8c 100644 --- a/Marco.Pms.Services/Service/ProjectServices.cs +++ b/Marco.Pms.Services/Service/ProjectServices.cs @@ -3,10 +3,12 @@ using AutoMapper.QueryableExtensions; using Marco.Pms.DataAccess.Data; using Marco.Pms.Model.Activities; using Marco.Pms.Model.Dtos.Project; +using Marco.Pms.Model.Dtos.Projects; using Marco.Pms.Model.Dtos.Util; using Marco.Pms.Model.Employees; using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.MongoDBModels.Project; +using Marco.Pms.Model.OrganizationModel; using Marco.Pms.Model.Projects; using Marco.Pms.Model.TenantModels; using Marco.Pms.Model.Utilities; @@ -1830,6 +1832,148 @@ namespace Marco.Pms.Services.Service } #endregion + #region =================================================================== Assign Service APIs =================================================================== + + public async Task> AssignServiceToProjectAsync(AssignServiceDto model, Guid tenantId, Employee loggedInEmployee) + { + await using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + var project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == model.ProjectId && p.TenantId == tenantId); + if (project == null) + { + return ApiResponse.ErrorResponse("Project not found", "Project not found in database", 404); + } + var todaysDate = DateTime.UtcNow.Date; + + 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(); + }); + + 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; + + foreach (var serviceId in model.ServiceIds) + { + + var projectService = projectServices.FirstOrDefault(ps => ps.ServiceId == serviceId); + if (projectService == null) + { + projectService = new ProjectServiceMapping + { + ProjectId = project.Id, + ServiceId = serviceId, + TenantId = project.TenantId, + PlannedStartDate = model.PlannedStartDate, + PlannedEndDate = model.PlannedEndDate, + ActualStartDate = DateTime.UtcNow, + IsActive = true + }; + _context.ProjectServiceMappings.Add(projectService); + } + } + + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + + var response = services.Select(s => new ProjectServiceVM + { + Project = _mapper.Map(project), + Service = _mapper.Map(s), + PlannedStartDate = model.PlannedStartDate, + PlannedEndDate = model.PlannedEndDate, + ActualStartDate = DateTime.UtcNow + }); + return ApiResponse.SuccessResponse(response, "Services has been assigned to the project", 200); + } + catch (DbUpdateException dbEx) + { + await transaction.RollbackAsync(); + + _logger.LogError(dbEx, "Database Exception has been occured, While assigning the sevice to the project"); + return ApiResponse.ErrorResponse("Internal error", "An database exception has been occured", 500); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception has been occured, While assigning the sevice to the project"); + return ApiResponse.ErrorResponse("Internal error", "An internal exception has been occured", 500); + } + } + public async Task> DeassignServiceToProjectAsync(DeassignServiceDto model, Guid tenantId, Employee loggedInEmployee) + { + await using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + var project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == model.ProjectId && p.TenantId == tenantId); + if (project == null) + { + return ApiResponse.ErrorResponse("Project not found", "Project not found in database", 404); + } + 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; + + if (!projectServices.Any()) + { + return ApiResponse.ErrorResponse("Project Service mapping not found", "Project Service mapping not found in database", 404); + } + + projectServices = projectServices.Select(ps => + { + ps.IsActive = false; + return ps; + }).ToList(); + + _context.ProjectServiceMappings.UpdateRange(projectServices); + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + + return ApiResponse.SuccessResponse(new { }, "Services has been deassigned 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.ErrorResponse("Internal error", "An database exception has been occured", 500); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception has been occured, While deassigning the sevice to the project"); + return ApiResponse.ErrorResponse("Internal error", "An internal exception has been occured", 500); + } + } + + #endregion + #region =================================================================== Helper Functions =================================================================== public async Task> GetAllProjectByTanentID(Guid tanentId) diff --git a/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs b/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs index 4f7eb5e..8ec9aae 100644 --- a/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs +++ b/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs @@ -1,4 +1,5 @@ using Marco.Pms.Model.Dtos.Project; +using Marco.Pms.Model.Dtos.Projects; using Marco.Pms.Model.Dtos.Util; using Marco.Pms.Model.Employees; using Marco.Pms.Model.Projects; @@ -42,5 +43,8 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces Task> GetEmployeeToWhomProjectLevelAssignedAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee); Task> GetAllPermissionFroProjectAsync(Guid projectId, Employee loggedInEmployee, Guid tenantId); + Task> AssignServiceToProjectAsync(AssignServiceDto model, Guid tenantId, Employee loggedInEmployee); + Task> DeassignServiceToProjectAsync(DeassignServiceDto model, Guid tenantId, Employee loggedInEmployee); + } }