Added the name of the project name in view model

This commit is contained in:
ashutosh.nehete 2025-10-08 12:16:25 +05:30
parent 629c4541d6
commit b1af96b923
2 changed files with 98 additions and 87 deletions

View File

@ -10,6 +10,7 @@ namespace Marco.Pms.Model.ViewModels.AttendanceVM
public string? LastName { get; set; } public string? LastName { get; set; }
public string? EmployeeAvatar { get; set; } public string? EmployeeAvatar { get; set; }
public string? OrganizationName { get; set; } public string? OrganizationName { get; set; }
public string? ProjectName { get; set; }
public DateTime? CheckInTime { get; set; } public DateTime? CheckInTime { get; set; }
public DateTime? CheckOutTime { get; set; } public DateTime? CheckOutTime { get; set; }
public string? JobRoleName { get; set; } public string? JobRoleName { get; set; }

View File

@ -4,7 +4,6 @@ using Marco.Pms.Model.Dtos.Attendance;
using Marco.Pms.Model.Employees; using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.Mapper; using Marco.Pms.Model.Mapper;
using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Utilities; using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.AttendanceVM; using Marco.Pms.Model.ViewModels.AttendanceVM;
using Marco.Pms.Services.Hubs; using Marco.Pms.Services.Hubs;
@ -146,18 +145,11 @@ namespace MarcoBMS.Services.Controllers
[HttpGet("project/log")] [HttpGet("project/log")]
public async Task<IActionResult> EmployeeAttendanceByDateRange([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null) public async Task<IActionResult> EmployeeAttendanceByDateRange([FromQuery] Guid? projectId, [FromQuery] Guid? organizationId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null)
{ {
Guid tenantId = GetTenantId(); Guid tenantId = GetTenantId();
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); 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<object>.ErrorResponse("Project not found."));
}
var hasTeamAttendancePermission = await _permission.HasPermission(PermissionsMaster.TeamAttendance, LoggedInEmployee.Id); var hasTeamAttendancePermission = await _permission.HasPermission(PermissionsMaster.TeamAttendance, LoggedInEmployee.Id);
var hasSelfAttendancePermission = await _permission.HasPermission(PermissionsMaster.SelfAttendance, LoggedInEmployee.Id); var hasSelfAttendancePermission = await _permission.HasPermission(PermissionsMaster.SelfAttendance, LoggedInEmployee.Id);
@ -175,35 +167,41 @@ namespace MarcoBMS.Services.Controllers
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400)); return BadRequest(ApiResponse<object>.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<object>.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<EmployeeAttendanceVM>(); var result = new List<EmployeeAttendanceVM>();
//Attendance? attendance = null; //Attendance? attendance = null;
if (dateFrom == null) fromDate = DateTime.UtcNow.Date; if (dateFrom == null) fromDate = DateTime.UtcNow.Date;
if (dateTo == null && dateFrom != null) toDate = fromDate.AddDays(-1); if (dateTo == null && dateFrom != null) toDate = fromDate.AddDays(-1);
if (hasTeamAttendancePermission) var lstAttendanceQuery = _context.Attendes
{
List<Attendance> lstAttendance = await _context.Attendes
.Include(a => a.Employee) .Include(a => a.Employee)
.ThenInclude(e => e!.Organization) .ThenInclude(e => e!.Organization)
.Include(a => a.Employee) .Include(a => a.Employee)
.ThenInclude(e => e!.JobRole) .ThenInclude(e => e!.JobRole)
.Where(a => a.ProjectID == projectId && .Where(a =>
a.AttendanceDate.Date >= fromDate.Date && a.AttendanceDate.Date >= fromDate.Date &&
a.AttendanceDate.Date <= toDate.Date && a.AttendanceDate.Date <= toDate.Date &&
a.TenantId == tenantId && a.TenantId == tenantId &&
a.Employee != null && a.Employee != null &&
a.Employee.Organization != null && a.Employee.Organization != null &&
a.Employee.JobRole != null a.Employee.JobRole != null);
).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);
}
if (hasTeamAttendancePermission)
{
List<Attendance> 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) foreach (Attendance? attendance in lstAttendance)
{ {
var result1 = new EmployeeAttendanceVM() var result1 = new EmployeeAttendanceVM()
@ -216,6 +214,7 @@ namespace MarcoBMS.Services.Controllers
FirstName = attendance.Employee?.FirstName, FirstName = attendance.Employee?.FirstName,
LastName = attendance.Employee?.LastName, LastName = attendance.Employee?.LastName,
JobRoleName = attendance.Employee?.JobRole?.Name, JobRoleName = attendance.Employee?.JobRole?.Name,
ProjectName = projects.Where(p => p.Id == attendance.ProjectID).Select(p => p.Name).FirstOrDefault(),
OrganizationName = attendance.Employee?.Organization?.Name OrganizationName = attendance.Employee?.Organization?.Name
}; };
@ -224,26 +223,12 @@ namespace MarcoBMS.Services.Controllers
} }
else if (hasSelfAttendancePermission) 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) var lstAttendances = await lstAttendanceQuery.Where(a => a.EmployeeId == LoggedInEmployee.Id).ToListAsync();
{
lstAttendanceQuery = lstAttendanceQuery.Where(a => a.Employee != null && a.Employee.OrganizationId == organizationId); 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) foreach (var attendance in lstAttendances)
{ {
@ -255,6 +240,7 @@ namespace MarcoBMS.Services.Controllers
FirstName = attendance.Employee?.FirstName, FirstName = attendance.Employee?.FirstName,
LastName = attendance.Employee?.LastName, LastName = attendance.Employee?.LastName,
JobRoleName = attendance.Employee?.JobRole?.Name, JobRoleName = attendance.Employee?.JobRole?.Name,
ProjectName = projects.Where(p => p.Id == attendance.ProjectID).Select(p => p.Name).FirstOrDefault(),
OrganizationName = attendance.Employee?.Organization?.Name, OrganizationName = attendance.Employee?.Organization?.Name,
CheckInTime = attendance.InTime, CheckInTime = attendance.InTime,
CheckOutTime = attendance.OutTime, CheckOutTime = attendance.OutTime,
@ -278,13 +264,13 @@ namespace MarcoBMS.Services.Controllers
/// <param name="includeInactive">Optional. Includes inactive employees in the team list if true.</param> /// <param name="includeInactive">Optional. Includes inactive employees in the team list if true.</param>
/// <param name="date">Optional. The date for which to fetch attendance, in "yyyy-MM-dd" format. Defaults to the current UTC date.</param> /// <param name="date">Optional. The date for which to fetch attendance, in "yyyy-MM-dd" format. Defaults to the current UTC date.</param>
/// <returns>An IActionResult containing a list of employee attendance records or an error response.</returns> /// <returns>An IActionResult containing a list of employee attendance records or an error response.</returns>
public async Task<IActionResult> EmployeeAttendanceByProjectAsync([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool includeInactive, [FromQuery] string? date = null) public async Task<IActionResult> EmployeeAttendanceByProjectAsync([FromQuery] Guid? projectId, [FromQuery] Guid? organizationId, [FromQuery] bool includeInactive, [FromQuery] string? date = null)
{ {
var tenantId = GetTenantId(); var tenantId = GetTenantId();
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
// --- 1. Initial Validation and Permission Checks --- // --- 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 // Validate date format
if (!DateTime.TryParse(date, out var forDate)) if (!DateTime.TryParse(date, out var forDate))
@ -292,14 +278,6 @@ namespace MarcoBMS.Services.Controllers
forDate = DateTime.UtcNow.Date; // Default to today's date 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<object>.ErrorResponse("Project not found."));
}
// --- 2. Delegate to Specific Logic Based on Permissions --- // --- 2. Delegate to Specific Logic Based on Permissions ---
try try
{ {
@ -326,34 +304,47 @@ namespace MarcoBMS.Services.Controllers
return StatusCode(403, ApiResponse<object>.ErrorResponse("You do not have permission to view attendance.", new { }, 403)); return StatusCode(403, ApiResponse<object>.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<object>.SuccessResponse(result, $"{result.Count} attendance records fetched successfully.")); return Ok(ApiResponse<object>.SuccessResponse(result, $"{result.Count} attendance records fetched successfully."));
} }
catch (Exception ex) 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<object>.ErrorResponse("An internal server error occurred.")); return StatusCode(500, ApiResponse<object>.ErrorResponse("An internal server error occurred."));
} }
} }
[HttpGet("regularize")] [HttpGet("regularize")]
public async Task<IActionResult> GetRequestRegularizeAttendance([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool IncludeInActive) public async Task<IActionResult> GetRequestRegularizeAttendance([FromQuery] Guid? projectId, [FromQuery] Guid? organizationId, [FromQuery] bool IncludeInActive)
{ {
Guid TenantId = GetTenantId(); Guid TenantId = GetTenantId();
Employee LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); Employee LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var result = new List<EmployeeAttendanceVM>(); var result = new List<EmployeeAttendanceVM>();
List<Attendance> 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<ProjectAllocation> projectteam = await _projectServices.GetTeamByProject(TenantId, projectId, organizationId, true); if (organizationId.HasValue)
var idList = projectteam.Select(p => p.EmployeeId).ToList(); {
var jobRole = await _context.JobRoles.ToListAsync(); lstAttendanceQuery = lstAttendanceQuery.Where(a => a.Employee != null && a.Employee.OrganizationId == organizationId);
}
if (projectId.HasValue)
{
lstAttendanceQuery = lstAttendanceQuery.Where(a => a.ProjectID == projectId);
}
List<Attendance> 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) 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()
{ {
@ -363,13 +354,13 @@ namespace MarcoBMS.Services.Controllers
Activity = attende.Activity, Activity = attende.Activity,
EmployeeAvatar = null, EmployeeAvatar = null,
EmployeeId = attende.EmployeeId, EmployeeId = attende.EmployeeId,
FirstName = teamMember.Employee.FirstName, FirstName = attende.Employee?.FirstName,
LastName = teamMember.Employee.LastName, ProjectName = projects.Where(p => p.Id == attende.ProjectID).Select(p => p.Name).FirstOrDefault(),
JobRoleName = teamMember.Employee.JobRole.Name, LastName = attende.Employee?.LastName,
OrganizationName = teamMember.Employee.Organization?.Name JobRoleName = attende.Employee?.JobRole?.Name,
OrganizationName = attende.Employee?.Organization?.Name
}; };
result.Add(result1); result.Add(result1);
}
} }
@ -793,7 +784,7 @@ namespace MarcoBMS.Services.Controllers
/// <summary> /// <summary>
/// Fetches attendance for an entire project team using a single, optimized database query. /// Fetches attendance for an entire project team using a single, optimized database query.
/// </summary> /// </summary>
private async Task<List<EmployeeAttendanceVM>> GetTeamAttendanceAsync(Guid tenantId, Guid projectId, Guid organizationId, DateTime forDate, bool includeInactive) private async Task<List<EmployeeAttendanceVM>> 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 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. // 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); .Where(e => e.OrganizationId == organizationId && e.Organization != null && e.JobRole != null && e.IsActive);
List<Attendance> 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<Attendance> lstAttendance = await lstAttendanceQuery.ToListAsync();
var employees = await query var employees = await query
.AsNoTracking() .AsNoTracking()
.ToListAsync(); .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 var response = employees
.Select(employee => .Select(employee =>
{ {
@ -822,9 +823,6 @@ namespace MarcoBMS.Services.Controllers
JobRoleName = employee.JobRole!.Name, JobRoleName = employee.JobRole!.Name,
}; };
//var member = emp.Where(e => e.Id == teamMember.EmployeeId);
var attendance = lstAttendance.Find(x => x.EmployeeId == employee.Id) ?? new Attendance(); var attendance = lstAttendance.Find(x => x.EmployeeId == employee.Id) ?? new Attendance();
if (attendance != null) if (attendance != null)
{ {
@ -832,6 +830,7 @@ namespace MarcoBMS.Services.Controllers
result1.CheckInTime = attendance.InTime; result1.CheckInTime = attendance.InTime;
result1.CheckOutTime = attendance.OutTime; result1.CheckOutTime = attendance.OutTime;
result1.Activity = attendance.Activity; result1.Activity = attendance.Activity;
result1.ProjectName = projects.Where(p => p.Id == attendance.ProjectID).Select(p => p.Name).FirstOrDefault();
} }
return result1; return result1;
}) })
@ -844,13 +843,23 @@ namespace MarcoBMS.Services.Controllers
/// <summary> /// <summary>
/// Fetches a single attendance record for the logged-in employee. /// Fetches a single attendance record for the logged-in employee.
/// </summary> /// </summary>
private async Task<List<EmployeeAttendanceVM>> GetSelfAttendanceAsync(Guid tenantId, Guid projectId, Guid employeeId, DateTime forDate) private async Task<List<EmployeeAttendanceVM>> GetSelfAttendanceAsync(Guid tenantId, Guid? projectId, Guid employeeId, DateTime forDate)
{ {
List<EmployeeAttendanceVM> result = new List<EmployeeAttendanceVM>(); List<EmployeeAttendanceVM> result = new List<EmployeeAttendanceVM>();
// This query fetches the employee's project allocation and their attendance in a single trip. // This query fetches the employee's project allocation and their attendance in a single trip.
Attendance lstAttendance = await _context.Attendes var lstAttendanceQuery = _context.Attendes
.FirstOrDefaultAsync(c => c.ProjectID == projectId && c.EmployeeId == employeeId && c.AttendanceDate.Date == forDate && c.TenantId == tenantId) ?? new Attendance(); .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 var employee = await _context.Employees
.Include(e => e.Organization) .Include(e => e.Organization)
@ -866,6 +875,7 @@ namespace MarcoBMS.Services.Controllers
EmployeeId = employee.Id, EmployeeId = employee.Id,
FirstName = employee.FirstName, FirstName = employee.FirstName,
OrganizationName = employee.Organization.Name, OrganizationName = employee.Organization.Name,
ProjectName = project?.Name,
LastName = employee.LastName, LastName = employee.LastName,
JobRoleName = employee.JobRole.Name, JobRoleName = employee.JobRole.Name,
CheckInTime = lstAttendance.InTime, CheckInTime = lstAttendance.InTime,