using System.Globalization; using Marco.Pms.DataAccess.Data; using Marco.Pms.Model.AttendanceModule; using Marco.Pms.Model.Dtos.Attendance; using Marco.Pms.Model.Employees; using Marco.Pms.Model.Projects; using Marco.Pms.Model.Utilities; using Marco.Pms.Model.ViewModels.Attendance; using MarcoBMS.Services.Helpers; using Microsoft.AspNetCore.Mvc; using Microsoft.CodeAnalysis; using Microsoft.EntityFrameworkCore; using static System.Runtime.InteropServices.JavaScript.JSType; namespace MarcoBMS.Services.Controllers { [ApiController] [Route("api/[controller]")] public class AttendanceController : ControllerBase { private readonly ApplicationDbContext _context; private readonly EmployeeHelper _employeeHelper; private readonly ProjectsHelper _projectsHelper; private readonly UserHelper _userHelper; public AttendanceController( ApplicationDbContext context, EmployeeHelper employeeHelper, ProjectsHelper projectsHelper, UserHelper userHelper) { _context = context; _employeeHelper = employeeHelper; _projectsHelper = projectsHelper; _userHelper = userHelper; } private int GetTenantId() { return _userHelper.GetTenantId(); //var tenant = User.FindFirst("TenantId")?.Value; //return (tenant != null ? Convert.ToInt32(tenant) : 1); } [HttpGet("log/attendance/{attendanceid}")] public async Task GetAttendanceLogById(int attendanceid) { int TenantId = GetTenantId(); List lstAttendance = await _context.AttendanceLogs.Where(c => c.AttendanceId == attendanceid && c.TenantId == TenantId).ToListAsync(); return Ok(ApiResponse.SuccessResponse(lstAttendance, String.Format("{0} Attendance records fetched successfully", lstAttendance.Count), 200)); } [HttpGet("log/employee/{employeeid}")] public async Task GetAttendanceLogByEmployeeId(int employeeid, [FromQuery] string? date = null) { int TenantId = GetTenantId(); DateOnly forDate = new DateOnly(); if (date != null && DateOnly.TryParse(date, out forDate) == false) { return BadRequest(ApiResponse.ErrorResponse("Invalid Date", "Invalid Date", 400)); } List lstAttendance = await _context.AttendanceLogs.Where(c => c.EmployeeID == employeeid && c.TenantId == TenantId).ToListAsync(); return Ok(ApiResponse.SuccessResponse(lstAttendance, System.String.Format("{0} Attendance records fetched successfully", lstAttendance.Count), 200)); } /// /// /// /// ProjectID /// YYYY-MM-dd /// [HttpGet("project/log")] public async Task EmployeeAttendanceByDateRange([FromQuery] int projectId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null) { int TenantId = GetTenantId(); DateTime fromDate = new DateTime(); DateTime toDate = new DateTime(); if (dateFrom != null && DateTime.TryParse(dateFrom, out fromDate) == false) { return BadRequest(ApiResponse.ErrorResponse("Invalid Date", "Invalid Date", 400)); } if (dateTo != null && DateTime.TryParse(dateTo, out toDate) == false) { return BadRequest(ApiResponse.ErrorResponse("Invalid Date", "Invalid Date", 400)); } if (projectId <= 0) { 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); List lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.AttendanceDate.Date <= fromDate && c.AttendanceDate.Date >= toDate && c.TenantId == TenantId).ToListAsync(); List projectteam = await _projectsHelper.GetTeamByProject(TenantId, projectId, true); foreach (ProjectAllocation teamMember in projectteam) { var result1 = new EmployeeAttendanceVM() { EmployeeAvatar = null, EmployeeId = teamMember.EmployeeId, FirstName = teamMember.Employee.FirstName, LastName = teamMember.Employee.LastName }; attendance = lstAttendance.Find(x => x.EmployeeID == teamMember.EmployeeId) ?? new Attendance(); if (attendance != null) { result1.Id = attendance.Id; result1.CheckInTime = attendance.InTime; result1.CheckOutTime = attendance.OutTime; result1.Activity = attendance.Activity; } result.Add(result1); } return Ok(ApiResponse.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200)); } /// /// /// /// ProjectID /// YYYY-MM-dd /// [HttpGet("project/team")] public async Task EmployeeAttendanceByProject([FromQuery] int projectId, [FromQuery] bool IncludeInActive, [FromQuery] string? date = null) { int TenantId = GetTenantId(); DateTime forDate = new DateTime(); if (date != null && DateTime.TryParse(date, out forDate) == false) { return BadRequest(ApiResponse.ErrorResponse("Invalid Date", "Invalid Date", 400)); } if (projectId <= 0) { 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 (date == null) forDate = DateTime.UtcNow.Date; List lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.AttendanceDate.Date == forDate && c.TenantId == TenantId).ToListAsync(); List projectteam = await _projectsHelper.GetTeamByProject(TenantId, projectId, IncludeInActive); var idList = projectteam.Select(p => p.EmployeeId).ToList(); //var emp = await _context.Employees.Where(e => idList.Contains(e.Id)).Include(e => e.JobRole).ToListAsync(); var jobRole = await _context.JobRoles.ToListAsync(); foreach (ProjectAllocation teamMember in projectteam) { if (teamMember.Employee.JobRole != null) { var result1 = new EmployeeAttendanceVM() { EmployeeAvatar = null, EmployeeId = teamMember.EmployeeId, FirstName = teamMember.Employee.FirstName, LastName = teamMember.Employee.LastName, JobRoleName = teamMember.Employee.JobRole.Name, }; //var member = emp.Where(e => e.Id == teamMember.EmployeeId); attendance = lstAttendance.Find(x => x.EmployeeID == teamMember.EmployeeId) ?? new Attendance(); if (attendance != null) { result1.Id = attendance.Id; result1.CheckInTime = attendance.InTime; result1.CheckOutTime = attendance.OutTime; result1.Activity = attendance.Activity; } result.Add(result1); } } result.Sort(delegate (EmployeeAttendanceVM x, EmployeeAttendanceVM y) { //return x.FirstName.CompareTo(y.FirstName); return string.Compare(x.FirstName, y.FirstName, StringComparison.Ordinal); }); return Ok(ApiResponse.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200)); } [HttpGet("regularize")] public async Task GetRequestRegularizeAttendance([FromQuery] int projectId, [FromQuery] bool IncludeInActive) { int TenantId = GetTenantId(); 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(); List projectteam = await _projectsHelper.GetTeamByProject(TenantId, projectId, IncludeInActive); var idList = projectteam.Select(p => p.EmployeeId).ToList(); var jobRole = await _context.JobRoles.ToListAsync(); foreach (Attendance attende in lstAttendance) { var result1 = new EmployeeAttendanceVM() { Id = attende.Id, CheckInTime = attende.InTime, CheckOutTime = attende.OutTime, Activity = attende.Activity, EmployeeAvatar = null, EmployeeId = attende.EmployeeID, }; var teamMember = projectteam.Find(m => m.EmployeeId == attende.EmployeeID); if (teamMember != null && teamMember.Employee.JobRole != null) { result1.FirstName = teamMember.Employee.FirstName; result1.LastName = teamMember.Employee.LastName; result1.JobRoleName = teamMember.Employee.JobRole.Name; } result.Add(result1); } result.Sort(delegate (EmployeeAttendanceVM x, EmployeeAttendanceVM y) { return string.Compare(x.FirstName, y.FirstName, StringComparison.Ordinal); }); return Ok(ApiResponse.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200)); } [HttpPost] [Route("record")] public async Task RecordAttendance([FromBody] RecordAttendanceDot recordAttendanceDot) { if (!ModelState.IsValid) { var errors = ModelState.Values .SelectMany(v => v.Errors) .Select(e => e.ErrorMessage) .ToList(); return BadRequest(ApiResponse.ErrorResponse("Invalid data", errors, 400)); } int TenantId = GetTenantId(); using var transaction = await _context.Database.BeginTransactionAsync(); try { Attendance? attendance = await _context.Attendes.FirstOrDefaultAsync(a => a.Id == recordAttendanceDot.Id && a.TenantId == TenantId); ; if (recordAttendanceDot.MarkTime == null) return BadRequest(ApiResponse.ErrorResponse("Invalid Mark Time", "Invalid Mark Time",400)); DateTime finalDateTime = GetDateFromTimeStamp(recordAttendanceDot, recordAttendanceDot.MarkTime); //if(recordAttendanceDot.Comment != null) if (attendance != null) { attendance.Comment = recordAttendanceDot.Comment; if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.CHECK_IN) { attendance.OutTime = null; attendance.Activity = ATTENDANCE_MARK_TYPE.CHECK_OUT; } else if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.CHECK_OUT) { attendance.IsApproved = true; attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE; //string timeString = "10:30 PM"; // Format: "hh:mm tt" attendance.OutTime = finalDateTime; } else if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE) { attendance.OutTime = finalDateTime; attendance.Activity = ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE; // do nothing } else if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.REGULARIZE) { attendance.IsApproved = true; attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE; // do nothing } else if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT) { attendance.IsApproved = false; attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT; // do nothing } attendance.Date = DateTime.UtcNow; // update code _context.Attendes.Update(attendance); } else { attendance = new Attendance(); attendance.TenantId = TenantId; attendance.AttendanceDate = recordAttendanceDot.Date; // attendance.Activity = recordAttendanceDot.Action; attendance.Comment = recordAttendanceDot.Comment; attendance.EmployeeID = recordAttendanceDot.EmployeeID; attendance.ProjectID = recordAttendanceDot.ProjectID; attendance.Date = DateTime.UtcNow; attendance.InTime = finalDateTime; attendance.OutTime = null; attendance.Activity = ATTENDANCE_MARK_TYPE.CHECK_OUT; _context.Attendes.Add(attendance); } await _context.SaveChangesAsync(); // Step 3: Always insert a new log entry var attendanceLog = new AttendanceLog { AttendanceId = attendance.Id, // Use existing or new AttendanceId Activity = attendance.Activity, ActivityTime = finalDateTime, Comment = recordAttendanceDot.Comment, EmployeeID = recordAttendanceDot.EmployeeID, Latitude = recordAttendanceDot.Latitude, Longitude = recordAttendanceDot.Longitude, TenantId = TenantId, UpdatedBy = recordAttendanceDot.EmployeeID, UpdatedOn = recordAttendanceDot.Date }; //if (recordAttendanceDot.Image != null && recordAttendanceDot.Image.Count > 0) //{ // attendanceLog.Photo = recordAttendanceDot.Image[0].Base64Data; //} _context.AttendanceLogs.Add(attendanceLog); await _context.SaveChangesAsync(); await transaction.CommitAsync(); // Commit transaction Employee employee = await _employeeHelper.GetEmployeeByID(recordAttendanceDot.EmployeeID); if(employee.JobRole != null) { EmployeeAttendanceVM vm = new EmployeeAttendanceVM() { CheckInTime = attendance.InTime, CheckOutTime = attendance.OutTime, EmployeeAvatar = null, EmployeeId = recordAttendanceDot.EmployeeID, FirstName = employee.FirstName, LastName = employee.LastName, Id = attendance.Id, Activity = attendance.Activity, JobRoleName = employee.JobRole.Name }; return Ok(ApiResponse.SuccessResponse(vm, "Attendance marked successfully.", 200)); } return Ok(ApiResponse.SuccessResponse(new EmployeeAttendanceVM(), "Attendance marked successfully.", 200)); } catch (Exception ex) { await transaction.RollbackAsync(); // Rollback on failure return BadRequest(ApiResponse.ErrorResponse(ex.Message, ex, 400)); } } private static DateTime GetDateFromTimeStamp(RecordAttendanceDot recordAttendanceDot, string timeString) { DateTime date = recordAttendanceDot.Date; // Parse time string to TimeSpan DateTime parsedTime = DateTime.ParseExact(timeString, "hh:mm tt", CultureInfo.InvariantCulture); // Combine date with time DateTime finalDateTime = new DateTime(date.Year, date.Month, date.Day, parsedTime.Hour, parsedTime.Minute, 0); return finalDateTime; } } }