Added an API to get todays attendance record for current logged-in-employee
This commit is contained in:
parent
a4714d5440
commit
5d8a5e0cc8
@ -1,6 +1,6 @@
|
||||
namespace Marco.Pms.Model.ViewModels.DashBoard
|
||||
{
|
||||
public class EmployeeAttendanceVM
|
||||
public class DashBoardEmployeeAttendanceVM
|
||||
{
|
||||
public string? FirstName { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
@ -2,7 +2,7 @@
|
||||
{
|
||||
public class ProjectAttendanceVM
|
||||
{
|
||||
public List<EmployeeAttendanceVM>? AttendanceTable { get; set; }
|
||||
public List<DashBoardEmployeeAttendanceVM>? AttendanceTable { get; set; }
|
||||
public int CheckedInEmployee { get; set; }
|
||||
public int AssignedEmployee { get; set; }
|
||||
}
|
||||
|
||||
@ -832,6 +832,13 @@ namespace Marco.Pms.Services.Controllers
|
||||
Available = true,
|
||||
MobileLink = "/dashboard/service-projects"
|
||||
});
|
||||
response.Add(new MenuSectionApplicationVM
|
||||
{
|
||||
Id = Guid.Parse("5fab4b88-c9a0-417b-aca2-130980fdb0cf"),
|
||||
Name = "Infra Projects",
|
||||
Available = true,
|
||||
MobileLink = "/dashboard/infra-projects"
|
||||
});
|
||||
|
||||
// Step 3: Log success
|
||||
response = response.Where(ms => !string.IsNullOrWhiteSpace(ms.MobileLink)).ToList();
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
using Marco.Pms.DataAccess.Data;
|
||||
using AutoMapper;
|
||||
using Marco.Pms.DataAccess.Data;
|
||||
using Marco.Pms.Model.Dtos.Attendance;
|
||||
using Marco.Pms.Model.Entitlements;
|
||||
using Marco.Pms.Model.Expenses;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Marco.Pms.Model.ViewModels.Activities;
|
||||
using Marco.Pms.Model.ViewModels.AttendanceVM;
|
||||
using Marco.Pms.Model.ViewModels.DashBoard;
|
||||
using Marco.Pms.Services.Service;
|
||||
using Marco.Pms.Services.Service.ServiceInterfaces;
|
||||
@ -26,6 +29,9 @@ namespace Marco.Pms.Services.Controllers
|
||||
private readonly ILoggingService _logger;
|
||||
private readonly PermissionServices _permissionServices;
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
|
||||
public static readonly Guid ActiveId = Guid.Parse("b74da4c2-d07e-46f2-9919-e75e49b12731");
|
||||
private static readonly Guid Draft = Guid.Parse("297e0d8f-f668-41b5-bfea-e03b354251c8");
|
||||
private static readonly Guid Review = Guid.Parse("6537018f-f4e9-4cb3-a210-6c3b2da999d7");
|
||||
@ -40,7 +46,8 @@ namespace Marco.Pms.Services.Controllers
|
||||
IProjectServices projectServices,
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
ILoggingService logger,
|
||||
PermissionServices permissionServices)
|
||||
PermissionServices permissionServices,
|
||||
IMapper mapper)
|
||||
{
|
||||
_context = context;
|
||||
_userHelper = userHelper;
|
||||
@ -48,8 +55,10 @@ namespace Marco.Pms.Services.Controllers
|
||||
_logger = logger;
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
_permissionServices = permissionServices;
|
||||
_mapper = mapper;
|
||||
tenantId = userHelper.GetTenantId();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches project progression data (planned and completed tasks) in graph form for a tenant and specified (or all) projects over a date range.
|
||||
/// </summary>
|
||||
@ -499,7 +508,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
return Ok(ApiResponse<object>.SuccessResponse(
|
||||
new ProjectAttendanceVM
|
||||
{
|
||||
AttendanceTable = new List<EmployeeAttendanceVM>(),
|
||||
AttendanceTable = new List<DashBoardEmployeeAttendanceVM>(),
|
||||
CheckedInEmployee = 0,
|
||||
AssignedEmployee = 0
|
||||
},
|
||||
@ -523,7 +532,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
.Join(employees,
|
||||
attendance => attendance.EmployeeId,
|
||||
employee => employee.Id,
|
||||
(attendance, employee) => new EmployeeAttendanceVM
|
||||
(attendance, employee) => new DashBoardEmployeeAttendanceVM
|
||||
{
|
||||
FirstName = employee.FirstName,
|
||||
LastName = employee.LastName,
|
||||
@ -1074,5 +1083,126 @@ namespace Marco.Pms.Services.Controllers
|
||||
ApiResponse<object>.ErrorResponse("An error occurred while fetching pending expenses.", "An error occurred while fetching pending expenses.", 500)); // [Error Response]
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves today's attendance details for a specific employee on a given project,
|
||||
/// defaulting to the currently logged-in employee when no employeeId is provided.
|
||||
/// Includes related project and employee information for UI display.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The project identifier whose attendance is requested.</param>
|
||||
/// <param name="employeeId">
|
||||
/// Optional employee identifier. When null, the currently logged-in employee is used.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// 200 OK with an <see cref="EmployeeAttendanceVM"/> payload on success, or a standardized
|
||||
/// error envelope on validation or processing failure.
|
||||
/// </returns>
|
||||
[HttpGet("get/attendance/employee/{projectId}")]
|
||||
public async Task<IActionResult> GetAttendanceByEmployeeAsync(Guid projectId, [FromQuery] Guid? employeeId)
|
||||
{
|
||||
// TenantId is assumed to come from a base controller, HttpContext, or similar.
|
||||
if (tenantId == Guid.Empty)
|
||||
{
|
||||
_logger.LogWarning("GetAttendanceByEmployeeAsync called with empty TenantId. ProjectId={ProjectId}", projectId);
|
||||
|
||||
return BadRequest(
|
||||
ApiResponse<object>.ErrorResponse("Invalid tenant information.", "TenantId is empty in GetAttendanceByEmployeeAsync.", 400));
|
||||
}
|
||||
|
||||
if (projectId == Guid.Empty)
|
||||
{
|
||||
_logger.LogWarning("GetAttendanceByEmployeeAsync called with empty ProjectId. TenantId={TenantId}", tenantId);
|
||||
|
||||
return BadRequest(
|
||||
ApiResponse<object>.ErrorResponse("Project reference is required.", "ProjectId is empty in GetAttendanceByEmployeeAsync.", 400));
|
||||
}
|
||||
|
||||
// Resolve the currently logged-in employee (e.g., from token or session).
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
var attendanceEmployeeId = employeeId ?? loggedInEmployee.Id;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// Step 1: Ensure employee is allocated to the project for this tenant.
|
||||
var projectAllocation = await _context.ProjectAllocations
|
||||
.Include(pa => pa.Employee)!.ThenInclude(e => e.JobRole)
|
||||
.Include(pa => pa.Employee)!.ThenInclude(e => e.Organization)
|
||||
.Include(pa => pa.Project)
|
||||
.FirstOrDefaultAsync(pa =>
|
||||
pa.ProjectId == projectId &&
|
||||
pa.EmployeeId == attendanceEmployeeId &&
|
||||
pa.IsActive &&
|
||||
pa.TenantId == tenantId);
|
||||
|
||||
if (projectAllocation == null)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"GetAttendanceByEmployeeAsync failed: Employee not allocated to project. TenantId={TenantId}, ProjectId={ProjectId}, EmployeeId={EmployeeId}, RequestedById={RequestedById}",
|
||||
tenantId, projectId, attendanceEmployeeId, loggedInEmployee.Id);
|
||||
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("The employee is not allocated to the selected project.", "Project allocation not found for given ProjectId, EmployeeId, and TenantId.",
|
||||
400));
|
||||
}
|
||||
|
||||
// Step 2: Fetch today's attendance (if any) for the selected employee and project.
|
||||
var today = DateTime.UtcNow.Date; // Prefer UTC for server-side comparisons.
|
||||
|
||||
var attendance = await _context.Attendes
|
||||
.Include(a => a.Approver)
|
||||
.Include(a => a.RequestedBy)
|
||||
.FirstOrDefaultAsync(a =>
|
||||
a.TenantId == tenantId &&
|
||||
a.EmployeeId == attendanceEmployeeId &&
|
||||
a.ProjectID == projectId &&
|
||||
a.AttendanceDate.Date == today);
|
||||
|
||||
// Step 3: Map to view model with defensive null handling.
|
||||
var attendanceVm = new EmployeeAttendanceVM
|
||||
{
|
||||
Id = attendance?.Id ?? Guid.Empty,
|
||||
EmployeeAvatar = null, // Can be filled from a file service or CDN later.
|
||||
EmployeeId = projectAllocation.EmployeeId,
|
||||
FirstName = projectAllocation.Employee?.FirstName,
|
||||
OrganizationName = projectAllocation.Employee?.Organization?.Name,
|
||||
LastName = projectAllocation.Employee?.LastName,
|
||||
JobRoleName = projectAllocation.Employee?.JobRole?.Name,
|
||||
ProjectId = projectId,
|
||||
ProjectName = projectAllocation.Project?.Name,
|
||||
CheckInTime = attendance?.InTime,
|
||||
CheckOutTime = attendance?.OutTime,
|
||||
Activity = attendance?.Activity ?? ATTENDANCE_MARK_TYPE.CHECK_IN,
|
||||
ApprovedAt = attendance?.ApprovedAt,
|
||||
Approver = attendance == null
|
||||
? null
|
||||
: _mapper.Map<BasicEmployeeVM>(attendance.Approver),
|
||||
RequestedAt = attendance?.RequestedAt,
|
||||
RequestedBy = attendance == null
|
||||
? null
|
||||
: _mapper.Map<BasicEmployeeVM>(attendance.RequestedBy)
|
||||
};
|
||||
|
||||
_logger.LogInfo("GetAttendanceByEmployeeAsync completed successfully. TenantId={TenantId}, ProjectId={ProjectId}, EmployeeId={EmployeeId}, HasAttendance={HasAttendance}",
|
||||
tenantId, projectId, attendanceEmployeeId, attendance != null);
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(attendanceVm, "Attendance fetched successfully.", 200));
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.LogWarning("GetAttendanceByEmployeeAsync was canceled. TenantId={TenantId}, ProjectId={ProjectId}, EmployeeId={EmployeeId}",
|
||||
tenantId, projectId, attendanceEmployeeId);
|
||||
|
||||
return StatusCode(499, ApiResponse<object>.ErrorResponse("The request was canceled.", "GetAttendanceByEmployeeAsync was canceled by the client.", 499));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "GetAttendanceByEmployeeAsync failed with an unexpected error. TenantId={TenantId}, ProjectId={ProjectId}, EmployeeId={EmployeeId}",
|
||||
tenantId, projectId, attendanceEmployeeId);
|
||||
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("An error occurred while fetching attendance.", "Unhandled exception in GetAttendanceByEmployeeAsync.", 500));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user