hrms-api-backend/BMA.EHR.Leave/Controllers/LeaveController.cs
2024-08-19 21:20:05 +07:00

2098 lines
102 KiB
C#

using Amazon.S3.Model;
using BMA.EHR.Application.Repositories;
using BMA.EHR.Application.Repositories.Commands;
using BMA.EHR.Application.Repositories.Leaves.LeaveRequests;
using BMA.EHR.Application.Repositories.Leaves.TimeAttendants;
using BMA.EHR.Application.Responses.Profiles;
using BMA.EHR.Domain.Common;
using BMA.EHR.Domain.Models.Leave.TimeAttendants;
using BMA.EHR.Domain.Shared;
using BMA.EHR.Infrastructure.Persistence;
using BMA.EHR.Leave.Service.DTOs.AdditionalCheck;
using BMA.EHR.Leave.Service.DTOs.Calendar;
using BMA.EHR.Leave.Service.DTOs.ChangeRound;
using BMA.EHR.Leave.Service.DTOs.CheckIn;
using BMA.EHR.Leave.Service.DTOs.DutyTime;
using BMA.EHR.Leave.Service.DTOs.LeaveRequest;
using BMA.EHR.Leave.Service.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Org.BouncyCastle.Ocsp;
using RabbitMQ.Client;
using Serilog;
using Swashbuckle.AspNetCore.Annotations;
using System.ComponentModel.DataAnnotations;
using System.Security.Claims;
using System.Text;
using SearchProfileResultDto = BMA.EHR.Leave.Service.DTOs.ChangeRound.SearchProfileResultDto;
namespace BMA.EHR.Leave.Service.Controllers
{
[Route("api/v{version:apiVersion}/leave")]
[ApiVersion("1.0")]
[ApiController]
[Produces("application/json")]
[Authorize]
[SwaggerTag("API ระบบลงเวลาและการลา")]
public class LeaveController : BaseController
{
#region " Fields "
private readonly DutyTimeRepository _dutyTimeRepository;
private readonly LeaveDbContext _context;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IWebHostEnvironment _hostingEnvironment;
private readonly IConfiguration _configuration;
private readonly UserProfileRepository _userProfileRepository;
private readonly UserTimeStampRepository _userTimeStampRepository;
private readonly MinIOService _minIOService;
private readonly ProcessUserTimeStampRepository _processUserTimeStampRepository;
private readonly UserDutyTimeRepository _userDutyTimeRepository;
private readonly AdditionalCheckRequestRepository _additionalCheckRequestRepository;
private readonly LeaveRequestRepository _leaveRequestRepository;
private readonly UserCalendarRepository _userCalendarRepository;
private readonly CommandRepository _commandRepository;
private readonly string _bucketName = "check-in";
private readonly string _fakeQueueName = "fake-checkin-queue";
private readonly string _realQueueName = "checkin-queue";
private readonly RabbitCheckInService _rabbitCheckInService;
#endregion
#region " Constuctor and Destructor "
public LeaveController(DutyTimeRepository dutyTimeRepository,
LeaveDbContext context,
IHttpContextAccessor httpContextAccessor,
IWebHostEnvironment hostingEnvironment,
IConfiguration configuration,
UserProfileRepository userProfileRepository,
UserTimeStampRepository userTimeStampRepository,
MinIOService minIOService,
ProcessUserTimeStampRepository processUserTimeStampRepository,
UserDutyTimeRepository userDutyTimeRepository,
AdditionalCheckRequestRepository additionalCheckRequestRepository,
UserCalendarRepository userCalendarRepository,
CommandRepository commandRepository,
LeaveRequestRepository leaveRequestRepository,
RabbitCheckInService rabbitCheckInService)
{
_dutyTimeRepository = dutyTimeRepository;
_context = context;
_httpContextAccessor = httpContextAccessor;
_hostingEnvironment = hostingEnvironment;
_configuration = configuration;
_userProfileRepository = userProfileRepository;
_userTimeStampRepository = userTimeStampRepository;
_minIOService = minIOService;
_processUserTimeStampRepository = processUserTimeStampRepository;
_userDutyTimeRepository = userDutyTimeRepository;
_additionalCheckRequestRepository = additionalCheckRequestRepository;
_userCalendarRepository = userCalendarRepository;
_commandRepository = commandRepository;
_leaveRequestRepository = leaveRequestRepository;
_rabbitCheckInService = rabbitCheckInService;
}
#endregion
#region " Properties "
private string? UserId => _httpContextAccessor?.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
private string? FullName => _httpContextAccessor?.HttpContext?.User?.FindFirst("name")?.Value;
private bool? PlacementAdmin => _httpContextAccessor?.HttpContext?.User?.IsInRole("placement1");
private string? AccessToken => _httpContextAccessor?.HttpContext?.Request.Headers["Authorization"];
private Guid OcId
{
get
{
if (UserId != null || UserId != "")
return _userProfileRepository.GetUserOCId(Guid.Parse(UserId!), AccessToken);
else
return Guid.Empty;
}
}
#endregion
#region " Methods "
#region " Duty Time รอบการทำงาน "
/// <summary>
/// LV1_004 - ข้อมูลทั้งหมดของรอบการปฏิบัติงาน (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpGet("duty-time")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> GetAllAsync()
{
var data = await _dutyTimeRepository.GetAllAsync();
return Success(data);
}
/// <summary>
/// ข้อมูลของรอบการปฏิบัติงาน (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpGet("duty-time/{id:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> GetByIdAsync(Guid id)
{
var data = await _dutyTimeRepository.GetByIdAsync(id);
return Success(data);
}
/// <summary>
/// LV1_001 - สร้างรอบการปฏิบัติงาน (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("duty-time")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> PostAsync([FromBody] CreateDutyTimeDto data)
{
// validate
var startMorning = TimeOnly.Parse(data.StartTimeMorning);
var endMorning = TimeOnly.Parse(data.EndTimeMorning);
var startAfternoon = TimeOnly.Parse(data.StartTimeAfternoon);
var endAfternoon = TimeOnly.Parse(data.EndTimeAfternoon);
if (startMorning >= endMorning)
{
throw new Exception(GlobalMessages.StartTimeGreaterEnd);
}
if (startAfternoon >= endAfternoon)
{
throw new Exception(GlobalMessages.StartTimeGreaterEnd);
}
var oldData = await _dutyTimeRepository.GetAllAsync();
if (oldData == null || oldData.Count == 0)
{
var inserted = new DutyTime
{
Id = Guid.NewGuid(),
Description = data.Description ?? "",
StartTimeMorning = data.StartTimeMorning,
EndTimeMorning = data.EndTimeMorning,
StartTimeAfternoon = data.StartTimeAfternoon,
EndTimeAfternoon = data.EndTimeAfternoon,
IsActive = data.IsActive,
IsDefault = true,
};
var ret = await _dutyTimeRepository.AddAsync(inserted);
return Success(ret);
}
else
{
if (data.IsDefault)
{
foreach (var d in oldData)
{
d.IsDefault = false;
await _dutyTimeRepository.UpdateAsync(d);
}
}
var inserted = new DutyTime
{
Id = Guid.NewGuid(),
Description = data.Description ?? "",
StartTimeMorning = data.StartTimeMorning,
EndTimeMorning = data.EndTimeMorning,
StartTimeAfternoon = data.StartTimeAfternoon,
EndTimeAfternoon = data.EndTimeAfternoon,
IsActive = data.IsActive,
IsDefault = data.IsDefault,
};
var ret = await _dutyTimeRepository.AddAsync(inserted);
return Success(ret);
}
}
/// <summary>
/// LV1_002 - แก้ไขรอบการปฏิบัติงาน (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPut("duty-time/{id:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> PutAsync(Guid id, [FromBody] UpdateDutyTimeDto data)
{
var oldData = await _dutyTimeRepository.GetByIdAsync(id);
if (oldData == null)
{
throw new Exception(GlobalMessages.DataNotFound);
}
else
{
var oldDataList = await _dutyTimeRepository.GetAllAsync();
if (data.IsDefault)
{
foreach (var d in oldDataList)
{
d.IsDefault = false;
await _dutyTimeRepository.UpdateAsync(d);
}
}
oldData.Description = data.Description ?? "";
oldData.IsDefault = data.IsDefault;
oldData.IsActive = data.IsActive;
await _dutyTimeRepository.UpdateAsync(oldData);
return Success(oldData);
}
}
/// <summary>
/// LV1_003 - ลบรอบการปฏิบัติงาน (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpDelete("duty-time/{id:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> DeleteAsync(Guid id)
{
var oldData = await _dutyTimeRepository.GetByIdAsync(id);
if (oldData == null)
{
throw new Exception(GlobalMessages.DataNotFound);
}
else
{
var inUseRound = await _userDutyTimeRepository.GetFirstInUseRound(oldData.Id);
//if (inUseRound != null || oldData.IsActive || oldData.IsDefault)
if (inUseRound != null)
{
throw new Exception("ไม่สามารถลบรอบการปฏิบัติงานที่ยังใช้งานอยู่ได้");
}
if (oldData.IsDefault)
{
throw new Exception("ไม่สามารถลบรอบการปฏิบัติงานที่ตั้งค่า Default ได้");
}
await _dutyTimeRepository.DeleteAsync(oldData);
return Success();
}
}
/// <summary>
/// LV1_012 - ข้อมูลทั้งหมดของรอบการปฏิบัติงานที่ active (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpGet("round")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> GetAllActiveAsync()
{
var data = await _dutyTimeRepository.GetAllActiveAsync();
return Success(data);
}
#endregion
#region " Check-In Check-Out ลงเวลา "
/// <summary>
/// LV1_006 - เช็คเวลาต้องลงเวลาเข้าหรือออกงาน (USER)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpGet("check-time")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> CheckTimeAsync()
{
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
var data = await _userTimeStampRepository.GetLastRecord(userId);
// TODO : รอดุึงรอบที่ผูกกับ user
var duty = await _dutyTimeRepository.GetDefaultAsync();
CheckInResultDto ret;
if (data == null)
{
ret = new CheckInResultDto
{
StartTimeMorning = duty == null ? "00:00" : duty.StartTimeMorning,
EndTimeMorning = duty == null ? "00:00" : duty.EndTimeMorning,
StartTimeAfternoon = duty == null ? "00:00" : duty.StartTimeAfternoon,
EndTimeAfternoon = duty == null ? "00:00" : duty.EndTimeAfternoon,
Description = duty == null ? "-" : duty.Description,
CheckInTime = null,
CheckInId = null,
};
}
else
{
if (data.CheckOut != null)
{
ret = new CheckInResultDto
{
StartTimeMorning = duty == null ? "00:00" : duty.StartTimeMorning,
EndTimeMorning = duty == null ? "00:00" : duty.EndTimeMorning,
StartTimeAfternoon = duty == null ? "00:00" : duty.StartTimeAfternoon,
EndTimeAfternoon = duty == null ? "00:00" : duty.EndTimeAfternoon,
Description = duty == null ? "-" : duty.Description,
CheckInTime = null,
CheckInId = null,
};
}
else
{
ret = new CheckInResultDto
{
StartTimeMorning = duty == null ? "00:00" : duty.StartTimeMorning,
EndTimeMorning = duty == null ? "00:00" : duty.EndTimeMorning,
StartTimeAfternoon = duty == null ? "00:00" : duty.StartTimeAfternoon,
EndTimeAfternoon = duty == null ? "00:00" : duty.EndTimeAfternoon,
Description = duty == null ? "-" : duty.Description,
CheckInTime = data.CheckIn,
CheckInId = data.Id,
};
}
}
return Success(ret);
}
/// <summary>
/// LV1_005 - ลงเวลาเข้า-ออกงาน (USER)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("check-in"), DisableRequestSizeLimit]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> CheckInAsync([FromForm] CheckTimeDto data)
{
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
var currentDate = DateTime.Now;
var checkFileBytes = new byte[0];
if (data.Img != null && data.Img.Length == 0)
{
var formFile = data.Img;
using (var memoryStream = new MemoryStream())
{
await formFile.CopyToAsync(memoryStream);
checkFileBytes = memoryStream.ToArray();
}
}
var checkData = new CheckTimeDtoRB
{
UserId = userId,
CurrentDate = currentDate,
CheckInId = data.CheckInId,
Lat = data.Lat,
Lon = data.Lon,
POI = data.POI,
IsLocation = data.IsLocation,
LocationName = data.LocationName,
Remark = data.Remark,
CheckInFileName = data.Img == null ? "no-file" : data.Img.FileName,
CheckInFileBytes = checkFileBytes,
Token = AccessToken ?? ""
};
//// create connection
//var factory = new ConnectionFactory()
//{
// HostName = _configuration["Rabbit:Host"],
// UserName = _configuration["Rabbit:User"],
// Password = _configuration["Rabbit:Password"],
//};
//// create channel
//using var connection = factory.CreateConnection();
//using var channel = connection.CreateModel();
//channel.QueueDeclare(queue: "checkin-queue", durable: false, exclusive: false, autoDelete: false, arguments: null);
//// แปลง Object เป็น JSON สตริง
//var serializedObject = JsonConvert.SerializeObject(checkData);
//// แปลง JSON สตริงเป็น byte array
//var body = Encoding.UTF8.GetBytes(serializedObject);
//channel.BasicPublish(exchange: "", routingKey: "checkin-queue", basicProperties: null, body: body);
//Console.WriteLine($"Send to Queue: {serializedObject}");
var serializedObject = JsonConvert.SerializeObject(checkData);
var body = Encoding.UTF8.GetBytes(serializedObject);
_rabbitCheckInService.SendMessageToQueue(_realQueueName, body);
return Success(new { date = currentDate });
}
/// <summary>
/// Fake Check in
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("fake-check-in"), DisableRequestSizeLimit]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[AllowAnonymous]
public ActionResult<ResponseObject> FakeCheckIn([FromBody] FakeCheckTimeDto data)
{
var currentDate = DateTime.Now;
//// create connection
//var factory = new ConnectionFactory()
//{
// HostName = _configuration["Rabbit:Host"],
// UserName = _configuration["Rabbit:User"],
// Password = _configuration["Rabbit:Password"],
//};
//// create channel
//using var connection = factory.CreateConnection();
//using var channel = connection.CreateModel();
//channel.QueueDeclare(queue: "fake-checkin-queue", durable: false, exclusive: false, autoDelete: false, arguments: null);
var serializedObject = JsonConvert.SerializeObject(data);
var body = Encoding.UTF8.GetBytes(serializedObject);
_rabbitCheckInService.SendMessageToQueue(_fakeQueueName, body);
//channel.BasicPublish(exchange: "", routingKey: "fake-checkin-queue", basicProperties: null, body: body);
//Console.WriteLine($"Send to Queue: {serializedObject}");
return Success(new { date = currentDate });
}
/// <summary>
/// Check in Processing
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("process-check-in"), DisableRequestSizeLimit]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[AllowAnonymous]
public async Task<ActionResult<ResponseObject>> ProcessCheckInAsync([FromBody] CheckTimeDtoRB data)
{
var userId = data.UserId ?? Guid.Empty;
var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, data.Token);
if (profile == null)
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
if (data.CheckInFileName == "no-file") throw new Exception(GlobalMessages.NoFileToUpload);
var currentDate = data.CurrentDate ?? DateTime.Now;
var fileName = $"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{data.CheckInFileName}";
using (var ms = new MemoryStream(data.CheckInFileBytes ?? new byte[0]))
{
await _minIOService.UploadFileAsync(fileName, ms);
}
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
if (defaultRound == null)
{
return Error("ไม่พบรอบการลงเวลาทำงาน Default", StatusCodes.Status404NotFound);
}
var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id);
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
// TODO : รอดุึงรอบที่ผูกกับ user
var duty = userRound ?? defaultRound;
// create check in object
if (data.CheckInId == null)
{
// validate duplicate check in
var currentCheckIn = await _userTimeStampRepository.GetTimestampByDateAsync(userId, currentDate);
if (currentCheckIn != null)
{
return Error(new Exception("ไม่สามารถลงเวลาได้ เนื่องจากมีการลงเวลาในวันนี้แล้ว!"), StatusCodes.Status400BadRequest);
}
var checkin = new UserTimeStamp
{
KeycloakUserId = userId,
CheckInLat = data.Lat,
CheckInLon = data.Lon,
IsLocationCheckIn = data.IsLocation,
CheckInLocationName = data.LocationName,
CheckInPOI = data.POI,
CheckInRemark = data.Remark,
CheckInImageUrl = fileName,
CheckIn = currentDate,
Prefix = profile.Prefix,
FirstName = profile.FirstName,
LastName = profile.LastName,
};
var checkInStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >
DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >
DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
"ABSENT" :
"LATE" :
"NORMAL";
// process - รอทำใน queue
var checkin_process = new ProcessUserTimeStamp
{
KeycloakUserId = userId,
CheckInLat = data.Lat,
CheckInLon = data.Lon,
IsLocationCheckIn = data.IsLocation,
CheckInLocationName = data.LocationName,
CheckInPOI = data.POI,
CheckInRemark = data.Remark,
CheckInImageUrl = fileName,
CheckIn = currentDate,
CheckInStatus = checkInStatus,
Prefix = profile.Prefix,
FirstName = profile.FirstName,
LastName = profile.LastName,
};
await _userTimeStampRepository.AddAsync(checkin);
await _processUserTimeStampRepository.AddAsync(checkin_process);
}
else
{
var checkout = await _userTimeStampRepository.GetByIdAsync(data.CheckInId.Value);
var currentCheckInProcess = await _processUserTimeStampRepository.GetTimestampByDateAsync(userId, checkout.CheckIn.Date);
var checkout_process = await _processUserTimeStampRepository.GetByIdAsync(currentCheckInProcess.Id);
if (checkout != null)
{
checkout.CheckOutLat = data.Lat;
checkout.CheckOutLon = data.Lon;
checkout.IsLocationCheckOut = data.IsLocation;
checkout.CheckOutLocationName = data.LocationName;
checkout.CheckOutPOI = data.POI;
checkout.CheckOutRemark = data.Remark;
checkout.CheckOutImageUrl = fileName;
checkout.CheckOut = currentDate;
await _userTimeStampRepository.UpdateAsync(checkout);
}
else
{
return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound);
}
var checkOutStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) <
DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
"ABSENT" :
DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) <
DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
"ABSENT" :
"NORMAL";
if (checkout_process != null)
{
checkout_process.CheckOutLat = data.Lat;
checkout_process.CheckOutLon = data.Lon;
checkout_process.IsLocationCheckOut = data.IsLocation;
checkout_process.CheckOutLocationName = data.LocationName;
checkout_process.CheckOutPOI = data.POI;
checkout_process.CheckOutRemark = data.Remark;
checkout_process.CheckOutImageUrl = fileName;
checkout_process.CheckOut = currentDate;
checkout_process.CheckOutStatus = checkOutStatus;
await _processUserTimeStampRepository.UpdateAsync(checkout_process);
}
else
{
return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound);
}
}
var checkInType = data.CheckInId == null ? "check-in" : "check-out";
return Success(new { user = $"{profile.FirstName} {profile.LastName}", date = currentDate, type = checkInType }); ;
}
/// <summary>
/// LV1_005 - ลงเวลาเข้า-ออกงาน (USER)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("check-in-old"), DisableRequestSizeLimit]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> CheckInOldAsync([FromForm] CheckTimeDto data)
{
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken);
if (profile == null)
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
if (data.Img == null) throw new Exception(GlobalMessages.NoFileToUpload);
var currentDate = DateTime.Now;
var fileName = $"{_bucketName}/{UserId}/{DateTime.Now.ToString("dd-MM-yyyy")}/{data.Img.FileName}";
using (var ms = new MemoryStream())
{
data.Img.CopyTo(ms);
await _minIOService.UploadFileAsync(fileName, ms);
}
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
if (defaultRound == null)
{
return Error("ไม่พบรอบการลงเวลาทำงาน Default", StatusCodes.Status404NotFound);
}
var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id);
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
// TODO : รอดุึงรอบที่ผูกกับ user
var duty = userRound ?? defaultRound;
// create check in object
if (data.CheckInId == null)
{
// validate duplicate check in
var currentCheckIn = await _userTimeStampRepository.GetTimestampByDateAsync(Guid.Parse(UserId), currentDate);
if (currentCheckIn != null)
{
return Error(new Exception("ไม่สามารถลงเวลาได้ เนื่องจากมีการลงเวลาในวันนี้แล้ว!"), StatusCodes.Status400BadRequest);
}
var checkin = new UserTimeStamp
{
KeycloakUserId = UserId != null ? Guid.Parse(UserId) : Guid.Empty,
CheckInLat = data.Lat,
CheckInLon = data.Lon,
IsLocationCheckIn = data.IsLocation,
CheckInLocationName = data.LocationName,
CheckInPOI = data.POI,
CheckInRemark = data.Remark,
CheckInImageUrl = fileName,
CheckIn = currentDate,
Prefix = profile.Prefix,
FirstName = profile.FirstName,
LastName = profile.LastName,
};
var checkInStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >
DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >
DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
"ABSENT" :
"LATE" :
"NORMAL";
// process - รอทำใน queue
var checkin_process = new ProcessUserTimeStamp
{
KeycloakUserId = UserId != null ? Guid.Parse(UserId) : Guid.Empty,
CheckInLat = data.Lat,
CheckInLon = data.Lon,
IsLocationCheckIn = data.IsLocation,
CheckInLocationName = data.LocationName,
CheckInPOI = data.POI,
CheckInRemark = data.Remark,
CheckInImageUrl = fileName,
CheckIn = currentDate,
CheckInStatus = checkInStatus,
Prefix = profile.Prefix,
FirstName = profile.FirstName,
LastName = profile.LastName,
};
await _userTimeStampRepository.AddAsync(checkin);
await _processUserTimeStampRepository.AddAsync(checkin_process);
}
else
{
var checkout = await _userTimeStampRepository.GetByIdAsync(data.CheckInId.Value);
var currentCheckInProcess = await _processUserTimeStampRepository.GetTimestampByDateAsync(Guid.Parse(UserId), checkout.CheckIn.Date);
var checkout_process = await _processUserTimeStampRepository.GetByIdAsync(currentCheckInProcess.Id);
if (checkout != null)
{
checkout.CheckOutLat = data.Lat;
checkout.CheckOutLon = data.Lon;
checkout.IsLocationCheckOut = data.IsLocation;
checkout.CheckOutLocationName = data.LocationName;
checkout.CheckOutPOI = data.POI;
checkout.CheckOutRemark = data.Remark;
checkout.CheckOutImageUrl = fileName;
checkout.CheckOut = currentDate;
await _userTimeStampRepository.UpdateAsync(checkout);
}
else
{
return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound);
}
var checkOutStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) <
DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
"ABSENT" :
DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) <
DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
"ABSENT" :
"NORMAL";
if (checkout_process != null)
{
checkout_process.CheckOutLat = data.Lat;
checkout_process.CheckOutLon = data.Lon;
checkout_process.IsLocationCheckOut = data.IsLocation;
checkout_process.CheckOutLocationName = data.LocationName;
checkout_process.CheckOutPOI = data.POI;
checkout_process.CheckOutRemark = data.Remark;
checkout_process.CheckOutImageUrl = fileName;
checkout_process.CheckOut = currentDate;
checkout_process.CheckOutStatus = checkOutStatus;
await _processUserTimeStampRepository.UpdateAsync(checkout_process);
}
else
{
return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound);
}
}
return Success(new { date = currentDate });
}
/// <summary>
/// LV1_007 - ประวัติการลงเวลา (USER)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpGet("check-in/history")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> CheckInHistoryAsync(int year, int page = 1, int pageSize = 10, string keyword = "")
{
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken);
if (profile == null)
{
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
}
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
if (defaultRound == null)
{
return Error("ไม่พบรอบการลงเวลาทำงาน Default", StatusCodes.Status404NotFound);
}
var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id);
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
// TODO : รอดุึงรอบที่ผูกกับ user
var duty = userRound ?? defaultRound;
var checkin_base = DateTime.Parse($"{DateTime.Now.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}");
var checkout_base = DateTime.Parse($"{DateTime.Now.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}");
var data = (await _processUserTimeStampRepository.GetTimeStampHistoryAsync(userId, year))
.Select(d => new CheckInHistoryDto
{
CheckInId = d.Id,
CheckInDate = d.CheckIn.Date,
CheckInTime = d.CheckIn.ToString("HH:mm:ss"),
CheckInLocation = d.CheckInPOI,
CheckInStatus = d.CheckInStatus != null || d.CheckInStatus != "" ? d.CheckInStatus :
DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
"LATE" :
"NORMAL",
CheckInIsLocation = d.IsLocationCheckIn,
CheckInLocationName = d.CheckInLocationName,
CheckOutDate = d.CheckOut == null ? null : d.CheckOut.Value.Date,
CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm:ss"),
CheckOutLocation = d.CheckOutPOI ?? "",
CheckOutStatus = d.CheckOut == null ? null :
d.CheckOutStatus != null || d.CheckOutStatus != "" ? d.CheckOutStatus :
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
"LATE" :
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
"ABSENT" :
"NORMAL",
CheckOutIsLocation = d.IsLocationCheckOut,
CheckOutLocationName = d.CheckOutLocationName,
IsEdit = _processUserTimeStampRepository.IsEditRequest(userId, d.CheckIn.Date)
//IsEdit = (d.EditStatus != null && d.EditStatus != "")
//EditReason = d.EditReason ?? "",ß
//EditStatus = d.EditStatus ?? ""
})
.ToList();
if (keyword != "")
{
data = data.Where(x => (x.CheckInLocation.Contains(keyword) || x.CheckOutLocation.Contains(keyword))).ToList();
}
var pageData = data
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToList();
return Success(new { data = pageData, total = data.Count });
}
/// <summary>
/// LV1_010 - รายการลงเวลาปฏิบัติงาน (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpGet("log-record")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> LogRecordAsync([Required] DateTime startDate, [Required] DateTime endDate, int page = 1, int pageSize = 10, string keyword = "")
{
if (startDate.Date > endDate.Date)
{
return Error(new Exception("วันเริ่มต้นต้องมีค่าน้อยกว่าหรือเท่ากับวันสิ้นสุด"), StatusCodes.Status400BadRequest);
}
//var count = await _userTimeStampRepository.CountRecordAsync();
var imgUrl = $"{_configuration["MinIO:Endpoint"]}{_configuration["MinIO:BucketName"]}";
var data = (await _userTimeStampRepository.GetTimeStampHistoryForAdminAsync(startDate, endDate))
.Select(d => new CheckInHistoryForAdminDto
{
Id = d.Id,
//FullName = _userProfileRepository.GetUserFullName(d.KeycloakUserId, AccessToken),
FullName = $"{d.Prefix}{d.FirstName} {d.LastName}",
CheckInDate = d.CheckIn.Date,
CheckInTime = d.CheckIn.ToString("HH:mm:ss"),
CheckInLocation = d.CheckInPOI,
CheckInLat = d.CheckInLat,
CheckInLon = d.CheckInLon,
CheckInImage = $"{imgUrl}/{d.CheckInImageUrl}",
// add from new specification
IsLocationCheckIn = d.IsLocationCheckIn,
CheckInLocationName = d.CheckInLocationName ?? "",
CheckOutDate = d.CheckOut == null ? null : d.CheckOut.Value.Date,
CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm:ss"),
CheckOutLocation = d.CheckOut == null ? "" : d.CheckOutPOI,
CheckOutLat = d.CheckOut == null ? null : d.CheckOutLat,
CheckOutLon = d.CheckOut == null ? null : d.CheckOutLon,
CheckOutImage = d.CheckOut == null ? "" : $"{imgUrl}/{d.CheckOutImageUrl}",
// add from new specification
IsLocationCheckOut = d.IsLocationCheckOut,
CheckOutLocationName = d.CheckOutLocationName ?? ""
})
.ToList();
if (keyword != "")
{
data = data.Where(x => x.FullName.Contains(keyword)).ToList();
}
var pageData = data
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToList();
return Success(new { data = pageData, total = data.Count });
}
/// <summary>
/// LV1_011 - รายละเอียดการลงเวลาปฎิบัติงานรายบุคคล Tabรายากรลงเวลาที่ประมวลผลแล้ว (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpGet("time-record/{id:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> GetTimeRecordAsync([Required] Guid id)
{
var imgUrl = $"{_configuration["MinIO:Endpoint"]}{_configuration["MinIO:BucketName"]}";
var d = (await _processUserTimeStampRepository.GetTimeStampById(id));
if (d == null)
{
throw new Exception(GlobalMessages.DataNotFound);
}
else
{
var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(d.KeycloakUserId, AccessToken);
if (profile == null)
{
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
}
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
if (defaultRound == null)
{
return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound);
}
//var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty);
var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id);
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
var duty = userRound ?? defaultRound;
var result = new CheckInDetailForAdminDto
{
Id = d.Id,
FullName = $"{d.Prefix}{d.FirstName} {d.LastName}",
//FullName = $"{profile.Prefix}{profile.FirstName} {profile.LastName}", // _userProfileRepository.GetUserFullName(d.KeycloakUserId, AccessToken),
CheckInDate = d.CheckIn.Date,
CheckInTime = d.CheckIn.ToString("HH:mm"),
CheckInPOI = d.CheckInPOI,
CheckInLat = d.CheckInLat,
CheckInLon = d.CheckInLon,
CheckInImg = $"{imgUrl}/{d.CheckInImageUrl}",
CheckInStatus = d.CheckInStatus != null || d.CheckInStatus != "" ? d.CheckInStatus :
DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
DateTime.Parse($"{d.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
"ABSENT" :
"LATE" :
"NORMAL",
CheckInDescription = d.CheckInRemark ?? "",
IsLocationCheckIn = d.IsLocationCheckIn,
CheckInLocationName = d.CheckInLocationName ?? "",
CheckOutDate = d.CheckOut == null ? null : d.CheckOut.Value.Date,
CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm"),
CheckOutPOI = d.CheckOut == null ? "" : d.CheckOutPOI,
CheckOutLat = d.CheckOut == null ? null : d.CheckOutLat,
CheckOutLon = d.CheckOut == null ? null : d.CheckOutLon,
CheckOutImg = d.CheckOut == null ? "" : $"{imgUrl}/{d.CheckOutImageUrl}",
CheckOutStatus = d.CheckOut == null ? null :
d.CheckOutStatus != null || d.CheckOutStatus != "" ? d.CheckOutStatus :
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
"ABSENT" :
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
"ABSENT" :
"NORMAL",
CheckOutDescription = d.CheckOutRemark ?? "",
IsLocationCheckOut = d.IsLocationCheckOut,
CheckOutLocationName = d.CheckOutLocationName ?? ""
};
return Success(result);
}
}
/// <summary>
/// LV1_009 - รายการลงเวลาปฏิบัติงานที่ประมวลผลแล้ว (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpGet("time-record")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[AllowAnonymous]
public async Task<ActionResult<ResponseObject>> GetTimeRecordAsync([Required] DateTime startDate, [Required] DateTime endDate, int page = 1, int pageSize = 10, string status = "NORMAL", string keyword = "")
{
if (startDate.Date > endDate.Date)
{
return Error(new Exception("วันเริ่มต้นต้องมีค่าน้อยกว่าหรือเท่ากับวันสิ้นสุด"), StatusCodes.Status400BadRequest);
}
var profiles = new List<GetProfileByKeycloakIdDto>();
//var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
//var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken);
//if (profile == null)
//{
// return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
//}
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
if (defaultRound == null)
{
return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound);
}
//var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty);
//var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id);
//var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
//var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
//var duty = userRound ?? defaultRound;
var duty = defaultRound;
var checkin_base = DateTime.Parse($"{DateTime.Now.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}");
var checkout_base = DateTime.Parse($"{DateTime.Now.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}");
//var count = await _processUserTimeStampRepository.GetTimeStampHistoryForAdminCountAsync(startDate, endDate);
var imgUrl = $"{_configuration["MinIO:Endpoint"]}{_configuration["MinIO:BucketName"]}";
var resultData = await _processUserTimeStampRepository.GetTimeStampHistoryForAdminAsync(startDate, endDate);
var data = new List<CheckInProcessHistoryForAdminDto>();
foreach (var d in resultData)
{
//var pf = profiles.FirstOrDefault(x => x.Keycloak == d.KeycloakUserId);
//if (pf == null)
//{
// pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(d.KeycloakUserId, AccessToken);
// if (pf == null)
// continue;
// else
// profiles.Add(pf);
//}
//var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(d.KeycloakUserId, AccessToken);
//if (pf == null) continue;
data.Add(new CheckInProcessHistoryForAdminDto
{
Id = d.Id,
FullName = $"{d.Prefix}{d.FirstName} {d.LastName}",
CheckInDate = d.CheckIn.Date,
CheckInTime = d.CheckIn.ToString("HH:mm"),
CheckInLocation = d.CheckInPOI,
CheckInLat = d.CheckInLat,
CheckInLon = d.CheckInLon,
CheckInStatus = d.CheckInStatus != "" ? d.CheckInStatus :
DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
DateTime.Parse($"{d.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
"ABSENT" :
"LATE" :
"NORMAL",
CheckInIsLocation = d.IsLocationCheckIn,
CheckInLocationName = d.CheckInLocationName,
//CheckInImageUrl = $"{imgUrl}/{d.CheckInImageUrl}",
CheckOutDate = d.CheckOut == null ? null : d.CheckOut.Value.Date,
CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm"),
CheckOutLocation = d.CheckOut == null ? "" : d.CheckOutPOI,
CheckOutLat = d.CheckOut == null ? null : d.CheckOutLat,
CheckOutLon = d.CheckOut == null ? null : d.CheckOutLon,
CheckOutStatus = d.CheckOutStatus != "" ? d.CheckOutStatus :
d.CheckOut == null ? null : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
"ABSENT" :
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
"ABSENT" :
"NORMAL",
CheckOutIsLocation = d.IsLocationCheckOut,
CheckOutLocationName = d.CheckOutLocationName
//CheckOutImageUrl = d.CheckOut == null ? "" : $"{imgUrl}/{d.CheckOutImageUrl}",
});
}
// var data = (await _processUserTimeStampRepository.GetTimeStampHistoryForAdminAsync(startDate, endDate))
// .Select(d => new CheckInProcessHistoryForAdminDto
// {
// Id = d.Id,
// FullName = _userProfileRepository.GetUserFullName(d.KeycloakUserId, AccessToken),
// CheckInDate = d.CheckIn.Date,
// CheckInTime = d.CheckIn.ToString("HH:mm"),
// CheckInLocation = d.CheckInPOI,
// CheckInLat = d.CheckInLat,
// CheckInLon = d.CheckInLon,
// CheckInStatus = d.CheckInStatus != "" ? d.CheckInStatus :
// DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
// DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
// DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
// DateTime.Parse($"{d.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
// "ABSENT" :
// "LATE" :
// "NORMAL",
// CheckInIsLocation = d.IsLocationCheckIn,
// CheckInLocationName = d.CheckInLocationName,
// //CheckInImageUrl = $"{imgUrl}/{d.CheckInImageUrl}",
// CheckOutDate = d.CheckOut == null ? null : d.CheckOut.Value.Date,
// CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm"),
// CheckOutLocation = d.CheckOut == null ? "" : d.CheckOutPOI,
// CheckOutLat = d.CheckOut == null ? null : d.CheckOutLat,
// CheckOutLon = d.CheckOut == null ? null : d.CheckOutLon,
// CheckOutStatus = d.CheckOutStatus != "" ? d.CheckOutStatus :
// d.CheckOut == null ? null : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
// DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
// "ABSENT" :
// DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
// DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
// "ABSENT" :
// "NORMAL",
// CheckOutIsLocation = d.IsLocationCheckOut,
// CheckOutLocationName = d.CheckOutLocationName
// //CheckOutImageUrl = d.CheckOut == null ? "" : $"{imgUrl}/{d.CheckOutImageUrl}",
// })
// .ToList();
if (keyword != "")
{
data = data.Where(x => x.FullName.Contains(keyword)).ToList();
}
if (status.Trim().ToUpper() != "ALL")
{
data = data.Where(x => x.CheckInStatus == status || x.CheckOutStatus == status).ToList();
}
var pageData = data
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToList();
return Success(new { data = pageData, total = data.Count });
}
#endregion
#region " เปลี่ยนรอบการทำงาน "
/// <summary>
/// LV1_006 - เช็คเวลาต้องลงเวลาเข้าหรือออกงาน (USER)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("search")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> SearchProfileAsync([FromBody] DTOs.ChangeRound.SearchProfileDto req)
{
var profile = await _userProfileRepository.SearchProfile(req.CitizenId, req.FirstName, req.LastName, AccessToken ?? "");
var pagedProfile = profile.Skip((req.Page - 1) * req.PageSize).Take(req.PageSize).ToList();
var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync();
var resultSet = new List<SearchProfileResultDto>();
foreach (var p in pagedProfile)
{
var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id);
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
var duty = userRound ?? getDefaultRound;
var res = new SearchProfileResultDto
{
ProfileId = p.Id,
CitizenId = p.CitizenId ?? "",
FullName = $"{p.Prefix ?? ""}{p.FirstName ?? ""} {p.LastName ?? ""}",
StartTimeMorning = duty.StartTimeMorning,
LeaveTimeAfterNoon = duty.EndTimeAfternoon,
EffectiveDate = effectiveDate == null ? null : effectiveDate.EffectiveDate.Value.Date
};
resultSet.Add(res);
}
return Success(new { data = resultSet, total = profile.Count });
}
/// <summary>
/// LV1_014 - เปลี่ยนรอบการลงเวลา (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("round")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> CreateChangeRoundAsync([FromBody] CreateChangeRoundDto req)
{
var currentDate = DateTime.Now.Date;
if (req.EffectiveDate.Date < currentDate)
{
return Error(new Exception($"วันที่มีผลต้องมากกว่าหรือเท่ากับวันที่ปัจจุบัน({currentDate.ToString("yyyy-MM-dd")})"), StatusCodes.Status400BadRequest);
}
var old = await _userDutyTimeRepository.GetExist(req.ProfileId, req.EffectiveDate);
if (old != null)
{
return Error(new Exception("ไม่สามารถทำรายการได้ เนื่องจากมีการกำหนดรอบการทำงานในวันที่นี้ไว้แล้ว"), StatusCodes.Status400BadRequest);
}
var data = new UserDutyTime
{
ProfileId = req.ProfileId,
DutyTimeId = req.RoundId,
EffectiveDate = req.EffectiveDate,
Remark = req.Remark,
};
await _userDutyTimeRepository.AddAsync(data);
return Success();
}
/// <summary>
/// LV1_015 - ประวัติการเปลี่ยนรอบการลงเวลา (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpGet("round/{id:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> GetChangeRoundHistoryByProfileIdAsync(Guid id, int page = 1, int pageSize = 10, string keyword = "")
{
var data = await _userDutyTimeRepository.GetListByProfileIdAsync(id);
var resultSet = new List<ChangeRoundHistoryDto>();
if (data != null)
{
resultSet = data
.GroupBy(item => item.ProfileId)
.SelectMany(group => group
.OrderBy(item => item.EffectiveDate) // เรียงลำดับตาม property ที่คุณต้องการ
.Select((item, index) => new ChangeRoundHistoryDto
{
Round = index + 1,
StartTimeMorning = item.DutyTime.StartTimeMorning,
LeaveTimeAfternoon = item.DutyTime.EndTimeAfternoon,
EffectiveDate = item.EffectiveDate.Value,
Remark = item.Remark
}))
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToList();
}
return Success(new { data = resultSet, total = data.Count });
}
#endregion
#region " ขอลงเวลาเป็นกรณีพิเศษ "
/// <summary>
/// LV1_017 - เพิ่มลงเวลากรณีพิเศษ (USER)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("user/edit")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> CreateAdditionalCheckRequestAsync([FromBody] CreateAdditionalCheckRequestDto req)
{
if (req.CheckDate.Date > DateTime.Now.Date)
{
return Error("ไม่สามารถขอลงเวลากรณีพิเศษในวันที่มากกว่าวันที่ปัจจุบันได้", StatusCodes.Status400BadRequest);
}
var userId = UserId != null ? Guid.Parse(UserId) : Guid.Empty;
var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken);
if (profile == null)
{
throw new Exception(GlobalMessages.DataNotFound);
}
var request = new AdditionalCheckRequest
{
KeycloakUserId = UserId != null ? Guid.Parse(UserId) : Guid.Empty,
CheckDate = req.CheckDate,
CheckInEdit = req.CheckInEdit,
CheckOutEdit = req.CheckOutEdit,
Description = req.Description,
Prefix = profile.Prefix,
FirstName = profile.FirstName,
LastName = profile.LastName,
};
await _additionalCheckRequestRepository.AddAsync(request);
return Success();
}
/// <summary>
/// LV1_018 - รายการลงเวลากรณีพิเศษ (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpGet("admin/edit")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> GetAdditionalCheckRequestAsync([Required] int year, [Required] int month, [Required] int page = 1, [Required] int pageSize = 10, string keyword = "")
{
var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequests(year, month);
var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync();
if (getDefaultRound == null)
{
return Error("ไม่พบรอบลงเวลา Default", StatusCodes.Status404NotFound);
}
var result = new List<GetAdditionalCheckRequestDto>();
foreach (var data in rawData)
{
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken);
// if (profile == null)
// {
// return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
// }
//var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty);
//var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id);
//var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
//var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
var checkInData = await _userTimeStampRepository.GetTimestampByDateAsync(data.KeycloakUserId, data.CheckDate);
//var duty = userRound ?? getDefaultRound;
var duty = getDefaultRound;
// create result object to return
var resObj = new GetAdditionalCheckRequestDto
{
Id = data.Id,
FullName = $"{data.Prefix}{data.FirstName} {data.LastName}",
//FullName = $"{profile.Prefix}{profile.FirstName} {profile.LastName}",
CreatedAt = data.CreatedAt,
CheckDate = data.CheckDate,
CheckInEdit = data.CheckInEdit,
CheckOutEdit = data.CheckOutEdit,
CheckInTime = checkInData == null ? "00:00" : checkInData.CheckIn.ToString("HH:mm"),
CheckOutTime = checkInData == null ? "00:00" : checkInData.CheckOut == null ? "00:00" : checkInData.CheckOut.Value.ToString("HH:mm"),
CheckInStatus = checkInData == null ? null :
DateTime.Parse(checkInData.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
DateTime.Parse($"{checkInData.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
DateTime.Parse(checkInData.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
DateTime.Parse($"{checkInData.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
"ABSENT" :
"LATE" :
"NORMAL",
CheckOutStatus = checkInData == null ? null :
checkInData.CheckOut == null ? null :
DateTime.Parse(checkInData.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
DateTime.Parse($"{checkInData.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
"ABSENT" :
DateTime.Parse(checkInData.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
DateTime.Parse($"{checkInData.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
"ABSENT" :
"NORMAL",
StartTimeMorning = duty.StartTimeMorning,
EndTimeMorning = duty.EndTimeMorning,
StartTimeAfternoon = duty.StartTimeAfternoon,
EndTimeAfternoon = duty.EndTimeAfternoon,
Reason = data.Comment ?? "",
Status = data.Status,
Description = data.Description,
StatusSort = data.Status.Trim().ToLower() == "pending" ? 1 :
data.Status.Trim().ToLower() == "approve" ? 2 : 3,
};
result.Add(resObj);
}
if (keyword != "")
{
result = result.Where(x => x.FullName.Contains(keyword)).ToList();
}
var pageResult = result.Skip((page - 1) * pageSize).Take(pageSize)
.OrderBy(x => x.StatusSort)
.ToList();
return Success(new { data = pageResult, total = result.Count });
}
/// <summary>
/// LV1_019 - อนุมัติลงเวลากรณีพิเศษ (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPut("admin/edit/approve/{id:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> ApproveRequestAsync(Guid id, [FromBody] ApproveRequestDto req)
{
if (req.Reason == null || req.Reason == string.Empty)
{
return Error("กรุณากรอกเหตุผล", StatusCodes.Status400BadRequest);
}
var requestData = await _additionalCheckRequestRepository.GetByIdAsync(id);
if (requestData == null)
{
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
}
requestData.Status = "APPROVE";
requestData.Comment = req.Reason;
await _additionalCheckRequestRepository.UpdateAsync(requestData);
// change user timestamp
var processTimeStamp = await _processUserTimeStampRepository.GetTimestampByDateAsync(requestData.KeycloakUserId, requestData.CheckDate.Date);
if (processTimeStamp == null)
{
var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(requestData.KeycloakUserId, AccessToken);
processTimeStamp = new ProcessUserTimeStamp
{
KeycloakUserId = requestData.KeycloakUserId,
CheckIn = DateTime.Parse($"{requestData.CheckDate.Date.ToString("yyyy-MM-dd")} {req.CheckInTime}"),
CheckOut = DateTime.Parse($"{requestData.CheckDate.Date.ToString("yyyy-MM-dd")} {req.CheckOutTime}"),
CheckInRemark = req.Reason,
CheckOutRemark = req.Reason,
CheckInLat = 0,
CheckInLon = 0,
CheckOutLat = 0,
CheckOutLon = 0,
CheckInPOI = "ลงเวลากรณีพิเศษ",
CheckOutPOI = "ลงเวลากรณีพิเศษ",
CheckInStatus = req.CheckInStatus,
CheckOutStatus = req.CheckOutStatus,
Prefix = profile.Prefix,
FirstName = profile.FirstName,
LastName = profile.LastName,
};
processTimeStamp.EditStatus = "APPROVE";
processTimeStamp.EditReason = req.Reason;
await _processUserTimeStampRepository.AddAsync(processTimeStamp);
}
else
{
if (requestData.CheckInEdit)
{
processTimeStamp.CheckIn = DateTime.Parse($"{requestData.CheckDate.Date.ToString("yyyy-MM-dd")} {req.CheckInTime}");
processTimeStamp.CheckInRemark = req.Reason;
processTimeStamp.CheckInLat = 0;
processTimeStamp.CheckInLon = 0;
processTimeStamp.CheckInPOI = "ลงเวลากรณีพิเศษ";
processTimeStamp.CheckInStatus = req.CheckInStatus;
}
if (requestData.CheckOutEdit)
{
processTimeStamp.CheckOut = DateTime.Parse($"{requestData.CheckDate.Date.ToString("yyyy-MM-dd")} {req.CheckOutTime}");
processTimeStamp.CheckOutRemark = req.Reason;
processTimeStamp.CheckOutLat = 0;
processTimeStamp.CheckOutLon = 0;
processTimeStamp.CheckOutPOI = "ลงเวลากรณีพิเศษ";
processTimeStamp.CheckOutStatus = req.CheckOutStatus;
}
processTimeStamp.EditStatus = "APPROVE";
processTimeStamp.EditReason = req.Reason;
await _processUserTimeStampRepository.UpdateAsync(processTimeStamp);
}
return Success();
}
/// <summary>
/// LV1_020 - ไม่อนุมัติลงเวลากรณีพิเศษ (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPut("admin/edit/reject/{id:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> RejectRequestAsync(Guid id, [FromBody] RejectRequestDto req)
{
if (req.Reason == null || req.Reason == string.Empty)
{
return Error("กรุณากรอกเหตุผล", StatusCodes.Status400BadRequest);
}
var requestData = await _additionalCheckRequestRepository.GetByIdAsync(id);
if (requestData == null)
{
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
}
// change user timestamp
var processTimeStamp = await _processUserTimeStampRepository.GetTimestampByDateAsync(requestData.KeycloakUserId, requestData.CheckDate.Date);
if (processTimeStamp != null)
{
processTimeStamp.EditStatus = "REJECT";
processTimeStamp.EditReason = req.Reason;
await _processUserTimeStampRepository.UpdateAsync(processTimeStamp);
}
requestData.Status = "REJECT";
requestData.Comment = req.Reason;
await _additionalCheckRequestRepository.UpdateAsync(requestData);
return Success();
}
/// <summary>
/// LV1_021 - รายละเอียดการลงเวลาปฎิบัติงานรายบุคคล Tabรายการลงเวลา (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpGet("log-record/{id:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> GetLogRecordAsync([Required] Guid id)
{
var imgUrl = $"{_configuration["MinIO:Endpoint"]}{_configuration["MinIO:BucketName"]}";
var d = (await _userTimeStampRepository.GetTimeStampById(id));
if (d == null)
{
throw new Exception(GlobalMessages.DataNotFound);
}
else
{
var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(d.KeycloakUserId, AccessToken);
if (profile == null)
{
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
}
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
if (defaultRound == null)
{
return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound);
}
//var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty);
var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id);
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
var duty = userRound ?? defaultRound;
var result = new CheckInDetailForAdminDto
{
Id = d.Id,
FullName = $"{d.Prefix}{d.FirstName} {d.LastName}",
//FullName = _userProfileRepository.GetUserFullName(d.KeycloakUserId, AccessToken),
CheckInDate = d.CheckIn.Date,
CheckInTime = d.CheckIn.ToString("HH:mm"),
CheckInPOI = d.CheckInPOI,
CheckInLat = d.CheckInLat,
CheckInLon = d.CheckInLon,
CheckInImg = $"{imgUrl}/{d.CheckInImageUrl}",
CheckInStatus = DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
DateTime.Parse($"{d.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
"ABSENT" :
"LATE" :
"NORMAL",
CheckInDescription = d.CheckInRemark ?? "",
CheckOutDate = d.CheckOut == null ? null : d.CheckOut.Value.Date,
CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm"),
CheckOutPOI = d.CheckOut == null ? "" : d.CheckOutPOI,
CheckOutLat = d.CheckOut == null ? null : d.CheckOutLat,
CheckOutLon = d.CheckOut == null ? null : d.CheckOutLon,
CheckOutImg = d.CheckOut == null ? "" : $"{imgUrl}/{d.CheckOutImageUrl}",
CheckOutStatus = d.CheckOut == null ? null :
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
"ABSENT" :
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
"ABSENT" :
"NORMAL",
CheckOutDescription = d.CheckOutRemark ?? "",
};
return Success(result);
}
}
/// <summary>
/// LV1_022 - ประวัติการยื่นขอลงเวลาพิเศษ (USER)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpGet("edit/history")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> GetAdditionalCheckRequestHistoryAsync([Required] int year, [Required] int month, [Required] int page = 1, [Required] int pageSize = 10, string keyword = "")
{
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequestsByUserId(userId, year, month);
var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync();
if (getDefaultRound == null)
{
return Error("ไม่พบรอบลงเวลา Default", StatusCodes.Status404NotFound);
}
var result = new List<GetAdditionalCheckRequestHistoryDto>();
foreach (var data in rawData)
{
var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken);
if (profile == null)
{
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
}
//var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty);
var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id);
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
var checkInData = await _userTimeStampRepository.GetTimestampByDateAsync(data.KeycloakUserId, data.CheckDate);
var duty = userRound ?? getDefaultRound;
DateTime? resultCheckInDate, resultCheckOutDate;
string resultCheckInTime, resultCheckOutTime;
if (data.CheckInEdit)
{
resultCheckInDate = data.CheckDate.Date;
resultCheckInTime = duty.StartTimeMorning;
}
else
{
resultCheckInDate = checkInData == null ? null : checkInData.CheckIn;
resultCheckInTime = checkInData == null ? "00:00" : checkInData.CheckIn.ToString("HH:mm");
}
if (data.CheckOutEdit)
{
resultCheckOutDate = data.CheckDate.Date;
resultCheckOutTime = duty.EndTimeAfternoon;
}
else
{
resultCheckOutDate = checkInData == null ? null :
checkInData.CheckOut == null ? null :
checkInData.CheckOut.Value.Date;
resultCheckOutTime = checkInData == null ? "00:00" :
checkInData.CheckOut == null ? "00:00" :
checkInData.CheckOut.Value.ToString("HH:mm");
}
// create result object to return
var resObj = new GetAdditionalCheckRequestHistoryDto
{
Id = data.Id,
CheckInDate = resultCheckInDate,
CheckOutDate = resultCheckOutDate,
CheckInTime = resultCheckInTime,
CheckOutTime = resultCheckOutTime,
CheckInStatus = checkInData == null ? null :
DateTime.Parse(checkInData.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
DateTime.Parse($"{checkInData.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
DateTime.Parse(checkInData.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
DateTime.Parse($"{checkInData.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
"ABSENT" :
"LATE" :
"NORMAL",
CheckOutStatus = checkInData == null ? null :
checkInData.CheckOut == null ? null :
DateTime.Parse(checkInData.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
DateTime.Parse($"{checkInData.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
"ABSENT" :
DateTime.Parse(checkInData.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
DateTime.Parse($"{checkInData.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
"ABSENT" :
"NORMAL",
CheckInLocation = checkInData == null ? "" : checkInData.CheckInPOI,
CheckOutLocation = checkInData == null ? "" : checkInData.CheckOutPOI ?? "",
EditReason = data.Comment ?? "",
EditStatus = data.Status,
};
result.Add(resObj);
}
if (keyword != "")
{
result = result.Where(x => x.EditReason!.Contains(keyword)).ToList();
}
var pageResult = result.Skip((page - 1) * pageSize).Take(pageSize)
.ToList();
return Success(new { data = pageResult, total = result.Count });
}
#endregion
#region " ปฏิทินการทำงานของ ขรก. "
/// <summary>
/// LV1_023 - แสดงปฏิทินวันทำงานรายคน (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpGet("admin/work/{id:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> GetCalendarByProfileAsync(Guid id)
{
var data = await _userCalendarRepository.GetExist(id);
if (data == null)
return Success(new { Work = "NORMAL" });
else
return Success(new { Work = data.Calendar });
}
/// <summary>
/// LV1_024 - บันทึกแก้ไขปฏิทินวันทำงาน (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPut("admin/work/{id:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> UpdateCalendarByProfileAsync(Guid id, [FromBody] UpdateCalendarDto req)
{
var data = await _userCalendarRepository.GetExist(id);
if (data != null)
{
data.Calendar = req.Work;
await _userCalendarRepository.UpdateAsync(data);
return Success();
}
else
{
data = new UserCalendar
{
ProfileId = id,
Calendar = req.Work
};
await _userCalendarRepository.AddAsync(data);
return Success();
}
}
#endregion
#region " แก้ไขสถานะการลงเวลา "
/// <summary>
/// LV1_025 - บันทึกแก้ไขสถานะการเข้า-ออกงาน (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPut("admin/edit/checkin/{id:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> EditCheckInStatusAsync(Guid id, [FromBody] EditCheckInStatusDto req)
{
var data = await _processUserTimeStampRepository.GetByIdAsync(id);
if (data == null)
return Error(GlobalMessages.DataNotFound);
data.CheckInStatus = req.CheckInStatus;
data.CheckOutStatus = req.CheckOutStatus;
data.EditReason = req.Reason;
await _processUserTimeStampRepository.UpdateAsync(data);
return Success();
}
#endregion
/// <summary>
/// LV1_026 - รายการลารายบุคคล (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPut("admin/summary/keycloak/{id:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> GetLeaveSummaryByProfileAsync(Guid id, [FromBody] GetLeaveSummaryDto req)
{
var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(id, AccessToken);
var thisYear = DateTime.Now.Year;
var startDate = req.StartDate;
var endDate = req.EndDate;
var leaveDayCount = await _leaveRequestRepository.GetSumApproveLeaveByRangeForUser(id, startDate, endDate);
var timeStamps = await _processUserTimeStampRepository.GetTimeStampHistoryByRangeForUserAsync(id, startDate, endDate);
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
if (defaultRound == null)
{
return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound);
}
var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id);
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
var duty = userRound ?? defaultRound;
var processTimeStamps = timeStamps
.Select(d => new
{
d.Id,
CheckInStatus = DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
"LATE" :
"NORMAL",
CheckOutStatus = d.CheckOut == null ? "" :
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
"LATE" :
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
"ABSENT" :
"NORMAL",
});
var absentCount = processTimeStamps.Count(x => x.CheckOutStatus == "ABSENT");
var lateCount = processTimeStamps.Count(x => x.CheckInStatus == "LATE");
return Success(new
{
leave = leaveDayCount,
late = lateCount
});
}
#endregion
}
}