using Marco.Pms.DataAccess.Data; using Marco.Pms.DataAccess.Repository.IRepository; 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 MarcoBMS.Services.Service; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.CodeAnalysis; using Microsoft.EntityFrameworkCore; using System.Globalization; namespace MarcoBMS.Services.Controllers { [ApiController] [Route("api/[controller]")] public class AttendanceController : ControllerBase { private readonly IUnitOfWork _unitOfWork; private readonly ApplicationDbContext _context; private readonly UserManager _userManager; private readonly IEmailSender _emailSender; private readonly IAttendenceRepository _attendenceRepository; private readonly EmployeeHelper _employeeHelper; private readonly ProjectHelper _projectHelper; public AttendanceController(UserManager userManager, IEmailSender emailSender, IEmployeeRepository empRepo, IUnitOfWork unitOfWork, ApplicationDbContext context, IAttendenceRepository attendenceRepository, EmployeeHelper employeeHelper, ProjectHelper projectHelper) { _unitOfWork = unitOfWork; _context = context; _userManager = userManager; _emailSender = emailSender; _attendenceRepository = attendenceRepository; _employeeHelper = employeeHelper; _projectHelper = projectHelper; } private int GetTenantId() { var tenant = User.FindFirst("TenantId")?.Value; return (tenant != null ? Convert.ToInt32(tenant) : 1); } private int GetUserId() { var tenant = User.FindFirst("Id")?.Value; return (tenant != null ? Convert.ToInt32(tenant) : 1); } [HttpGet("log/attendance/{attendanceid}")] public async Task GetAttendanceLogById(int attendanceid) { int TenantId = GetUserId(); 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 = GetUserId(); DateOnly forDate = new DateOnly(); if (date != null && DateOnly.TryParse(date, out forDate) == false) { return StatusCode(400, ApiResponse.SuccessResponse(date, "Invalid Date", 400));// new { error = ex.Message }); } 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/team")] public async Task EmployeeAttendanceByProject([FromQuery] int projectId, [FromQuery] string? date = null) { int TenantId = GetUserId(); DateTime forDate = new DateTime(); if (date != null && DateTime.TryParse(date, out forDate) == false) { return StatusCode(400, ApiResponse.SuccessResponse(date, "Invalid Date", 400));// new { error = ex.Message }); } if (projectId <= 0) { return BadRequest("Project ID is required and must be greater than zero."); } 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 _projectHelper.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); 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)); } [HttpPost] [Route("record")] public async Task RecordAttendance([FromBody] RecordAttendanceDot recordAttendanceDot) { if (!ModelState.IsValid) return BadRequest(ModelState); int TenantId = GetTenantId(); using var transaction = await _context.Database.BeginTransactionAsync(); try { Attendance attendance = await _context.Attendes.FirstOrDefaultAsync(a => a.EmployeeID == recordAttendanceDot.EmployeeID && a.AttendanceDate.Date == recordAttendanceDot.Date.Date && a.TenantId == TenantId); ; 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" DateTime finalDateTime = GetDateFromTimeStamp(recordAttendanceDot, recordAttendanceDot.MarkTime); attendance.OutTime = finalDateTime; } else if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE) { DateTime finalDateTime = GetDateFromTimeStamp(recordAttendanceDot, recordAttendanceDot.MarkTime); 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; DateTime finalDateTime = GetDateFromTimeStamp(recordAttendanceDot, recordAttendanceDot.MarkTime); 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 = recordAttendanceDot.Date, Comment = recordAttendanceDot.Comment, EmployeeID = recordAttendanceDot.EmployeeID, Latitude = recordAttendanceDot.Latitude, Longitude = recordAttendanceDot.Longitude, TenantId = TenantId, UpdatedBy = GetUserId(), UpdatedOn = DateTime.UtcNow }; //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); 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 }; return Ok(ApiResponse.SuccessResponse(vm, "Attendance marked successfully.", 200)); } catch (Exception ex) { await transaction.RollbackAsync(); // Rollback on failure return StatusCode(500, ApiResponse.SuccessResponse(new object(), ex.Message, 500));// new { error = ex.Message }); } return Ok(ApiResponse.SuccessResponse("success", "Roles modified.", 200)); } 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; } } }