diff --git a/Marco.Pms.Services/Controllers/DashboardController.cs b/Marco.Pms.Services/Controllers/DashboardController.cs index 8a51336..8ed0ba0 100644 --- a/Marco.Pms.Services/Controllers/DashboardController.cs +++ b/Marco.Pms.Services/Controllers/DashboardController.cs @@ -5,6 +5,7 @@ using Marco.Pms.Model.Employees; using Marco.Pms.Model.Projects; using Marco.Pms.Model.Utilities; using Marco.Pms.Model.ViewModels.DashBoard; +using Marco.Pms.Services.Service; using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Service; using Microsoft.AspNetCore.Authorization; @@ -21,11 +22,13 @@ namespace Marco.Pms.Services.Controllers private readonly ApplicationDbContext _context; private readonly UserHelper _userHelper; private readonly ILoggingService _logger; - public DashboardController(ApplicationDbContext context, UserHelper userHelper, ILoggingService logger) + private readonly PermissionServices _permissionServices; + public DashboardController(ApplicationDbContext context, UserHelper userHelper, ILoggingService logger, PermissionServices permissionServices) { _context = context; _userHelper = userHelper; _logger = logger; + _permissionServices = permissionServices; } [HttpGet("progression")] public async Task GetGraph([FromQuery] double days, [FromQuery] string FromDate, [FromQuery] Guid? projectId) @@ -360,36 +363,67 @@ namespace Marco.Pms.Services.Controllers { _logger.LogInfo("GetAttendanceOverView called for ProjectId: {ProjectId}, Days: {Days}", projectId, days); + // Step 1: Validate project existence + var project = await _context.Projects.AsNoTracking().FirstOrDefaultAsync(p => p.Id == projectId); + if (project == null) + { + _logger.LogWarning("Project not found for ProjectId: {ProjectId}", projectId); + return BadRequest(ApiResponse.ErrorResponse("Project not found", "Project not found", 400)); + } + + // Step 2: Permission check + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + bool hasAssigned = await _permissionServices.HasProjectPermission(loggedInEmployee, projectId.ToString()); + + if (!hasAssigned) + { + _logger.LogWarning("Unauthorized access attempt. EmployeeId: {EmployeeId}, ProjectId: {ProjectId}", loggedInEmployee.Id, projectId); + return StatusCode(403, ApiResponse.ErrorResponse( + "You don't have permission to access this feature", + "You don't have permission to access this feature", 403)); + } + + // Step 3: Validate and parse days input if (!int.TryParse(days, out int dayCount) || dayCount <= 0) { + _logger.LogWarning("Invalid days input received: {Days}", days); return BadRequest(ApiResponse.ErrorResponse("Invalid number of days", "Days must be a positive integer", 400)); } + // Step 4: Define date range DateTime today = DateTime.UtcNow.Date; DateTime startDate = today.AddDays(-dayCount); - // Step 1: Get project allocations and related job roles + // Step 5: Load project allocations and related job roles var allocations = await _context.ProjectAllocations .Where(pa => pa.ProjectId == projectId) .ToListAsync(); + if (!allocations.Any()) + { + _logger.LogInfo("No employee allocations found for project: {ProjectId}", projectId); + return Ok(ApiResponse.SuccessResponse(new List(), "No allocations found", 200)); + } + var jobRoleIds = allocations.Select(pa => pa.JobRoleId).Distinct().ToList(); + var jobRoles = await _context.JobRoles .Where(jr => jobRoleIds.Contains(jr.Id)) .ToListAsync(); - // Step 2: Get attendance records for the given range + // Step 6: Load attendance records for given date range var attendances = await _context.Attendes - .Where(a => a.ProjectID == projectId - && a.InTime.HasValue - && a.InTime.Value.Date >= startDate - && a.InTime.Value.Date <= today) + .Where(a => + a.ProjectID == projectId && + a.InTime.HasValue && + a.InTime.Value.Date >= startDate && + a.InTime.Value.Date <= today) .ToListAsync(); - var result = new List(); + var overviewList = new List(); - // Step 3: Generate report per day and job role - for (DateTime date = today; date >= startDate; date = date.AddDays(-1)) + // Step 7: Process attendance per date per role + for (DateTime date = today; date > startDate; date = date.AddDays(-1)) { foreach (var jobRole in jobRoles) { @@ -398,26 +432,27 @@ namespace Marco.Pms.Services.Controllers .Select(pa => pa.EmployeeId) .ToList(); - var count = attendances + int presentCount = attendances .Count(a => employeeIds.Contains(a.EmployeeID) && a.InTime!.Value.Date == date); - result.Add(new AttendanceOverviewVM + overviewList.Add(new AttendanceOverviewVM { Role = jobRole.Name, Date = date.ToString("yyyy-MM-dd"), - Present = count + Present = presentCount }); } } - var ordered = result + // Step 8: Order result for consistent presentation + var sortedResult = overviewList .OrderByDescending(r => r.Date) .ThenByDescending(r => r.Present) .ToList(); - _logger.LogInfo("Attendance overview fetched for ProjectId: {ProjectId}, Total Records: {Count}", projectId, ordered.Count); + _logger.LogInfo("Attendance overview fetched. ProjectId: {ProjectId}, Records: {Count}", projectId, sortedResult.Count); - return Ok(ApiResponse.SuccessResponse(ordered, $"{ordered.Count} records fetched for attendance overview", 200)); + return Ok(ApiResponse.SuccessResponse(sortedResult, $"{sortedResult.Count} records fetched for attendance overview", 200)); } } }