PSTW_CentralizeSystem/Controllers/API/OvertimeAPI.cs
2025-05-13 17:13:30 +08:00

1649 lines
62 KiB
C#

using Azure.Core;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Mono.TextTemplating;
using Newtonsoft.Json;
using PSTW_CentralSystem.Areas.OTcalculate.Models;
using PSTW_CentralSystem.Controllers.API;
using PSTW_CentralSystem.Controllers.API.Inventory;
using PSTW_CentralSystem.DBContext;
using PSTW_CentralSystem.Models;
using System.ComponentModel.Design;
using System.Data;
using System;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Reflection;
using static System.Collections.Specialized.BitVector32;
using System.Security.Claims;
using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages;
using Microsoft.AspNetCore.Mvc.Rendering;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using PSTW_CentralSystem.Areas.OTcalculate.Services;
using Microsoft.AspNetCore.Hosting;
using DocumentFormat.OpenXml.InkML;
namespace PSTW_CentralSystem.Controllers.API
{
[ApiController]
[Route("[controller]")]
public class OvertimeAPI : Controller
{
private readonly ILogger<OvertimeAPI> _logger;
private readonly CentralSystemContext _centralDbContext;
private readonly UserManager<UserModel> _userManager;
private readonly OvertimePdfService _pdfService;
private readonly IWebHostEnvironment _env;
private readonly OvertimeExcelService _excelService;
public OvertimeAPI(ILogger<OvertimeAPI> logger, CentralSystemContext centralDbContext, UserManager<UserModel> userManager, OvertimePdfService pdfService, IWebHostEnvironment env, OvertimeExcelService excelService)
{
_logger = logger;
_centralDbContext = centralDbContext;
_userManager = userManager;
_pdfService = pdfService;
_env = env;
_excelService = excelService;
}
#region Settings
[HttpGet("GetUpdateDates")]
public IActionResult GetUpdateDates()
{
try
{
var latestRateUpdate = _centralDbContext.Rates.OrderByDescending(r => r.LastUpdated).FirstOrDefault()?.LastUpdated;
var latestCalendarUpdate = _centralDbContext.Holidays.OrderByDescending(c => c.LastUpdated).FirstOrDefault()?.LastUpdated;
var latestFlexiHourUpdate = _centralDbContext.Hrusersetting.OrderByDescending(r => r.FlexiHourUpdate).FirstOrDefault()?.FlexiHourUpdate;
var latestRegionUpdate = _centralDbContext.Hrusersetting.OrderByDescending(c => c.StateUpdate).FirstOrDefault()?.StateUpdate;
var latestApprovalFlowUpdate = _centralDbContext.Hrusersetting.OrderByDescending(c => c.ApprovalUpdate).FirstOrDefault()?.ApprovalUpdate;
var updateDates = new
{
rateUpdateDate = latestRateUpdate.HasValue ? latestRateUpdate.Value.ToString("dd MMMM yyyy") : null,
calendarUpdateDate = latestCalendarUpdate.HasValue ? latestCalendarUpdate.Value.ToString("dd MMMM yyyy") : null,
flexiHourUpdateDate = latestFlexiHourUpdate.HasValue ? latestFlexiHourUpdate.Value.ToString("dd MMMM yyyy") : null,
regionUpdateDate = latestRegionUpdate.HasValue ? latestRegionUpdate.Value.ToString("dd MMMM yyyy") : null,
approvalFlowUpdateDate = latestApprovalFlowUpdate.HasValue ? latestApprovalFlowUpdate.Value.ToString("dd MMMM yyyy") : null
};
return Json(updateDates);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
#endregion
#region Rate
[HttpPost("UpdateRates")]
public async Task<IActionResult> UpdateRate([FromBody] List<RateModel> rates)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
foreach (var rate in rates)
{
var existingRate = await _centralDbContext.Rates
.FirstOrDefaultAsync(r => r.UserId == rate.UserId);
if (existingRate != null)
{
existingRate.RateValue = rate.RateValue;
existingRate.LastUpdated = DateTime.Now;
_centralDbContext.Rates.Update(existingRate);
}
else
{
_centralDbContext.Rates.Add(new RateModel
{
UserId = rate.UserId,
RateValue = rate.RateValue,
LastUpdated = DateTime.Now
});
}
}
await _centralDbContext.SaveChangesAsync();
var updatedRates = await _centralDbContext.Rates
.Include(r => r.Users)
.Select(r => new
{
r.RateId,
r.RateValue,
r.UserId,
FullName = r.Users.FullName,
DepartmentName = r.Users.Department.DepartmentName
})
.ToListAsync();
return Json(updatedRates);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpPost("GetUserRates")]
public async Task<IActionResult> GetUserRates()
{
try
{
var userRates = await _centralDbContext.Rates
.Include(rates => rates.Users)
.ThenInclude(user => user.Department)
.Select(rates => new
{
rates.RateId,
rates.RateValue,
rates.UserId,
rates.Users.FullName,
rates.Users.Department.DepartmentName
})
.ToListAsync();
return Json(userRates);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
#endregion
#region FlexiHour State Approval
private async Task UpdateOrInsertUserSettingAsync(int userId, int? flexiHourId = null, int? stateId = null, int? approvalFlowId = null)
{
var setting = await _centralDbContext.Hrusersetting
.FirstOrDefaultAsync(h => h.UserId == userId);
if (setting != null)
{
if (flexiHourId.HasValue)
{
setting.FlexiHourId = flexiHourId;
setting.FlexiHourUpdate = DateTime.Now;
}
if (stateId.HasValue)
{
setting.StateId = stateId;
setting.StateUpdate = DateTime.Now;
}
if (approvalFlowId.HasValue)
{
setting.ApprovalFlowId = approvalFlowId;
setting.ApprovalUpdate = DateTime.Now;
}
_centralDbContext.Hrusersetting.Update(setting);
}
else
{
var newSetting = new HrUserSettingModel
{
UserId = userId,
FlexiHourId = flexiHourId,
FlexiHourUpdate = flexiHourId.HasValue ? DateTime.Now : null,
StateId = stateId,
StateUpdate = stateId.HasValue ? DateTime.Now : null,
ApprovalFlowId = approvalFlowId,
ApprovalUpdate = approvalFlowId.HasValue ? DateTime.Now : null
};
_centralDbContext.Hrusersetting.Add(newSetting);
}
}
#endregion
#region FlexiHour
[HttpGet("GetFlexiHours")]
public IActionResult GetAllFlexiHours()
{
var flexiHours = _centralDbContext.Flexihour
.Select(f => new { f.FlexiHourId, f.FlexiHour })
.ToList();
return Ok(flexiHours);
}
[HttpGet("GetUserFlexiHours")]
public async Task<IActionResult> GetUserFlexiHours()
{
try
{
var users = await _centralDbContext.Users
.Include(u => u.Department)
.ToListAsync();
var hrUserSettings = await _centralDbContext.Hrusersetting
.Include(hr => hr.FlexiHour)
.ToListAsync();
var result = users.Select(u => new
{
UserId = u.Id,
FullName = u.FullName,
DepartmentName = u.Department != null ? u.Department.DepartmentName : "N/A",
FlexiHour = hrUserSettings
.Where(hr => hr.UserId == u.Id)
.Select(hr => hr.FlexiHour != null ? hr.FlexiHour.FlexiHour : "N/A")
.FirstOrDefault() ?? "N/A",
State = hrUserSettings
.Where(hr => hr.UserId == u.Id)
.Select(hr => hr.State != null ? hr.State.StateName : "N/A")
.FirstOrDefault() ?? "N/A"
}).ToList();
// Log this data to inspect the response
Console.WriteLine(JsonConvert.SerializeObject(result)); // Debugging log
return Ok(result);
}
catch (Exception ex)
{
return BadRequest(new { message = ex.Message });
}
}
[HttpPost("UpdateUserFlexiHours")]
public async Task<IActionResult> UpdateUserFlexiHours([FromBody] List<HrUserSettingModel> updates)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
try
{
foreach (var update in updates)
{
await UpdateOrInsertUserSettingAsync(update.UserId, flexiHourId: update.FlexiHourId);
}
await _centralDbContext.SaveChangesAsync();
return Ok();
}
catch (Exception ex)
{
return BadRequest(new { message = ex.Message });
}
}
#endregion
#region State
[HttpGet("GetUserStates")]
public async Task<IActionResult> GetUserStates()
{
try
{
var users = await _centralDbContext.Users
.Include(u => u.Department)
.ToListAsync();
var hrUserSettings = await _centralDbContext.Hrusersetting
.Include(h => h.State)
.Include(h => h.Approvalflow)
.ToListAsync();
var result = users.Select(u =>
{
var hrSetting = hrUserSettings.FirstOrDefault(h => h.UserId == u.Id);
return new
{
u.Id,
u.FullName,
DepartmentName = u.Department != null ? u.Department.DepartmentName : "N/A",
State = hrSetting != null && hrSetting.State != null ? hrSetting.State.StateName : "N/A",
approvalName = hrSetting?.Approvalflow?.ApprovalName ?? "N/A"
};
}).ToList();
return Ok(result);
}
catch (Exception ex)
{
return StatusCode(500, ex.Message);
}
}
[HttpPost("UpdateUserStates")]
public async Task<IActionResult> UpdateUserStates([FromBody] List<HrUserSettingModel> updates)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
try
{
foreach (var update in updates)
{
var existingSetting = await _centralDbContext.Hrusersetting.FirstOrDefaultAsync(h => h.UserId == update.UserId);
if (existingSetting != null)
{
existingSetting.StateId = update.StateId;
}
else
{
// Handle the case where a user setting doesn't exist.
// You might want to create a new one with a default ApprovalFlowId or return an error.
_centralDbContext.Hrusersetting.Add(new HrUserSettingModel
{
UserId = update.UserId,
StateId = update.StateId,
// Consider setting a default ApprovalFlowId here if needed
});
}
}
await _centralDbContext.SaveChangesAsync();
return Ok();
}
catch (Exception ex)
{
return BadRequest(new { message = ex.Message });
}
}
[HttpPost("UpdateUserApprovalFlow")]
public async Task<IActionResult> UpdateUserApprovalFlow([FromBody] List<HrUserSettingModel> updates)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
try
{
foreach (var update in updates)
{
var existingSetting = await _centralDbContext.Hrusersetting.FirstOrDefaultAsync(h => h.UserId == update.UserId);
if (existingSetting != null)
{
existingSetting.ApprovalFlowId = update.ApprovalFlowId;
existingSetting.ApprovalUpdate = DateTime.Now;
}
else
{
_centralDbContext.Hrusersetting.Add(new HrUserSettingModel
{
UserId = update.UserId,
ApprovalFlowId = update.ApprovalFlowId,
ApprovalUpdate = DateTime.Now
});
}
}
await _centralDbContext.SaveChangesAsync();
return Ok();
}
catch (Exception ex)
{
return BadRequest(new { message = ex.Message });
}
}
[HttpPost("UpdateApprovalFlow")]
public async Task<IActionResult> UpdateApprovalFlow([FromBody] ApprovalFlowModel model)
{
if (!ModelState.IsValid)
return BadRequest("Invalid data");
var existing = await _centralDbContext.Approvalflow.FindAsync(model.ApprovalFlowId);
if (existing == null)
return NotFound("Approval flow not found");
existing.ApprovalName = model.ApprovalName;
existing.HoU = model.HoU;
existing.HoD = model.HoD;
existing.Manager = model.Manager;
existing.HR = model.HR;
await _centralDbContext.SaveChangesAsync();
return Ok();
}
[HttpGet("GetApprovalFlowList")]
public async Task<IActionResult> GetApprovalFlowList()
{
try
{
var flows = await _centralDbContext.Approvalflow
.Select(af => new
{
af.ApprovalFlowId,
af.ApprovalName,
hou = af.HoU,
hod = af.HoD,
manager = af.Manager,
hr = af.HR
})
.ToListAsync();
return Ok(flows);
}
catch (Exception ex)
{
return StatusCode(500, new { message = ex.Message });
}
}
#endregion
#region Approval Flow
[HttpPost("CreateApprovalFlow")]
public async Task<IActionResult> CreateApprovalFlow([FromBody] ApprovalFlowModel model)
{
if (string.IsNullOrEmpty(model.ApprovalName))
return BadRequest(new { message = "Approval Name is required." });
if (!model.HR.HasValue)
return BadRequest(new { message = "HR approver is required." });
if (!ModelState.IsValid)
return BadRequest(new { message = "Invalid approval flow data." });
try
{
_centralDbContext.Approvalflow.Add(model);
await _centralDbContext.SaveChangesAsync();
return Ok(new { message = "Approval flow created successfully." });
}
catch (Exception ex)
{
return StatusCode(500, new { message = $"Error saving approval flow: {ex.InnerException?.Message ?? ex.Message}" });
}
}
[HttpGet("GetApprovalFlowRecord")]
public async Task<IActionResult> GetApprovalFlowRecord()
{
try
{
var approvalFlows = await _centralDbContext.Approvalflow
.Select(af => new { af.ApprovalFlowId, af.ApprovalName })
.ToListAsync();
return Ok(approvalFlows);
}
catch (Exception ex)
{
return StatusCode(500, new { message = $"Error fetching approval flows: {ex.Message}" });
}
}
[HttpPut("EditApprovalFlow")]
public async Task<IActionResult> EditApprovalFlow([FromBody] ApprovalFlowModel model)
{
if (!ModelState.IsValid)
{
return BadRequest("Invalid data.");
}
var existingFlow = await _centralDbContext.Approvalflow.FindAsync(model.ApprovalFlowId);
if (existingFlow == null)
{
return NotFound("Approval flow not found.");
}
// Update fields
existingFlow.ApprovalName = model.ApprovalName;
existingFlow.HoU = model.HoU;
existingFlow.HoD = model.HoD;
existingFlow.Manager = model.Manager;
existingFlow.HR = model.HR;
try
{
await _centralDbContext.SaveChangesAsync();
return Ok(new { message = "Approval flow updated successfully." });
}
catch (Exception ex)
{
// Log the exception
return StatusCode(500, new { message = "Failed to update approval flow.", detail = ex.Message });
}
}
[HttpDelete("DeleteApprovalFlow/{id}")]
public async Task<IActionResult> DeleteApprovalFlow(int id)
{
try
{
var approvalFlow = await _centralDbContext.Approvalflow.FindAsync(id);
if (approvalFlow == null)
{
return NotFound(new { message = "Approval flow not found." });
}
_centralDbContext.Approvalflow.Remove(approvalFlow);
await _centralDbContext.SaveChangesAsync();
return Ok(new { message = "Approval flow deleted successfully." });
}
catch (Exception ex)
{
return StatusCode(500, new { message = $"Error deleting approval flow: {ex.Message}" });
}
}
[HttpGet("GetAllUsers")]
public async Task<IActionResult> GetAllUsers()
{
try
{
var users = await _centralDbContext.Users
.Select(u => new { u.Id, u.FullName })
.ToListAsync();
return Ok(users);
}
catch (Exception ex)
{
return StatusCode(500, new { message = ex.Message });
}
}
#endregion
#region Calendar
[HttpGet("GetStatesName")]
public async Task<IActionResult> GetStatesName()
{
try
{
var states = await _centralDbContext.States
.Select(s => new
{
s.StateId,
s.StateName
})
.ToListAsync();
return Json(states);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpPost("UpdateHoliday")]
public async Task<IActionResult> UpdateHoliday([FromBody] List<CalendarModel> holidays)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
foreach (var calendar in holidays)
{
var existingCalendar = await _centralDbContext.Holidays
.FirstOrDefaultAsync(h => h.StateId == calendar.StateId && h.HolidayDate == calendar.HolidayDate);
if (existingCalendar != null)
{
existingCalendar.HolidayName = calendar.HolidayName;
existingCalendar.LastUpdated = DateTime.Now;
_centralDbContext.Holidays.Update(existingCalendar);
}
else
{
_centralDbContext.Holidays.Add(new CalendarModel
{
HolidayName = calendar.HolidayName,
HolidayDate = calendar.HolidayDate,
StateId = calendar.StateId,
LastUpdated = DateTime.Now
});
}
}
await _centralDbContext.SaveChangesAsync();
var updatedHoliday = await _centralDbContext.Holidays
.Include(h => h.States)
.Select(h => new
{
h.HolidayId,
h.HolidayName,
h.HolidayDate,
h.StateId,
StateName = h.States.StateName
})
.ToListAsync();
return Json(updatedHoliday);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpGet("GetAllHolidays")]
public IActionResult GetAllHolidays()
{
var holidays = _centralDbContext.Holidays.ToList();
return Ok(holidays);
}
[HttpDelete("DeleteHoliday/{id}")]
public async Task<IActionResult> DeleteHoliday(int id)
{
try
{
var holiday = await _centralDbContext.Holidays.FindAsync(id);
if (holiday == null)
{
return NotFound("Holiday not found.");
}
_centralDbContext.Holidays.Remove(holiday);
await _centralDbContext.SaveChangesAsync();
return Ok(new { message = "Holiday deleted successfully." });
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
#endregion
#region Weekend
[HttpGet("GetWeekendDay")]
public async Task<IActionResult> GetWeekendDay()
{
try
{
var weekends = await _centralDbContext.Weekends
.Select(w => new
{
w.WeekendId,
w.Day
})
.ToListAsync();
return Json(weekends);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpPost("UpdateWeekend")]
public async Task<IActionResult> UpdateWeekend([FromBody] List<StateModel> states)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
foreach (var state in states)
{
var existingState = await _centralDbContext.States
.FirstOrDefaultAsync(s => s.StateId == state.StateId);
if (existingState != null)
{
// Corrected: Updating WeekendId
existingState.WeekendId = state.WeekendId;
_centralDbContext.States.Update(existingState);
}
else
{
// Ensure new states are added correctly
_centralDbContext.States.Add(new StateModel
{
StateId = state.StateId,
StateName = state.StateName,
WeekendId = state.WeekendId
});
}
}
await _centralDbContext.SaveChangesAsync();
var updatedWeekend = await _centralDbContext.States
.Include(w => w.Weekends)
.Select(w => new
{
w.StateId,
w.StateName,
w.WeekendId,
Day = w.Weekends.Day
})
.ToListAsync();
return Json(updatedWeekend);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpGet("GetAllWeekends")]
public IActionResult GetAllWeekends()
{
var weekends = _centralDbContext.Weekends.ToList();
return Ok(weekends);
}
[HttpGet("GetStateWeekends")]
public async Task<IActionResult> GetStateWeekends()
{
try
{
var stateWeekends = await _centralDbContext.States
.Include(s => s.Weekends)
.Where(s => s.WeekendId != null)
.Select(s => new
{
s.StateId,
s.StateName,
Day = s.Weekends.Day
})
.ToListAsync();
return Json(stateWeekends);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
#endregion
#region OtRegister
[HttpGet("GetStationsByDepartment")]
public IActionResult GetStationsByDepartment()
{
var stations = _centralDbContext.Stations
.Where(s => s.DepartmentId == 2)
.Select(s => new
{
s.StationId,
StationName = s.StationName ?? "Unnamed Station"
})
.ToList();
return Ok(stations);
}
[HttpPost("AddOvertime")]
public async Task<IActionResult> AddOvertimeAsync([FromBody] OvertimeRequestDto request)
{
_logger.LogInformation("AddOvertimeAsync called.");
_logger.LogInformation("Received request: {@Request}", request);
if (request == null)
{
_logger.LogError("Request is null.");
return BadRequest("Invalid data.");
}
try
{
if (request.UserId == 0)
{
_logger.LogWarning("No user ID provided.");
return BadRequest("User ID is required.");
}
// Parse times (make them optional)
TimeSpan? officeFrom = string.IsNullOrEmpty(request.OfficeFrom) ? (TimeSpan?)null : TimeSpan.Parse(request.OfficeFrom);
TimeSpan? officeTo = string.IsNullOrEmpty(request.OfficeTo) ? (TimeSpan?)null : TimeSpan.Parse(request.OfficeTo);
TimeSpan? afterFrom = string.IsNullOrEmpty(request.AfterFrom) ? (TimeSpan?)null : TimeSpan.Parse(request.AfterFrom);
TimeSpan? afterTo = string.IsNullOrEmpty(request.AfterTo) ? (TimeSpan?)null : TimeSpan.Parse(request.AfterTo);
// Office time validation: if officeFrom is set, officeTo must be set too, and vice versa
if ((officeFrom != null && officeTo == null) || (officeFrom == null && officeTo != null))
{
return BadRequest("Both Office From and To must be filled if one is provided.");
}
// No need for specific validation for AfterFrom and AfterTo being both present
// If one is null and the other isn't, that's acceptable now.
// Map to DB model (continue using null for optional fields)
var newRecord = new OtRegisterModel
{
OtDate = request.OtDate,
OfficeFrom = officeFrom,
OfficeTo = officeTo,
OfficeBreak = request.OfficeBreak ?? null, // Make OfficeBreak optional
AfterFrom = afterFrom,
AfterTo = afterTo,
AfterBreak = request.AfterBreak ?? null, // Make AfterBreak optional
StationId = request.StationId,
OtDescription = request.OtDescription,
OtDays = request.OtDays,
UserId = request.UserId,
StatusId = request.StatusId // Pass StatusId as it is, which can be null
};
_centralDbContext.Otregisters.Add(newRecord);
await _centralDbContext.SaveChangesAsync();
return Ok(new { message = "Overtime registered successfully." });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error registering overtime.");
return StatusCode(500, $"An error occurred while saving overtime: {ex.InnerException?.Message ?? ex.Message}");
}
}
[HttpGet("GetUserStateAndHolidays/{userId}")]
public async Task<IActionResult> GetUserStateAndHolidaysAsync(int userId)
{
try
{
var hrSettings = await _centralDbContext.Hrusersetting
.Include(h => h.State)
.ThenInclude(s => s.Weekends)
.Where(h => h.UserId == userId)
.FirstOrDefaultAsync();
if (hrSettings?.State == null)
{
return Ok(new { state = (object)null, publicHolidays = new List<object>() }); // Or handle no state differently
}
// Fetch public holidays for the user's state and the current year (example)
var publicHolidays = await _centralDbContext.Holidays
.Where(ph => ph.StateId == hrSettings.StateId && ph.HolidayDate.Year == DateTime.Now.Year)
.Select(ph => new { Date = ph.HolidayDate.ToString("yyyy-MM-dd") })
.ToListAsync();
return Ok(new
{
state = new
{
stateId = hrSettings.StateId,
stateName = hrSettings.State?.StateName,
weekendDay = hrSettings.State?.Weekends?.Day,
weekendId = hrSettings.State?.WeekendId
},
publicHolidays
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Error fetching user state and public holidays.");
return StatusCode(500, "An error occurred while fetching user state and public holidays.");
}
}
#endregion
#region Ot Records
[HttpGet("GetUserOvertimeRecords/{userId}")]
public IActionResult GetUserOvertimeRecords(int userId)
{
try
{
var records = _centralDbContext.Otregisters
.Where(o => o.UserId == userId)
.OrderByDescending(o => o.OtDate)
.Select(o => new
{
o.OvertimeId,
o.OtDate,
o.OfficeFrom,
o.OfficeTo,
o.OfficeBreak,
o.AfterFrom,
o.AfterTo,
o.AfterBreak,
o.StationId,
StationName = o.Stations != null ? o.Stations.StationName : "N/A",
o.OtDescription,
o.OtDays,
o.UserId
})
.ToList();
return Ok(records);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to fetch OT records.");
return StatusCode(500, "Error retrieving OT records.");
}
}
[HttpDelete("DeleteOvertimeRecord/{id}")]
public IActionResult DeleteOvertimeRecord(int id)
{
try
{
var record = _centralDbContext.Otregisters.FirstOrDefault(o => o.OvertimeId == id);
if (record == null)
{
_logger.LogWarning("Overtime record not found for Id: {OvertimeId}", id);
return NotFound("Overtime record not found.");
}
_centralDbContext.Otregisters.Remove(record);
_centralDbContext.SaveChanges();
_logger.LogInformation("Overtime record deleted successfully for Id: {OvertimeId}", id);
return Ok(new { message = "Record deleted successfully." });
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to delete OT record.");
return StatusCode(500, "Error deleting OT record.");
}
}
[HttpGet("GenerateOvertimePdf")]
public IActionResult GenerateOvertimePdf(int month, int year)
{
var userIdStr = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (string.IsNullOrEmpty(userIdStr) || !int.TryParse(userIdStr, out int userId))
return Unauthorized();
var user = _centralDbContext.Users
.Include(u => u.Department)
.FirstOrDefault(u => u.Id == userId);
if (user == null)
return NotFound("User not found.");
var fullName = user.FullName;
var departmentId = user.departmentId ?? 0;
var departmentName = user.Department?.DepartmentName ?? "N/A";
var records = _centralDbContext.Otregisters
.Include(o => o.Stations)
.Where(o => o.UserId == userId && o.OtDate.Month == month && o.OtDate.Year == year)
.ToList();
var hrUserSetting = _centralDbContext.Hrusersetting.FirstOrDefault(h => h.UserId == userId);
// StateId
var userStateId = hrUserSetting?.StateId ?? 0;
// WeekendId
var weekendId = _centralDbContext.States
.Where(s => s.StateId == userStateId)
.Select(s => s.WeekendId)
.FirstOrDefault() ?? 2; // Default Sat-Sun if null
// Public Holidays
var publicHolidays = _centralDbContext.Holidays
.Where(c => c.StateId == userStateId)
.ToList();
// Step 1: Generate all days of the month
var daysInMonth = DateTime.DaysInMonth(year, month);
var allDays = Enumerable.Range(1, daysInMonth)
.Select(day => new DateTime(year, month, day))
.ToList();
// Step 2: Merge records with missing days
var mergedRecords = new List<OtRegisterModel>();
foreach (var date in allDays)
{
var dayRecords = records
.Where(r => r.OtDate.Date == date.Date)
.OrderBy(r => r.OfficeFrom)
.ToList();
if (dayRecords.Any())
{
mergedRecords.AddRange(dayRecords);
}
else
{
mergedRecords.Add(new OtRegisterModel
{
OtDate = date,
OtDays = date.DayOfWeek.ToString(),
OtDescription = "",
});
}
}
byte[]? logoImage = null;
var logoPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images", "logo.jpg");
logoImage = System.IO.File.Exists(logoPath) ? System.IO.File.ReadAllBytes(logoPath) : null;
var stream = _pdfService.GenerateOvertimeTablePdf(
mergedRecords,
departmentId,
fullName,
departmentName,
userStateId,
weekendId,
publicHolidays,
isAdminUser: IsAdmin(userId),
logoImage
);
return File(stream, "application/pdf", $"OvertimeRecords_{year}_{month}.pdf");
}
private bool IsAdmin(int userId)
{
var userRoles = _centralDbContext.UserRoles
.Where(ur => ur.UserId == userId)
.Join(_centralDbContext.Roles, ur => ur.RoleId, r => r.Id, (ur, r) => r.Name)
.ToList();
return userRoles.Any(role => role.Contains("SuperAdmin") || role.Contains("SystemAdmin"));
}
[HttpGet("GenerateOvertimeExcel")]
public IActionResult GenerateOvertimeExcel(int month, int year)
{
var userIdStr = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (string.IsNullOrEmpty(userIdStr) || !int.TryParse(userIdStr, out int userId))
return Unauthorized();
var user = _centralDbContext.Users
.Include(u => u.Department)
.FirstOrDefault(u => u.Id == userId);
if (user == null)
return NotFound("User not found.");
var fullName = user.FullName;
var departmentId = user.departmentId ?? 0;
var departmentName = user.Department?.DepartmentName ?? "N/A";
var records = _centralDbContext.Otregisters
.Include(o => o.Stations)
.Where(o => o.UserId == userId && o.OtDate.Month == month && o.OtDate.Year == year)
.ToList();
var hrUserSetting = _centralDbContext.Hrusersetting.FirstOrDefault(h => h.UserId == userId);
var userStateId = hrUserSetting?.StateId ?? 0;
var weekendId = _centralDbContext.States
.Where(s => s.StateId == userStateId)
.Select(s => s.WeekendId)
.FirstOrDefault() ?? 2;
var publicHolidays = _centralDbContext.Holidays
.Where(c => c.StateId == userStateId)
.ToList();
var daysInMonth = DateTime.DaysInMonth(year, month);
var allDays = Enumerable.Range(1, daysInMonth)
.Select(day => new DateTime(year, month, day))
.ToList();
var mergedRecords = new List<OtRegisterModel>();
foreach (var date in allDays)
{
var dayRecords = records
.Where(r => r.OtDate.Date == date.Date)
.OrderBy(r => r.OfficeFrom)
.ToList();
if (dayRecords.Any())
{
mergedRecords.AddRange(dayRecords);
}
else
{
mergedRecords.Add(new OtRegisterModel
{
OtDate = date,
OtDays = date.DayOfWeek.ToString(),
OtDescription = "",
});
}
}
var logoPath = Path.Combine(_env.WebRootPath, "images", "logo.jpg");
byte[]? logoImage = System.IO.File.Exists(logoPath) ? System.IO.File.ReadAllBytes(logoPath) : null;
var stream = _excelService.GenerateOvertimeExcel(
mergedRecords,
departmentId,
fullName,
departmentName,
userStateId,
weekendId,
publicHolidays,
isAdminUser: IsAdmin(userId),
logoImage: logoImage
);
return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
$"OvertimeRecords_{year}_{month}.xlsx");
}
[HttpPost("SubmitOvertime")]
public async Task<IActionResult> SubmitOvertime([FromForm] OvertimeSubmissionModel model)
{
if (model.File == null || model.File.Length == 0)
return BadRequest("No file uploaded.");
var userIdStr = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (string.IsNullOrEmpty(userIdStr) || !int.TryParse(userIdStr, out int userId))
return Unauthorized();
try
{
var uploadsFolder = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "Media", "Overtime");
if (!Directory.Exists(uploadsFolder))
Directory.CreateDirectory(uploadsFolder);
var uniqueFileName = $"{Guid.NewGuid()}_{model.File.FileName}";
var filePath = Path.Combine(uploadsFolder, uniqueFileName);
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
await model.File.CopyToAsync(fileStream);
}
var relativePath = Path.Combine("Media", "Overtime", uniqueFileName).Replace("\\", "/");
// Create a NEW OtStatusModel for the resubmission
var statusModel = new OtStatusModel
{
UserId = userId,
Month = model.Month,
Year = model.Year,
FilePath = relativePath,
SubmitDate = DateTime.Now,
HouStatus = "Pending",
HodStatus = "Pending",
ManagerStatus = "Pending",
HrStatus = "Pending"
};
_centralDbContext.Otstatus.Add(statusModel);
await _centralDbContext.SaveChangesAsync(); // Save the new OtStatus record to get its StatusId
// Update StatusId in OtRegister records for the current month/year
var monthStart = new DateTime(model.Year, model.Month, 1);
var monthEnd = monthStart.AddMonths(1);
var otRecords = _centralDbContext.Otregisters
.Where(r => r.UserId == userId && r.OtDate >= monthStart && r.OtDate < monthEnd)
.ToList();
foreach (var record in otRecords)
{
record.StatusId = statusModel.StatusId;
}
_centralDbContext.Otregisters.UpdateRange(otRecords);
await _centralDbContext.SaveChangesAsync();
// Update HrUserSetting with the NEW StatusId
var userSetting = _centralDbContext.Hrusersetting.FirstOrDefault(s => s.UserId == userId);
if (userSetting != null)
{
userSetting.ApprovalUpdate = DateTime.Now;
_centralDbContext.Hrusersetting.Update(userSetting);
}
await _centralDbContext.SaveChangesAsync();
return Ok();
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to submit overtime.");
return StatusCode(500, "An error occurred while submitting overtime.");
}
}
[HttpGet("CheckOvertimeSubmitted/{userId}/{month}/{year}")]
public IActionResult CheckOvertimeSubmitted(int userId, int month, int year)
{
try
{
// Get the latest OtStatus record for the user, month, and year
var latestStatus = _centralDbContext.Otstatus
.Where(s => s.UserId == userId && s.Month == month && s.Year == year)
.OrderByDescending(s => s.SubmitDate)
.FirstOrDefault();
if (latestStatus == null)
{
return Ok(false); // Not submitted yet
}
// Check if the latest submission has been rejected at any level
if (latestStatus.HouStatus?.ToLower() == "rejected" ||
latestStatus.HodStatus?.ToLower() == "rejected" ||
latestStatus.ManagerStatus?.ToLower() == "rejected" ||
latestStatus.HrStatus?.ToLower() == "rejected")
{
return Ok(false); // Latest submission was rejected, enable submit
}
// If not rejected, check if it's in a pending state (new submission)
if (latestStatus.HouStatus?.ToLower() == "pending" ||
latestStatus.HodStatus?.ToLower() == "pending" ||
latestStatus.ManagerStatus?.ToLower() == "pending" ||
latestStatus.HrStatus?.ToLower() == "pending")
{
return Ok(true); // Newly submitted or resubmitted, disable submit
}
// If not pending and not rejected, it implies it's fully approved or in a final rejected state
return Ok(true); // Disable submit
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while checking overtime submission status.");
return StatusCode(500, new { error = "Internal server error occurred." });
}
}
#endregion
#region Ot Edit
[HttpGet("GetOvertimeRecordById/{id}")]
public async Task<IActionResult> GetOvertimeRecordById(int id)
{
var record = await _centralDbContext.Otregisters.FindAsync(id);
if (record == null)
return NotFound();
return Ok(record);
}
[HttpPost]
[Route("UpdateOvertimeRecord")]
public IActionResult UpdateOvertimeRecord([FromForm] OtRegisterModel model)
{
_logger.LogInformation("UpdateOvertimeRecord called. Model: {@Model}", model);
try
{
if (!ModelState.IsValid)
{
_logger.LogWarning("ModelState is invalid. Errors: {@ModelStateErrors}", ModelState.Values.SelectMany(v => v.Errors));
return BadRequest(ModelState);
}
var existing = _centralDbContext.Otregisters.FirstOrDefault(o => o.OvertimeId == model.OvertimeId);
if (existing == null)
{
_logger.LogWarning("Overtime record not found for Id: {OvertimeId}", model.OvertimeId);
return NotFound("Overtime record not found.");
}
_logger.LogInformation("Existing record found: {@ExistingRecord}", existing);
existing.OtDate = model.OtDate;
existing.OfficeFrom = model.OfficeFrom;
existing.OfficeTo = model.OfficeTo;
existing.OfficeBreak = model.OfficeBreak;
existing.AfterFrom = model.AfterFrom;
existing.AfterTo = model.AfterTo;
existing.AfterBreak = model.AfterBreak;
existing.StationId = model.StationId;
existing.OtDescription = model.OtDescription;
existing.OtDays = model.OtDays;
existing.UserId = model.UserId;
_centralDbContext.SaveChanges();
_logger.LogInformation("Overtime record updated successfully for Id: {OvertimeId}", model.OvertimeId);
return Ok(new { message = "Record updated successfully." });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error updating overtime record. Stack Trace: {StackTrace}", ex.StackTrace);
return StatusCode(500, "Failed to update record.");
}
}
#endregion
#region Ot Status
[HttpGet("GetUserOtStatus")]
public IActionResult GetUserOtStatus()
{
var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (string.IsNullOrEmpty(userIdClaim))
return BadRequest("User ID is not available.");
if (!int.TryParse(userIdClaim, out int userId))
return BadRequest("Invalid User ID.");
var approvalFlowId = _centralDbContext.Hrusersetting
.Where(x => x.UserId == userId)
.Select(x => x.ApprovalFlowId)
.FirstOrDefault();
var flow = _centralDbContext.Approvalflow
.FirstOrDefault(f => f.ApprovalFlowId == approvalFlowId);
if (flow == null)
return BadRequest("Approval flow not found.");
bool includeHou = flow.HoU.HasValue;
bool includeHod = flow.HoD.HasValue;
bool includeManager = flow.Manager.HasValue;
bool includeHr = flow.HR.HasValue;
var otStatuses = _centralDbContext.Otstatus
.Where(o => o.UserId == userId)
.Select(o => new
{
o.Month,
o.Year,
o.SubmitDate,
HouStatus = includeHou ? o.HouStatus : null,
HodStatus = includeHod ? o.HodStatus : null,
ManagerStatus = includeManager ? o.ManagerStatus : null,
HrStatus = includeHr ? o.HrStatus : null,
o.FilePath,
Updated = o.HouUpdate != null || o.HodUpdate != null || o.ManagerUpdate != null || o.HrUpdate != null
})
.ToList();
return Ok(new
{
includeHou,
includeHod,
includeManager,
includeHr,
otStatuses
});
}
#endregion
#region OT Approval
[HttpGet("GetPendingApproval")]
public IActionResult GetPendingApproval(int month, int year)
{
var userIdStr = _userManager.GetUserId(User);
if (string.IsNullOrEmpty(userIdStr) || !int.TryParse(userIdStr, out int currentUserId))
return Unauthorized("Invalid or missing user ID");
// Get all flows where the user is an approver
var flows = _centralDbContext.Approvalflow
.Where(f => f.HoU == currentUserId || f.HoD == currentUserId || f.Manager == currentUserId || f.HR == currentUserId)
.ToList();
if (!flows.Any())
return Unauthorized("You are not assigned to any approval flow.");
// Map flow ID to role (the role user is in for that flow)
var flowRoleMap = new Dictionary<int, string>();
foreach (var flow in flows)
{
if (flow.HoU == currentUserId) flowRoleMap[flow.ApprovalFlowId] = "HoU";
if (flow.HoD == currentUserId) flowRoleMap[flow.ApprovalFlowId] = "HoD";
if (flow.Manager == currentUserId) flowRoleMap[flow.ApprovalFlowId] = "Manager";
if (flow.HR == currentUserId) flowRoleMap[flow.ApprovalFlowId] = "HR";
}
// Load all OT status entries and their associated approval flow IDs
var otEntriesWithFlow = (from status in _centralDbContext.Otstatus
join user in _centralDbContext.Users on status.UserId equals user.Id
join setting in _centralDbContext.Hrusersetting on status.UserId equals setting.UserId
where status.Month == month && status.Year == year && setting.ApprovalFlowId.HasValue
select new
{
status.StatusId,
status.SubmitDate,
status.HouStatus,
status.HodStatus,
status.ManagerStatus,
status.HrStatus,
setting.ApprovalFlowId,
fullName = user.FullName
}).ToList();
var filteredList = new List<object>();
var distinctRoles = new HashSet<string>();
foreach (var entry in otEntriesWithFlow)
{
if (!flowRoleMap.ContainsKey(entry.ApprovalFlowId.Value))
continue; // User is not an approver for this flow
var role = flowRoleMap[entry.ApprovalFlowId.Value];
distinctRoles.Add(role);
var flow = _centralDbContext.Approvalflow.FirstOrDefault(f => f.ApprovalFlowId == entry.ApprovalFlowId);
if (flow == null) continue;
// If any earlier status is rejected, do not allow further approvals
if (entry.HouStatus == "Rejected" || entry.HodStatus == "Rejected" || entry.ManagerStatus == "Rejected" || entry.HrStatus == "Rejected")
continue;
bool shouldShow = false;
// Show if the current user still needs to act
if ((role == "HoU" && entry.HouStatus == "Pending") ||
(role == "HoD" && (!flow.HoU.HasValue || entry.HouStatus == "Approved") && entry.HodStatus == "Pending") ||
(role == "Manager" && (!flow.HoU.HasValue || entry.HouStatus == "Approved") && (!flow.HoD.HasValue || entry.HodStatus == "Approved") && entry.ManagerStatus == "Pending") ||
(role == "HR" && (!flow.HoU.HasValue || entry.HouStatus == "Approved") && (!flow.HoD.HasValue || entry.HodStatus == "Approved") && (!flow.Manager.HasValue || entry.ManagerStatus == "Approved") && entry.HrStatus == "Pending"))
{
shouldShow = true;
}
// Also show if the current user has already acted (approved or rejected)
else if ((role == "HoU" && (entry.HouStatus == "Approved" || entry.HouStatus == "Rejected")) ||
(role == "HoD" && (entry.HodStatus == "Approved" || entry.HodStatus == "Rejected")) ||
(role == "Manager" && (entry.ManagerStatus == "Approved" || entry.ManagerStatus == "Rejected")) ||
(role == "HR" && (entry.HrStatus == "Approved" || entry.HrStatus == "Rejected")))
{
shouldShow = true;
}
if (shouldShow)
{
filteredList.Add(entry);
}
}
return Json(new
{
Roles = distinctRoles.ToList(),
Data = filteredList
});
}
[HttpPost("UpdateApprovalStatus")]
public IActionResult UpdateApprovalStatus([FromBody] UpdateStatusDto dto)
{
if (dto == null)
return BadRequest("DTO is null or improperly formatted.");
if (dto.StatusId == 0 || string.IsNullOrWhiteSpace(dto.Decision))
return BadRequest("Missing StatusId or Decision.");
var userIdStr = _userManager.GetUserId(User);
if (string.IsNullOrEmpty(userIdStr) || !int.TryParse(userIdStr, out int currentUserId))
return Unauthorized("Invalid user");
var status = _centralDbContext.Otstatus.FirstOrDefault(s => s.StatusId == dto.StatusId);
if (status == null) return NotFound("OT Status not found");
var setting = _centralDbContext.Hrusersetting.FirstOrDefault(s => s.UserId == status.UserId);
if (setting == null) return BadRequest("User setting not found");
var flow = _centralDbContext.Approvalflow.FirstOrDefault(f => f.ApprovalFlowId == setting.ApprovalFlowId);
if (flow == null) return BadRequest("Approval flow not found");
var now = DateTime.Now;
if (dto.Decision.ToLower() == "rejected")
{
if (flow.HoU == currentUserId && status.HouStatus == "Pending")
{
status.HouStatus = "Rejected";
status.HouSubmitDate = now;
}
else if (flow.HoD == currentUserId && status.HodStatus == "Pending" && (!flow.HoU.HasValue || status.HouStatus == "Approved"))
{
status.HodStatus = "Rejected";
status.HodSubmitDate = now;
}
else if (flow.Manager == currentUserId && status.ManagerStatus == "Pending" &&
(!flow.HoU.HasValue || status.HouStatus == "Approved") &&
(!flow.HoD.HasValue || status.HodStatus == "Approved"))
{
status.ManagerStatus = "Rejected";
status.ManagerSubmitDate = now;
}
else if (flow.HR == currentUserId && status.HrStatus == "Pending" &&
(!flow.HoU.HasValue || status.HouStatus == "Approved") &&
(!flow.HoD.HasValue || status.HodStatus == "Approved") &&
(!flow.Manager.HasValue || status.ManagerStatus == "Approved"))
{
status.HrStatus = "Rejected";
status.HrSubmitDate = now;
}
else
{
return BadRequest("Not authorized to reject this request or already decided based on the flow.");
}
}
else if (dto.Decision.ToLower() == "approved")
{
if (flow.HoU == currentUserId && status.HouStatus == "Pending")
{
status.HouStatus = "Approved";
status.HouSubmitDate = now;
}
else if (flow.HoD == currentUserId && status.HodStatus == "Pending" && (!flow.HoU.HasValue || status.HouStatus == "Approved"))
{
status.HodStatus = "Approved";
status.HodSubmitDate = now;
}
else if (flow.Manager == currentUserId && status.ManagerStatus == "Pending" &&
(!flow.HoU.HasValue || status.HouStatus == "Approved") &&
(!flow.HoD.HasValue || status.HodStatus == "Approved"))
{
status.ManagerStatus = "Approved";
status.ManagerSubmitDate = now;
}
else if (flow.HR == currentUserId && status.HrStatus == "Pending" &&
(!flow.HoU.HasValue || status.HouStatus == "Approved") &&
(!flow.HoD.HasValue || status.HodStatus == "Approved") &&
(!flow.Manager.HasValue || status.ManagerStatus == "Approved"))
{
status.HrStatus = "Approved";
status.HrSubmitDate = now;
}
else
{
return BadRequest("Not authorized to approve this request or already decided based on the flow.");
}
}
else
{
return BadRequest("Invalid decision value.");
}
_centralDbContext.SaveChanges();
return Ok(new { message = "Status updated successfully." });
}
#endregion
#region OT Review
[HttpGet("GetOtRecordsByStatusId/{statusId}")]
public IActionResult GetOtRecordsByStatusId(int statusId)
{
var otStatus = _centralDbContext.Otstatus.FirstOrDefault(s => s.StatusId == statusId);
if (otStatus == null)
return NotFound("OT status not found.");
var user = _centralDbContext.Users.FirstOrDefault(u => u.Id == otStatus.UserId);
if (user == null)
return NotFound("User not found.");
var department = _centralDbContext.Departments
.Where(d => d.DepartmentId == user.departmentId)
.Select(d => d.DepartmentName)
.FirstOrDefault();
var otRecords = _centralDbContext.Otregisters
.Include(o => o.Stations)
.Where(o => o.StatusId == statusId)
.Select(o => new
{
o.OvertimeId,
o.OtDate,
o.OfficeFrom,
o.OfficeTo,
o.OfficeBreak,
o.AfterFrom,
o.AfterTo,
o.AfterBreak,
o.StationId,
StationName = o.Stations != null ? o.Stations.StationName : "N/A",
o.OtDescription,
o.OtDays,
o.UserId
})
.ToList();
var result = new
{
userInfo = new
{
fullName = user.FullName,
departmentName = department,
filePath = otStatus.FilePath
},
records = otRecords
};
return Ok(result);
}
[HttpDelete("DeleteOvertimeInOtReview/{id}")]
public IActionResult DeleteOvertimeInOtReview(int id)
{
var record = _centralDbContext.Otregisters.FirstOrDefault(o => o.OvertimeId == id);
if (record == null)
return NotFound(new { message = "Overtime record not found." });
_centralDbContext.Otregisters.Remove(record);
_centralDbContext.SaveChanges();
return Ok(new { message = "Overtime record deleted successfully." });
}
#endregion
}
}