From b1af96b923f8b094e9e1766142789d2ca5b7198b Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Wed, 8 Oct 2025 12:16:25 +0530 Subject: [PATCH] Added the name of the project name in view model --- .../AttendanceVM/EmployeeAttendanceVM.cs | 1 + .../Controllers/AttendanceController.cs | 184 +++++++++--------- 2 files changed, 98 insertions(+), 87 deletions(-) diff --git a/Marco.Pms.Model/ViewModels/AttendanceVM/EmployeeAttendanceVM.cs b/Marco.Pms.Model/ViewModels/AttendanceVM/EmployeeAttendanceVM.cs index 9c2bfc8..8f72e49 100644 --- a/Marco.Pms.Model/ViewModels/AttendanceVM/EmployeeAttendanceVM.cs +++ b/Marco.Pms.Model/ViewModels/AttendanceVM/EmployeeAttendanceVM.cs @@ -10,6 +10,7 @@ namespace Marco.Pms.Model.ViewModels.AttendanceVM public string? LastName { get; set; } public string? EmployeeAvatar { get; set; } public string? OrganizationName { get; set; } + public string? ProjectName { get; set; } public DateTime? CheckInTime { get; set; } public DateTime? CheckOutTime { get; set; } public string? JobRoleName { get; set; } diff --git a/Marco.Pms.Services/Controllers/AttendanceController.cs b/Marco.Pms.Services/Controllers/AttendanceController.cs index 963b8c4..de15a20 100644 --- a/Marco.Pms.Services/Controllers/AttendanceController.cs +++ b/Marco.Pms.Services/Controllers/AttendanceController.cs @@ -4,7 +4,6 @@ using Marco.Pms.Model.Dtos.Attendance; using Marco.Pms.Model.Employees; using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.Mapper; -using Marco.Pms.Model.Projects; using Marco.Pms.Model.Utilities; using Marco.Pms.Model.ViewModels.AttendanceVM; using Marco.Pms.Services.Hubs; @@ -146,18 +145,11 @@ namespace MarcoBMS.Services.Controllers [HttpGet("project/log")] - public async Task EmployeeAttendanceByDateRange([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null) + public async Task EmployeeAttendanceByDateRange([FromQuery] Guid? projectId, [FromQuery] Guid? organizationId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null) { Guid tenantId = GetTenantId(); var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); - var project = await _context.Projects.AsNoTracking().FirstOrDefaultAsync(p => p.Id == projectId && p.TenantId == tenantId); - if (project == null) - { - _logger.LogWarning("Project {ProjectId} not found in database", projectId); - return NotFound(ApiResponse.ErrorResponse("Project not found.")); - } - var hasTeamAttendancePermission = await _permission.HasPermission(PermissionsMaster.TeamAttendance, LoggedInEmployee.Id); var hasSelfAttendancePermission = await _permission.HasPermission(PermissionsMaster.SelfAttendance, LoggedInEmployee.Id); @@ -175,35 +167,41 @@ namespace MarcoBMS.Services.Controllers return BadRequest(ApiResponse.ErrorResponse("Invalid Date", "Invalid Date", 400)); } - if (projectId == Guid.Empty) - { - _logger.LogWarning("The project Id sent by user is less than or equal to zero"); - return BadRequest(ApiResponse.ErrorResponse("Project ID is required and must be greater than zero.", "Project ID is required and must be greater than zero.", 400)); - } - var result = new List(); //Attendance? attendance = null; if (dateFrom == null) fromDate = DateTime.UtcNow.Date; if (dateTo == null && dateFrom != null) toDate = fromDate.AddDays(-1); - if (hasTeamAttendancePermission) - { - List lstAttendance = await _context.Attendes + var lstAttendanceQuery = _context.Attendes .Include(a => a.Employee) .ThenInclude(e => e!.Organization) .Include(a => a.Employee) .ThenInclude(e => e!.JobRole) - .Where(a => a.ProjectID == projectId && - a.AttendanceDate.Date >= fromDate.Date && - a.AttendanceDate.Date <= toDate.Date && - a.TenantId == tenantId && - a.Employee != null && - a.Employee.Organization != null && - a.Employee.JobRole != null - ).ToListAsync(); + .Where(a => + a.AttendanceDate.Date >= fromDate.Date && + a.AttendanceDate.Date <= toDate.Date && + a.TenantId == tenantId && + a.Employee != null && + a.Employee.Organization != null && + a.Employee.JobRole != null); + if (organizationId.HasValue) + { + lstAttendanceQuery = lstAttendanceQuery.Where(a => a.Employee != null && a.Employee.OrganizationId == organizationId); + } + + if (projectId.HasValue) + { + lstAttendanceQuery = lstAttendanceQuery.Where(a => a.ProjectID == projectId); + } + + if (hasTeamAttendancePermission) + { + List lstAttendance = await lstAttendanceQuery.ToListAsync(); + + var projectIds = lstAttendance.Select(a => a.ProjectID).ToList(); + var projects = await _context.Projects.Where(p => projectIds.Contains(p.Id) && p.TenantId == tenantId).ToListAsync(); - var jobRole = await _context.JobRoles.ToListAsync(); foreach (Attendance? attendance in lstAttendance) { var result1 = new EmployeeAttendanceVM() @@ -216,6 +214,7 @@ namespace MarcoBMS.Services.Controllers FirstName = attendance.Employee?.FirstName, LastName = attendance.Employee?.LastName, JobRoleName = attendance.Employee?.JobRole?.Name, + ProjectName = projects.Where(p => p.Id == attendance.ProjectID).Select(p => p.Name).FirstOrDefault(), OrganizationName = attendance.Employee?.Organization?.Name }; @@ -224,26 +223,12 @@ namespace MarcoBMS.Services.Controllers } else if (hasSelfAttendancePermission) { - var lstAttendanceQuery = _context.Attendes - .Include(a => a.Employee) - .ThenInclude(e => e!.Organization) - .Include(a => a.Employee) - .ThenInclude(e => e!.JobRole) - .Where(a => a.ProjectID == projectId && - a.EmployeeId == LoggedInEmployee.Id && - a.AttendanceDate.Date >= fromDate.Date && - a.AttendanceDate.Date <= toDate.Date && - a.TenantId == tenantId && - a.Employee != null && - a.Employee.Organization != null && - a.Employee.JobRole != null); - if (organizationId.HasValue) - { - lstAttendanceQuery = lstAttendanceQuery.Where(a => a.Employee != null && a.Employee.OrganizationId == organizationId); - } + var lstAttendances = await lstAttendanceQuery.Where(a => a.EmployeeId == LoggedInEmployee.Id).ToListAsync(); + + var projectIds = lstAttendances.Select(a => a.ProjectID).ToList(); + var projects = await _context.Projects.Where(p => projectIds.Contains(p.Id) && p.TenantId == tenantId).ToListAsync(); - var lstAttendances = await lstAttendanceQuery.ToListAsync(); foreach (var attendance in lstAttendances) { @@ -255,6 +240,7 @@ namespace MarcoBMS.Services.Controllers FirstName = attendance.Employee?.FirstName, LastName = attendance.Employee?.LastName, JobRoleName = attendance.Employee?.JobRole?.Name, + ProjectName = projects.Where(p => p.Id == attendance.ProjectID).Select(p => p.Name).FirstOrDefault(), OrganizationName = attendance.Employee?.Organization?.Name, CheckInTime = attendance.InTime, CheckOutTime = attendance.OutTime, @@ -278,13 +264,13 @@ namespace MarcoBMS.Services.Controllers /// Optional. Includes inactive employees in the team list if true. /// Optional. The date for which to fetch attendance, in "yyyy-MM-dd" format. Defaults to the current UTC date. /// An IActionResult containing a list of employee attendance records or an error response. - public async Task EmployeeAttendanceByProjectAsync([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool includeInactive, [FromQuery] string? date = null) + public async Task EmployeeAttendanceByProjectAsync([FromQuery] Guid? projectId, [FromQuery] Guid? organizationId, [FromQuery] bool includeInactive, [FromQuery] string? date = null) { var tenantId = GetTenantId(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); // --- 1. Initial Validation and Permission Checks --- - _logger.LogInfo("Fetching attendance for ProjectId: {ProjectId}, TenantId: {TenantId}", projectId, tenantId); + _logger.LogInfo("Fetching attendance for ProjectId: {ProjectId}, TenantId: {TenantId}", projectId ?? Guid.Empty, tenantId); // Validate date format if (!DateTime.TryParse(date, out var forDate)) @@ -292,14 +278,6 @@ namespace MarcoBMS.Services.Controllers forDate = DateTime.UtcNow.Date; // Default to today's date } - // Check if the project exists and if the employee has access - var project = await _context.Projects.AsNoTracking().FirstOrDefaultAsync(p => p.Id == projectId && p.TenantId == tenantId); - if (project == null) - { - _logger.LogWarning("Project {ProjectId} not found in database", projectId); - return NotFound(ApiResponse.ErrorResponse("Project not found.")); - } - // --- 2. Delegate to Specific Logic Based on Permissions --- try { @@ -326,50 +304,63 @@ namespace MarcoBMS.Services.Controllers return StatusCode(403, ApiResponse.ErrorResponse("You do not have permission to view attendance.", new { }, 403)); } - _logger.LogInfo("Successfully fetched {Count} attendance records for ProjectId: {ProjectId}", result.Count, projectId); + _logger.LogInfo("Successfully fetched {Count} attendance records for ProjectId: {ProjectId}", result.Count, projectId ?? Guid.Empty); return Ok(ApiResponse.SuccessResponse(result, $"{result.Count} attendance records fetched successfully.")); } catch (Exception ex) { - _logger.LogError(ex, "An error occurred while fetching attendance for ProjectId: {ProjectId}", projectId); + _logger.LogError(ex, "An error occurred while fetching attendance for ProjectId: {ProjectId}", projectId ?? Guid.Empty); return StatusCode(500, ApiResponse.ErrorResponse("An internal server error occurred.")); } } [HttpGet("regularize")] - public async Task GetRequestRegularizeAttendance([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool IncludeInActive) + public async Task GetRequestRegularizeAttendance([FromQuery] Guid? projectId, [FromQuery] Guid? organizationId, [FromQuery] bool IncludeInActive) { Guid TenantId = GetTenantId(); Employee LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var result = new List(); - List lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE && c.TenantId == TenantId).ToListAsync(); + var lstAttendanceQuery = _context.Attendes + .Include(a => a.Employee) + .ThenInclude(e => e!.Organization) + .Include(a => a.Employee) + .ThenInclude(e => e!.JobRole) + .Where(c => c.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE && c.Employee != null && c.Employee.JobRole != null && c.TenantId == TenantId); - List projectteam = await _projectServices.GetTeamByProject(TenantId, projectId, organizationId, true); - var idList = projectteam.Select(p => p.EmployeeId).ToList(); - var jobRole = await _context.JobRoles.ToListAsync(); + if (organizationId.HasValue) + { + lstAttendanceQuery = lstAttendanceQuery.Where(a => a.Employee != null && a.Employee.OrganizationId == organizationId); + } + + if (projectId.HasValue) + { + lstAttendanceQuery = lstAttendanceQuery.Where(a => a.ProjectID == projectId); + } + + List lstAttendance = await lstAttendanceQuery.ToListAsync(); + + var projectIds = lstAttendance.Select(a => a.ProjectID).ToList(); + var projects = await _context.Projects.Where(p => projectIds.Contains(p.Id) && p.TenantId == TenantId).ToListAsync(); foreach (Attendance attende in lstAttendance) { - var teamMember = projectteam.Find(m => m.EmployeeId == attende.EmployeeId); - if (teamMember != null && teamMember.Employee != null && teamMember.Employee.JobRole != null) + var result1 = new EmployeeAttendanceVM() { - var result1 = new EmployeeAttendanceVM() - { - Id = attende.Id, - CheckInTime = attende.InTime, - CheckOutTime = attende.OutTime, - Activity = attende.Activity, - EmployeeAvatar = null, - EmployeeId = attende.EmployeeId, - FirstName = teamMember.Employee.FirstName, - LastName = teamMember.Employee.LastName, - JobRoleName = teamMember.Employee.JobRole.Name, - OrganizationName = teamMember.Employee.Organization?.Name - }; - result.Add(result1); - } + Id = attende.Id, + CheckInTime = attende.InTime, + CheckOutTime = attende.OutTime, + Activity = attende.Activity, + EmployeeAvatar = null, + EmployeeId = attende.EmployeeId, + FirstName = attende.Employee?.FirstName, + ProjectName = projects.Where(p => p.Id == attende.ProjectID).Select(p => p.Name).FirstOrDefault(), + LastName = attende.Employee?.LastName, + JobRoleName = attende.Employee?.JobRole?.Name, + OrganizationName = attende.Employee?.Organization?.Name + }; + result.Add(result1); } @@ -793,7 +784,7 @@ namespace MarcoBMS.Services.Controllers /// /// Fetches attendance for an entire project team using a single, optimized database query. /// - private async Task> GetTeamAttendanceAsync(Guid tenantId, Guid projectId, Guid organizationId, DateTime forDate, bool includeInactive) + private async Task> GetTeamAttendanceAsync(Guid tenantId, Guid? projectId, Guid organizationId, DateTime forDate, bool includeInactive) { // This single query joins ProjectAllocations with Employees and performs a LEFT JOIN with Attendances. // This is far more efficient than fetching collections and joining them in memory. @@ -803,12 +794,22 @@ namespace MarcoBMS.Services.Controllers .Where(e => e.OrganizationId == organizationId && e.Organization != null && e.JobRole != null && e.IsActive); - List lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.AttendanceDate.Date == forDate && c.TenantId == tenantId).ToListAsync(); + var lstAttendanceQuery = _context.Attendes.Where(c => c.AttendanceDate.Date == forDate && c.TenantId == tenantId); + + if (projectId.HasValue) + { + lstAttendanceQuery = lstAttendanceQuery.Where(a => a.ProjectID == projectId); + } + + List lstAttendance = await lstAttendanceQuery.ToListAsync(); var employees = await query .AsNoTracking() .ToListAsync(); + var projectIds = lstAttendance.Select(a => a.ProjectID).ToList(); + var projects = await _context.Projects.Where(p => projectIds.Contains(p.Id) && p.TenantId == tenantId).ToListAsync(); + var response = employees .Select(employee => { @@ -822,9 +823,6 @@ namespace MarcoBMS.Services.Controllers JobRoleName = employee.JobRole!.Name, }; - //var member = emp.Where(e => e.Id == teamMember.EmployeeId); - - var attendance = lstAttendance.Find(x => x.EmployeeId == employee.Id) ?? new Attendance(); if (attendance != null) { @@ -832,6 +830,7 @@ namespace MarcoBMS.Services.Controllers result1.CheckInTime = attendance.InTime; result1.CheckOutTime = attendance.OutTime; result1.Activity = attendance.Activity; + result1.ProjectName = projects.Where(p => p.Id == attendance.ProjectID).Select(p => p.Name).FirstOrDefault(); } return result1; }) @@ -844,13 +843,23 @@ namespace MarcoBMS.Services.Controllers /// /// Fetches a single attendance record for the logged-in employee. /// - private async Task> GetSelfAttendanceAsync(Guid tenantId, Guid projectId, Guid employeeId, DateTime forDate) + private async Task> GetSelfAttendanceAsync(Guid tenantId, Guid? projectId, Guid employeeId, DateTime forDate) { List result = new List(); // This query fetches the employee's project allocation and their attendance in a single trip. - Attendance lstAttendance = await _context.Attendes - .FirstOrDefaultAsync(c => c.ProjectID == projectId && c.EmployeeId == employeeId && c.AttendanceDate.Date == forDate && c.TenantId == tenantId) ?? new Attendance(); + var lstAttendanceQuery = _context.Attendes + .Where(c => c.EmployeeId == employeeId && c.AttendanceDate.Date == forDate && c.TenantId == tenantId); + + if (projectId.HasValue) + { + lstAttendanceQuery = lstAttendanceQuery.Where(a => a.ProjectID == projectId); + } + + Attendance lstAttendance = await lstAttendanceQuery.FirstOrDefaultAsync() ?? new Attendance(); + + var project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == lstAttendance.ProjectID && p.TenantId == tenantId); + var employee = await _context.Employees .Include(e => e.Organization) @@ -866,6 +875,7 @@ namespace MarcoBMS.Services.Controllers EmployeeId = employee.Id, FirstName = employee.FirstName, OrganizationName = employee.Organization.Name, + ProjectName = project?.Name, LastName = employee.LastName, JobRoleName = employee.JobRole.Name, CheckInTime = lstAttendance.InTime,