2504 lines
98 KiB
C#
2504 lines
98 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;
|
|
using static PSTW_CentralSystem.Areas.OTcalculate.Models.OtRegisterModel;
|
|
|
|
|
|
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 IWebHostEnvironment _env;
|
|
|
|
|
|
public OvertimeAPI(ILogger<OvertimeAPI> logger, CentralSystemContext centralDbContext, UserManager<UserModel> userManager, IWebHostEnvironment env)
|
|
{
|
|
_logger = logger;
|
|
_centralDbContext = centralDbContext;
|
|
_userManager = userManager;
|
|
_env = env;
|
|
}
|
|
|
|
#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);
|
|
}
|
|
}
|
|
|
|
[HttpGet("CheckIncompleteUserSettings")]
|
|
public async Task<IActionResult> CheckIncompleteUserSettings()
|
|
{
|
|
try
|
|
{
|
|
var incompleteUserIds = new List<int>();
|
|
|
|
var allUserIds = await _userManager.Users.Select(u => u.Id).ToListAsync();
|
|
|
|
foreach (var userId in allUserIds)
|
|
{
|
|
bool isIncomplete = false;
|
|
|
|
var hrUserSetting = await _centralDbContext.Hrusersetting
|
|
.Where(h => h.UserId == userId)
|
|
.FirstOrDefaultAsync();
|
|
|
|
if (hrUserSetting == null)
|
|
{
|
|
isIncomplete = true;
|
|
}
|
|
else
|
|
{
|
|
if (hrUserSetting.FlexiHourId == null || hrUserSetting.FlexiHourId == 0)
|
|
{
|
|
isIncomplete = true;
|
|
}
|
|
if (hrUserSetting.StateId == null || hrUserSetting.StateId == 0)
|
|
{
|
|
isIncomplete = true;
|
|
}
|
|
if (hrUserSetting.ApprovalFlowId == null || hrUserSetting.ApprovalFlowId == 0)
|
|
{
|
|
isIncomplete = true;
|
|
}
|
|
}
|
|
|
|
var rateSetting = await _centralDbContext.Rates
|
|
.Where(r => r.UserId == userId)
|
|
.FirstOrDefaultAsync();
|
|
|
|
if (rateSetting == null)
|
|
{
|
|
isIncomplete = true;
|
|
}
|
|
else
|
|
{
|
|
if (rateSetting.RateValue == 0.00m)
|
|
{
|
|
isIncomplete = true;
|
|
}
|
|
}
|
|
|
|
if (isIncomplete)
|
|
{
|
|
incompleteUserIds.Add(userId);
|
|
}
|
|
}
|
|
|
|
if (incompleteUserIds.Any())
|
|
{
|
|
return Ok(new { hasIncompleteSettings = true, numberOfIncompleteUsers = incompleteUserIds.Count });
|
|
}
|
|
else
|
|
{
|
|
return Ok(new { hasIncompleteSettings = false, numberOfIncompleteUsers = 0 });
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return StatusCode(500, $"An error occurred: {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();
|
|
|
|
Console.WriteLine(JsonConvert.SerializeObject(result));
|
|
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;
|
|
existingSetting.StateUpdate = DateTime.Now;
|
|
}
|
|
else
|
|
{
|
|
_centralDbContext.Hrusersetting.Add(new HrUserSettingModel
|
|
{
|
|
UserId = update.UserId,
|
|
StateId = update.StateId,
|
|
StateUpdate = DateTime.Now,
|
|
});
|
|
}
|
|
}
|
|
|
|
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.");
|
|
}
|
|
|
|
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)
|
|
{
|
|
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." });
|
|
}
|
|
|
|
var usersWithThisFlow = await _centralDbContext.Hrusersetting
|
|
.AnyAsync(u => u.ApprovalFlowId == id);
|
|
|
|
if (usersWithThisFlow)
|
|
{
|
|
return BadRequest(new
|
|
{
|
|
message = "Cannot delete this approval flow because it's being used by one or more users."
|
|
});
|
|
}
|
|
|
|
_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)
|
|
{
|
|
existingState.WeekendId = state.WeekendId;
|
|
_centralDbContext.States.Update(existingState);
|
|
}
|
|
else
|
|
{
|
|
_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 async Task<IActionResult> GetStationsByDepartment([FromQuery] int? departmentId)
|
|
{
|
|
if (!departmentId.HasValue)
|
|
{
|
|
_logger.LogWarning("GetStationsByDepartment called without a departmentId.");
|
|
return Ok(new List<object>());
|
|
}
|
|
|
|
var stations = await _centralDbContext.Stations
|
|
.Where(s => s.DepartmentId == departmentId.Value)
|
|
.Select(s => new
|
|
{
|
|
s.StationId,
|
|
StationName = s.StationName ?? "Unnamed Station"
|
|
})
|
|
.ToListAsync();
|
|
|
|
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.");
|
|
}
|
|
|
|
var user = await _userManager.FindByIdAsync(request.UserId.ToString());
|
|
if (user == null)
|
|
{
|
|
_logger.LogError("User with ID {UserId} not found for overtime submission.", request.UserId);
|
|
return Unauthorized("User not found.");
|
|
}
|
|
|
|
var userRoles = await _userManager.GetRolesAsync(user);
|
|
var isSuperAdmin = userRoles.Contains("SuperAdmin");
|
|
var isSystemAdmin = userRoles.Contains("SystemAdmin");
|
|
|
|
var userWithDepartment = await _centralDbContext.Users
|
|
.Include(u => u.Department)
|
|
.FirstOrDefaultAsync(u => u.Id == request.UserId);
|
|
|
|
int? userDepartmentId = userWithDepartment?.Department?.DepartmentId;
|
|
|
|
bool stationRequired = false;
|
|
if (userDepartmentId == 2 || userDepartmentId == 3)
|
|
{
|
|
stationRequired = true;
|
|
}
|
|
else if (isSuperAdmin || isSystemAdmin)
|
|
{
|
|
stationRequired = true;
|
|
}
|
|
|
|
if (stationRequired && (!request.StationId.HasValue || request.StationId.Value <= 0))
|
|
{
|
|
return BadRequest("A station must be selected.");
|
|
}
|
|
|
|
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);
|
|
|
|
if ((officeFrom != null && officeTo == null) || (officeFrom == null && officeTo != null))
|
|
{
|
|
return BadRequest("Both Office From and To times must be provided if one is entered.");
|
|
}
|
|
if ((afterFrom != null && afterTo == null) || (afterFrom == null && afterTo != null))
|
|
{
|
|
return BadRequest("Both After Office From and To times must be provided if one is entered.");
|
|
}
|
|
|
|
TimeSpan minAllowedFromMidnightTo = new TimeSpan(16, 30, 0);
|
|
TimeSpan maxAllowedFromMidnightTo = new TimeSpan(23, 30, 0);
|
|
|
|
if (officeFrom.HasValue && officeTo.HasValue)
|
|
{
|
|
if (officeTo == TimeSpan.Zero)
|
|
{
|
|
if (officeFrom == TimeSpan.Zero)
|
|
{
|
|
return BadRequest("Invalid Office Hour Time: 'From' and 'To' cannot both be 00:00 (midnight).");
|
|
}
|
|
|
|
if (officeFrom.Value < minAllowedFromMidnightTo || officeFrom.Value > maxAllowedFromMidnightTo)
|
|
{
|
|
return BadRequest("Invalid Office Hour Time: If 'To' is 12:00 am (00:00), 'From' must start between 4:30 pm and 11:30 pm on the same day to be saved.");
|
|
}
|
|
}
|
|
else if (officeTo <= officeFrom)
|
|
{
|
|
return BadRequest("Invalid Office Hour Time: 'To' time must be later than 'From' time (same day only).");
|
|
}
|
|
}
|
|
|
|
if (afterFrom.HasValue && afterTo.HasValue)
|
|
{
|
|
if (afterTo == TimeSpan.Zero)
|
|
{
|
|
if (afterFrom == TimeSpan.Zero)
|
|
{
|
|
return BadRequest("Invalid After Office Hour Time: 'From' and 'To' cannot both be 00:00 (midnight).");
|
|
}
|
|
|
|
if (afterFrom.Value < minAllowedFromMidnightTo || afterFrom.Value > maxAllowedFromMidnightTo)
|
|
{
|
|
return BadRequest("Invalid After Office Hour Time: If 'To' is 12:00 am (00:00), 'From' must start between 4:30 pm and 11:30 pm on the same day to be saved.");
|
|
}
|
|
}
|
|
else if (afterTo <= afterFrom)
|
|
{
|
|
return BadRequest("Invalid After Office Hour Time: 'To' time must be later than 'From' time (same day only).");
|
|
}
|
|
}
|
|
|
|
if ((officeFrom == null && officeTo == null) && (afterFrom == null && afterTo == null))
|
|
{
|
|
return BadRequest("Please enter either Office Hours or After Office Hours.");
|
|
}
|
|
|
|
var newRecord = new OtRegisterModel
|
|
{
|
|
OtDate = request.OtDate,
|
|
OfficeFrom = officeFrom,
|
|
OfficeTo = officeTo,
|
|
OfficeBreak = request.OfficeBreak,
|
|
AfterFrom = afterFrom,
|
|
AfterTo = afterTo,
|
|
AfterBreak = request.AfterBreak,
|
|
StationId = request.StationId,
|
|
OtDescription = request.OtDescription,
|
|
OtDays = request.OtDays,
|
|
UserId = request.UserId,
|
|
StatusId = request.StatusId
|
|
};
|
|
|
|
_centralDbContext.Otregisters.Add(newRecord);
|
|
await _centralDbContext.SaveChangesAsync();
|
|
|
|
return Ok(new { message = "Overtime registered successfully." });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error registering overtime for user {UserId}.", request.UserId);
|
|
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)
|
|
.Include(h => h.FlexiHour)
|
|
.Where(h => h.UserId == userId)
|
|
.FirstOrDefaultAsync();
|
|
|
|
if (hrSettings?.State == null)
|
|
{
|
|
return Ok(new { state = (object)null, publicHolidays = new List<object>() });
|
|
}
|
|
|
|
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,
|
|
flexiHour = hrSettings.FlexiHour?.FlexiHour
|
|
},
|
|
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.");
|
|
}
|
|
}
|
|
|
|
[HttpGet("CheckUserSettings/{userId}")]
|
|
public async Task<IActionResult> CheckUserSettings(int userId)
|
|
{
|
|
try
|
|
{
|
|
var hrSettings = await _centralDbContext.Hrusersetting
|
|
.Where(h => h.UserId == userId)
|
|
.FirstOrDefaultAsync();
|
|
|
|
if (hrSettings == null)
|
|
{
|
|
return Ok(new { isComplete = false });
|
|
}
|
|
|
|
if (hrSettings.FlexiHourId == null || hrSettings.StateId == null || hrSettings.ApprovalFlowId == null)
|
|
{
|
|
return Ok(new { isComplete = false });
|
|
}
|
|
|
|
var rateSetting = await _centralDbContext.Rates
|
|
.Where(r => r.UserId == userId)
|
|
.FirstOrDefaultAsync();
|
|
|
|
if (rateSetting == null)
|
|
{
|
|
return Ok(new { isComplete = false });
|
|
}
|
|
else
|
|
{
|
|
if (rateSetting.RateValue <= 0.00m)
|
|
{
|
|
return Ok(new { isComplete = false });
|
|
}
|
|
}
|
|
|
|
return Ok(new { isComplete = true });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error checking user settings for user {UserId}", userId);
|
|
return StatusCode(500, $"An error occurred while checking user settings: {ex.Message}");
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Ot Records
|
|
|
|
[HttpGet("GetUserOvertimeRecords/{userId}")]
|
|
public IActionResult GetUserOvertimeRecords(int userId)
|
|
{
|
|
try
|
|
{
|
|
var records = _centralDbContext.Otregisters
|
|
.Include(o => o.Stations)
|
|
.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.");
|
|
}
|
|
}
|
|
|
|
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"));
|
|
}
|
|
|
|
[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("\\", "/");
|
|
|
|
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();
|
|
|
|
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();
|
|
|
|
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
|
|
{
|
|
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);
|
|
}
|
|
|
|
if (latestStatus.HouStatus?.ToLower() == "rejected" ||
|
|
latestStatus.HodStatus?.ToLower() == "rejected" ||
|
|
latestStatus.ManagerStatus?.ToLower() == "rejected" ||
|
|
latestStatus.HrStatus?.ToLower() == "rejected")
|
|
{
|
|
return Ok(false);
|
|
}
|
|
|
|
if (latestStatus.HouStatus?.ToLower() == "pending" ||
|
|
latestStatus.HodStatus?.ToLower() == "pending" ||
|
|
latestStatus.ManagerStatus?.ToLower() == "pending" ||
|
|
latestStatus.HrStatus?.ToLower() == "pending")
|
|
{
|
|
return Ok(true);
|
|
}
|
|
|
|
return Ok(true);
|
|
}
|
|
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("GetStationsByDepartmentAir")]
|
|
public IActionResult GetStationsByDepartmentAir()
|
|
{
|
|
var stations = _centralDbContext.Stations
|
|
|
|
.Where(s => s.DepartmentId == 2)
|
|
.Select(s => new
|
|
{
|
|
s.StationId,
|
|
StationName = s.StationName ?? "Unnamed Station"
|
|
})
|
|
|
|
.ToList();
|
|
|
|
return Ok(stations);
|
|
}
|
|
|
|
[HttpGet("GetStationsByDepartmentMarine")]
|
|
public IActionResult GetStationsByDepartmentMarine()
|
|
{
|
|
var stations = _centralDbContext.Stations
|
|
|
|
.Where(s => s.DepartmentId == 3)
|
|
.Select(s => new
|
|
{
|
|
s.StationId,
|
|
StationName = s.StationName ?? "Unnamed Station"
|
|
})
|
|
|
|
.ToList();
|
|
|
|
return Ok(stations);
|
|
}
|
|
|
|
[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);
|
|
}
|
|
|
|
[HttpGet("GetUserFlexiHour/{userId}")]
|
|
public async Task<IActionResult> GetUserFlexiHour(int userId)
|
|
{
|
|
try
|
|
{
|
|
var userSetting = await _centralDbContext.Hrusersetting
|
|
.Include(hs => hs.FlexiHour)
|
|
.FirstOrDefaultAsync(hs => hs.UserId == userId);
|
|
|
|
if (userSetting == null || userSetting.FlexiHour == null)
|
|
{
|
|
_logger.LogWarning("Flexi hour not found for UserId: {UserId}", userId);
|
|
return NotFound(new { message = "Flexi hour not found for this user." });
|
|
}
|
|
|
|
return Ok(new { flexiHour = userSetting.FlexiHour });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error fetching user flexi hour for UserId: {UserId}", userId);
|
|
return StatusCode(500, new { message = "An error occurred while fetching flexi hour." });
|
|
}
|
|
}
|
|
|
|
[HttpPost]
|
|
[Route("UpdateOvertimeRecord")]
|
|
public IActionResult UpdateOvertimeRecord([FromBody] OtRegisterUpdateDto model)
|
|
{
|
|
_logger.LogInformation("UpdateOvertimeRecord called. Model: {@Model}", model);
|
|
|
|
try
|
|
{
|
|
if (!ModelState.IsValid)
|
|
{
|
|
return BadRequest(ModelState);
|
|
}
|
|
|
|
var timeValidationError = ValidateTimeRanges(model);
|
|
if (timeValidationError != null)
|
|
{
|
|
return BadRequest(new { message = timeValidationError });
|
|
}
|
|
|
|
var existing = _centralDbContext.Otregisters.FirstOrDefault(o => o.OvertimeId == model.OvertimeId);
|
|
if (existing == null)
|
|
{
|
|
return NotFound(new { message = "Overtime record not found." });
|
|
}
|
|
|
|
existing.OtDate = model.OtDate;
|
|
existing.OfficeFrom = TimeSpan.TryParse(model.OfficeFrom, out var officeFrom) ? officeFrom : null;
|
|
existing.OfficeTo = TimeSpan.TryParse(model.OfficeTo, out var officeTo) ? officeTo : null;
|
|
existing.OfficeBreak = model.OfficeBreak;
|
|
existing.AfterFrom = TimeSpan.TryParse(model.AfterFrom, out var afterFrom) ? afterFrom : null;
|
|
existing.AfterTo = TimeSpan.TryParse(model.AfterTo, out var afterTo) ? afterTo : null;
|
|
existing.AfterBreak = model.AfterBreak;
|
|
existing.StationId = model.StationId;
|
|
existing.OtDescription = model.OtDescription;
|
|
existing.OtDays = model.OtDays;
|
|
existing.UserId = model.UserId;
|
|
|
|
_centralDbContext.SaveChanges();
|
|
return Ok(new { message = "Record updated successfully." });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error updating overtime record.");
|
|
return StatusCode(500, new { message = "Failed to update record." });
|
|
}
|
|
}
|
|
|
|
private string ValidateTimeRanges(OtRegisterUpdateDto model)
|
|
{
|
|
TimeSpan? officeFrom = string.IsNullOrEmpty(model.OfficeFrom) ? (TimeSpan?)null : TimeSpan.Parse(model.OfficeFrom);
|
|
TimeSpan? officeTo = string.IsNullOrEmpty(model.OfficeTo) ? (TimeSpan?)null : TimeSpan.Parse(model.OfficeTo);
|
|
TimeSpan? afterFrom = string.IsNullOrEmpty(model.AfterFrom) ? (TimeSpan?)null : TimeSpan.Parse(model.AfterFrom);
|
|
TimeSpan? afterTo = string.IsNullOrEmpty(model.AfterTo) ? (TimeSpan?)null : TimeSpan.Parse(model.AfterTo);
|
|
|
|
TimeSpan minAllowedFromMidnightTo = new TimeSpan(16, 30, 0);
|
|
TimeSpan maxAllowedFromMidnightTo = new TimeSpan(23, 30, 0);
|
|
|
|
if (officeFrom.HasValue && officeTo.HasValue)
|
|
{
|
|
if (officeTo == TimeSpan.Zero)
|
|
{
|
|
if (officeFrom == TimeSpan.Zero)
|
|
{
|
|
return "Invalid Office Hour Time: 'From' and 'To' cannot both be 00:00 (midnight).";
|
|
}
|
|
|
|
if (officeFrom.Value < minAllowedFromMidnightTo || officeFrom.Value > maxAllowedFromMidnightTo)
|
|
{
|
|
return "Invalid Office Hour Time: If 'To' is 12:00 am (00:00), 'From' must start between 4:30 pm and 11:30 pm on the same day to be saved.";
|
|
}
|
|
}
|
|
else if (officeTo <= officeFrom)
|
|
{
|
|
return "Invalid Office Hour Time: 'To' time must be later than 'From' time (same day only).";
|
|
}
|
|
}
|
|
|
|
if (afterFrom.HasValue && afterTo.HasValue)
|
|
{
|
|
if (afterTo == TimeSpan.Zero)
|
|
{
|
|
if (afterFrom == TimeSpan.Zero)
|
|
{
|
|
return "Invalid After Office Hour Time: 'From' and 'To' cannot both be 00:00 (midnight).";
|
|
}
|
|
|
|
if (afterFrom.Value < minAllowedFromMidnightTo || afterFrom.Value > maxAllowedFromMidnightTo)
|
|
{
|
|
return "Invalid After Office Hour Time: If 'To' is 12:00 am (00:00), 'From' must start between 4:30 pm and 11:30 pm on the same day to be saved.";
|
|
}
|
|
}
|
|
else if (afterTo <= afterFrom)
|
|
{
|
|
return "Invalid After Office Hour Time: 'To' time must be later than 'From' time (same day only).";
|
|
}
|
|
}
|
|
|
|
if ((officeFrom == null && officeTo == null) && (afterFrom == null && afterTo == null))
|
|
{
|
|
return "Please enter either Office Hours or After Office Hours.";
|
|
}
|
|
|
|
return null;
|
|
}
|
|
#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();
|
|
|
|
bool includeHou = false;
|
|
bool includeHod = false;
|
|
bool includeManager = false;
|
|
bool includeHr = false;
|
|
|
|
if (approvalFlowId != null)
|
|
{
|
|
var flow = _centralDbContext.Approvalflow
|
|
.FirstOrDefault(f => f.ApprovalFlowId == approvalFlowId);
|
|
|
|
if (flow != null)
|
|
{
|
|
includeHou = flow.HoU.HasValue;
|
|
includeHod = flow.HoD.HasValue;
|
|
includeManager = flow.Manager.HasValue;
|
|
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,
|
|
o.HouUpdate,
|
|
o.HodUpdate,
|
|
o.ManagerUpdate,
|
|
o.HrUpdate
|
|
})
|
|
.ToList();
|
|
|
|
return Ok(new
|
|
{
|
|
includeHou,
|
|
includeHod,
|
|
includeManager,
|
|
includeHr,
|
|
otStatuses,
|
|
hasApprovalFlow = approvalFlowId != null
|
|
});
|
|
}
|
|
#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");
|
|
|
|
var flows = _centralDbContext.Approvalflow
|
|
.Where(f => f.HoU == currentUserId || f.HoD == currentUserId || f.Manager == currentUserId || f.HR == currentUserId)
|
|
.ToList();
|
|
|
|
if (!flows.Any())
|
|
return Json(new { Roles = new List<string>(), Data = new List<object>(), OverallPendingMonths = new List<string>() });
|
|
|
|
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";
|
|
}
|
|
|
|
var allRelevantOtEntries = (from status in _centralDbContext.Otstatus
|
|
join setting in _centralDbContext.Hrusersetting on status.UserId equals setting.UserId
|
|
where setting.ApprovalFlowId.HasValue && flowRoleMap.Keys.Contains(setting.ApprovalFlowId.Value)
|
|
select new
|
|
{
|
|
status.Month,
|
|
status.Year,
|
|
setting.ApprovalFlowId,
|
|
status.HouStatus,
|
|
status.HodStatus,
|
|
status.ManagerStatus,
|
|
status.HrStatus
|
|
}).ToList();
|
|
|
|
var pendingMonthsAndYears = new HashSet<string>();
|
|
|
|
var allFlows = _centralDbContext.Approvalflow.ToList();
|
|
|
|
foreach (var entry in allRelevantOtEntries)
|
|
{
|
|
var role = flowRoleMap[entry.ApprovalFlowId.Value];
|
|
var flow = allFlows.FirstOrDefault(f => f.ApprovalFlowId == entry.ApprovalFlowId);
|
|
if (flow == null) continue;
|
|
|
|
bool isPendingForCurrentUser = false;
|
|
|
|
if (role == "HoU" && (entry.HouStatus == null || entry.HouStatus == "Pending") &&
|
|
!(entry.HodStatus == "Rejected" || entry.ManagerStatus == "Rejected" || entry.HrStatus == "Rejected"))
|
|
{
|
|
isPendingForCurrentUser = true;
|
|
}
|
|
else if (role == "HoD" && (entry.HodStatus == null || entry.HodStatus == "Pending") &&
|
|
(!flow.HoU.HasValue || entry.HouStatus == "Approved") &&
|
|
!(entry.HouStatus == "Rejected" || entry.ManagerStatus == "Rejected" || entry.HrStatus == "Rejected"))
|
|
{
|
|
isPendingForCurrentUser = true;
|
|
}
|
|
else if (role == "Manager" && (entry.ManagerStatus == null || entry.ManagerStatus == "Pending") &&
|
|
(!flow.HoU.HasValue || entry.HouStatus == "Approved") &&
|
|
(!flow.HoD.HasValue || entry.HodStatus == "Approved") &&
|
|
!(entry.HouStatus == "Rejected" || entry.HodStatus == "Rejected" || entry.HrStatus == "Rejected"))
|
|
{
|
|
isPendingForCurrentUser = true;
|
|
}
|
|
else if (role == "HR" && (entry.HrStatus == null || entry.HrStatus == "Pending") &&
|
|
(!flow.HoU.HasValue || entry.HouStatus == "Approved") &&
|
|
(!flow.HoD.HasValue || entry.HodStatus == "Approved") &&
|
|
(!flow.Manager.HasValue || entry.ManagerStatus == "Approved") &&
|
|
!(entry.HouStatus == "Rejected" || entry.HodStatus == "Rejected" || entry.ManagerStatus == "Rejected"))
|
|
{
|
|
isPendingForCurrentUser = true;
|
|
}
|
|
|
|
if (isPendingForCurrentUser)
|
|
{
|
|
pendingMonthsAndYears.Add($"{entry.Month:D2}/{entry.Year}");
|
|
}
|
|
}
|
|
|
|
var otEntriesForSelectedMonth = (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.UserId,
|
|
status.SubmitDate,
|
|
status.HouStatus,
|
|
status.HodStatus,
|
|
status.ManagerStatus,
|
|
status.HrStatus,
|
|
setting.ApprovalFlowId,
|
|
fullName = user.FullName
|
|
}).ToList();
|
|
|
|
var processedList = new List<object>();
|
|
var distinctRoles = new HashSet<string>();
|
|
|
|
foreach (var entry in otEntriesForSelectedMonth)
|
|
{
|
|
if (!entry.ApprovalFlowId.HasValue || !flowRoleMap.ContainsKey(entry.ApprovalFlowId.Value))
|
|
continue;
|
|
|
|
var role = flowRoleMap[entry.ApprovalFlowId.Value];
|
|
distinctRoles.Add(role);
|
|
|
|
var flow = allFlows.FirstOrDefault(f => f.ApprovalFlowId == entry.ApprovalFlowId);
|
|
if (flow == null) continue;
|
|
|
|
bool canApprove = false;
|
|
string currentUserStatus = "N/A";
|
|
|
|
if (role == "HoU")
|
|
{
|
|
currentUserStatus = entry.HouStatus ?? "Pending";
|
|
canApprove = currentUserStatus == "Pending" &&
|
|
!(entry.HodStatus == "Rejected" || entry.ManagerStatus == "Rejected" || entry.HrStatus == "Rejected");
|
|
}
|
|
else if (role == "HoD")
|
|
{
|
|
currentUserStatus = entry.HodStatus ?? "Pending";
|
|
canApprove = currentUserStatus == "Pending" &&
|
|
(!flow.HoU.HasValue || entry.HouStatus == "Approved") &&
|
|
!(entry.HouStatus == "Rejected" || entry.ManagerStatus == "Rejected" || entry.HrStatus == "Rejected");
|
|
}
|
|
else if (role == "Manager")
|
|
{
|
|
currentUserStatus = entry.ManagerStatus ?? "Pending";
|
|
canApprove = currentUserStatus == "Pending" &&
|
|
(!flow.HoU.HasValue || entry.HouStatus == "Approved") &&
|
|
(!flow.HoD.HasValue || entry.HodStatus == "Approved") &&
|
|
!(entry.HouStatus == "Rejected" || entry.HodStatus == "Rejected" || entry.HrStatus == "Rejected");
|
|
}
|
|
else if (role == "HR")
|
|
{
|
|
currentUserStatus = entry.HrStatus ?? "Pending";
|
|
canApprove = currentUserStatus == "Pending" &&
|
|
(!flow.HoU.HasValue || entry.HouStatus == "Approved") &&
|
|
(!flow.HoD.HasValue || entry.HodStatus == "Approved") &&
|
|
(!flow.Manager.HasValue || entry.ManagerStatus == "Approved") &&
|
|
!(entry.HouStatus == "Rejected" || entry.HodStatus == "Rejected" || entry.ManagerStatus == "Rejected");
|
|
}
|
|
|
|
bool isOverallRejected = (entry.HouStatus == "Rejected" ||
|
|
entry.HodStatus == "Rejected" ||
|
|
entry.ManagerStatus == "Rejected" ||
|
|
entry.HrStatus == "Rejected");
|
|
|
|
processedList.Add(new
|
|
{
|
|
entry.StatusId,
|
|
entry.SubmitDate,
|
|
entry.HouStatus,
|
|
entry.HodStatus,
|
|
entry.ManagerStatus,
|
|
entry.HrStatus,
|
|
entry.ApprovalFlowId,
|
|
entry.fullName,
|
|
Role = role,
|
|
CanApprove = canApprove,
|
|
CurrentUserStatus = currentUserStatus,
|
|
IsOverallRejected = isOverallRejected
|
|
});
|
|
}
|
|
|
|
return Json(new
|
|
{
|
|
Roles = distinctRoles.ToList(),
|
|
Data = processedList,
|
|
OverallPendingMonths = pendingMonthsAndYears.OrderByDescending(m => m).ToList()
|
|
});
|
|
}
|
|
|
|
[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 (status.HouStatus == "Rejected" || status.HodStatus == "Rejected" || status.ManagerStatus == "Rejected" || status.HrStatus == "Rejected")
|
|
{
|
|
return BadRequest("This request has already been rejected by a prior approver.");
|
|
}
|
|
|
|
bool isAuthorizedToAct = false;
|
|
string currentRoleStatus = "";
|
|
|
|
if (flow.HoU == currentUserId)
|
|
{
|
|
currentRoleStatus = status.HouStatus ?? "Pending";
|
|
if (currentRoleStatus == "Pending") isAuthorizedToAct = true;
|
|
}
|
|
else if (flow.HoD == currentUserId)
|
|
{
|
|
currentRoleStatus = status.HodStatus ?? "Pending";
|
|
if (currentRoleStatus == "Pending" && (!flow.HoU.HasValue || status.HouStatus == "Approved")) isAuthorizedToAct = true;
|
|
}
|
|
else if (flow.Manager == currentUserId)
|
|
{
|
|
currentRoleStatus = status.ManagerStatus ?? "Pending";
|
|
if (currentRoleStatus == "Pending" &&
|
|
(!flow.HoU.HasValue || status.HouStatus == "Approved") &&
|
|
(!flow.HoD.HasValue || status.HodStatus == "Approved")) isAuthorizedToAct = true;
|
|
}
|
|
else if (flow.HR == currentUserId)
|
|
{
|
|
currentRoleStatus = status.HrStatus ?? "Pending";
|
|
if (currentRoleStatus == "Pending" &&
|
|
(!flow.HoU.HasValue || status.HouStatus == "Approved") &&
|
|
(!flow.HoD.HasValue || status.HodStatus == "Approved") &&
|
|
(!flow.Manager.HasValue || status.ManagerStatus == "Approved")) isAuthorizedToAct = true;
|
|
}
|
|
|
|
if (!isAuthorizedToAct)
|
|
{
|
|
return BadRequest("You are not authorized to act on this request at this stage or the status has already been set.");
|
|
}
|
|
|
|
if (dto.Decision.ToLower() == "rejected")
|
|
{
|
|
if (flow.HoU == currentUserId) { status.HouStatus = "Rejected"; status.HouSubmitDate = now; }
|
|
else if (flow.HoD == currentUserId) { status.HodStatus = "Rejected"; status.HodSubmitDate = now; }
|
|
else if (flow.Manager == currentUserId) { status.ManagerStatus = "Rejected"; status.ManagerSubmitDate = now; }
|
|
else if (flow.HR == currentUserId) { status.HrStatus = "Rejected"; status.HrSubmitDate = now; }
|
|
}
|
|
else if (dto.Decision.ToLower() == "approved")
|
|
{
|
|
if (flow.HoU == currentUserId) { status.HouStatus = "Approved"; status.HouSubmitDate = now; }
|
|
else if (flow.HoD == currentUserId) { status.HodStatus = "Approved"; status.HodSubmitDate = now; }
|
|
else if (flow.Manager == currentUserId) { status.ManagerStatus = "Approved"; status.ManagerSubmitDate = now; }
|
|
else if (flow.HR == currentUserId) { status.HrStatus = "Approved"; status.HrSubmitDate = now; }
|
|
}
|
|
else
|
|
{
|
|
return BadRequest("Invalid decision value.");
|
|
}
|
|
|
|
_centralDbContext.SaveChanges();
|
|
return Ok(new { message = "Status updated successfully." });
|
|
}
|
|
#endregion
|
|
|
|
#region OT Review
|
|
|
|
[HttpGet("GetAllStations")]
|
|
public IActionResult GetAllStations()
|
|
{
|
|
var stations = _centralDbContext.Stations.Select(s => new { s.StationId, s.StationName }).ToList();
|
|
return Ok(stations);
|
|
}
|
|
|
|
[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 userBasicSalary = _centralDbContext.Rates
|
|
.Where(r => r.UserId == user.Id)
|
|
.Select(r => r.RateValue)
|
|
.FirstOrDefault();
|
|
|
|
var userSetting = _centralDbContext.Hrusersetting
|
|
.Include(us => us.State)
|
|
.Include(us => us.Approvalflow)
|
|
.ThenInclude(af => af.HeadOfUnit)
|
|
.Include(us => us.FlexiHour)
|
|
.FirstOrDefault(us => us.UserId == user.Id);
|
|
|
|
if (userSetting?.State == null)
|
|
return NotFound("User state information not found in HR User Settings.");
|
|
|
|
if (userSetting?.Approvalflow == null)
|
|
return NotFound("Approval flow information not found for the user.");
|
|
|
|
var publicHolidaysForTable = _centralDbContext.Holidays
|
|
.Where(h => h.StateId == userSetting.State.StateId)
|
|
.Select(h => h.HolidayDate.Date.ToString("yyyy-MM-dd"))
|
|
.ToList();
|
|
|
|
var otRecords = _centralDbContext.Otregisters
|
|
.Include(o => o.Stations)
|
|
.Where(o => o.StatusId == statusId)
|
|
.ToList()
|
|
.Select(o =>
|
|
{
|
|
string dayType;
|
|
DateTime otDate = o.OtDate.Date;
|
|
DayOfWeek dayOfWeek = otDate.DayOfWeek;
|
|
|
|
if (publicHolidaysForTable.Contains(otDate.ToString("yyyy-MM-dd")))
|
|
{
|
|
dayType = "Public Holiday";
|
|
}
|
|
else if (userSetting.State.WeekendId == 1)
|
|
{
|
|
if (dayOfWeek == DayOfWeek.Friday) dayType = "Off Day";
|
|
else if (dayOfWeek == DayOfWeek.Saturday) dayType = "Rest Day";
|
|
else dayType = "Normal Day";
|
|
}
|
|
else if (userSetting.State.WeekendId == 2)
|
|
{
|
|
if (dayOfWeek == DayOfWeek.Saturday) dayType = "Off Day";
|
|
else if (dayOfWeek == DayOfWeek.Sunday) dayType = "Rest Day";
|
|
else dayType = "Normal Day";
|
|
}
|
|
else
|
|
{
|
|
dayType = "Normal Day";
|
|
}
|
|
|
|
return 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,
|
|
Rate = userBasicSalary, // Pass the Basic Salary as 'Rate'
|
|
DayType = dayType,
|
|
};
|
|
})
|
|
.ToList();
|
|
|
|
var currentLoggedInUserId = GetCurrentLoggedInUserId();
|
|
string approverRole = GetApproverRole(currentLoggedInUserId, statusId);
|
|
|
|
bool hasApproverActed = false;
|
|
switch (approverRole)
|
|
{
|
|
case "HoU":
|
|
hasApproverActed = !string.IsNullOrEmpty(otStatus.HouStatus) && otStatus.HouStatus != "Pending";
|
|
break;
|
|
case "HoD":
|
|
hasApproverActed = !string.IsNullOrEmpty(otStatus.HodStatus) && otStatus.HodStatus != "Pending";
|
|
break;
|
|
case "Manager":
|
|
hasApproverActed = !string.IsNullOrEmpty(otStatus.ManagerStatus) && otStatus.ManagerStatus != "Pending";
|
|
break;
|
|
case "HR":
|
|
hasApproverActed = !string.IsNullOrEmpty(otStatus.HrStatus) && otStatus.HrStatus != "Pending";
|
|
break;
|
|
default:
|
|
hasApproverActed = true;
|
|
break;
|
|
}
|
|
|
|
var result = new
|
|
{
|
|
userInfo = new
|
|
{
|
|
fullName = user.FullName,
|
|
departmentName = department,
|
|
user.departmentId,
|
|
filePath = otStatus.FilePath,
|
|
fullNameLower = user.FullName?.ToLower(),
|
|
flexiHour = userSetting.FlexiHour?.FlexiHour,
|
|
stateId = userSetting.State.StateId,
|
|
weekendId = userSetting.State.WeekendId,
|
|
rate = userBasicSalary
|
|
},
|
|
records = otRecords,
|
|
isHoU = userSetting.Approvalflow?.HoU == currentLoggedInUserId,
|
|
otStatus.HouStatus,
|
|
otStatus.HodStatus,
|
|
otStatus.ManagerStatus,
|
|
otStatus.HrStatus,
|
|
approverRole,
|
|
hasApproverActed
|
|
|
|
};
|
|
|
|
return Ok(result);
|
|
}
|
|
|
|
[HttpGet("GetPublicHolidaysByState/{stateId}")]
|
|
public IActionResult GetPublicHolidaysByState(int stateId)
|
|
{
|
|
var holidays = _centralDbContext.Holidays
|
|
.Where(h => h.StateId == stateId)
|
|
.Select(h => new
|
|
{
|
|
date = h.HolidayDate.ToString("yyyy-MM-dd"),
|
|
name = h.HolidayName
|
|
})
|
|
.ToList();
|
|
|
|
return Ok(holidays);
|
|
}
|
|
|
|
[HttpPut("UpdateOtRecordByApprover")]
|
|
public IActionResult UpdateOtRecordByApprover([FromBody] OtRegisterEditDto updatedRecordDto)
|
|
{
|
|
if (updatedRecordDto == null)
|
|
{
|
|
return BadRequest("Invalid record data.");
|
|
}
|
|
|
|
var existingRecord = _centralDbContext.Otregisters.FirstOrDefault(o => o.OvertimeId == updatedRecordDto.OvertimeId);
|
|
if (existingRecord == null)
|
|
{
|
|
return NotFound(new { message = "Overtime record not found." });
|
|
}
|
|
|
|
var otStatus = _centralDbContext.Otstatus.FirstOrDefault(s => s.StatusId == updatedRecordDto.StatusId);
|
|
if (otStatus == null)
|
|
{
|
|
return NotFound("OT status not found.");
|
|
}
|
|
|
|
var currentLoggedInUserId = GetCurrentLoggedInUserId();
|
|
string approverRole = GetApproverRole(currentLoggedInUserId, otStatus.StatusId);
|
|
|
|
bool hasApproverActed = false;
|
|
switch (approverRole)
|
|
{
|
|
case "HoU":
|
|
hasApproverActed = !string.IsNullOrEmpty(otStatus.HouStatus) && otStatus.HouStatus != "Pending";
|
|
break;
|
|
case "HoD":
|
|
hasApproverActed = !string.IsNullOrEmpty(otStatus.HodStatus) && otStatus.HodStatus != "Pending";
|
|
break;
|
|
case "Manager":
|
|
hasApproverActed = !string.IsNullOrEmpty(otStatus.ManagerStatus) && otStatus.ManagerStatus != "Pending";
|
|
break;
|
|
case "HR":
|
|
hasApproverActed = !string.IsNullOrEmpty(otStatus.HrStatus) && otStatus.HrStatus != "Pending";
|
|
break;
|
|
}
|
|
|
|
if (hasApproverActed)
|
|
{
|
|
return Forbid("You cannot edit this record as you have already acted on this OT submission.");
|
|
}
|
|
|
|
var originalRecordData = JsonConvert.SerializeObject(existingRecord, new JsonSerializerSettings
|
|
{
|
|
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
|
|
});
|
|
|
|
existingRecord.OtDate = updatedRecordDto.OtDate;
|
|
existingRecord.OfficeFrom = ParseTimeStringToTimeSpan(updatedRecordDto.OfficeFrom);
|
|
existingRecord.OfficeTo = ParseTimeStringToTimeSpan(updatedRecordDto.OfficeTo);
|
|
existingRecord.OfficeBreak = updatedRecordDto.OfficeBreak;
|
|
existingRecord.AfterFrom = ParseTimeStringToTimeSpan(updatedRecordDto.AfterFrom);
|
|
existingRecord.AfterTo = ParseTimeStringToTimeSpan(updatedRecordDto.AfterTo);
|
|
existingRecord.AfterBreak = updatedRecordDto.AfterBreak;
|
|
existingRecord.StationId = updatedRecordDto.StationId;
|
|
existingRecord.OtDescription = updatedRecordDto.OtDescription;
|
|
|
|
_centralDbContext.SaveChanges();
|
|
|
|
var updateLog = new OtUpdateLog
|
|
{
|
|
ApproverRole = approverRole,
|
|
ApproverUserId = currentLoggedInUserId,
|
|
UpdateTimestamp = DateTime.Now,
|
|
ChangeType = "Edit",
|
|
BeforeEdit = JsonConvert.DeserializeObject<OtRegisterModel>(originalRecordData),
|
|
AfterEdit = updatedRecordDto
|
|
};
|
|
|
|
var logJson = JsonConvert.SerializeObject(updateLog);
|
|
|
|
switch (approverRole)
|
|
{
|
|
case "HoU":
|
|
otStatus.HouUpdate = AppendUpdateLog(otStatus.HouUpdate, logJson);
|
|
break;
|
|
case "HoD":
|
|
otStatus.HodUpdate = AppendUpdateLog(otStatus.HodUpdate, logJson);
|
|
break;
|
|
case "Manager":
|
|
otStatus.ManagerUpdate = AppendUpdateLog(otStatus.ManagerUpdate, logJson);
|
|
break;
|
|
case "HR":
|
|
otStatus.HrUpdate = AppendUpdateLog(otStatus.HrUpdate, logJson);
|
|
break;
|
|
default:
|
|
return Unauthorized("You are not authorized to edit this record.");
|
|
}
|
|
_centralDbContext.SaveChanges();
|
|
|
|
return Ok(new { message = "Overtime record updated successfully and changes logged." });
|
|
}
|
|
|
|
[HttpDelete("DeleteOvertimeInOtReview/{id}")]
|
|
public IActionResult DeleteOvertimeInOtReview(int id)
|
|
{
|
|
var recordToDelete = _centralDbContext.Otregisters.FirstOrDefault(o => o.OvertimeId == id);
|
|
if (recordToDelete == null)
|
|
{
|
|
return NotFound(new { message = "Overtime record not found." });
|
|
}
|
|
|
|
var statusIdForRecord = recordToDelete.StatusId;
|
|
if (!statusIdForRecord.HasValue)
|
|
{
|
|
return BadRequest("Overtime record is not associated with a status.");
|
|
}
|
|
|
|
var otStatus = _centralDbContext.Otstatus.FirstOrDefault(s => s.StatusId == statusIdForRecord.Value);
|
|
if (otStatus == null)
|
|
{
|
|
return NotFound("OT status for this record not found.");
|
|
}
|
|
|
|
var currentLoggedInUserId = GetCurrentLoggedInUserId();
|
|
string approverRole = GetApproverRole(currentLoggedInUserId, statusIdForRecord.Value);
|
|
|
|
bool hasApproverActed = false;
|
|
switch (approverRole)
|
|
{
|
|
case "HoU":
|
|
hasApproverActed = !string.IsNullOrEmpty(otStatus.HouStatus) && otStatus.HouStatus != "Pending";
|
|
break;
|
|
case "HoD":
|
|
hasApproverActed = !string.IsNullOrEmpty(otStatus.HodStatus) && otStatus.HodStatus != "Pending";
|
|
break;
|
|
case "Manager":
|
|
hasApproverActed = !string.IsNullOrEmpty(otStatus.ManagerStatus) && otStatus.ManagerStatus != "Pending";
|
|
break;
|
|
case "HR":
|
|
hasApproverActed = !string.IsNullOrEmpty(otStatus.HrStatus) && otStatus.HrStatus != "Pending";
|
|
break;
|
|
}
|
|
|
|
if (hasApproverActed)
|
|
{
|
|
return Forbid("You cannot delete this record as you have already acted on this OT submission.");
|
|
}
|
|
|
|
var deletedRecordData = JsonConvert.SerializeObject(recordToDelete, new JsonSerializerSettings
|
|
{
|
|
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
|
|
});
|
|
|
|
_centralDbContext.Otregisters.Remove(recordToDelete);
|
|
_centralDbContext.SaveChanges();
|
|
|
|
var deleteLog = new OtUpdateLog
|
|
{
|
|
ApproverRole = approverRole,
|
|
ApproverUserId = currentLoggedInUserId,
|
|
UpdateTimestamp = DateTime.Now,
|
|
ChangeType = "Delete",
|
|
DeletedRecord = JsonConvert.DeserializeObject<OtRegisterModel>(deletedRecordData)
|
|
};
|
|
|
|
var logJson = JsonConvert.SerializeObject(deleteLog);
|
|
|
|
switch (approverRole)
|
|
{
|
|
case "HoU":
|
|
otStatus.HouUpdate = AppendUpdateLog(otStatus.HouUpdate, logJson);
|
|
break;
|
|
case "HoD":
|
|
otStatus.HodUpdate = AppendUpdateLog(otStatus.HodUpdate, logJson);
|
|
break;
|
|
case "Manager":
|
|
otStatus.ManagerUpdate = AppendUpdateLog(otStatus.ManagerUpdate, logJson);
|
|
break;
|
|
case "HR":
|
|
otStatus.HrUpdate = AppendUpdateLog(otStatus.HrUpdate, logJson);
|
|
break;
|
|
default:
|
|
Console.WriteLine($"Unauthorized deletion attempt by user {currentLoggedInUserId} for status {statusIdForRecord.Value}");
|
|
break;
|
|
}
|
|
_centralDbContext.SaveChanges();
|
|
|
|
return Ok(new { message = "Overtime record deleted successfully and deletion logged." });
|
|
}
|
|
|
|
private int GetCurrentLoggedInUserId()
|
|
{
|
|
if (User.Identity.IsAuthenticated)
|
|
{
|
|
var userIdClaim = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
|
|
if (userIdClaim != null && int.TryParse(userIdClaim.Value, out int userId))
|
|
{
|
|
return userId;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
private string GetApproverRole(int currentUserId, int statusId)
|
|
{
|
|
var otStatus = _centralDbContext.Otstatus.FirstOrDefault(s => s.StatusId == statusId);
|
|
|
|
if (otStatus == null)
|
|
{
|
|
return "Unknown: Status not found";
|
|
}
|
|
|
|
var submittedByUserId = otStatus.UserId;
|
|
|
|
var hrUserSetting = _centralDbContext.Hrusersetting
|
|
.Include(hus => hus.Approvalflow)
|
|
.FirstOrDefault(hus => hus.UserId == submittedByUserId);
|
|
|
|
if (hrUserSetting?.Approvalflow == null)
|
|
{
|
|
return "Unknown: Approval flow not configured for user";
|
|
}
|
|
|
|
var approvalFlow = hrUserSetting.Approvalflow;
|
|
|
|
if (approvalFlow.HoU == currentUserId) return "HoU";
|
|
if (approvalFlow.HoD == currentUserId) return "HoD";
|
|
if (approvalFlow.Manager == currentUserId) return "Manager";
|
|
if (approvalFlow.HR == currentUserId) return "HR";
|
|
|
|
return "Unknown: Not an assigned approver";
|
|
}
|
|
|
|
private string AppendUpdateLog(string? existingLogJson, string newLogJson)
|
|
{
|
|
List<OtUpdateLog> logs;
|
|
if (!string.IsNullOrEmpty(existingLogJson))
|
|
{
|
|
try
|
|
{
|
|
logs = JsonConvert.DeserializeObject<List<OtUpdateLog>>(existingLogJson);
|
|
if (logs == null) logs = new List<OtUpdateLog>();
|
|
}
|
|
catch
|
|
{
|
|
logs = new List<OtUpdateLog>();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logs = new List<OtUpdateLog>();
|
|
}
|
|
|
|
var newLogEntry = JsonConvert.DeserializeObject<OtUpdateLog>(newLogJson);
|
|
if (newLogEntry != null)
|
|
{
|
|
logs.Add(newLogEntry);
|
|
}
|
|
|
|
return JsonConvert.SerializeObject(logs);
|
|
}
|
|
|
|
private TimeSpan? ParseTimeStringToTimeSpan(string? timeString)
|
|
{
|
|
if (string.IsNullOrEmpty(timeString))
|
|
{
|
|
return null;
|
|
}
|
|
if (TimeSpan.TryParse(timeString, out TimeSpan result))
|
|
{
|
|
return result;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region OT Review/ OT Record PDF Excel
|
|
[HttpGet("GetOvertimePdfByStatusId/{statusId}")]
|
|
public IActionResult GetOvertimePdfByStatusId(int statusId)
|
|
{
|
|
var otStatus = _centralDbContext.Otstatus.FirstOrDefault(s => s.StatusId == statusId);
|
|
if (otStatus == null)
|
|
return NotFound("OT status not found.");
|
|
|
|
var user = _centralDbContext.Users
|
|
.Include(u => u.Department)
|
|
.FirstOrDefault(u => u.Id == otStatus.UserId);
|
|
if (user == null)
|
|
return NotFound("User not found.");
|
|
|
|
var userRate = _centralDbContext.Rates
|
|
.Where(r => r.UserId == user.Id)
|
|
.OrderByDescending(r => r.LastUpdated)
|
|
.FirstOrDefault()?.RateValue ?? 0m;
|
|
|
|
var otRecords = _centralDbContext.Otregisters
|
|
.Include(o => o.Stations)
|
|
.Where(o => o.StatusId == statusId)
|
|
.ToList();
|
|
|
|
DateTime? selectedMonth = otRecords.Any()
|
|
? otRecords.First().OtDate
|
|
: DateTime.Now;
|
|
|
|
byte[]? logoImage = null;
|
|
var logoPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images", "logo.jpg");
|
|
if (System.IO.File.Exists(logoPath))
|
|
{
|
|
logoImage = System.IO.File.ReadAllBytes(logoPath);
|
|
}
|
|
|
|
var currentLoggedInUserId = GetCurrentLoggedInUserId();
|
|
|
|
var userSetting = _centralDbContext.Hrusersetting
|
|
.Include(us => us.Approvalflow)
|
|
.Include(us => us.FlexiHour)
|
|
.FirstOrDefault(us => us.UserId == user.Id);
|
|
|
|
bool isHoU = false;
|
|
if (userSetting?.Approvalflow != null && userSetting.Approvalflow.HoU == currentLoggedInUserId)
|
|
{
|
|
isHoU = true;
|
|
}
|
|
|
|
string? flexiHour = userSetting?.FlexiHour?.FlexiHour;
|
|
|
|
var approvedSignatures = new List<ApprovalSignatureData>();
|
|
|
|
var approvalFlow = userSetting?.Approvalflow;
|
|
|
|
if (approvalFlow != null)
|
|
{
|
|
|
|
var approvalStages = new List<(int? approverId, string statusField, DateTime? submitDate, UserModel? approverUser)>
|
|
{
|
|
(approvalFlow.HoU, otStatus.HouStatus, otStatus.HouSubmitDate, _centralDbContext.Users.FirstOrDefault(u => u.Id == approvalFlow.HoU)),
|
|
(approvalFlow.HoD, otStatus.HodStatus, otStatus.HodSubmitDate, _centralDbContext.Users.FirstOrDefault(u => u.Id == approvalFlow.HoD)),
|
|
(approvalFlow.Manager, otStatus.ManagerStatus, otStatus.ManagerSubmitDate, _centralDbContext.Users.FirstOrDefault(u => u.Id == approvalFlow.Manager)),
|
|
(approvalFlow.HR, otStatus.HrStatus, otStatus.HrSubmitDate, _centralDbContext.Users.FirstOrDefault(u => u.Id == approvalFlow.HR))
|
|
};
|
|
|
|
foreach (var stage in approvalStages
|
|
.Where(s => s.approverUser != null && s.statusField == "Approved" && s.submitDate.HasValue)
|
|
.OrderBy(s => s.submitDate))
|
|
{
|
|
byte[]? signatureImageBytes = null;
|
|
|
|
var staffSign = _centralDbContext.Staffsign
|
|
.FirstOrDefault(ss => ss.UserId == stage.approverId.Value);
|
|
|
|
approvedSignatures.Add(new ApprovalSignatureData
|
|
{
|
|
ApproverName = stage.approverUser.FullName,
|
|
SignatureImage = signatureImageBytes,
|
|
ApprovedDate = stage.submitDate
|
|
});
|
|
}
|
|
}
|
|
|
|
var pdfGenerator = new OvertimePDF(_centralDbContext);
|
|
var stream = pdfGenerator.GenerateOvertimeTablePdf(otRecords, user, userRate, selectedMonth, logoImage, isHoU, flexiHour, approvedSignatures);
|
|
|
|
string fileName = $"OvertimeReport_{user.FullName}_{DateTime.Now:yyyyMMdd}.pdf";
|
|
return File(stream.ToArray(), "application/pdf", fileName);
|
|
}
|
|
|
|
[HttpGet("GetUserOvertimePdf/{userId}/{month}/{year}")]
|
|
public IActionResult GetUserOvertimePdf(int userId, int month, int year)
|
|
{
|
|
var user = _centralDbContext.Users
|
|
.Include(u => u.Department)
|
|
.FirstOrDefault(u => u.Id == userId);
|
|
|
|
if (user == null)
|
|
return NotFound("User not found.");
|
|
|
|
var userRate = _centralDbContext.Rates
|
|
.Where(r => r.UserId == user.Id)
|
|
.OrderByDescending(r => r.LastUpdated)
|
|
.FirstOrDefault()?.RateValue ?? 0m;
|
|
|
|
var otRecords = _centralDbContext.Otregisters
|
|
.Include(o => o.Stations)
|
|
.Where(o => o.UserId == userId && o.OtDate.Month == month && o.OtDate.Year == year)
|
|
.Select(o => new OtRegisterModel
|
|
{
|
|
OtDate = o.OtDate,
|
|
OfficeFrom = o.OfficeFrom,
|
|
OfficeTo = o.OfficeTo,
|
|
OfficeBreak = o.OfficeBreak,
|
|
AfterFrom = o.AfterFrom,
|
|
AfterTo = o.AfterTo,
|
|
AfterBreak = o.AfterBreak,
|
|
StationId = o.StationId,
|
|
Stations = o.Stations,
|
|
OtDescription = o.OtDescription,
|
|
OtDays = o.OtDays
|
|
})
|
|
.ToList();
|
|
|
|
DateTime? selectedMonth = new DateTime(year, month, 1);
|
|
|
|
byte[]? logoImage = null;
|
|
var logoPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images", "logo.jpg");
|
|
if (System.IO.File.Exists(logoPath))
|
|
{
|
|
logoImage = System.IO.File.ReadAllBytes(logoPath);
|
|
}
|
|
|
|
var flexiHour = _centralDbContext.Hrusersetting
|
|
.Include(us => us.FlexiHour)
|
|
.FirstOrDefault(us => us.UserId == userId)?.FlexiHour?.FlexiHour;
|
|
|
|
var pdfGenerator = new OvertimePDF(_centralDbContext);
|
|
var stream = pdfGenerator.GenerateSimpleOvertimeTablePdf(otRecords, user, userRate, selectedMonth, logoImage, flexiHour);
|
|
|
|
string fileName = $"OvertimeReport_{user.FullName}_{year}_{month}.pdf";
|
|
return File(stream.ToArray(), "application/pdf", fileName);
|
|
}
|
|
|
|
[HttpGet("GetOvertimeExcelByStatusId/{statusId}")]
|
|
public IActionResult GetOvertimeExcelByStatusId(int statusId)
|
|
{
|
|
var otStatus = _centralDbContext.Otstatus.FirstOrDefault(s => s.StatusId == statusId);
|
|
if (otStatus == null)
|
|
return NotFound("OT status not found.");
|
|
|
|
var user = _centralDbContext.Users
|
|
.Include(u => u.Department)
|
|
.FirstOrDefault(u => u.Id == otStatus.UserId);
|
|
if (user == null)
|
|
return NotFound("User not found.");
|
|
|
|
var userRate = _centralDbContext.Rates
|
|
.Where(r => r.UserId == user.Id)
|
|
.OrderByDescending(r => r.LastUpdated)
|
|
.FirstOrDefault()?.RateValue ?? 0m;
|
|
|
|
var otRecords = _centralDbContext.Otregisters
|
|
.Include(o => o.Stations)
|
|
.Where(o => o.StatusId == statusId)
|
|
.ToList();
|
|
|
|
DateTime? selectedMonth = otRecords.Any()
|
|
? otRecords.First().OtDate
|
|
: DateTime.Now;
|
|
|
|
var currentLoggedInUserId = GetCurrentLoggedInUserId();
|
|
|
|
var userSetting = _centralDbContext.Hrusersetting
|
|
.Include(us => us.Approvalflow)
|
|
.Include(us => us.FlexiHour)
|
|
.FirstOrDefault(us => us.UserId == user.Id);
|
|
|
|
bool isHoU = false;
|
|
if (userSetting?.Approvalflow != null && userSetting.Approvalflow.HoU == currentLoggedInUserId)
|
|
{
|
|
isHoU = true;
|
|
}
|
|
|
|
string? flexiHour = userSetting?.FlexiHour?.FlexiHour;
|
|
|
|
var excelGenerator = new OvertimeExcel(_centralDbContext, _env);
|
|
|
|
var logoPath = Path.Combine(_env.WebRootPath, "images", "logo.jpg");
|
|
byte[]? logoImage = System.IO.File.Exists(logoPath) ? System.IO.File.ReadAllBytes(logoPath) : null;
|
|
|
|
var stream = excelGenerator.GenerateOvertimeExcel(otRecords, user, userRate, selectedMonth, isHoU, flexiHour, logoImage, isSimplifiedExport: false, otStatus);
|
|
|
|
string fileName = $"OvertimeReport_{user.FullName}_{DateTime.Now:yyyyMMdd}.xlsx";
|
|
return File(stream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
|
|
}
|
|
|
|
[HttpGet("GenerateUserOvertimeExcel/{userId}/{month}/{year}")]
|
|
public IActionResult GenerateUserOvertimeExcel(int userId, int month, int year)
|
|
{
|
|
try
|
|
{
|
|
var user = _centralDbContext.Users
|
|
.Include(u => u.Department)
|
|
.FirstOrDefault(u => u.Id == userId);
|
|
|
|
if (user == null)
|
|
return NotFound("User not found.");
|
|
|
|
var userRate = _centralDbContext.Rates
|
|
.Where(r => r.UserId == user.Id)
|
|
.OrderByDescending(r => r.LastUpdated)
|
|
.FirstOrDefault()?.RateValue ?? 0m;
|
|
|
|
var userSetting = _centralDbContext.Hrusersetting
|
|
.Include(us => us.FlexiHour)
|
|
.FirstOrDefault(us => us.UserId == userId);
|
|
|
|
string flexiHour = userSetting?.FlexiHour?.FlexiHour;
|
|
|
|
var startDate = new DateTime(year, month, 1);
|
|
var endDate = startDate.AddMonths(1);
|
|
|
|
var otRecords = _centralDbContext.Otregisters
|
|
.Include(o => o.Stations)
|
|
.Where(o => o.UserId == userId && o.OtDate >= startDate && o.OtDate < endDate)
|
|
.ToList();
|
|
|
|
bool isAdminUser = IsAdmin(user.Id);
|
|
|
|
var excelGenerator = new OvertimeExcel(_centralDbContext, _env);
|
|
|
|
var logoPath = Path.Combine(_env.WebRootPath, "images", "logo.jpg");
|
|
byte[]? logoImage = System.IO.File.Exists(logoPath) ? System.IO.File.ReadAllBytes(logoPath) : null;
|
|
|
|
var stream = excelGenerator.GenerateOvertimeExcel(
|
|
otRecords,
|
|
user,
|
|
userRate,
|
|
startDate,
|
|
isHoU: false,
|
|
flexiHour: flexiHour,
|
|
logoImage: logoImage,
|
|
isSimplifiedExport: true
|
|
);
|
|
|
|
string fileName = $"OvertimeReport_{user.FullName}_{month}_{year}.xlsx";
|
|
return File(stream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error generating user overtime Excel");
|
|
return StatusCode(500, "Error generating Excel file");
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
} |