From 9edc0e645edd4666ecdb0e3cc6f718ee3d67a40f Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Wed, 6 Aug 2025 13:07:41 +0530 Subject: [PATCH 1/8] Added to an API to get project assignment histery of an employee --- .../Controllers/ProjectController.cs | 17 +++++++++++++++++ Marco.Pms.Services/Service/ProjectServices.cs | 19 +++++++++++++++++++ .../ServiceInterfaces/IProjectServices.cs | 2 ++ 3 files changed, 38 insertions(+) diff --git a/Marco.Pms.Services/Controllers/ProjectController.cs b/Marco.Pms.Services/Controllers/ProjectController.cs index 2c03d69..0520414 100644 --- a/Marco.Pms.Services/Controllers/ProjectController.cs +++ b/Marco.Pms.Services/Controllers/ProjectController.cs @@ -276,6 +276,23 @@ namespace MarcoBMS.Services.Controllers return StatusCode(response.StatusCode, response); } + [HttpGet("allocation-histery/{employeeId}")] + public async Task GetProjectByEmployeeBasic([FromRoute] Guid employeeId) + { + // --- Step 1: Input Validation --- + if (!ModelState.IsValid) + { + var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToList(); + _logger.LogWarning("Get project list by employee Id called with invalid model state \n Errors: {Errors}", string.Join(", ", errors)); + return BadRequest(ApiResponse.ErrorResponse("Invalid request data provided.", errors, 400)); + } + + // --- Step 2: Prepare data without I/O --- + Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + var response = await _projectServices.GetProjectByEmployeeBasicAsync(employeeId, tenantId, loggedInEmployee); + return StatusCode(response.StatusCode, response); + } + [HttpPost("assign-projects/{employeeId}")] public async Task AssigneProjectsToEmployee([FromBody] List projectAllocationDtos, [FromRoute] Guid employeeId) { diff --git a/Marco.Pms.Services/Service/ProjectServices.cs b/Marco.Pms.Services/Service/ProjectServices.cs index 9406ec9..c92adbe 100644 --- a/Marco.Pms.Services/Service/ProjectServices.cs +++ b/Marco.Pms.Services/Service/ProjectServices.cs @@ -895,6 +895,25 @@ namespace Marco.Pms.Services.Service return ApiResponse>.SuccessResponse(resultVm, "Assignments managed successfully.", 200); } + public async Task> GetProjectByEmployeeBasicAsync(Guid employeeId, Guid tenantId, Employee loggedInEmployee) + { + var projectAllocation = await _context.ProjectAllocations + .Include(pa => pa.Project) + .Include(pa => pa.Employee).ThenInclude(e => e!.JobRole) + .Where(pa => pa.EmployeeId == employeeId && pa.TenantId == tenantId && pa.Project != null && pa.Employee != null) + .Select(pa => new + { + ProjectName = pa.Project!.Name, + ProjectShortName = pa.Project.ShortName, + AssignedDate = pa.AllocationDate, + RemovedDate = pa.ReAllocationDate, + Designation = pa.Employee!.JobRole!.Name + }) + .ToListAsync(); + + return ApiResponse.SuccessResponse(projectAllocation, $"{projectAllocation.Count} records provided employee assigned to projects fetched", 200); + } + #endregion #region =================================================================== Project InfraStructure Get APIs =================================================================== diff --git a/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs b/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs index b5acccc..e76df7f 100644 --- a/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs +++ b/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs @@ -20,6 +20,8 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces Task>> ManageAllocationAsync(List projectAllocationDots, Guid tenantId, Employee loggedInEmployee); Task> GetProjectsByEmployeeAsync(Guid employeeId, Guid tenantId, Employee loggedInEmployee); Task>> AssigneProjectsToEmployeeAsync(List projectAllocationDtos, Guid employeeId, Guid tenantId, Employee loggedInEmployee); + Task> GetProjectByEmployeeBasicAsync(Guid employeeId, Guid tenantId, Employee loggedInEmployee); + Task> GetInfraDetailsAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee); Task> GetWorkItemsAsync(Guid workAreaId, Guid tenantId, Employee loggedInEmployee); Task ManageProjectInfraAsync(List infraDtos, Guid tenantId, Employee loggedInEmployee); From 7555b73f020f4d82b4ffe6ce24ba02a66b02fd08 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Wed, 6 Aug 2025 14:51:55 +0530 Subject: [PATCH 2/8] Added an API to get task by employeeId --- .../Controllers/ProjectController.cs | 19 +++ Marco.Pms.Services/Service/ProjectServices.cs | 135 ++++++++++++++++-- .../ServiceInterfaces/IProjectServices.cs | 1 + 3 files changed, 141 insertions(+), 14 deletions(-) diff --git a/Marco.Pms.Services/Controllers/ProjectController.cs b/Marco.Pms.Services/Controllers/ProjectController.cs index 0520414..436dff9 100644 --- a/Marco.Pms.Services/Controllers/ProjectController.cs +++ b/Marco.Pms.Services/Controllers/ProjectController.cs @@ -355,6 +355,25 @@ namespace MarcoBMS.Services.Controllers var response = await _projectServices.GetWorkItemsAsync(workAreaId, tenantId, loggedInEmployee); return StatusCode(response.StatusCode, response); } + [HttpGet("tasks-employee/{employeeId}")] + public async Task GetTasksByEmployee(Guid employeeId, [FromQuery] DateTime? fromDate, DateTime? toDate) + { + // --- Step 1: Input Validation --- + if (!ModelState.IsValid) + { + var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToList(); + _logger.LogWarning("Get Work Items by employeeId called with invalid model state \n Errors: {Errors}", string.Join(", ", errors)); + return BadRequest(ApiResponse.ErrorResponse("Invalid request data provided.", errors, 400)); + } + + if (!toDate.HasValue) toDate = DateTime.UtcNow; + if (!fromDate.HasValue) fromDate = toDate.Value.AddDays(-7); + + // --- Step 2: Prepare data without I/O --- + Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + var response = await _projectServices.GetTasksByEmployeeAsync(employeeId, fromDate.Value, toDate.Value, tenantId, loggedInEmployee); + return StatusCode(response.StatusCode, response); + } #endregion diff --git a/Marco.Pms.Services/Service/ProjectServices.cs b/Marco.Pms.Services/Service/ProjectServices.cs index c92adbe..f3299fa 100644 --- a/Marco.Pms.Services/Service/ProjectServices.cs +++ b/Marco.Pms.Services/Service/ProjectServices.cs @@ -897,23 +897,53 @@ namespace Marco.Pms.Services.Service public async Task> GetProjectByEmployeeBasicAsync(Guid employeeId, Guid tenantId, Employee loggedInEmployee) { - var projectAllocation = await _context.ProjectAllocations - .Include(pa => pa.Project) - .Include(pa => pa.Employee).ThenInclude(e => e!.JobRole) - .Where(pa => pa.EmployeeId == employeeId && pa.TenantId == tenantId && pa.Project != null && pa.Employee != null) - .Select(pa => new - { - ProjectName = pa.Project!.Name, - ProjectShortName = pa.Project.ShortName, - AssignedDate = pa.AllocationDate, - RemovedDate = pa.ReAllocationDate, - Designation = pa.Employee!.JobRole!.Name - }) - .ToListAsync(); + // Log the start of the method execution with key input parameters + _logger.LogInfo("Fetching projects for EmployeeId: {EmployeeId}, TenantId: {TenantId} by User: {UserId}", + employeeId, tenantId, loggedInEmployee.Id); - return ApiResponse.SuccessResponse(projectAllocation, $"{projectAllocation.Count} records provided employee assigned to projects fetched", 200); + try + { + // Retrieve project allocations linked to the specified employee and tenant + var projectAllocation = await _context.ProjectAllocations + .AsNoTracking() // Optimization: no tracking since entities are not updated + .Include(pa => pa.Project) // Include related Project data + .Include(pa => pa.Employee).ThenInclude(e => e!.JobRole) // Include related Employee and their JobRole + .Where(pa => pa.EmployeeId == employeeId + && pa.TenantId == tenantId + && pa.Project != null + && pa.Employee != null) + .Select(pa => new + { + ProjectName = pa.Project!.Name, + ProjectShortName = pa.Project.ShortName, + AssignedDate = pa.AllocationDate, + RemovedDate = pa.ReAllocationDate, + Designation = pa.Employee!.JobRole!.Name + }) + .ToListAsync(); + + // Log successful retrieval including count of records + _logger.LogInfo("Successfully fetched {Count} projects for EmployeeId: {EmployeeId}", + projectAllocation.Count, employeeId); + + return ApiResponse.SuccessResponse( + projectAllocation, + $"{projectAllocation.Count} project assignments fetched for employee.", + 200); + } + catch (Exception ex) + { + // Log the exception with stack trace for debugging + _logger.LogError(ex, "Error occurred while fetching projects for EmployeeId: {EmployeeId}, TenantId: {TenantId}", + employeeId, tenantId); + + return ApiResponse.ErrorResponse( + "An error occurred while fetching project assignments.", + 500); + } } + #endregion #region =================================================================== Project InfraStructure Get APIs =================================================================== @@ -1044,6 +1074,83 @@ namespace Marco.Pms.Services.Service } } + /// + /// Retrieves tasks assigned to a specific employee within a date range for a tenant. + /// + /// The ID of the employee to filter tasks. + /// The start date to filter task assignments. + /// The end date to filter task assignments. + /// The tenant ID to filter tasks. + /// The employee requesting the data (for authorization/logging). + /// An ApiResponse containing the task details. + public async Task> GetTasksByEmployeeAsync(Guid employeeId, DateTime fromDate, DateTime toDate, Guid tenantId, Employee loggedInEmployee) + { + _logger.LogInfo("Fetching tasks for EmployeeId: {EmployeeId} from {FromDate} to {ToDate} for TenantId: {TenantId}", + employeeId, fromDate, toDate, tenantId); + + try + { + // Query TaskMembers with related necessary fields in one projection to minimize DB calls and data size + var taskData = await _context.TaskMembers + .Where(tm => tm.EmployeeId == employeeId && + tm.TenantId == tenantId && + tm.TaskAllocation != null && + tm.TaskAllocation.AssignmentDate.Date >= fromDate.Date && + tm.TaskAllocation.AssignmentDate.Date <= toDate.Date) + .Select(tm => new + { + AssignmentDate = tm.TaskAllocation!.AssignmentDate, + PlannedTask = tm.TaskAllocation.PlannedTask, + CompletedTask = tm.TaskAllocation.CompletedTask, + ProjectId = tm.TaskAllocation.WorkItem!.WorkArea!.Floor!.Building!.ProjectId, + BuildingName = tm.TaskAllocation.WorkItem.WorkArea.Floor.Building!.Name, + FloorName = tm.TaskAllocation.WorkItem.WorkArea.Floor.FloorName, + AreaName = tm.TaskAllocation.WorkItem.WorkArea.AreaName, + ActivityName = tm.TaskAllocation.WorkItem.ActivityMaster!.ActivityName, + ActivityUnit = tm.TaskAllocation.WorkItem.ActivityMaster.UnitOfMeasurement + }) + .OrderByDescending(t => t.AssignmentDate) + .ToListAsync(); + + _logger.LogInfo("Retrieved {TaskCount} tasks for EmployeeId: {EmployeeId}", taskData.Count, employeeId); + + // Extract distinct project IDs to fetch project details efficiently + var distinctProjectIds = taskData.Select(t => t.ProjectId).Distinct().ToList(); + + var projects = await _context.Projects + .Where(p => distinctProjectIds.Contains(p.Id)) + .Select(p => new { p.Id, p.Name }) + .ToListAsync(); + + // Prepare the response + var response = taskData.Select(t => + { + var project = projects.FirstOrDefault(p => p.Id == t.ProjectId); + + return new + { + ProjectName = project?.Name ?? "Unknown Project", + t.AssignmentDate, + t.PlannedTask, + t.CompletedTask, + Location = $"{t.BuildingName} > {t.FloorName} > {t.AreaName}", + ActivityName = t.ActivityName, + ActivityUnit = t.ActivityUnit + }; + }).ToList(); + + _logger.LogInfo("Successfully prepared task response for EmployeeId: {EmployeeId}", employeeId); + + return ApiResponse.SuccessResponse(response, "Task fetched successfully", 200); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error while fetching tasks for EmployeeId: {EmployeeId}", employeeId); + return ApiResponse.ErrorResponse("An error occurred while fetching the tasks.", 500); + } + } + + #endregion #region =================================================================== Project Infrastructre Manage APIs =================================================================== diff --git a/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs b/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs index e76df7f..a1f78f8 100644 --- a/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs +++ b/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs @@ -24,6 +24,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces Task> GetInfraDetailsAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee); Task> GetWorkItemsAsync(Guid workAreaId, Guid tenantId, Employee loggedInEmployee); + Task> GetTasksByEmployeeAsync(Guid employeeId, DateTime fromDate, DateTime toDate, Guid tenantId, Employee loggedInEmployee); Task ManageProjectInfraAsync(List infraDtos, Guid tenantId, Employee loggedInEmployee); Task>> CreateProjectTaskAsync(List workItemDtos, Guid tenantId, Employee loggedInEmployee); Task DeleteProjectTaskAsync(Guid id, Guid tenantId, Employee loggedInEmployee); From 0aee183fdd820ee45961149e5fe39d9209c6b515 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Wed, 6 Aug 2025 17:54:37 +0530 Subject: [PATCH 3/8] Showing profile of all emplyee event if it inactive --- Marco.Pms.Services/Helpers/EmployeeHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marco.Pms.Services/Helpers/EmployeeHelper.cs b/Marco.Pms.Services/Helpers/EmployeeHelper.cs index 09dcbe2..a23460b 100644 --- a/Marco.Pms.Services/Helpers/EmployeeHelper.cs +++ b/Marco.Pms.Services/Helpers/EmployeeHelper.cs @@ -20,7 +20,7 @@ namespace MarcoBMS.Services.Helpers public async Task GetEmployeeByID(Guid EmployeeID) { - return await _context.Employees.Include(e => e.JobRole).FirstOrDefaultAsync(e => e.Id == EmployeeID && e.IsActive == true) ?? new Employee { }; + return await _context.Employees.Include(e => e.JobRole).FirstOrDefaultAsync(e => e.Id == EmployeeID) ?? new Employee { }; } public async Task GetEmployeeByApplicationUserID(string ApplicationUserID) From d113fc3c3df34be8ebe6d8e1301a80ba213d3316 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Wed, 13 Aug 2025 11:07:36 +0530 Subject: [PATCH 4/8] Sending the project degination in place of employee degination in allocation history API --- .../ViewModels/Projects/ProjectHisteryVM.cs | 11 +++++++++ Marco.Pms.Services/Service/ProjectServices.cs | 24 ++++++++++++++++--- .../appsettings.Development.json | 2 +- 3 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 Marco.Pms.Model/ViewModels/Projects/ProjectHisteryVM.cs diff --git a/Marco.Pms.Model/ViewModels/Projects/ProjectHisteryVM.cs b/Marco.Pms.Model/ViewModels/Projects/ProjectHisteryVM.cs new file mode 100644 index 0000000..4df0480 --- /dev/null +++ b/Marco.Pms.Model/ViewModels/Projects/ProjectHisteryVM.cs @@ -0,0 +1,11 @@ +namespace Marco.Pms.Model.ViewModels.Projects +{ + public class ProjectHisteryVM + { + public string? ProjectName { get; set; } + public string? ProjectShortName { get; set; } + public DateTime AssignedDate { get; set; } + public DateTime? RemovedDate { get; set; } + public string? Designation { get; set; } + } +} \ No newline at end of file diff --git a/Marco.Pms.Services/Service/ProjectServices.cs b/Marco.Pms.Services/Service/ProjectServices.cs index 0b95cb4..6cb7e68 100644 --- a/Marco.Pms.Services/Service/ProjectServices.cs +++ b/Marco.Pms.Services/Service/ProjectServices.cs @@ -918,17 +918,35 @@ namespace Marco.Pms.Services.Service ProjectShortName = pa.Project.ShortName, AssignedDate = pa.AllocationDate, RemovedDate = pa.ReAllocationDate, - Designation = pa.Employee!.JobRole!.Name + Designation = pa.Employee!.JobRole!.Name, + DesignationId = pa.JobRoleId }) .ToListAsync(); + var designationIds = projectAllocation.Select(pa => pa.DesignationId).ToList(); + + var designations = await _context.JobRoles.Where(jr => designationIds.Contains(jr.Id)).ToListAsync(); + + var response = projectAllocation.Select(pa => + { + var designation = designations.FirstOrDefault(jr => jr.Id == pa.DesignationId); + return new ProjectHisteryVM + { + ProjectName = pa.ProjectName, + ProjectShortName = pa.ProjectShortName, + AssignedDate = pa.AssignedDate, + RemovedDate = pa.RemovedDate, + Designation = designation?.Name + }; + }).ToList(); + // Log successful retrieval including count of records _logger.LogInfo("Successfully fetched {Count} projects for EmployeeId: {EmployeeId}", projectAllocation.Count, employeeId); return ApiResponse.SuccessResponse( - projectAllocation, - $"{projectAllocation.Count} project assignments fetched for employee.", + response, + $"{response.Count} project assignments fetched for employee.", 200); } catch (Exception ex) diff --git a/Marco.Pms.Services/appsettings.Development.json b/Marco.Pms.Services/appsettings.Development.json index 579b059..4bb9519 100644 --- a/Marco.Pms.Services/appsettings.Development.json +++ b/Marco.Pms.Services/appsettings.Development.json @@ -49,6 +49,6 @@ "MongoDB": { "SerilogDatabaseUrl": "mongodb://localhost:27017/DotNetLogs", "ConnectionString": "mongodb://localhost:27017/MarcoBMS_Caches?socketTimeoutMS=500&serverSelectionTimeoutMS=500&connectTimeoutMS=500", - "ModificationConnectionString": "mongodb://localhost:27017/ModificationLog?socketTimeoutMS=500&serverSelectionTimeoutMS=500&connectTimeoutMS=500" + "ModificationConnectionString": "mongodb://localhost:27017/ModificationLog?socketTimeoutMS=500&serverSelectionTimeoutMS=500&connectTimeoutMS=500&replicaSet=rs01&directConnection=true" } } From c84ea987c51a6b81c6ca906b74ff259add79a551 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Mon, 18 Aug 2025 12:21:01 +0530 Subject: [PATCH 5/8] Added the pagenantion in employee search API --- .../Controllers/EmployeeController.cs | 87 ++++++++++++++++--- 1 file changed, 76 insertions(+), 11 deletions(-) diff --git a/Marco.Pms.Services/Controllers/EmployeeController.cs b/Marco.Pms.Services/Controllers/EmployeeController.cs index cdc28ed..3f39412 100644 --- a/Marco.Pms.Services/Controllers/EmployeeController.cs +++ b/Marco.Pms.Services/Controllers/EmployeeController.cs @@ -191,24 +191,89 @@ namespace MarcoBMS.Services.Controllers var response = await employeeQuery.Take(10).Select(e => _mapper.Map(e)).ToListAsync(); return Ok(ApiResponse.SuccessResponse(response, $"{response.Count} records of employees fetched successfully", 200)); } - [HttpGet] - [Route("search/{name}/{projectid?}")] - public async Task SearchEmployee(string name, Guid? projectid) + + /// + /// Retrieves a paginated list of employees assigned to a specified project (if provided), + /// with optional search functionality. + /// Ensures that the logged-in user has necessary permissions before accessing project employees. + /// + /// Optional project identifier to filter employees by project. + /// Optional search string to filter employees by name. + /// Page number for pagination (default = 1). + /// Paginated list of employees in BasicEmployeeVM format wrapped in ApiResponse. + + [HttpGet("search")] + public async Task GetEmployeesByProjectBasic(Guid? projectId, [FromQuery] string? searchString, [FromQuery] int pageNumber = 1) { - if (!ModelState.IsValid) + const int pageSize = 10; // Fixed page size for pagination + + // Log API entry with context + _logger.LogInfo("Fetching employees. ProjectId: {ProjectId}, SearchString: {SearchString}, PageNumber: {PageNumber}", + projectId ?? Guid.Empty, searchString ?? "", pageNumber); + + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + _logger.LogDebug("Logged-in EmployeeId: {EmployeeId}", loggedInEmployee.Id); + + // Initialize query scoped by tenant + var employeeQuery = _context.Employees.Where(e => e.TenantId == tenantId); + + // Filter by project if projectId is supplied + if (projectId.HasValue && projectId.Value != Guid.Empty) { - var errors = ModelState.Values - .SelectMany(v => v.Errors) - .Select(e => e.ErrorMessage) - .ToList(); - return BadRequest(ApiResponse.ErrorResponse("Invalid data", errors, 400)); + _logger.LogDebug("Project filter applied. Checking permission for EmployeeId: {EmployeeId} on ProjectId: {ProjectId}", + loggedInEmployee.Id, projectId); + // Validate project access permission + var hasProjectPermission = await _permission.HasProjectPermission(loggedInEmployee, projectId.Value); + if (!hasProjectPermission) + { + _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} does not have permission for ProjectId: {ProjectId}", + loggedInEmployee.Id, projectId); + + return StatusCode(403, ApiResponse.ErrorResponse( + "Access denied", + "User does not have access to view employees for this project", + 403)); + } + + // Employees allocated to the project + var employeeIds = await _context.ProjectAllocations + .Where(pa => pa.ProjectId == projectId && pa.IsActive && pa.TenantId == tenantId) + .Select(pa => pa.EmployeeId) + .ToListAsync(); + + _logger.LogDebug("Project employees retrieved. Total linked employees found: {Count}", employeeIds.Count); + + // Apply project allocation filter + employeeQuery = employeeQuery.Where(e => employeeIds.Contains(e.Id)); } - var result = await _employeeHelper.SearchEmployeeByProjectId(GetTenantId(), name.ToLower(), projectid); - return Ok(ApiResponse.SuccessResponse(result, "Filter applied.", 200)); + // Apply search filter if provided + if (!string.IsNullOrWhiteSpace(searchString)) + { + var searchStringLower = searchString.ToLower(); + _logger.LogDebug("Search filter applied. Search term: {SearchTerm}", searchStringLower); + + employeeQuery = employeeQuery.Where(e => + (e.FirstName + " " + e.LastName).ToLower().Contains(searchStringLower)); + } + + // Pagination and Projection (executed in DB) + var employees = await employeeQuery + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize) + .Select(e => _mapper.Map(e)) + .ToListAsync(); + + _logger.LogInfo("Employees fetched successfully. Records returned: {Count}", employees.Count); + + return Ok(ApiResponse.SuccessResponse( + employees, + $"{employees.Count} employee records fetched successfully", + 200)); } + [HttpGet] [Route("profile/get/{employeeId}")] public async Task GetEmployeeProfileById(Guid employeeId) From b4cb81772e3abd14631a8643dbf3a8a5f524ef12 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Tue, 19 Aug 2025 11:31:14 +0530 Subject: [PATCH 6/8] Added the function to re activeate the employee --- .../Controllers/EmployeeController.cs | 150 ++++++++++-------- 1 file changed, 84 insertions(+), 66 deletions(-) diff --git a/Marco.Pms.Services/Controllers/EmployeeController.cs b/Marco.Pms.Services/Controllers/EmployeeController.cs index 3f39412..446794a 100644 --- a/Marco.Pms.Services/Controllers/EmployeeController.cs +++ b/Marco.Pms.Services/Controllers/EmployeeController.cs @@ -512,86 +512,104 @@ namespace MarcoBMS.Services.Controllers } [HttpDelete("{id}")] - public async Task SuspendEmployee(Guid id) + public async Task SuspendEmployee(Guid id, [FromQuery] bool active = false) { Guid tenantId = _userHelper.GetTenantId(); var LoggedEmployee = await _userHelper.GetCurrentEmployeeAsync(); Employee? employee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == id && e.IsActive && e.TenantId == tenantId); - if (employee != null) + if (employee == null) { - if (employee.IsSystem) + _logger.LogWarning("Employee with ID {EmploueeId} not found in database", id); + return NotFound(ApiResponse.ErrorResponse("Employee Not found successfully", "Employee Not found successfully", 404)); + } + if (employee.IsSystem) + { + _logger.LogWarning("Employee with ID {LoggedEmployeeId} tries to suspend system-defined employee with ID {EmployeeId}", LoggedEmployee.Id, employee.Id); + return BadRequest(ApiResponse.ErrorResponse("System-defined employees cannot be suspended.", "System-defined employees cannot be suspended.", 400)); + } + var assignedToTasks = await _context.TaskMembers.Where(t => t.EmployeeId == employee.Id).ToListAsync(); + if (assignedToTasks.Count != 0) + { + List taskIds = assignedToTasks.Select(t => t.TaskAllocationId).ToList(); + var tasks = await _context.TaskAllocations.Where(t => taskIds.Contains(t.Id)).ToListAsync(); + + foreach (var assignedToTask in assignedToTasks) { - _logger.LogWarning("Employee with ID {LoggedEmployeeId} tries to suspend system-defined employee with ID {EmployeeId}", LoggedEmployee.Id, employee.Id); - return BadRequest(ApiResponse.ErrorResponse("System-defined employees cannot be suspended.", "System-defined employees cannot be suspended.", 400)); + var task = tasks.Find(t => t.Id == assignedToTask.TaskAllocationId); + if (task != null && task.CompletedTask == 0) + { + _logger.LogWarning("Employee with ID {EmployeeId} is currently assigned to any incomplete task", employee.Id); + return BadRequest(ApiResponse.ErrorResponse("Employee is currently assigned to any incomplete task", "Employee is currently assigned to any incomplete task", 400)); + } } - else + } + var attendance = await _context.Attendes.Where(a => a.EmployeeID == employee.Id && (a.OutTime == null || a.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE)).ToListAsync(); + if (attendance.Count != 0) + { + _logger.LogWarning("Employee with ID {EmployeeId} have any pending check-out or regularization requests", employee.Id); + return BadRequest(ApiResponse.ErrorResponse("Employee have any pending check-out or regularization requests", "Employee have any pending check-out or regularization requests", 400)); + } + if (active) + { + employee.IsActive = true; + var user = await _context.ApplicationUsers.FirstOrDefaultAsync(u => u.Id == employee.ApplicationUserId); + if (user != null) { - var assignedToTasks = await _context.TaskMembers.Where(t => t.EmployeeId == employee.Id).ToListAsync(); - if (assignedToTasks.Count != 0) - { - List taskIds = assignedToTasks.Select(t => t.TaskAllocationId).ToList(); - var tasks = await _context.TaskAllocations.Where(t => taskIds.Contains(t.Id)).ToListAsync(); + user.IsActive = true; + _logger.LogInfo("The application user associated with employee ID {EmployeeId} has been actived.", employee.Id); - foreach (var assignedToTask in assignedToTasks) - { - var task = tasks.Find(t => t.Id == assignedToTask.TaskAllocationId); - if (task != null && task.CompletedTask == 0) - { - _logger.LogWarning("Employee with ID {EmployeeId} is currently assigned to any incomplete task", employee.Id); - return BadRequest(ApiResponse.ErrorResponse("Employee is currently assigned to any incomplete task", "Employee is currently assigned to any incomplete task", 400)); - } - } - } - var attendance = await _context.Attendes.Where(a => a.EmployeeID == employee.Id && (a.OutTime == null || a.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE)).ToListAsync(); - if (attendance.Count != 0) - { - _logger.LogWarning("Employee with ID {EmployeeId} have any pending check-out or regularization requests", employee.Id); - return BadRequest(ApiResponse.ErrorResponse("Employee have any pending check-out or regularization requests", "Employee have any pending check-out or regularization requests", 400)); - } - employee.IsActive = false; - var projectAllocations = await _context.ProjectAllocations.Where(a => a.EmployeeId == employee.Id).ToListAsync(); - if (projectAllocations.Count != 0) - { - List allocations = new List(); - foreach (var projectAllocation in projectAllocations) - { - projectAllocation.ReAllocationDate = DateTime.UtcNow; - projectAllocation.IsActive = false; - allocations.Add(projectAllocation); - } - _logger.LogInfo("Employee with ID {EmployeeId} has been removed from all assigned projects.", employee.Id); - } - var user = await _context.ApplicationUsers.FirstOrDefaultAsync(u => u.Id == employee.ApplicationUserId); - if (user != null) - { - user.IsActive = false; - _logger.LogInfo("The application user associated with employee ID {EmployeeId} has been suspended.", employee.Id); - - var refreshTokens = await _context.RefreshTokens.AsNoTracking().Where(t => t.UserId == user.Id).ToListAsync(); - if (refreshTokens.Count != 0) - { - _context.RefreshTokens.RemoveRange(refreshTokens); - _logger.LogInfo("Refresh tokens associated with employee ID {EmployeeId} has been removed.", employee.Id); - } - - } - var roleMapping = await _context.EmployeeRoleMappings.AsNoTracking().Where(r => r.EmployeeId == employee.Id).ToListAsync(); - if (roleMapping.Count != 0) - { - _context.EmployeeRoleMappings.RemoveRange(roleMapping); - _logger.LogInfo("Application role mapping associated with employee ID {EmployeeId} has been removed.", employee.Id); - } - await _context.SaveChangesAsync(); - _logger.LogInfo("Employee with ID {EmployeId} Deleted successfully", employee.Id); - var notification = new { LoggedInUserId = LoggedEmployee.Id, Keyword = "Employee", EmployeeId = employee.Id }; - - await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification); } + _logger.LogInfo("Employee with ID {EmployeId} Actived successfully", employee.Id); } else { - _logger.LogWarning("Employee with ID {EmploueeId} not found in database", id); + employee.IsActive = false; + var projectAllocations = await _context.ProjectAllocations.Where(a => a.EmployeeId == employee.Id).ToListAsync(); + if (projectAllocations.Count != 0) + { + List allocations = new List(); + foreach (var projectAllocation in projectAllocations) + { + projectAllocation.ReAllocationDate = DateTime.UtcNow; + projectAllocation.IsActive = false; + allocations.Add(projectAllocation); + } + _logger.LogInfo("Employee with ID {EmployeeId} has been removed from all assigned projects.", employee.Id); + } + var user = await _context.ApplicationUsers.FirstOrDefaultAsync(u => u.Id == employee.ApplicationUserId); + if (user != null) + { + user.IsActive = false; + _logger.LogInfo("The application user associated with employee ID {EmployeeId} has been suspended.", employee.Id); + + var refreshTokens = await _context.RefreshTokens.AsNoTracking().Where(t => t.UserId == user.Id).ToListAsync(); + if (refreshTokens.Count != 0) + { + _context.RefreshTokens.RemoveRange(refreshTokens); + _logger.LogInfo("Refresh tokens associated with employee ID {EmployeeId} has been removed.", employee.Id); + } + + } + var roleMapping = await _context.EmployeeRoleMappings.AsNoTracking().Where(r => r.EmployeeId == employee.Id).ToListAsync(); + if (roleMapping.Count != 0) + { + _context.EmployeeRoleMappings.RemoveRange(roleMapping); + _logger.LogInfo("Application role mapping associated with employee ID {EmployeeId} has been removed.", employee.Id); + } + _logger.LogInfo("Employee with ID {EmployeId} Deleted successfully", employee.Id); } + try + { + await _context.SaveChangesAsync(); + } + catch (DbUpdateException ex) + { + _logger.LogError(ex, "Exception Occured While activting/deactivting employee {EmployeeId}", employee.Id); + return StatusCode(500, ApiResponse.ErrorResponse("Internal Error Occured", "Error occured while saving the entity", 500)); + } + var notification = new { LoggedInUserId = LoggedEmployee.Id, Keyword = "Employee", EmployeeId = employee.Id }; + + await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification); return Ok(ApiResponse.SuccessResponse(new { }, "Employee Suspended successfully", 200)); } private static Employee GetNewEmployeeModel(CreateUserDto model, Guid TenantId, string ApplicationUserId) From 889b5a84b6c6d60685e421d251b0768cb524a683 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Tue, 19 Aug 2025 11:46:14 +0530 Subject: [PATCH 7/8] change the employee fetching logic in employe suspend employee --- Marco.Pms.Services/Controllers/EmployeeController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marco.Pms.Services/Controllers/EmployeeController.cs b/Marco.Pms.Services/Controllers/EmployeeController.cs index 446794a..a036538 100644 --- a/Marco.Pms.Services/Controllers/EmployeeController.cs +++ b/Marco.Pms.Services/Controllers/EmployeeController.cs @@ -516,7 +516,7 @@ namespace MarcoBMS.Services.Controllers { Guid tenantId = _userHelper.GetTenantId(); var LoggedEmployee = await _userHelper.GetCurrentEmployeeAsync(); - Employee? employee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == id && e.IsActive && e.TenantId == tenantId); + Employee? employee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == id && e.TenantId == tenantId); if (employee == null) { _logger.LogWarning("Employee with ID {EmploueeId} not found in database", id); From ac23a8724da43e2ad744ed9b52f613f063e937f9 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Sat, 23 Aug 2025 09:53:32 +0530 Subject: [PATCH 8/8] Added page size the search emplyee API --- Marco.Pms.Services/Controllers/EmployeeController.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Marco.Pms.Services/Controllers/EmployeeController.cs b/Marco.Pms.Services/Controllers/EmployeeController.cs index a036538..7c937b6 100644 --- a/Marco.Pms.Services/Controllers/EmployeeController.cs +++ b/Marco.Pms.Services/Controllers/EmployeeController.cs @@ -203,10 +203,9 @@ namespace MarcoBMS.Services.Controllers /// Paginated list of employees in BasicEmployeeVM format wrapped in ApiResponse. [HttpGet("search")] - public async Task GetEmployeesByProjectBasic(Guid? projectId, [FromQuery] string? searchString, [FromQuery] int pageNumber = 1) + public async Task GetEmployeesByProjectBasic(Guid? projectId, [FromQuery] string? searchString, + [FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 10) { - const int pageSize = 10; // Fixed page size for pagination - // Log API entry with context _logger.LogInfo("Fetching employees. ProjectId: {ProjectId}, SearchString: {SearchString}, PageNumber: {PageNumber}", projectId ?? Guid.Empty, searchString ?? "", pageNumber);