2024-08-20 10:37:47 +07:00
|
|
|
|
using BMA.EHR.Application.Repositories;
|
2024-01-17 12:42:19 +07:00
|
|
|
|
using BMA.EHR.Application.Repositories.Commands;
|
2024-03-08 13:22:22 +07:00
|
|
|
|
using BMA.EHR.Application.Repositories.Leaves.LeaveRequests;
|
2023-11-10 14:40:53 +07:00
|
|
|
|
using BMA.EHR.Application.Repositories.Leaves.TimeAttendants;
|
2024-10-30 13:41:12 +07:00
|
|
|
|
using BMA.EHR.Application.Repositories.MessageQueue;
|
2026-04-17 15:35:29 +07:00
|
|
|
|
using BMA.EHR.Application.Responses.Leaves;
|
2024-07-11 12:44:56 +07:00
|
|
|
|
using BMA.EHR.Application.Responses.Profiles;
|
2023-11-09 14:35:08 +07:00
|
|
|
|
using BMA.EHR.Domain.Common;
|
2026-04-17 15:35:29 +07:00
|
|
|
|
using BMA.EHR.Domain.Extensions;
|
2023-11-10 14:40:53 +07:00
|
|
|
|
using BMA.EHR.Domain.Models.Leave.TimeAttendants;
|
2026-02-05 11:57:19 +07:00
|
|
|
|
using BMA.EHR.Domain.Models.Notifications;
|
2023-11-09 14:35:08 +07:00
|
|
|
|
using BMA.EHR.Domain.Shared;
|
|
|
|
|
|
using BMA.EHR.Infrastructure.Persistence;
|
2023-11-24 11:23:18 +07:00
|
|
|
|
using BMA.EHR.Leave.Service.DTOs.AdditionalCheck;
|
2024-01-09 10:25:18 +07:00
|
|
|
|
using BMA.EHR.Leave.Service.DTOs.Calendar;
|
2023-11-23 15:48:09 +07:00
|
|
|
|
using BMA.EHR.Leave.Service.DTOs.ChangeRound;
|
2023-11-15 15:38:39 +07:00
|
|
|
|
using BMA.EHR.Leave.Service.DTOs.CheckIn;
|
|
|
|
|
|
using BMA.EHR.Leave.Service.DTOs.DutyTime;
|
2024-03-08 13:22:22 +07:00
|
|
|
|
using BMA.EHR.Leave.Service.DTOs.LeaveRequest;
|
2025-10-06 15:12:40 +07:00
|
|
|
|
using iTextSharp.text;
|
2023-11-09 14:35:08 +07:00
|
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
2025-03-21 13:01:17 +07:00
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
2024-08-20 10:37:47 +07:00
|
|
|
|
using Microsoft.Extensions.ObjectPool;
|
2025-10-06 15:12:40 +07:00
|
|
|
|
using Nest;
|
2024-08-19 16:00:00 +07:00
|
|
|
|
using Newtonsoft.Json;
|
2024-09-24 11:53:09 +07:00
|
|
|
|
using Newtonsoft.Json.Linq;
|
2024-08-19 16:00:00 +07:00
|
|
|
|
using RabbitMQ.Client;
|
2025-01-27 13:32:57 +07:00
|
|
|
|
using RabbitMQ.Client.Events;
|
2023-11-09 14:35:08 +07:00
|
|
|
|
using Swashbuckle.AspNetCore.Annotations;
|
2023-11-15 14:02:21 +07:00
|
|
|
|
using System.ComponentModel.DataAnnotations;
|
2025-10-08 17:23:10 +07:00
|
|
|
|
using System.Diagnostics;
|
2023-11-09 14:35:08 +07:00
|
|
|
|
using System.Security.Claims;
|
2024-08-19 16:00:00 +07:00
|
|
|
|
using System.Text;
|
2025-01-27 13:32:57 +07:00
|
|
|
|
using System.Threading.Tasks;
|
2024-07-11 13:06:52 +07:00
|
|
|
|
using SearchProfileResultDto = BMA.EHR.Leave.Service.DTOs.ChangeRound.SearchProfileResultDto;
|
2023-11-09 14:35:08 +07:00
|
|
|
|
|
2023-11-15 15:38:39 +07:00
|
|
|
|
namespace BMA.EHR.Leave.Service.Controllers
|
2023-11-09 14:35:08 +07:00
|
|
|
|
{
|
2023-11-10 14:40:53 +07:00
|
|
|
|
[Route("api/v{version:apiVersion}/leave")]
|
2023-11-09 14:35:08 +07:00
|
|
|
|
[ApiVersion("1.0")]
|
|
|
|
|
|
[ApiController]
|
|
|
|
|
|
[Produces("application/json")]
|
|
|
|
|
|
[Authorize]
|
2023-11-10 14:40:53 +07:00
|
|
|
|
[SwaggerTag("API ระบบลงเวลาและการลา")]
|
|
|
|
|
|
public class LeaveController : BaseController
|
2023-11-09 14:35:08 +07:00
|
|
|
|
{
|
|
|
|
|
|
#region " Fields "
|
|
|
|
|
|
|
2023-11-10 14:40:53 +07:00
|
|
|
|
private readonly DutyTimeRepository _dutyTimeRepository;
|
2023-11-09 14:35:08 +07:00
|
|
|
|
private readonly LeaveDbContext _context;
|
2026-02-05 11:57:19 +07:00
|
|
|
|
private readonly ApplicationDBContext _appDbContext;
|
2023-11-09 14:35:08 +07:00
|
|
|
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
|
|
|
|
|
private readonly IWebHostEnvironment _hostingEnvironment;
|
|
|
|
|
|
private readonly IConfiguration _configuration;
|
|
|
|
|
|
private readonly UserProfileRepository _userProfileRepository;
|
2023-11-10 14:40:53 +07:00
|
|
|
|
private readonly UserTimeStampRepository _userTimeStampRepository;
|
2023-11-13 15:26:14 +07:00
|
|
|
|
private readonly MinIOService _minIOService;
|
2023-11-23 10:35:55 +07:00
|
|
|
|
private readonly ProcessUserTimeStampRepository _processUserTimeStampRepository;
|
2023-11-23 15:48:09 +07:00
|
|
|
|
private readonly UserDutyTimeRepository _userDutyTimeRepository;
|
2023-11-24 11:23:18 +07:00
|
|
|
|
private readonly AdditionalCheckRequestRepository _additionalCheckRequestRepository;
|
2024-03-08 13:22:22 +07:00
|
|
|
|
private readonly LeaveRequestRepository _leaveRequestRepository;
|
2024-01-09 10:25:18 +07:00
|
|
|
|
private readonly UserCalendarRepository _userCalendarRepository;
|
2024-09-24 11:53:09 +07:00
|
|
|
|
private readonly PermissionRepository _permission;
|
2026-01-20 10:49:13 +07:00
|
|
|
|
private readonly CheckInJobStatusRepository _checkInJobStatusRepository;
|
2024-01-09 10:25:18 +07:00
|
|
|
|
|
2024-01-17 12:42:19 +07:00
|
|
|
|
private readonly CommandRepository _commandRepository;
|
|
|
|
|
|
|
2024-10-30 13:41:12 +07:00
|
|
|
|
private readonly NotificationRepository _notificationRepository;
|
|
|
|
|
|
|
2023-11-13 15:26:14 +07:00
|
|
|
|
private readonly string _bucketName = "check-in";
|
2023-11-09 14:35:08 +07:00
|
|
|
|
|
2024-08-20 10:37:47 +07:00
|
|
|
|
private readonly ObjectPool<IModel> _objectPool;
|
|
|
|
|
|
private readonly string _fakeCheckInQueue = "fake-bma-checkin-queue";
|
|
|
|
|
|
private readonly string _realCheckInQueue = "bma-checkin-queue";
|
|
|
|
|
|
|
2025-01-27 13:32:57 +07:00
|
|
|
|
private readonly HttpClient _httpClient;
|
|
|
|
|
|
|
2026-03-30 09:52:27 +07:00
|
|
|
|
private readonly LeaveProcessJobStatusRepository _leaveProcessJobStatusRepository;
|
|
|
|
|
|
|
2023-11-09 14:35:08 +07:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region " Constuctor and Destructor "
|
|
|
|
|
|
|
2023-11-10 14:40:53 +07:00
|
|
|
|
public LeaveController(DutyTimeRepository dutyTimeRepository,
|
2023-11-09 14:35:08 +07:00
|
|
|
|
LeaveDbContext context,
|
|
|
|
|
|
IHttpContextAccessor httpContextAccessor,
|
|
|
|
|
|
IWebHostEnvironment hostingEnvironment,
|
|
|
|
|
|
IConfiguration configuration,
|
2023-11-10 14:40:53 +07:00
|
|
|
|
UserProfileRepository userProfileRepository,
|
2023-11-13 15:26:14 +07:00
|
|
|
|
UserTimeStampRepository userTimeStampRepository,
|
2023-11-23 10:35:55 +07:00
|
|
|
|
MinIOService minIOService,
|
2023-11-23 15:48:09 +07:00
|
|
|
|
ProcessUserTimeStampRepository processUserTimeStampRepository,
|
2023-11-24 11:23:18 +07:00
|
|
|
|
UserDutyTimeRepository userDutyTimeRepository,
|
2024-01-09 10:25:18 +07:00
|
|
|
|
AdditionalCheckRequestRepository additionalCheckRequestRepository,
|
2024-01-17 12:42:19 +07:00
|
|
|
|
UserCalendarRepository userCalendarRepository,
|
2024-03-08 13:22:22 +07:00
|
|
|
|
CommandRepository commandRepository,
|
2024-08-20 10:37:47 +07:00
|
|
|
|
LeaveRequestRepository leaveRequestRepository,
|
2024-09-24 11:53:09 +07:00
|
|
|
|
ObjectPool<IModel> objectPool,
|
2024-10-30 13:41:12 +07:00
|
|
|
|
PermissionRepository permission,
|
2025-01-27 13:32:57 +07:00
|
|
|
|
NotificationRepository notificationRepository,
|
2026-01-20 10:49:13 +07:00
|
|
|
|
CheckInJobStatusRepository checkInJobStatusRepository,
|
2026-02-05 11:57:19 +07:00
|
|
|
|
HttpClient httpClient,
|
2026-03-30 09:52:27 +07:00
|
|
|
|
ApplicationDBContext appDbContext,
|
|
|
|
|
|
LeaveProcessJobStatusRepository leaveProcessJobStatusRepository)
|
2023-11-09 14:35:08 +07:00
|
|
|
|
{
|
2023-11-10 14:40:53 +07:00
|
|
|
|
_dutyTimeRepository = dutyTimeRepository;
|
2023-11-09 14:35:08 +07:00
|
|
|
|
_context = context;
|
2026-02-05 11:57:19 +07:00
|
|
|
|
_appDbContext = appDbContext;
|
2023-11-09 14:35:08 +07:00
|
|
|
|
_httpContextAccessor = httpContextAccessor;
|
|
|
|
|
|
_hostingEnvironment = hostingEnvironment;
|
|
|
|
|
|
_configuration = configuration;
|
|
|
|
|
|
_userProfileRepository = userProfileRepository;
|
2023-11-10 14:40:53 +07:00
|
|
|
|
_userTimeStampRepository = userTimeStampRepository;
|
2023-11-13 15:26:14 +07:00
|
|
|
|
_minIOService = minIOService;
|
2023-11-23 10:35:55 +07:00
|
|
|
|
_processUserTimeStampRepository = processUserTimeStampRepository;
|
2023-11-23 15:48:09 +07:00
|
|
|
|
_userDutyTimeRepository = userDutyTimeRepository;
|
2023-11-24 11:23:18 +07:00
|
|
|
|
_additionalCheckRequestRepository = additionalCheckRequestRepository;
|
2024-01-09 10:25:18 +07:00
|
|
|
|
_userCalendarRepository = userCalendarRepository;
|
2024-01-17 12:42:19 +07:00
|
|
|
|
_commandRepository = commandRepository;
|
2024-03-08 13:22:22 +07:00
|
|
|
|
_leaveRequestRepository = leaveRequestRepository;
|
2024-10-30 13:41:12 +07:00
|
|
|
|
_notificationRepository = notificationRepository;
|
2026-01-20 10:49:13 +07:00
|
|
|
|
_checkInJobStatusRepository = checkInJobStatusRepository;
|
2026-03-30 09:52:27 +07:00
|
|
|
|
_leaveProcessJobStatusRepository = leaveProcessJobStatusRepository;
|
2024-08-20 10:37:47 +07:00
|
|
|
|
_objectPool = objectPool;
|
2024-09-24 11:53:09 +07:00
|
|
|
|
_permission = permission;
|
2025-01-27 13:32:57 +07:00
|
|
|
|
|
|
|
|
|
|
_httpClient = httpClient;
|
|
|
|
|
|
var authString = $"{_configuration["Rabbit:User"] ?? ""}:{_configuration["Rabbit:Password"] ?? ""}";
|
|
|
|
|
|
|
|
|
|
|
|
var authToken = Convert.ToBase64String(Encoding.ASCII.GetBytes(authString));
|
|
|
|
|
|
_httpClient.DefaultRequestHeaders.Authorization =
|
|
|
|
|
|
new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authToken);
|
2023-11-09 14:35:08 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#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");
|
|
|
|
|
|
|
2024-05-29 13:33:23 +07:00
|
|
|
|
private string? AccessToken => _httpContextAccessor?.HttpContext?.Request.Headers["Authorization"];
|
|
|
|
|
|
|
2023-11-09 14:35:08 +07:00
|
|
|
|
private Guid OcId
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
2026-03-17 15:47:03 +07:00
|
|
|
|
// First try to get from claims
|
|
|
|
|
|
var ocIdFromClaims = OrgRootDnaId;
|
|
|
|
|
|
if (ocIdFromClaims.HasValue && ocIdFromClaims.Value != Guid.Empty)
|
|
|
|
|
|
return ocIdFromClaims.Value;
|
|
|
|
|
|
|
|
|
|
|
|
// Fallback to API call for backward compatibility
|
|
|
|
|
|
if (UserId != null && UserId != "")
|
2024-06-20 13:37:25 +07:00
|
|
|
|
return _userProfileRepository.GetUserOCId(Guid.Parse(UserId!), AccessToken);
|
2023-11-09 14:35:08 +07:00
|
|
|
|
else
|
|
|
|
|
|
return Guid.Empty;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region " Methods "
|
|
|
|
|
|
|
2023-11-10 14:40:53 +07:00
|
|
|
|
#region " Duty Time รอบการทำงาน "
|
|
|
|
|
|
|
2023-11-09 14:35:08 +07:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// LV1_004 - ข้อมูลทั้งหมดของรอบการปฏิบัติงาน (ADMIN)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
2023-11-10 14:40:53 +07:00
|
|
|
|
[HttpGet("duty-time")]
|
2023-11-09 14:35:08 +07:00
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
public async Task<ActionResult<ResponseObject>> GetAllAsync()
|
|
|
|
|
|
{
|
2024-09-24 11:53:09 +07:00
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_WORK_ROUND");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
2023-11-10 14:40:53 +07:00
|
|
|
|
var data = await _dutyTimeRepository.GetAllAsync();
|
2023-11-09 14:35:08 +07:00
|
|
|
|
|
|
|
|
|
|
return Success(data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// ข้อมูลของรอบการปฏิบัติงาน (ADMIN)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
2023-11-10 14:40:53 +07:00
|
|
|
|
[HttpGet("duty-time/{id:guid}")]
|
2023-11-09 14:35:08 +07:00
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
public async Task<ActionResult<ResponseObject>> GetByIdAsync(Guid id)
|
|
|
|
|
|
{
|
2023-11-10 14:40:53 +07:00
|
|
|
|
var data = await _dutyTimeRepository.GetByIdAsync(id);
|
2023-11-09 14:35:08 +07:00
|
|
|
|
|
|
|
|
|
|
return Success(data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// LV1_001 - สร้างรอบการปฏิบัติงาน (ADMIN)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
2023-11-10 14:40:53 +07:00
|
|
|
|
[HttpPost("duty-time")]
|
2023-11-09 14:35:08 +07:00
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
public async Task<ActionResult<ResponseObject>> PostAsync([FromBody] CreateDutyTimeDto data)
|
|
|
|
|
|
{
|
2024-09-24 11:53:09 +07:00
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("CREATE", "SYS_WORK_ROUND");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-09 14:35:08 +07:00
|
|
|
|
// 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-10 14:40:53 +07:00
|
|
|
|
var oldData = await _dutyTimeRepository.GetAllAsync();
|
2023-11-09 14:35:08 +07:00
|
|
|
|
if (oldData == null || oldData.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
var inserted = new DutyTime
|
|
|
|
|
|
{
|
|
|
|
|
|
Id = Guid.NewGuid(),
|
2023-11-28 11:14:19 +07:00
|
|
|
|
Description = data.Description ?? "",
|
2023-11-09 14:35:08 +07:00
|
|
|
|
StartTimeMorning = data.StartTimeMorning,
|
|
|
|
|
|
EndTimeMorning = data.EndTimeMorning,
|
|
|
|
|
|
StartTimeAfternoon = data.StartTimeAfternoon,
|
|
|
|
|
|
EndTimeAfternoon = data.EndTimeAfternoon,
|
2023-12-12 14:17:00 +07:00
|
|
|
|
IsActive = data.IsActive,
|
2023-11-09 14:35:08 +07:00
|
|
|
|
IsDefault = true,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-11-10 14:40:53 +07:00
|
|
|
|
var ret = await _dutyTimeRepository.AddAsync(inserted);
|
2023-11-09 14:35:08 +07:00
|
|
|
|
|
|
|
|
|
|
return Success(ret);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (data.IsDefault)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var d in oldData)
|
|
|
|
|
|
{
|
|
|
|
|
|
d.IsDefault = false;
|
2023-11-10 14:40:53 +07:00
|
|
|
|
await _dutyTimeRepository.UpdateAsync(d);
|
2023-11-09 14:35:08 +07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var inserted = new DutyTime
|
|
|
|
|
|
{
|
|
|
|
|
|
Id = Guid.NewGuid(),
|
2023-12-06 13:33:13 +07:00
|
|
|
|
Description = data.Description ?? "",
|
2023-11-09 14:35:08 +07:00
|
|
|
|
StartTimeMorning = data.StartTimeMorning,
|
|
|
|
|
|
EndTimeMorning = data.EndTimeMorning,
|
|
|
|
|
|
StartTimeAfternoon = data.StartTimeAfternoon,
|
|
|
|
|
|
EndTimeAfternoon = data.EndTimeAfternoon,
|
2023-12-15 09:59:03 +07:00
|
|
|
|
IsActive = data.IsActive,
|
2023-11-09 14:35:08 +07:00
|
|
|
|
IsDefault = data.IsDefault,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-11-10 14:40:53 +07:00
|
|
|
|
var ret = await _dutyTimeRepository.AddAsync(inserted);
|
2023-11-09 14:35:08 +07:00
|
|
|
|
|
|
|
|
|
|
return Success(ret);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// LV1_002 - แก้ไขรอบการปฏิบัติงาน (ADMIN)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
2023-11-10 14:40:53 +07:00
|
|
|
|
[HttpPut("duty-time/{id:guid}")]
|
2023-11-09 14:35:08 +07:00
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
public async Task<ActionResult<ResponseObject>> PutAsync(Guid id, [FromBody] UpdateDutyTimeDto data)
|
|
|
|
|
|
{
|
2024-09-24 11:53:09 +07:00
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_WORK_ROUND");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-10 14:40:53 +07:00
|
|
|
|
var oldData = await _dutyTimeRepository.GetByIdAsync(id);
|
2023-11-09 14:35:08 +07:00
|
|
|
|
if (oldData == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception(GlobalMessages.DataNotFound);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
2023-11-10 14:40:53 +07:00
|
|
|
|
{
|
|
|
|
|
|
var oldDataList = await _dutyTimeRepository.GetAllAsync();
|
2023-11-09 14:35:08 +07:00
|
|
|
|
if (data.IsDefault)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var d in oldDataList)
|
|
|
|
|
|
{
|
|
|
|
|
|
d.IsDefault = false;
|
2023-11-10 14:40:53 +07:00
|
|
|
|
await _dutyTimeRepository.UpdateAsync(d);
|
2023-11-09 14:35:08 +07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-12 14:17:00 +07:00
|
|
|
|
oldData.Description = data.Description ?? "";
|
2023-11-09 14:35:08 +07:00
|
|
|
|
oldData.IsDefault = data.IsDefault;
|
|
|
|
|
|
oldData.IsActive = data.IsActive;
|
|
|
|
|
|
|
2025-10-20 15:12:01 +07:00
|
|
|
|
if (!data.IsActive)
|
|
|
|
|
|
{
|
|
|
|
|
|
// ลบรายการที่เคยผูกไว้ทั้งหมด
|
|
|
|
|
|
var userDutyTimes = await _context.UserDutyTimes.Where(x => x.DutyTimeId == oldData.Id).ToListAsync();
|
|
|
|
|
|
_context.UserDutyTimes.RemoveRange(userDutyTimes);
|
|
|
|
|
|
await _context.SaveChangesAsync();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-10 14:40:53 +07:00
|
|
|
|
await _dutyTimeRepository.UpdateAsync(oldData);
|
2023-11-09 14:35:08 +07:00
|
|
|
|
|
|
|
|
|
|
return Success(oldData);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// LV1_003 - ลบรอบการปฏิบัติงาน (ADMIN)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
2023-11-10 14:40:53 +07:00
|
|
|
|
[HttpDelete("duty-time/{id:guid}")]
|
2023-11-09 14:35:08 +07:00
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
public async Task<ActionResult<ResponseObject>> DeleteAsync(Guid id)
|
|
|
|
|
|
{
|
2024-09-24 11:53:09 +07:00
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("DELETE", "SYS_WORK_ROUND");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-10 14:40:53 +07:00
|
|
|
|
var oldData = await _dutyTimeRepository.GetByIdAsync(id);
|
2023-11-09 14:35:08 +07:00
|
|
|
|
if (oldData == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception(GlobalMessages.DataNotFound);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2023-12-21 10:25:11 +07:00
|
|
|
|
var inUseRound = await _userDutyTimeRepository.GetFirstInUseRound(oldData.Id);
|
2023-12-21 09:40:19 +07:00
|
|
|
|
|
|
|
|
|
|
//if (inUseRound != null || oldData.IsActive || oldData.IsDefault)
|
|
|
|
|
|
if (inUseRound != null)
|
2023-11-09 14:35:08 +07:00
|
|
|
|
{
|
|
|
|
|
|
throw new Exception("ไม่สามารถลบรอบการปฏิบัติงานที่ยังใช้งานอยู่ได้");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-21 09:40:19 +07:00
|
|
|
|
if (oldData.IsDefault)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception("ไม่สามารถลบรอบการปฏิบัติงานที่ตั้งค่า Default ได้");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-10 14:40:53 +07:00
|
|
|
|
await _dutyTimeRepository.DeleteAsync(oldData);
|
2023-11-09 14:35:08 +07:00
|
|
|
|
return Success();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-10 14:40:53 +07:00
|
|
|
|
/// <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()
|
|
|
|
|
|
{
|
2024-09-24 11:53:09 +07:00
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("GET", "SYS_WORK_ROUND_EDIT");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
2023-11-10 14:40:53 +07:00
|
|
|
|
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)]
|
2026-01-30 13:35:58 +07:00
|
|
|
|
public async Task<ActionResult<ResponseObject>> CheckTimeAsync(CancellationToken cancellationToken = default)
|
2023-11-10 14:40:53 +07:00
|
|
|
|
{
|
|
|
|
|
|
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
|
2026-01-30 13:35:58 +07:00
|
|
|
|
|
|
|
|
|
|
// Get user's last check-in record and profile in parallel
|
|
|
|
|
|
var dataTask = _userTimeStampRepository.GetLastRecord(userId);
|
2026-04-17 09:41:52 +07:00
|
|
|
|
var profileTask = _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
|
2026-01-30 13:35:58 +07:00
|
|
|
|
var defaultRoundTask = _dutyTimeRepository.GetDefaultAsync();
|
|
|
|
|
|
|
|
|
|
|
|
await Task.WhenAll(dataTask, profileTask, defaultRoundTask);
|
|
|
|
|
|
|
|
|
|
|
|
var data = await dataTask;
|
|
|
|
|
|
var profile = await profileTask;
|
|
|
|
|
|
var getDefaultRound = await defaultRoundTask;
|
2023-11-10 14:40:53 +07:00
|
|
|
|
|
2025-03-21 21:34:39 +07:00
|
|
|
|
if (profile == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception(GlobalMessages.DataNotFound);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (getDefaultRound == 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 ?? getDefaultRound;
|
|
|
|
|
|
|
2026-01-30 13:35:58 +07:00
|
|
|
|
// Determine check-in status and data
|
|
|
|
|
|
DateTime? checkInTime = null;
|
|
|
|
|
|
Guid? checkInId = null;
|
|
|
|
|
|
|
|
|
|
|
|
if (data != null)
|
2023-11-10 14:40:53 +07:00
|
|
|
|
{
|
2023-12-06 14:02:49 +07:00
|
|
|
|
if (data.CheckOut != null)
|
2023-11-10 14:40:53 +07:00
|
|
|
|
{
|
2025-01-23 14:58:04 +07:00
|
|
|
|
// fix issue SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921
|
2026-01-30 13:35:58 +07:00
|
|
|
|
var currentDate = DateTime.Now.Date;
|
|
|
|
|
|
// ถ้า check-in + check-out ไปแล้วในวันเดียวกัน
|
|
|
|
|
|
if (data.CheckIn.Date == currentDate && data.CheckOut.Value.Date == currentDate)
|
2023-12-06 14:02:49 +07:00
|
|
|
|
{
|
2024-12-15 20:10:13 +07:00
|
|
|
|
return Error("คุณได้ทำการลงเวลาเข้าและออกเรียบร้อยแล้ว คุณจะสามารถลงเวลาได้อีกครั้งในวันถัดไป");
|
|
|
|
|
|
}
|
2026-01-30 13:35:58 +07:00
|
|
|
|
// ถ้า check-out คนละวัน ให้แสดงว่ายังไม่ได้ check-in วันนี้
|
2023-12-06 14:02:49 +07:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-01-30 13:35:58 +07:00
|
|
|
|
// มี check-in แต่ยังไม่ check-out
|
|
|
|
|
|
checkInTime = data.CheckIn;
|
|
|
|
|
|
checkInId = data.Id;
|
2023-12-06 14:02:49 +07:00
|
|
|
|
}
|
2023-11-10 14:40:53 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-30 13:35:58 +07:00
|
|
|
|
// Create response DTO (duty is never null here due to fallback logic)
|
|
|
|
|
|
var ret = new CheckInResultDto
|
|
|
|
|
|
{
|
|
|
|
|
|
StartTimeMorning = duty.StartTimeMorning,
|
|
|
|
|
|
EndTimeMorning = duty.EndTimeMorning,
|
|
|
|
|
|
StartTimeAfternoon = duty.StartTimeAfternoon,
|
|
|
|
|
|
EndTimeAfternoon = duty.EndTimeAfternoon,
|
|
|
|
|
|
Description = duty.Description,
|
|
|
|
|
|
CheckInTime = checkInTime,
|
|
|
|
|
|
CheckInId = checkInId,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-11-10 14:40:53 +07:00
|
|
|
|
return Success(ret);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-13 15:26:14 +07:00
|
|
|
|
/// <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)
|
2024-08-19 16:00:00 +07:00
|
|
|
|
{
|
2024-08-20 10:37:47 +07:00
|
|
|
|
// prepare data and convert request body and send to queue
|
2024-08-19 16:00:00 +07:00
|
|
|
|
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
|
|
|
|
|
|
var currentDate = DateTime.Now;
|
2026-01-23 09:32:17 +07:00
|
|
|
|
|
|
|
|
|
|
// ตรวจสอบว่ามีงานที่กำลัง pending หรือ processing อยู่หรือไม่
|
|
|
|
|
|
var existingJobs = await _checkInJobStatusRepository.GetPendingOrProcessingJobsAsync(userId);
|
|
|
|
|
|
if (existingJobs != null && existingJobs.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
// กรองเฉพาะงานที่เป็นประเภทเดียวกัน (CHECK_IN หรือ CHECK_OUT)
|
|
|
|
|
|
var checkType = data.CheckInId == null ? "CHECK_IN" : "CHECK_OUT";
|
|
|
|
|
|
var sameTypeJob = existingJobs.FirstOrDefault(j => j.CheckType == checkType);
|
|
|
|
|
|
|
|
|
|
|
|
if (sameTypeJob != null)
|
|
|
|
|
|
{
|
2026-02-04 10:32:44 +07:00
|
|
|
|
|
|
|
|
|
|
return Error($"มีงาน {checkType} กำลังดำเนินการอยู่", StatusCodes.Status500InternalServerError);
|
|
|
|
|
|
// var timeDiff = (currentDate - sameTypeJob.CreatedDate).TotalMinutes;
|
|
|
|
|
|
// if (timeDiff < 2)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// return Error($"มีงาน {checkType} กำลังดำเนินการอยู่ กรุณารอสักครู่", StatusCodes.Status409Conflict);
|
|
|
|
|
|
// }
|
2026-01-23 09:32:17 +07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-19 16:00:00 +07:00
|
|
|
|
var checkFileBytes = new byte[0];
|
|
|
|
|
|
|
2024-12-10 14:19:19 +07:00
|
|
|
|
// fix issue : ระบบลงเวลาปฏิบัติงาน>>รูปภาพไม่แสดงในฝั่งของ Admin #804
|
|
|
|
|
|
if (data.Img != null && data.Img.Length > 0)
|
2024-08-19 16:00:00 +07:00
|
|
|
|
{
|
|
|
|
|
|
var formFile = data.Img;
|
|
|
|
|
|
using (var memoryStream = new MemoryStream())
|
|
|
|
|
|
{
|
|
|
|
|
|
await formFile.CopyToAsync(memoryStream);
|
|
|
|
|
|
checkFileBytes = memoryStream.ToArray();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-20 10:49:13 +07:00
|
|
|
|
// add task id for check in queue
|
|
|
|
|
|
string taskId = Guid.NewGuid().ToString();
|
|
|
|
|
|
|
2024-08-19 16:00:00 +07:00
|
|
|
|
var checkData = new CheckTimeDtoRB
|
|
|
|
|
|
{
|
|
|
|
|
|
UserId = userId,
|
|
|
|
|
|
CurrentDate = currentDate,
|
|
|
|
|
|
CheckInId = data.CheckInId,
|
2026-01-20 10:49:13 +07:00
|
|
|
|
TaskId = Guid.Parse(taskId),
|
2024-08-19 16:00:00 +07:00
|
|
|
|
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 ?? ""
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-08-20 10:37:47 +07:00
|
|
|
|
var channel = _objectPool.Get();
|
2026-01-23 09:32:17 +07:00
|
|
|
|
CheckInJobStatus? jobStatus = null;
|
|
|
|
|
|
|
2024-08-20 10:37:47 +07:00
|
|
|
|
try
|
2024-08-19 22:08:16 +07:00
|
|
|
|
{
|
2024-12-13 12:57:30 +07:00
|
|
|
|
var queue = _configuration["Rabbit:Queue"] ?? "basic-queue";
|
|
|
|
|
|
channel.QueueDeclare(queue: queue, durable: true, exclusive: false, autoDelete: false, arguments: null);
|
2024-08-20 10:37:47 +07:00
|
|
|
|
var serializedObject = JsonConvert.SerializeObject(checkData);
|
|
|
|
|
|
var body = Encoding.UTF8.GetBytes(serializedObject);
|
|
|
|
|
|
|
2025-01-27 13:32:57 +07:00
|
|
|
|
var properties = channel.CreateBasicProperties();
|
|
|
|
|
|
properties.Persistent = true;
|
2026-01-20 10:49:13 +07:00
|
|
|
|
properties.MessageId = taskId;
|
|
|
|
|
|
|
|
|
|
|
|
// บันทึกสถานะงานก่อนส่งไป RabbitMQ
|
2026-01-23 09:32:17 +07:00
|
|
|
|
jobStatus = new CheckInJobStatus
|
2026-01-20 10:49:13 +07:00
|
|
|
|
{
|
|
|
|
|
|
TaskId = Guid.Parse(taskId),
|
|
|
|
|
|
KeycloakUserId = userId,
|
|
|
|
|
|
CreatedDate = currentDate,
|
|
|
|
|
|
Status = "PENDING",
|
|
|
|
|
|
CheckType = data.CheckInId == null ? "CHECK_IN" : "CHECK_OUT",
|
|
|
|
|
|
CheckInId = data.CheckInId,
|
|
|
|
|
|
AdditionalData = JsonConvert.SerializeObject(new
|
|
|
|
|
|
{
|
|
|
|
|
|
IsLocation = data.IsLocation,
|
|
|
|
|
|
LocationName = data.LocationName,
|
|
|
|
|
|
POI = data.POI
|
|
|
|
|
|
})
|
|
|
|
|
|
};
|
|
|
|
|
|
await _checkInJobStatusRepository.AddAsync(jobStatus);
|
2025-01-27 13:32:57 +07:00
|
|
|
|
|
2026-01-23 09:32:17 +07:00
|
|
|
|
// ส่งไป RabbitMQ
|
2024-08-20 10:37:47 +07:00
|
|
|
|
channel.BasicPublish(exchange: "",
|
2024-12-13 12:57:30 +07:00
|
|
|
|
routingKey: queue,
|
2025-01-27 13:32:57 +07:00
|
|
|
|
basicProperties: properties,
|
2024-08-20 10:37:47 +07:00
|
|
|
|
body: body);
|
|
|
|
|
|
|
2025-01-29 14:49:42 +07:00
|
|
|
|
return Success(new { date = currentDate, taskId = taskId, keycloakId = userId });
|
2024-08-20 10:37:47 +07:00
|
|
|
|
}
|
2026-01-23 09:32:17 +07:00
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
// ถ้าส่งไป queue ไม่สำเร็จ ให้ลบ job status ที่สร้างไว้ออก
|
|
|
|
|
|
if (jobStatus != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
await _checkInJobStatusRepository.DeleteAsync(jobStatus);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
// Ignore delete error
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-02-04 10:32:44 +07:00
|
|
|
|
return Error($"ไม่สามารถส่งงานไปยัง Queue ได้: {ex.Message}");
|
|
|
|
|
|
//throw new Exception($"ไม่สามารถส่งงานไปยัง Queue ได้: {ex.Message}");
|
2026-01-23 09:32:17 +07:00
|
|
|
|
}
|
2024-08-20 10:37:47 +07:00
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
_objectPool.Return(channel);
|
|
|
|
|
|
}
|
2025-01-27 13:32:57 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-20 10:49:13 +07:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// ตรวจสอบสถานะงาน check-in ด้วย Task ID
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="taskId">Task ID ที่ได้จากการเรียก CheckInAsync</param>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="404">ไม่พบข้อมูลงาน</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
|
|
|
|
|
[HttpGet("job-status/{taskId:guid}")]
|
2025-01-27 13:32:57 +07:00
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
2026-01-20 10:49:13 +07:00
|
|
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
2025-01-27 13:32:57 +07:00
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
2026-01-20 10:49:13 +07:00
|
|
|
|
public async Task<ActionResult<ResponseObject>> GetJobStatusAsync(Guid taskId)
|
2025-01-27 13:32:57 +07:00
|
|
|
|
{
|
2026-01-20 10:49:13 +07:00
|
|
|
|
var jobStatus = await _checkInJobStatusRepository.GetByTaskIdAsync(taskId);
|
2024-08-19 16:00:00 +07:00
|
|
|
|
|
2026-01-20 10:49:13 +07:00
|
|
|
|
if (jobStatus == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error("ไม่พบข้อมูลงาน", StatusCodes.Status404NotFound);
|
|
|
|
|
|
}
|
2025-01-27 13:32:57 +07:00
|
|
|
|
|
2026-01-20 10:49:13 +07:00
|
|
|
|
var result = new
|
|
|
|
|
|
{
|
|
|
|
|
|
taskId = jobStatus.TaskId,
|
|
|
|
|
|
keycloakUserId = jobStatus.KeycloakUserId,
|
|
|
|
|
|
status = jobStatus.Status,
|
|
|
|
|
|
checkType = jobStatus.CheckType,
|
|
|
|
|
|
checkInId = jobStatus.CheckInId,
|
|
|
|
|
|
createdDate = jobStatus.CreatedDate,
|
|
|
|
|
|
processingDate = jobStatus.ProcessingDate,
|
|
|
|
|
|
completedDate = jobStatus.CompletedDate,
|
|
|
|
|
|
errorMessage = jobStatus.ErrorMessage,
|
|
|
|
|
|
additionalData = jobStatus.AdditionalData != null ?
|
|
|
|
|
|
JsonConvert.DeserializeObject(jobStatus.AdditionalData) : null
|
|
|
|
|
|
};
|
2025-01-27 13:32:57 +07:00
|
|
|
|
|
2026-01-20 10:49:13 +07:00
|
|
|
|
return Success(result);
|
|
|
|
|
|
}
|
2025-01-27 13:32:57 +07:00
|
|
|
|
|
2026-01-20 10:49:13 +07:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// ดึงรายการงานที่ยัง pending หรือ processing ของผู้ใช้
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
|
|
|
|
|
[HttpGet("pending-jobs")]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
public async Task<ActionResult<ResponseObject>> GetPendingJobsAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
|
|
|
|
|
|
var jobs = await _checkInJobStatusRepository.GetPendingOrProcessingJobsAsync(userId);
|
2024-08-19 21:20:05 +07:00
|
|
|
|
|
2026-01-20 10:49:13 +07:00
|
|
|
|
var result = jobs.Select(job => new
|
|
|
|
|
|
{
|
|
|
|
|
|
taskId = job.TaskId,
|
|
|
|
|
|
status = job.Status,
|
|
|
|
|
|
checkType = job.CheckType,
|
|
|
|
|
|
checkInId = job.CheckInId,
|
|
|
|
|
|
createdDate = job.CreatedDate,
|
|
|
|
|
|
processingDate = job.ProcessingDate
|
|
|
|
|
|
}).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
return Success(new { count = result.Count, jobs = result });
|
|
|
|
|
|
}
|
2024-08-19 22:08:16 +07:00
|
|
|
|
|
2026-01-20 10:49:13 +07:00
|
|
|
|
[HttpGet("check-status")]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
public async Task<ActionResult<ResponseObject>> CheckInCheckStatus()
|
|
|
|
|
|
{
|
|
|
|
|
|
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
|
|
|
|
|
|
// var currentDate = DateTime.Now;
|
|
|
|
|
|
// var channel = _objectPool.Get();
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// var _url = _configuration["Rabbit:URL"] ?? "";
|
|
|
|
|
|
// var _queue = _configuration["Rabbit:Queue"] ?? "basic-queue";
|
|
|
|
|
|
|
|
|
|
|
|
// // Step 1: ตรวจสอบจำนวน message ทั้งหมดในคิว
|
|
|
|
|
|
// string queueUrl = $"{_url}{_queue}";
|
|
|
|
|
|
// var queueResponse = await _httpClient.GetAsync(queueUrl);
|
|
|
|
|
|
// if (!queueResponse.IsSuccessStatusCode)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// return Error("Error accessing RabbitMQ API", (int)queueResponse.StatusCode);
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// var queueContent = await queueResponse.Content.ReadAsStringAsync();
|
|
|
|
|
|
// var queueData = JObject.Parse(queueContent);
|
|
|
|
|
|
// int totalMessages = queueData["messages"]?.Value<int>() ?? 0;
|
|
|
|
|
|
|
|
|
|
|
|
// // Step 2: วนลูปดึง message ทีละ 100 งาน
|
|
|
|
|
|
// int batchSize = 100;
|
|
|
|
|
|
// var allMessages = new List<string>();
|
|
|
|
|
|
// int processedMessages = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// while (processedMessages < totalMessages)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// var requestBody = new StringContent(
|
|
|
|
|
|
// $"{{\"count\":{batchSize},\"requeue\":true,\"encoding\":\"auto\",\"ackmode\":\"ack_requeue_true\"}}",
|
|
|
|
|
|
// Encoding.UTF8,
|
|
|
|
|
|
// "application/json"
|
|
|
|
|
|
// );
|
|
|
|
|
|
|
|
|
|
|
|
// string getMessagesUrl = $"{_url}{_queue}/get";
|
|
|
|
|
|
// var response = await _httpClient.PostAsync(getMessagesUrl, requestBody);
|
|
|
|
|
|
// if (!response.IsSuccessStatusCode)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// return StatusCode((int)response.StatusCode, "Error retrieving messages from RabbitMQ.");
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// var content = await response.Content.ReadAsStringAsync();
|
|
|
|
|
|
// var messages = JArray.Parse(content);
|
|
|
|
|
|
|
|
|
|
|
|
// if (messages.Count == 0)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// processedMessages += messages.Count;
|
|
|
|
|
|
// allMessages.AddRange(messages.Select(m => m["properties"].ToString()));
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
var jobs = await _checkInJobStatusRepository.GetPendingOrProcessingJobsAsync(userId);
|
2025-01-27 13:32:57 +07:00
|
|
|
|
// Step 3: ค้นหา taskIds ที่อยู่ใน messages ทั้งหมด
|
2026-01-20 10:49:13 +07:00
|
|
|
|
//var foundTasks = allMessages.FirstOrDefault(x => x.Contains(userId.ToString("D")));
|
2024-08-19 16:00:00 +07:00
|
|
|
|
|
2026-01-20 10:49:13 +07:00
|
|
|
|
return Success(new { keycloakId = userId, InQueue = (jobs != null && jobs.Count > 0) });
|
2024-08-19 16:00:00 +07:00
|
|
|
|
|
2025-01-27 13:32:57 +07:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(ex, ex.Message);
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
2026-01-20 10:49:13 +07:00
|
|
|
|
//_objectPool.Return(channel);
|
2025-01-27 13:32:57 +07:00
|
|
|
|
}
|
2024-08-19 16:00:00 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-19 17:18:58 +07:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Fake Check in
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
2024-08-20 10:37:47 +07:00
|
|
|
|
[HttpPost("fake-check-in"), DisableRequestSizeLimit]
|
2024-08-19 17:18:58 +07:00
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
[AllowAnonymous]
|
|
|
|
|
|
public ActionResult<ResponseObject> FakeCheckIn([FromBody] FakeCheckTimeDto data)
|
|
|
|
|
|
{
|
|
|
|
|
|
var currentDate = DateTime.Now;
|
2024-08-20 10:37:47 +07:00
|
|
|
|
var channel = _objectPool.Get();
|
|
|
|
|
|
try
|
2024-08-19 22:08:16 +07:00
|
|
|
|
{
|
2024-08-20 10:37:47 +07:00
|
|
|
|
channel.QueueDeclare(queue: _fakeCheckInQueue, durable: true, exclusive: false, autoDelete: false, arguments: null);
|
2024-08-19 17:18:58 +07:00
|
|
|
|
|
2025-01-27 13:32:57 +07:00
|
|
|
|
// Create Task ID
|
|
|
|
|
|
string taskId = Guid.NewGuid().ToString();
|
|
|
|
|
|
var properties = channel.CreateBasicProperties();
|
|
|
|
|
|
properties.Persistent = true;
|
|
|
|
|
|
properties.MessageId = taskId; // แนบ Message ID เพื่อใช้ตรวจสอบ
|
|
|
|
|
|
|
2024-08-20 10:37:47 +07:00
|
|
|
|
var serializedObject = JsonConvert.SerializeObject(data);
|
2024-08-19 22:08:16 +07:00
|
|
|
|
|
2024-08-20 10:37:47 +07:00
|
|
|
|
var body = Encoding.UTF8.GetBytes(serializedObject);
|
2024-08-19 17:18:58 +07:00
|
|
|
|
|
2024-08-20 10:37:47 +07:00
|
|
|
|
channel.BasicPublish(exchange: "",
|
|
|
|
|
|
routingKey: _fakeCheckInQueue,
|
2025-01-27 13:32:57 +07:00
|
|
|
|
basicProperties: properties,
|
2024-08-20 10:37:47 +07:00
|
|
|
|
body: body);
|
2024-08-19 17:18:58 +07:00
|
|
|
|
|
2025-01-27 13:32:57 +07:00
|
|
|
|
return Success(new { date = currentDate, taskId = taskId });
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
_objectPool.Return(channel);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[HttpGet("fake-check-status/{id:guid}")]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
[AllowAnonymous]
|
|
|
|
|
|
public async Task<ActionResult<ResponseObject>> FakeCheckInCheckStatus(Guid id)
|
|
|
|
|
|
{
|
|
|
|
|
|
var currentDate = DateTime.Now;
|
|
|
|
|
|
var channel = _objectPool.Get();
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var _url = _configuration["Rabbit:URL"] ?? "";
|
|
|
|
|
|
|
|
|
|
|
|
// Step 1: ตรวจสอบจำนวน message ทั้งหมดในคิว
|
|
|
|
|
|
string queueUrl = $"{_url}{_fakeCheckInQueue}";
|
|
|
|
|
|
var queueResponse = await _httpClient.GetAsync(queueUrl);
|
|
|
|
|
|
if (!queueResponse.IsSuccessStatusCode)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error("Error accessing RabbitMQ API", (int)queueResponse.StatusCode);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var queueContent = await queueResponse.Content.ReadAsStringAsync();
|
|
|
|
|
|
var queueData = JObject.Parse(queueContent);
|
|
|
|
|
|
int totalMessages = queueData["messages"]?.Value<int>() ?? 0;
|
|
|
|
|
|
|
|
|
|
|
|
// Step 2: วนลูปดึง message ทีละ 100 งาน
|
|
|
|
|
|
int batchSize = 100;
|
|
|
|
|
|
var allMessages = new List<string>();
|
|
|
|
|
|
int processedMessages = 0;
|
|
|
|
|
|
|
|
|
|
|
|
while (processedMessages < totalMessages)
|
|
|
|
|
|
{
|
|
|
|
|
|
var requestBody = new StringContent(
|
|
|
|
|
|
$"{{\"count\":{batchSize},\"requeue\":true,\"encoding\":\"auto\",\"ackmode\":\"ack_requeue_true\"}}",
|
|
|
|
|
|
Encoding.UTF8,
|
|
|
|
|
|
"application/json"
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
string getMessagesUrl = $"{_url}{_fakeCheckInQueue}/get";
|
|
|
|
|
|
var response = await _httpClient.PostAsync(getMessagesUrl, requestBody);
|
|
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
|
|
|
|
{
|
|
|
|
|
|
return StatusCode((int)response.StatusCode, "Error retrieving messages from RabbitMQ.");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var content = await response.Content.ReadAsStringAsync();
|
|
|
|
|
|
var messages = JArray.Parse(content);
|
|
|
|
|
|
|
|
|
|
|
|
if (messages.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
processedMessages += messages.Count;
|
|
|
|
|
|
allMessages.AddRange(messages.Select(m => m["properties"].ToString()));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Step 3: ค้นหา taskIds ที่อยู่ใน messages ทั้งหมด
|
|
|
|
|
|
var foundTasks = allMessages.FirstOrDefault(x => x.Contains(id.ToString("D")));
|
|
|
|
|
|
|
|
|
|
|
|
return Success(new { taskId = id, InQueue = foundTasks != null });
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(ex, ex.Message);
|
2024-08-20 10:37:47 +07:00
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
_objectPool.Return(channel);
|
|
|
|
|
|
}
|
2024-08-19 17:18:58 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-19 16:00:00 +07:00
|
|
|
|
/// <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;
|
2026-01-20 10:49:13 +07:00
|
|
|
|
var taskId = data.TaskId ?? Guid.Empty;
|
2024-08-19 16:00:00 +07:00
|
|
|
|
|
2026-01-20 10:49:13 +07:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// อัปเดตสถานะเป็น PROCESSING
|
|
|
|
|
|
if (taskId != Guid.Empty)
|
|
|
|
|
|
{
|
|
|
|
|
|
await _checkInJobStatusRepository.UpdateToProcessingAsync(taskId);
|
|
|
|
|
|
}
|
2024-08-19 16:00:00 +07:00
|
|
|
|
|
2026-04-17 09:41:52 +07:00
|
|
|
|
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, data.Token);
|
2024-08-19 16:00:00 +07:00
|
|
|
|
|
2026-01-20 10:49:13 +07:00
|
|
|
|
if (profile == null)
|
2026-02-05 10:39:57 +07:00
|
|
|
|
{
|
|
|
|
|
|
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลผู้ใช้");
|
2026-02-05 11:57:19 +07:00
|
|
|
|
//var staffList = await _userProfileRepository.GetOCStaffAsync(profile)
|
2026-01-20 10:49:13 +07:00
|
|
|
|
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
|
2026-02-05 10:39:57 +07:00
|
|
|
|
}
|
2024-12-13 11:51:14 +07:00
|
|
|
|
|
2026-02-05 11:57:19 +07:00
|
|
|
|
var currentDate = data.CurrentDate ?? DateTime.Now;
|
|
|
|
|
|
|
2026-02-05 10:39:57 +07:00
|
|
|
|
if (data.CheckInFileName == "no-file")
|
|
|
|
|
|
{
|
|
|
|
|
|
//throw new Exception(GlobalMessages.NoFileToUpload);
|
|
|
|
|
|
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, GlobalMessages.NoFileToUpload);
|
2026-02-05 11:57:19 +07:00
|
|
|
|
|
|
|
|
|
|
// send notification to user
|
|
|
|
|
|
var noti1 = new Notification
|
|
|
|
|
|
{
|
|
|
|
|
|
Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจาก {GlobalMessages.NoFileToUpload}",
|
|
|
|
|
|
ReceiverUserId = profile.Id,
|
|
|
|
|
|
Type = "",
|
|
|
|
|
|
Payload = "",
|
|
|
|
|
|
};
|
|
|
|
|
|
_appDbContext.Set<Notification>().Add(noti1);
|
|
|
|
|
|
await _appDbContext.SaveChangesAsync();
|
|
|
|
|
|
|
2026-02-05 10:39:57 +07:00
|
|
|
|
return Error(GlobalMessages.NoFileToUpload, StatusCodes.Status400BadRequest);
|
|
|
|
|
|
}
|
2026-02-05 11:57:19 +07:00
|
|
|
|
|
2026-02-19 15:07:04 +07:00
|
|
|
|
// last check-in record
|
|
|
|
|
|
var lastCheckIn = await _userTimeStampRepository.GetLastRecord(userId);
|
2026-01-20 10:49:13 +07:00
|
|
|
|
|
|
|
|
|
|
var check_status = data.CheckInId == null ? "check-in-picture" : "check-out-picture";
|
2026-02-19 15:07:04 +07:00
|
|
|
|
var check_out_status = "check-out-picture";
|
2026-01-20 10:49:13 +07:00
|
|
|
|
|
|
|
|
|
|
var fileName = $"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{check_status}/{data.CheckInFileName}";
|
2026-02-19 15:07:04 +07:00
|
|
|
|
var fileNameCheckOut = $"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{check_out_status}/{data.CheckInFileName}";
|
2026-01-20 10:49:13 +07:00
|
|
|
|
using (var ms = new MemoryStream(data.CheckInFileBytes ?? new byte[0]))
|
|
|
|
|
|
{
|
2026-01-30 09:49:54 +07:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
await _minIOService.UploadFileAsync(fileName, ms);
|
2026-02-20 16:32:57 +07:00
|
|
|
|
// if (lastCheckIn != null && lastCheckIn.CheckOut == null)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// // ยังไม่เคย check-out มาก่อน หรือ check-out เป็น null ให้ใช้ชื่อไฟล์แบบ check-out
|
|
|
|
|
|
// await _minIOService.UploadFileAsync(fileNameCheckOut, ms);
|
|
|
|
|
|
// }
|
2026-01-30 09:49:54 +07:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, $"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}");
|
2026-02-05 11:57:19 +07:00
|
|
|
|
|
|
|
|
|
|
// send notification to user
|
|
|
|
|
|
var noti1 = new Notification
|
|
|
|
|
|
{
|
|
|
|
|
|
Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่สามารถอัปโหลดรูปภาพได้ {ex.Message}",
|
|
|
|
|
|
ReceiverUserId = profile.Id,
|
|
|
|
|
|
Type = "",
|
|
|
|
|
|
Payload = "",
|
|
|
|
|
|
};
|
|
|
|
|
|
_appDbContext.Set<Notification>().Add(noti1);
|
|
|
|
|
|
await _appDbContext.SaveChangesAsync();
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-01-30 09:49:54 +07:00
|
|
|
|
return Error($"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}", StatusCodes.Status500InternalServerError);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-20 10:49:13 +07:00
|
|
|
|
}
|
2024-08-19 16:00:00 +07:00
|
|
|
|
|
2026-02-20 16:32:57 +07:00
|
|
|
|
if (lastCheckIn != null && lastCheckIn.CheckOut == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
using (var ms2 = new MemoryStream(data.CheckInFileBytes ?? new byte[0]))
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
await _minIOService.UploadFileAsync(fileNameCheckOut, ms2);
|
|
|
|
|
|
// if (lastCheckIn != null && lastCheckIn.CheckOut == null)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// // ยังไม่เคย check-out มาก่อน หรือ check-out เป็น null ให้ใช้ชื่อไฟล์แบบ check-out
|
|
|
|
|
|
// await _minIOService.UploadFileAsync(fileNameCheckOut, ms);
|
|
|
|
|
|
// }
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, $"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}");
|
|
|
|
|
|
|
|
|
|
|
|
// send notification to user
|
|
|
|
|
|
var noti1 = new Notification
|
|
|
|
|
|
{
|
|
|
|
|
|
Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่สามารถอัปโหลดรูปภาพได้ {ex.Message}",
|
|
|
|
|
|
ReceiverUserId = profile.Id,
|
|
|
|
|
|
Type = "",
|
|
|
|
|
|
Payload = "",
|
|
|
|
|
|
};
|
|
|
|
|
|
_appDbContext.Set<Notification>().Add(noti1);
|
|
|
|
|
|
await _appDbContext.SaveChangesAsync();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Error($"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}", StatusCodes.Status500InternalServerError);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-01-30 09:49:54 +07:00
|
|
|
|
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
|
|
|
|
|
|
if (defaultRound == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบรอบการลงเวลาทำงาน Default");
|
2026-02-05 11:57:19 +07:00
|
|
|
|
// send notification to user
|
|
|
|
|
|
var noti1 = new Notification
|
|
|
|
|
|
{
|
|
|
|
|
|
Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่พบรอบการลงเวลาทำงาน Default",
|
|
|
|
|
|
ReceiverUserId = profile.Id,
|
|
|
|
|
|
Type = "",
|
|
|
|
|
|
Payload = "",
|
|
|
|
|
|
};
|
|
|
|
|
|
_appDbContext.Set<Notification>().Add(noti1);
|
|
|
|
|
|
await _appDbContext.SaveChangesAsync();
|
2026-01-30 09:49:54 +07:00
|
|
|
|
return Error("ไม่พบรอบการลงเวลาทำงาน Default", StatusCodes.Status404NotFound);
|
|
|
|
|
|
}
|
2024-08-19 16:00:00 +07:00
|
|
|
|
|
2026-01-30 09:49:54 +07:00
|
|
|
|
var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id, currentDate);
|
|
|
|
|
|
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
|
|
|
|
|
|
var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
|
2024-08-19 16:00:00 +07:00
|
|
|
|
|
2026-01-30 09:49:54 +07:00
|
|
|
|
// TODO : รอดุึงรอบที่ผูกกับ user
|
|
|
|
|
|
var duty = userRound ?? defaultRound;
|
2024-08-19 16:00:00 +07:00
|
|
|
|
|
2026-01-30 09:49:54 +07:00
|
|
|
|
// create check in object
|
|
|
|
|
|
if (data.CheckInId == null)
|
2024-08-19 16:00:00 +07:00
|
|
|
|
{
|
2026-02-19 15:07:04 +07:00
|
|
|
|
if (lastCheckIn != null && lastCheckIn.CheckOut == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var checkout = await _userTimeStampRepository.GetByIdAsync(lastCheckIn!.Id);
|
|
|
|
|
|
if(checkout != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var currentCheckInProcess = await _processUserTimeStampRepository.GetTimestampByDateAsync(userId, checkout.CheckIn.Date);
|
|
|
|
|
|
var checkout_process = await _processUserTimeStampRepository.GetByIdAsync(currentCheckInProcess!.Id);
|
|
|
|
|
|
var endTime1 = "";
|
|
|
|
|
|
var startTime1 = "";
|
|
|
|
|
|
var endTimeMorning1 = "";
|
|
|
|
|
|
if (!data.IsLocation && data.LocationName == "ไปประชุม / อบรม / สัมมนา")
|
|
|
|
|
|
{
|
|
|
|
|
|
startTime1 = "13:00";
|
|
|
|
|
|
endTime1 = "14:30";
|
|
|
|
|
|
endTimeMorning1 = "12:00";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
endTime1 = duty.EndTimeAfternoon;
|
|
|
|
|
|
startTime1 = duty.StartTimeAfternoon;
|
|
|
|
|
|
endTimeMorning1 = duty.EndTimeMorning;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string checkOutStatus = "NORMAL";
|
|
|
|
|
|
var leaveReq1 = await _leaveRequestRepository.GetLeavePeriodAsync(userId, currentDate.Date);
|
|
|
|
|
|
if (leaveReq1 != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var leaveRange = leaveReq1.LeaveRangeEnd == null ? "" : leaveReq1.LeaveRangeEnd.ToUpper();
|
|
|
|
|
|
if (leaveRange == "AFTERNOON" || leaveRange == "ALL")
|
|
|
|
|
|
{
|
|
|
|
|
|
if(DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) <
|
|
|
|
|
|
DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning1}"))
|
|
|
|
|
|
checkOutStatus = "ABSENT";
|
|
|
|
|
|
else
|
|
|
|
|
|
checkOutStatus = "NORMAL";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921
|
|
|
|
|
|
var currentDateTime = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm"));
|
|
|
|
|
|
var dutyEndTimeAfternoon = DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {endTime1}");
|
|
|
|
|
|
var dutyEndTimeMorning = DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {endTimeMorning1}");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(currentDateTime.Date > checkout.CheckIn.Date)
|
|
|
|
|
|
{
|
|
|
|
|
|
// ถ้า check-out เป็นวันถัดไป สถานะปกติเสมอ
|
|
|
|
|
|
checkOutStatus = "NORMAL";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// ถ้า check-out เป็นวันเดียวกับ check-in
|
|
|
|
|
|
// ตรวจสอบเวลาว่าสิ้นสุดก่อนบ่ายหรือไม่
|
|
|
|
|
|
if(currentDateTime < dutyEndTimeMorning) // ถ้าออกก่อนเวลาสิ้นสุดตอนเช้า ขาดราชการ
|
|
|
|
|
|
{
|
|
|
|
|
|
checkOutStatus = "ABSENT";
|
|
|
|
|
|
}
|
|
|
|
|
|
else if(currentDateTime >= dutyEndTimeAfternoon) // ถ้าออกหลังเวลาสิ้นสุดตอนบ่าย ปกติ
|
|
|
|
|
|
{
|
|
|
|
|
|
checkOutStatus = "NORMAL";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
checkOutStatus = "ABSENT";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921
|
|
|
|
|
|
var currentDateTime = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm"));
|
|
|
|
|
|
var dutyEndTimeAfternoon = DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {endTime1}");
|
|
|
|
|
|
var dutyEndTimeMorning = DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {endTimeMorning1}");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(currentDateTime.Date > checkout.CheckIn.Date)
|
|
|
|
|
|
{
|
|
|
|
|
|
// ถ้า check-out เป็นวันถัดไป สถานะปกติเสมอ
|
|
|
|
|
|
checkOutStatus = "NORMAL";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// ถ้า check-out เป็นวันเดียวกับ check-in
|
|
|
|
|
|
// ตรวจสอบเวลาว่าสิ้นสุดก่อนบ่ายหรือไม่
|
|
|
|
|
|
if(currentDateTime < dutyEndTimeMorning) // ถ้าออกก่อนเวลาสิ้นสุดตอนเช้า ขาดราชการ
|
|
|
|
|
|
{
|
|
|
|
|
|
checkOutStatus = "ABSENT";
|
|
|
|
|
|
}
|
|
|
|
|
|
else if(currentDateTime >= dutyEndTimeAfternoon) // ถ้าออกหลังเวลาสิ้นสุดตอนบ่าย ปกติ
|
|
|
|
|
|
{
|
|
|
|
|
|
checkOutStatus = "NORMAL";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
checkOutStatus = "ABSENT";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 = fileNameCheckOut;
|
|
|
|
|
|
checkout_process.CheckOut = currentDate;
|
|
|
|
|
|
checkout_process.CheckOutStatus = checkOutStatus;
|
|
|
|
|
|
|
|
|
|
|
|
await _processUserTimeStampRepository.UpdateAsync(checkout_process);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-30 09:49:54 +07:00
|
|
|
|
// validate duplicate check in
|
|
|
|
|
|
var currentCheckIn = await _userTimeStampRepository.GetTimestampByDateAsync(userId, currentDate);
|
2024-08-19 16:00:00 +07:00
|
|
|
|
|
2026-01-30 09:49:54 +07:00
|
|
|
|
if (currentCheckIn != null)
|
|
|
|
|
|
{
|
2026-02-05 10:39:57 +07:00
|
|
|
|
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่สามารถลงเวลาได้ เนื่องจากมีการลงเวลาในวันนี้แล้ว");
|
2026-02-05 11:57:19 +07:00
|
|
|
|
|
|
|
|
|
|
// send notification to user
|
|
|
|
|
|
var noti1 = new Notification
|
|
|
|
|
|
{
|
|
|
|
|
|
Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากมีการลงเวลาในวันนี้แล้ว",
|
|
|
|
|
|
ReceiverUserId = profile.Id,
|
|
|
|
|
|
Type = "",
|
|
|
|
|
|
Payload = "",
|
|
|
|
|
|
};
|
|
|
|
|
|
_appDbContext.Set<Notification>().Add(noti1);
|
|
|
|
|
|
await _appDbContext.SaveChangesAsync();
|
|
|
|
|
|
|
2026-01-30 09:49:54 +07:00
|
|
|
|
return Error(new Exception("ไม่สามารถลงเวลาได้ เนื่องจากมีการลงเวลาในวันนี้แล้ว!"), StatusCodes.Status400BadRequest);
|
|
|
|
|
|
}
|
2024-08-19 16:00:00 +07:00
|
|
|
|
|
2026-01-30 09:49:54 +07:00
|
|
|
|
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,
|
|
|
|
|
|
CitizenId = profile.CitizenId,
|
|
|
|
|
|
|
|
|
|
|
|
Root = profile.Root,
|
|
|
|
|
|
Child1 = profile.Child1,
|
|
|
|
|
|
Child2 = profile.Child2,
|
|
|
|
|
|
Child3 = profile.Child3,
|
|
|
|
|
|
Child4 = profile.Child4,
|
|
|
|
|
|
|
|
|
|
|
|
RootId = profile.RootId,
|
|
|
|
|
|
Child1Id = profile.Child1Id,
|
|
|
|
|
|
Child2Id = profile.Child2Id,
|
|
|
|
|
|
Child3Id = profile.Child3Id,
|
|
|
|
|
|
Child4Id = profile.Child4Id,
|
|
|
|
|
|
Gender = profile.Gender,
|
|
|
|
|
|
|
|
|
|
|
|
ProfileId = profile.Id,
|
|
|
|
|
|
ProfileType = profile.ProfileType,
|
|
|
|
|
|
|
|
|
|
|
|
RootDnaId = profile.RootDnaId,
|
|
|
|
|
|
Child1DnaId = profile.Child1DnaId,
|
|
|
|
|
|
Child2DnaId = profile.Child2DnaId,
|
|
|
|
|
|
Child3DnaId = profile.Child3DnaId,
|
|
|
|
|
|
Child4DnaId = profile.Child4DnaId,
|
|
|
|
|
|
};
|
2025-05-13 12:46:25 +07:00
|
|
|
|
|
2026-01-30 09:49:54 +07:00
|
|
|
|
var startTime = "";
|
|
|
|
|
|
var endTime = "";
|
|
|
|
|
|
if (!data.IsLocation && data.LocationName == "ไปประชุม / อบรม / สัมมนา")
|
|
|
|
|
|
{
|
|
|
|
|
|
//startTime = "09:30";
|
|
|
|
|
|
startTime = "10:30";
|
|
|
|
|
|
endTime = "12:00";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
startTime = duty.StartTimeMorning;
|
|
|
|
|
|
endTime = duty.EndTimeMorning;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string checkInStatus = "NORMAL";
|
|
|
|
|
|
var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(userId, currentDate.Date);
|
|
|
|
|
|
if (leaveReq != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var leaveRange = leaveReq.LeaveRange == null ? "" : leaveReq.LeaveRange.ToUpper();
|
|
|
|
|
|
if (leaveRange == "MORNING" || leaveRange == "ALL")
|
|
|
|
|
|
checkInStatus = "NORMAL";
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
checkInStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >
|
|
|
|
|
|
DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {startTime}") ?
|
|
|
|
|
|
DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >
|
|
|
|
|
|
DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
|
|
|
|
|
|
"ABSENT" :
|
|
|
|
|
|
"LATE" :
|
|
|
|
|
|
"NORMAL";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-09 19:10:16 +07:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
checkInStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >
|
|
|
|
|
|
DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {startTime}") ?
|
|
|
|
|
|
DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >
|
|
|
|
|
|
DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
|
|
|
|
|
|
"ABSENT" :
|
|
|
|
|
|
"LATE" :
|
|
|
|
|
|
"NORMAL";
|
|
|
|
|
|
}
|
2026-01-30 09:49:54 +07:00
|
|
|
|
|
2024-08-19 16:00:00 +07:00
|
|
|
|
|
2026-01-30 09:49:54 +07:00
|
|
|
|
// 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,
|
|
|
|
|
|
CitizenId = profile.CitizenId,
|
|
|
|
|
|
|
|
|
|
|
|
Root = profile.Root,
|
|
|
|
|
|
Child1 = profile.Child1,
|
|
|
|
|
|
Child2 = profile.Child2,
|
|
|
|
|
|
Child3 = profile.Child3,
|
|
|
|
|
|
Child4 = profile.Child4,
|
|
|
|
|
|
|
|
|
|
|
|
RootId = profile.RootId,
|
|
|
|
|
|
Child1Id = profile.Child1Id,
|
|
|
|
|
|
Child2Id = profile.Child2Id,
|
|
|
|
|
|
Child3Id = profile.Child3Id,
|
|
|
|
|
|
Child4Id = profile.Child4Id,
|
|
|
|
|
|
Gender = profile.Gender,
|
|
|
|
|
|
|
|
|
|
|
|
ProfileId = profile.Id,
|
|
|
|
|
|
ProfileType = profile.ProfileType,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
RootDnaId = profile.RootDnaId,
|
|
|
|
|
|
Child1DnaId = profile.Child1DnaId,
|
|
|
|
|
|
Child2DnaId = profile.Child2DnaId,
|
|
|
|
|
|
Child3DnaId = profile.Child3DnaId,
|
|
|
|
|
|
Child4DnaId = profile.Child4DnaId,
|
|
|
|
|
|
};
|
2024-08-19 16:00:00 +07:00
|
|
|
|
|
2026-01-30 09:49:54 +07:00
|
|
|
|
await _userTimeStampRepository.AddAsync(checkin);
|
|
|
|
|
|
await _processUserTimeStampRepository.AddAsync(checkin_process);
|
2024-08-19 16:00:00 +07:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-01-30 09:49:54 +07:00
|
|
|
|
var checkout = await _userTimeStampRepository.GetByIdAsync(data.CheckInId.Value);
|
2026-02-19 15:07:04 +07:00
|
|
|
|
//var currentCheckIn = await _userTimeStampRepository.GetTimestampByDateAsync(userId, currentDate);
|
2026-02-05 10:39:57 +07:00
|
|
|
|
|
|
|
|
|
|
if (checkout == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลการลงเวลาทำงาน");
|
2026-02-05 11:57:19 +07:00
|
|
|
|
|
|
|
|
|
|
// send notification to user
|
|
|
|
|
|
var noti1 = new Notification
|
|
|
|
|
|
{
|
|
|
|
|
|
Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่พบข้อมูลการลงเวลาทำงาน",
|
|
|
|
|
|
ReceiverUserId = profile.Id,
|
|
|
|
|
|
Type = "",
|
|
|
|
|
|
Payload = "",
|
|
|
|
|
|
};
|
|
|
|
|
|
_appDbContext.Set<Notification>().Add(noti1);
|
|
|
|
|
|
await _appDbContext.SaveChangesAsync();
|
|
|
|
|
|
|
2026-02-05 10:39:57 +07:00
|
|
|
|
return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound);
|
|
|
|
|
|
}
|
2026-01-09 19:10:16 +07:00
|
|
|
|
|
2026-01-30 09:49:54 +07:00
|
|
|
|
var currentCheckInProcess = await _processUserTimeStampRepository.GetTimestampByDateAsync(userId, checkout.CheckIn.Date);
|
2026-02-05 10:39:57 +07:00
|
|
|
|
|
|
|
|
|
|
if (currentCheckInProcess == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลการประมวลผลเวลาทำงาน (CheckIn)");
|
2026-02-05 11:57:19 +07:00
|
|
|
|
|
|
|
|
|
|
// send notification to user
|
|
|
|
|
|
var noti1 = new Notification
|
|
|
|
|
|
{
|
|
|
|
|
|
Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่พบข้อมูลการประมวลผลเวลาทำงาน (CheckIn)",
|
|
|
|
|
|
ReceiverUserId = profile.Id,
|
|
|
|
|
|
Type = "",
|
|
|
|
|
|
Payload = "",
|
|
|
|
|
|
};
|
|
|
|
|
|
_appDbContext.Set<Notification>().Add(noti1);
|
|
|
|
|
|
await _appDbContext.SaveChangesAsync();
|
|
|
|
|
|
|
2026-02-05 10:39:57 +07:00
|
|
|
|
return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound);
|
|
|
|
|
|
}
|
2026-01-14 16:38:55 +07:00
|
|
|
|
|
2026-01-30 09:49:54 +07:00
|
|
|
|
var checkout_process = await _processUserTimeStampRepository.GetByIdAsync(currentCheckInProcess.Id);
|
2026-01-16 16:19:49 +07:00
|
|
|
|
|
2026-02-05 10:39:57 +07:00
|
|
|
|
// Update checkout record
|
|
|
|
|
|
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;
|
2026-01-30 09:49:54 +07:00
|
|
|
|
|
2026-02-05 10:39:57 +07:00
|
|
|
|
await _userTimeStampRepository.UpdateAsync(checkout);
|
2026-01-30 09:49:54 +07:00
|
|
|
|
|
|
|
|
|
|
var endTime = "";
|
|
|
|
|
|
var startTime = "";
|
|
|
|
|
|
var endTimeMorning = "";
|
|
|
|
|
|
if (!data.IsLocation && data.LocationName == "ไปประชุม / อบรม / สัมมนา")
|
|
|
|
|
|
{
|
|
|
|
|
|
startTime = "13:00";
|
|
|
|
|
|
endTime = "14:30";
|
|
|
|
|
|
endTimeMorning = "12:00";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
endTime = duty.EndTimeAfternoon;
|
|
|
|
|
|
startTime = duty.StartTimeAfternoon;
|
|
|
|
|
|
endTimeMorning = duty.EndTimeMorning;
|
2026-01-16 16:19:49 +07:00
|
|
|
|
}
|
2026-01-30 09:49:54 +07:00
|
|
|
|
string checkOutStatus = "NORMAL";
|
|
|
|
|
|
var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(userId, currentDate.Date);
|
|
|
|
|
|
if (leaveReq != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var leaveRange = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper();
|
|
|
|
|
|
if (leaveRange == "AFTERNOON" || leaveRange == "ALL")
|
|
|
|
|
|
{
|
2026-03-27 09:48:10 +07:00
|
|
|
|
if (checkout.CheckIn.Date < currentDate.Date)
|
|
|
|
|
|
{
|
|
|
|
|
|
// ถ้า check-out เป็นวันถัดไป สถานะปกติเสมอ
|
2026-01-30 09:49:54 +07:00
|
|
|
|
checkOutStatus = "NORMAL";
|
2026-03-27 09:48:10 +07:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if(DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) <
|
|
|
|
|
|
DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning}"))
|
|
|
|
|
|
checkOutStatus = "ABSENT";
|
|
|
|
|
|
else
|
|
|
|
|
|
checkOutStatus = "NORMAL";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-30 09:49:54 +07:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921
|
2026-02-04 10:18:44 +07:00
|
|
|
|
var currentDateTime = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm"));
|
|
|
|
|
|
var dutyEndTimeAfternoon = DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {endTime}");
|
|
|
|
|
|
var dutyEndTimeMorning = DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {endTimeMorning}");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(currentDateTime.Date > checkout.CheckIn.Date)
|
|
|
|
|
|
{
|
|
|
|
|
|
// ถ้า check-out เป็นวันถัดไป สถานะปกติเสมอ
|
|
|
|
|
|
checkOutStatus = "NORMAL";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// ถ้า check-out เป็นวันเดียวกับ check-in
|
|
|
|
|
|
// ตรวจสอบเวลาว่าสิ้นสุดก่อนบ่ายหรือไม่
|
|
|
|
|
|
if(currentDateTime < dutyEndTimeMorning) // ถ้าออกก่อนเวลาสิ้นสุดตอนเช้า ขาดราชการ
|
|
|
|
|
|
{
|
|
|
|
|
|
checkOutStatus = "ABSENT";
|
|
|
|
|
|
}
|
|
|
|
|
|
else if(currentDateTime >= dutyEndTimeAfternoon) // ถ้าออกหลังเวลาสิ้นสุดตอนบ่าย ปกติ
|
|
|
|
|
|
{
|
|
|
|
|
|
checkOutStatus = "NORMAL";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
checkOutStatus = "ABSENT";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// checkOutStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) <
|
|
|
|
|
|
// DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
|
|
|
|
|
|
// // "ABSENT" :
|
|
|
|
|
|
// checkout.CheckIn.Date < currentDate.Date ? "NORMAL" :
|
|
|
|
|
|
// DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >=
|
|
|
|
|
|
// DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {endTime}") ?
|
|
|
|
|
|
// "NORMAL" :
|
|
|
|
|
|
// "ABSENT" :
|
|
|
|
|
|
// DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) <
|
|
|
|
|
|
// DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning}") ?
|
|
|
|
|
|
// "ABSENT" :
|
|
|
|
|
|
// "NORMAL";
|
2026-01-30 09:49:54 +07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-09 19:10:16 +07:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921
|
2026-02-04 10:18:44 +07:00
|
|
|
|
var currentDateTime = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm"));
|
|
|
|
|
|
var dutyEndTimeAfternoon = DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {endTime}");
|
|
|
|
|
|
var dutyEndTimeMorning = DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {endTimeMorning}");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(currentDateTime.Date > checkout.CheckIn.Date)
|
|
|
|
|
|
{
|
|
|
|
|
|
// ถ้า check-out เป็นวันถัดไป สถานะปกติเสมอ
|
|
|
|
|
|
checkOutStatus = "NORMAL";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// ถ้า check-out เป็นวันเดียวกับ check-in
|
|
|
|
|
|
// ตรวจสอบเวลาว่าสิ้นสุดก่อนบ่ายหรือไม่
|
|
|
|
|
|
if(currentDateTime < dutyEndTimeMorning) // ถ้าออกก่อนเวลาสิ้นสุดตอนเช้า ขาดราชการ
|
|
|
|
|
|
{
|
|
|
|
|
|
checkOutStatus = "ABSENT";
|
|
|
|
|
|
}
|
|
|
|
|
|
else if(currentDateTime >= dutyEndTimeAfternoon) // ถ้าออกหลังเวลาสิ้นสุดตอนบ่าย ปกติ
|
|
|
|
|
|
{
|
|
|
|
|
|
checkOutStatus = "NORMAL";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
checkOutStatus = "ABSENT";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// checkOutStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) <
|
|
|
|
|
|
// DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
|
|
|
|
|
|
// // "ABSENT" :
|
|
|
|
|
|
// checkout.CheckIn.Date < currentDate.Date ? "NORMAL" :
|
|
|
|
|
|
// DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >=
|
|
|
|
|
|
// DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTime}") ?
|
|
|
|
|
|
// "NORMAL" :
|
|
|
|
|
|
// "ABSENT" :
|
|
|
|
|
|
// DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) <
|
|
|
|
|
|
// DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning}") ?
|
|
|
|
|
|
// "ABSENT" :
|
|
|
|
|
|
// "NORMAL";
|
2026-01-30 09:49:54 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
{
|
|
|
|
|
|
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลการประมวลผลเวลาทำงาน");
|
2026-02-05 11:57:19 +07:00
|
|
|
|
// send notification to user
|
|
|
|
|
|
var noti1 = new Notification
|
|
|
|
|
|
{
|
|
|
|
|
|
Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่พบข้อมูลการประมวลผลเวลาทำงาน",
|
|
|
|
|
|
ReceiverUserId = profile.Id,
|
|
|
|
|
|
Type = "",
|
|
|
|
|
|
Payload = "",
|
|
|
|
|
|
};
|
|
|
|
|
|
_appDbContext.Set<Notification>().Add(noti1);
|
|
|
|
|
|
await _appDbContext.SaveChangesAsync();
|
2026-01-30 09:49:54 +07:00
|
|
|
|
return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound);
|
|
|
|
|
|
}
|
2024-08-19 16:00:00 +07:00
|
|
|
|
}
|
2026-01-30 09:49:54 +07:00
|
|
|
|
// อัปเดตสถานะเป็น COMPLETED
|
|
|
|
|
|
if (taskId != Guid.Empty)
|
2024-08-19 16:00:00 +07:00
|
|
|
|
{
|
2026-01-30 09:49:54 +07:00
|
|
|
|
var additionalData = JsonConvert.SerializeObject(new
|
|
|
|
|
|
{
|
|
|
|
|
|
CheckInType = data.CheckInId == null ? "check-in" : "check-out",
|
|
|
|
|
|
FileName = fileName,
|
|
|
|
|
|
ProcessedDate = currentDate
|
|
|
|
|
|
});
|
|
|
|
|
|
await _checkInJobStatusRepository.UpdateToCompletedAsync(taskId, additionalData);
|
2024-08-19 16:00:00 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-30 09:49:54 +07:00
|
|
|
|
var checkInType = data.CheckInId == null ? "check-in" : "check-out";
|
|
|
|
|
|
return Success(new { user = $"{profile.FirstName} {profile.LastName}", date = currentDate, type = checkInType }); ;
|
2024-08-19 16:00:00 +07:00
|
|
|
|
}
|
2026-01-30 09:49:54 +07:00
|
|
|
|
catch (Exception ex)
|
2026-01-20 10:49:13 +07:00
|
|
|
|
{
|
2026-01-30 09:49:54 +07:00
|
|
|
|
// อัปเดตสถานะเป็น FAILED
|
|
|
|
|
|
if (taskId != Guid.Empty)
|
2026-01-20 10:49:13 +07:00
|
|
|
|
{
|
2026-01-30 09:49:54 +07:00
|
|
|
|
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, ex.Message);
|
|
|
|
|
|
}
|
2026-02-05 10:39:57 +07:00
|
|
|
|
return Error(ex);
|
2026-01-20 10:49:13 +07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-08-19 16:00:00 +07:00
|
|
|
|
|
|
|
|
|
|
/// <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)
|
2023-11-13 15:26:14 +07:00
|
|
|
|
{
|
2023-12-21 10:25:11 +07:00
|
|
|
|
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
|
2026-04-17 09:41:52 +07:00
|
|
|
|
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
|
2023-12-21 10:25:11 +07:00
|
|
|
|
|
|
|
|
|
|
if (profile == null)
|
|
|
|
|
|
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
|
|
|
|
|
|
|
2023-11-13 15:26:14 +07:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-21 10:25:11 +07:00
|
|
|
|
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;
|
|
|
|
|
|
|
2023-11-13 15:26:14 +07:00
|
|
|
|
// create check in object
|
|
|
|
|
|
if (data.CheckInId == null)
|
|
|
|
|
|
{
|
2023-11-15 14:02:21 +07:00
|
|
|
|
// validate duplicate check in
|
|
|
|
|
|
var currentCheckIn = await _userTimeStampRepository.GetTimestampByDateAsync(Guid.Parse(UserId), currentDate);
|
2023-11-23 17:04:45 +07:00
|
|
|
|
|
2023-11-23 10:35:55 +07:00
|
|
|
|
|
|
|
|
|
|
|
2023-11-15 14:02:21 +07:00
|
|
|
|
if (currentCheckIn != null)
|
|
|
|
|
|
{
|
2024-01-15 10:52:39 +07:00
|
|
|
|
return Error(new Exception("ไม่สามารถลงเวลาได้ เนื่องจากมีการลงเวลาในวันนี้แล้ว!"), StatusCodes.Status400BadRequest);
|
2023-11-15 14:02:21 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-11-13 15:26:14 +07:00
|
|
|
|
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,
|
2024-07-11 14:52:54 +07:00
|
|
|
|
CheckIn = currentDate,
|
|
|
|
|
|
Prefix = profile.Prefix,
|
|
|
|
|
|
FirstName = profile.FirstName,
|
|
|
|
|
|
LastName = profile.LastName,
|
2023-11-13 15:26:14 +07:00
|
|
|
|
};
|
|
|
|
|
|
|
2023-12-21 10:25:11 +07:00
|
|
|
|
var checkInStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >
|
|
|
|
|
|
DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
|
2024-01-15 10:52:39 +07:00
|
|
|
|
|
|
|
|
|
|
DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >
|
|
|
|
|
|
DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
|
|
|
|
|
|
"ABSENT" :
|
2023-12-21 10:25:11 +07:00
|
|
|
|
"LATE" :
|
|
|
|
|
|
"NORMAL";
|
|
|
|
|
|
|
2023-11-23 10:35:55 +07:00
|
|
|
|
// 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,
|
2023-12-21 10:25:11 +07:00
|
|
|
|
CheckIn = currentDate,
|
2024-07-11 14:52:54 +07:00
|
|
|
|
CheckInStatus = checkInStatus,
|
|
|
|
|
|
Prefix = profile.Prefix,
|
|
|
|
|
|
FirstName = profile.FirstName,
|
|
|
|
|
|
LastName = profile.LastName,
|
2023-11-23 10:35:55 +07:00
|
|
|
|
};
|
|
|
|
|
|
|
2023-11-13 15:26:14 +07:00
|
|
|
|
await _userTimeStampRepository.AddAsync(checkin);
|
2023-11-23 10:35:55 +07:00
|
|
|
|
await _processUserTimeStampRepository.AddAsync(checkin_process);
|
2023-11-13 15:26:14 +07:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2024-01-11 10:13:56 +07:00
|
|
|
|
|
|
|
|
|
|
|
2023-11-13 15:26:14 +07:00
|
|
|
|
var checkout = await _userTimeStampRepository.GetByIdAsync(data.CheckInId.Value);
|
2024-01-11 10:13:56 +07:00
|
|
|
|
|
|
|
|
|
|
var currentCheckInProcess = await _processUserTimeStampRepository.GetTimestampByDateAsync(Guid.Parse(UserId), checkout.CheckIn.Date);
|
|
|
|
|
|
|
2023-11-23 10:35:55 +07:00
|
|
|
|
var checkout_process = await _processUserTimeStampRepository.GetByIdAsync(currentCheckInProcess.Id);
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-11-13 15:26:14 +07:00
|
|
|
|
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;
|
2023-11-14 10:29:07 +07:00
|
|
|
|
|
|
|
|
|
|
await _userTimeStampRepository.UpdateAsync(checkout);
|
2023-11-13 15:26:14 +07:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2023-11-24 14:21:33 +07:00
|
|
|
|
return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound);
|
2023-11-13 15:26:14 +07:00
|
|
|
|
}
|
2023-11-23 10:35:55 +07:00
|
|
|
|
|
2023-12-21 10:25:11 +07:00
|
|
|
|
var checkOutStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) <
|
|
|
|
|
|
DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
|
2024-01-15 10:52:39 +07:00
|
|
|
|
"ABSENT" :
|
2023-12-21 10:25:11 +07:00
|
|
|
|
DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) <
|
|
|
|
|
|
DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
|
|
|
|
|
|
"ABSENT" :
|
|
|
|
|
|
"NORMAL";
|
|
|
|
|
|
|
2023-11-23 10:35:55 +07:00
|
|
|
|
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;
|
2023-12-21 10:25:11 +07:00
|
|
|
|
checkout_process.CheckOutStatus = checkOutStatus;
|
2023-11-23 10:35:55 +07:00
|
|
|
|
|
|
|
|
|
|
await _processUserTimeStampRepository.UpdateAsync(checkout_process);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2023-11-24 14:21:33 +07:00
|
|
|
|
return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound);
|
2023-11-23 10:35:55 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-13 15:26:14 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return Success(new { date = currentDate });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-13 16:56:17 +07:00
|
|
|
|
/// <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);
|
2023-11-14 10:29:07 +07:00
|
|
|
|
|
2026-04-17 09:41:52 +07:00
|
|
|
|
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
|
2023-11-24 14:21:33 +07:00
|
|
|
|
if (profile == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
|
|
|
|
|
|
}
|
2023-11-14 10:29:07 +07:00
|
|
|
|
|
2023-11-24 14:21:33 +07:00
|
|
|
|
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
|
|
|
|
|
|
if (defaultRound == null)
|
2023-11-14 10:29:07 +07:00
|
|
|
|
{
|
2023-11-24 14:21:33 +07:00
|
|
|
|
return Error("ไม่พบรอบการลงเวลาทำงาน Default", StatusCodes.Status404NotFound);
|
2023-11-14 10:29:07 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-18 15:50:42 +07:00
|
|
|
|
var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id);
|
|
|
|
|
|
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
|
|
|
|
|
|
var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
|
2023-11-24 14:21:33 +07:00
|
|
|
|
|
|
|
|
|
|
// TODO : รอดุึงรอบที่ผูกกับ user
|
|
|
|
|
|
var duty = userRound ?? defaultRound;
|
|
|
|
|
|
|
2023-11-14 10:29:07 +07:00
|
|
|
|
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}");
|
|
|
|
|
|
|
2023-12-17 11:05:48 +07:00
|
|
|
|
|
2025-07-07 12:01:34 +07:00
|
|
|
|
// var test = await _processUserTimeStampRepository.GetTimeStampHistoryAsync(userId, year);
|
|
|
|
|
|
// return Success(test);
|
|
|
|
|
|
|
2026-03-11 11:57:00 +07:00
|
|
|
|
var data = (await _processUserTimeStampRepository.GetTimeStampHistoryAsync2(userId, year))
|
2023-11-13 16:56:17 +07:00
|
|
|
|
.Select(d => new CheckInHistoryDto
|
|
|
|
|
|
{
|
|
|
|
|
|
CheckInId = d.Id,
|
|
|
|
|
|
|
|
|
|
|
|
CheckInDate = d.CheckIn.Date,
|
2023-11-15 15:38:39 +07:00
|
|
|
|
CheckInTime = d.CheckIn.ToString("HH:mm:ss"),
|
2023-11-13 16:56:17 +07:00
|
|
|
|
CheckInLocation = d.CheckInPOI,
|
2024-01-10 10:08:34 +07:00
|
|
|
|
CheckInStatus = d.CheckInStatus != null || d.CheckInStatus != "" ? d.CheckInStatus :
|
|
|
|
|
|
DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
|
2023-11-14 10:29:07 +07:00
|
|
|
|
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
|
2023-11-15 14:02:21 +07:00
|
|
|
|
"LATE" :
|
2023-11-14 10:29:07 +07:00
|
|
|
|
"NORMAL",
|
2023-12-21 09:58:46 +07:00
|
|
|
|
CheckInIsLocation = d.IsLocationCheckIn,
|
2026-01-29 10:07:29 +07:00
|
|
|
|
CheckInLocationName = d.CheckInLocationName ?? "",
|
2023-11-13 16:56:17 +07:00
|
|
|
|
CheckOutDate = d.CheckOut == null ? null : d.CheckOut.Value.Date,
|
2023-11-15 15:38:39 +07:00
|
|
|
|
CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm:ss"),
|
2023-11-13 16:56:17 +07:00
|
|
|
|
CheckOutLocation = d.CheckOutPOI ?? "",
|
2023-12-18 10:46:39 +07:00
|
|
|
|
CheckOutStatus = d.CheckOut == null ? null :
|
2024-01-10 10:08:34 +07:00
|
|
|
|
d.CheckOutStatus != null || d.CheckOutStatus != "" ? d.CheckOutStatus :
|
2023-12-18 10:46:39 +07:00
|
|
|
|
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
|
2023-11-15 14:02:21 +07:00
|
|
|
|
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
|
|
|
|
|
|
"LATE" :
|
2023-12-18 10:46:39 +07:00
|
|
|
|
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
|
|
|
|
|
|
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
|
|
|
|
|
|
"ABSENT" :
|
2023-11-14 10:29:07 +07:00
|
|
|
|
"NORMAL",
|
2023-11-13 16:56:17 +07:00
|
|
|
|
|
2023-12-21 09:58:46 +07:00
|
|
|
|
CheckOutIsLocation = d.IsLocationCheckOut,
|
2026-01-29 10:07:29 +07:00
|
|
|
|
CheckOutLocationName = d.CheckOutLocationName ?? "",
|
2023-12-21 09:58:46 +07:00
|
|
|
|
|
2023-12-17 11:05:48 +07:00
|
|
|
|
IsEdit = _processUserTimeStampRepository.IsEditRequest(userId, d.CheckIn.Date)
|
|
|
|
|
|
|
|
|
|
|
|
//IsEdit = (d.EditStatus != null && d.EditStatus != "")
|
2023-11-29 06:07:57 +07:00
|
|
|
|
|
2023-12-17 11:05:48 +07:00
|
|
|
|
//EditReason = d.EditReason ?? "",ß
|
2023-11-29 06:07:57 +07:00
|
|
|
|
//EditStatus = d.EditStatus ?? ""
|
2023-11-24 16:52:48 +07:00
|
|
|
|
|
2023-11-13 16:56:17 +07:00
|
|
|
|
})
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
2023-11-24 14:21:33 +07:00
|
|
|
|
if (keyword != "")
|
|
|
|
|
|
{
|
2025-10-02 19:50:55 +07:00
|
|
|
|
data = data.Where(x => (x.CheckInLocationName!.Contains(keyword) || x.CheckInLocation!.Contains(keyword) ||
|
|
|
|
|
|
x.CheckOutLocationName!.Contains(keyword) || x.CheckOutLocation!.Contains(keyword)))
|
|
|
|
|
|
.ToList();
|
2023-11-24 14:21:33 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var pageData = data
|
|
|
|
|
|
.Skip((page - 1) * pageSize)
|
|
|
|
|
|
.Take(pageSize)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
return Success(new { data = pageData, total = data.Count });
|
2023-11-13 16:56:17 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-13 16:58:38 +07:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// LV1_010 - รายการลงเวลาปฏิบัติงาน (ADMIN)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
2023-11-13 16:56:17 +07:00
|
|
|
|
[HttpGet("log-record")]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
2025-10-06 15:12:40 +07:00
|
|
|
|
public async Task<ActionResult<ResponseObject>> LogRecordAsync([Required] DateTime startDate, [Required] DateTime endDate, int page = 1, int pageSize = 10, string keyword = "", string profileType = "ALL", string? sortBy = "", bool? descending = false)
|
2023-11-13 16:56:17 +07:00
|
|
|
|
{
|
2024-09-24 11:53:09 +07:00
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_CHECKIN");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-15 15:38:39 +07:00
|
|
|
|
if (startDate.Date > endDate.Date)
|
|
|
|
|
|
{
|
2023-11-24 14:21:33 +07:00
|
|
|
|
return Error(new Exception("วันเริ่มต้นต้องมีค่าน้อยกว่าหรือเท่ากับวันสิ้นสุด"), StatusCodes.Status400BadRequest);
|
2023-11-15 15:38:39 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-24 14:21:33 +07:00
|
|
|
|
//var count = await _userTimeStampRepository.CountRecordAsync();
|
2023-11-23 10:35:55 +07:00
|
|
|
|
|
2023-11-15 15:38:39 +07:00
|
|
|
|
|
2023-11-13 16:56:17 +07:00
|
|
|
|
var imgUrl = $"{_configuration["MinIO:Endpoint"]}{_configuration["MinIO:BucketName"]}";
|
2025-06-24 14:02:44 +07:00
|
|
|
|
string role = jsonData["result"]?.ToString();
|
|
|
|
|
|
var nodeId = string.Empty;
|
|
|
|
|
|
var profileAdmin = new GetUserOCAllDto();
|
|
|
|
|
|
profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken);
|
|
|
|
|
|
if (role == "NORMAL" || role == "CHILD")
|
|
|
|
|
|
{
|
|
|
|
|
|
nodeId = profileAdmin?.Node == 4
|
|
|
|
|
|
? profileAdmin?.Child4DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 3
|
|
|
|
|
|
? profileAdmin?.Child3DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 2
|
|
|
|
|
|
? profileAdmin?.Child2DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 1
|
|
|
|
|
|
? profileAdmin?.Child1DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 0
|
|
|
|
|
|
? profileAdmin?.RootDnaId
|
|
|
|
|
|
: "";
|
|
|
|
|
|
}
|
2025-12-12 01:36:06 +07:00
|
|
|
|
else if (role == "BROHTER")
|
|
|
|
|
|
{
|
|
|
|
|
|
nodeId = profileAdmin?.Node == 4
|
|
|
|
|
|
? profileAdmin?.Child3DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 3
|
|
|
|
|
|
? profileAdmin?.Child2DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 2
|
|
|
|
|
|
? profileAdmin?.Child1DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 1
|
|
|
|
|
|
? profileAdmin?.RootDnaId
|
|
|
|
|
|
: "";
|
|
|
|
|
|
}
|
2026-02-19 17:01:44 +07:00
|
|
|
|
else if (role == "ROOT" /*|| role == "PARENT"*/)
|
2025-06-24 14:02:44 +07:00
|
|
|
|
{
|
|
|
|
|
|
nodeId = profileAdmin?.RootDnaId;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//var data = (await _userTimeStampRepository.GetTimeStampHistoryForAdminAsync(startDate, endDate))
|
|
|
|
|
|
var data = (await _userTimeStampRepository.GetTimeStampHistoryForAdminRoleAsync(startDate, endDate, role, nodeId, profileAdmin?.Node))
|
2023-11-13 16:56:17 +07:00
|
|
|
|
.Select(d => new CheckInHistoryForAdminDto
|
|
|
|
|
|
{
|
2023-11-15 15:38:39 +07:00
|
|
|
|
Id = d.Id,
|
2024-07-11 14:52:54 +07:00
|
|
|
|
//FullName = _userProfileRepository.GetUserFullName(d.KeycloakUserId, AccessToken),
|
2024-08-26 11:58:42 +07:00
|
|
|
|
FullName = $"{d.Prefix ?? ""}{d.FirstName ?? ""} {d.LastName ?? ""}",
|
2025-10-06 15:12:40 +07:00
|
|
|
|
Prefix = d.Prefix ?? "",
|
|
|
|
|
|
FirstName = d.FirstName ?? "",
|
|
|
|
|
|
LastName = d.LastName ?? "",
|
2025-01-27 14:18:22 +07:00
|
|
|
|
ProfileType = d.ProfileType ?? "",
|
2023-11-13 16:56:17 +07:00
|
|
|
|
|
|
|
|
|
|
CheckInDate = d.CheckIn.Date,
|
2023-11-15 15:38:39 +07:00
|
|
|
|
CheckInTime = d.CheckIn.ToString("HH:mm:ss"),
|
2023-11-13 16:56:17 +07:00
|
|
|
|
CheckInLocation = d.CheckInPOI,
|
|
|
|
|
|
CheckInLat = d.CheckInLat,
|
|
|
|
|
|
CheckInLon = d.CheckInLon,
|
2023-11-24 16:52:48 +07:00
|
|
|
|
CheckInImage = $"{imgUrl}/{d.CheckInImageUrl}",
|
2023-11-13 16:56:17 +07:00
|
|
|
|
|
2023-12-13 09:17:43 +07:00
|
|
|
|
// add from new specification
|
|
|
|
|
|
IsLocationCheckIn = d.IsLocationCheckIn,
|
|
|
|
|
|
CheckInLocationName = d.CheckInLocationName ?? "",
|
|
|
|
|
|
|
2024-08-26 11:58:42 +07:00
|
|
|
|
CheckOutDate = d.CheckOut?.Date,
|
2023-11-15 15:38:39 +07:00
|
|
|
|
CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm:ss"),
|
2023-11-13 16:56:17 +07:00
|
|
|
|
CheckOutLocation = d.CheckOut == null ? "" : d.CheckOutPOI,
|
|
|
|
|
|
CheckOutLat = d.CheckOut == null ? null : d.CheckOutLat,
|
|
|
|
|
|
CheckOutLon = d.CheckOut == null ? null : d.CheckOutLon,
|
2023-11-24 16:52:48 +07:00
|
|
|
|
CheckOutImage = d.CheckOut == null ? "" : $"{imgUrl}/{d.CheckOutImageUrl}",
|
2023-12-13 09:17:43 +07:00
|
|
|
|
|
|
|
|
|
|
// add from new specification
|
|
|
|
|
|
IsLocationCheckOut = d.IsLocationCheckOut,
|
|
|
|
|
|
CheckOutLocationName = d.CheckOutLocationName ?? ""
|
2023-11-13 16:56:17 +07:00
|
|
|
|
})
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
2023-11-24 14:21:33 +07:00
|
|
|
|
if (keyword != "")
|
|
|
|
|
|
{
|
|
|
|
|
|
data = data.Where(x => x.FullName.Contains(keyword)).ToList();
|
|
|
|
|
|
}
|
2023-11-15 15:38:39 +07:00
|
|
|
|
|
2025-01-27 16:33:00 +07:00
|
|
|
|
if (profileType.Trim().ToUpper() != "ALL")
|
|
|
|
|
|
data = data.Where(x => x.ProfileType == profileType.Trim().ToUpper()).ToList();
|
|
|
|
|
|
|
2025-10-06 15:12:40 +07:00
|
|
|
|
if (!string.IsNullOrWhiteSpace(sortBy))
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (sortBy.ToUpper())
|
|
|
|
|
|
{
|
|
|
|
|
|
case "FULLNAME":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
data = data.OrderByDescending(x => x.Prefix)
|
|
|
|
|
|
.ThenByDescending(x => x.FirstName)
|
|
|
|
|
|
.ThenByDescending(x => x.LastName)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
data = data.OrderBy(x => x.Prefix)
|
|
|
|
|
|
.ThenBy(x => x.FirstName)
|
|
|
|
|
|
.ThenBy(x => x.LastName)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "CHECKINTIME":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
data = data.OrderByDescending(x => x.CheckInTime).ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
data = data.OrderBy(x => x.CheckInTime).ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "CHECKINLOCATION":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
data = data.OrderByDescending(x => x.CheckInLocation)
|
|
|
|
|
|
.ThenByDescending(x => x.CheckInLat)
|
|
|
|
|
|
.ThenByDescending(x => x.CheckInLon)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
data = data.OrderBy(x => x.CheckInLocation)
|
|
|
|
|
|
.ThenBy(x => x.CheckInLat)
|
|
|
|
|
|
.ThenBy(x => x.CheckInLon)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "CHECKOUTTIME":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
data = data.OrderByDescending(x => x.CheckOutTime).ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
data = data.OrderBy(x => x.CheckOutTime).ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "CHECKOUTLOCATION":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
data = data.OrderByDescending(x => x.CheckOutLocation)
|
|
|
|
|
|
.ThenByDescending(x => x.CheckOutLat)
|
|
|
|
|
|
.ThenByDescending(x => x.CheckOutLon)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
data = data.OrderBy(x => x.CheckOutLocation)
|
|
|
|
|
|
.ThenBy(x => x.CheckOutLat)
|
|
|
|
|
|
.ThenBy(x => x.CheckOutLon)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
default: break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-11-24 14:21:33 +07:00
|
|
|
|
var pageData = data
|
|
|
|
|
|
.Skip((page - 1) * pageSize)
|
|
|
|
|
|
.Take(pageSize)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Success(new { data = pageData, total = data.Count });
|
2023-11-13 16:56:17 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-15 14:29:02 +07:00
|
|
|
|
/// <summary>
|
2023-11-28 09:53:38 +07:00
|
|
|
|
/// LV1_011 - รายละเอียดการลงเวลาปฎิบัติงานรายบุคคล Tabรายากรลงเวลาที่ประมวลผลแล้ว (ADMIN)
|
2023-11-15 14:29:02 +07:00
|
|
|
|
/// </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)
|
|
|
|
|
|
{
|
2024-10-22 08:20:33 +07:00
|
|
|
|
var getWorkflow = await _permission.GetPermissionAPIWorkflowAsync(id.ToString(), "SYS_CHECKIN");
|
|
|
|
|
|
if (getWorkflow == false)
|
2024-09-24 11:53:09 +07:00
|
|
|
|
{
|
2024-10-22 08:20:33 +07:00
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("GET", "SYS_CHECKIN");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
2024-09-24 11:53:09 +07:00
|
|
|
|
}
|
2023-11-24 14:21:33 +07:00
|
|
|
|
|
2024-09-24 11:53:09 +07:00
|
|
|
|
var imgUrl = $"{_configuration["MinIO:Endpoint"]}{_configuration["MinIO:BucketName"]}";
|
2023-11-24 14:21:33 +07:00
|
|
|
|
|
2023-11-28 09:53:38 +07:00
|
|
|
|
var d = (await _processUserTimeStampRepository.GetTimeStampById(id));
|
2023-11-15 15:38:39 +07:00
|
|
|
|
if (d == null)
|
2023-11-15 14:29:02 +07:00
|
|
|
|
{
|
|
|
|
|
|
throw new Exception(GlobalMessages.DataNotFound);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-04-17 09:41:52 +07:00
|
|
|
|
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(d.KeycloakUserId, AccessToken);
|
2023-11-24 14:21:33 +07:00
|
|
|
|
if (profile == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
|
|
|
|
|
|
if (defaultRound == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-18 15:50:42 +07:00
|
|
|
|
//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);
|
2023-11-24 14:21:33 +07:00
|
|
|
|
|
|
|
|
|
|
var duty = userRound ?? defaultRound;
|
|
|
|
|
|
|
2023-11-15 15:38:39 +07:00
|
|
|
|
var result = new CheckInDetailForAdminDto
|
2023-11-15 14:29:02 +07:00
|
|
|
|
{
|
2023-11-15 15:38:39 +07:00
|
|
|
|
Id = d.Id,
|
2024-07-11 14:52:54 +07:00
|
|
|
|
FullName = $"{d.Prefix}{d.FirstName} {d.LastName}",
|
|
|
|
|
|
|
2025-01-27 14:18:22 +07:00
|
|
|
|
ProfileType = (d.ProfileType != "" || d.ProfileType != null) ? d.ProfileType : (profile.ProfileType ?? ""),
|
|
|
|
|
|
|
2024-07-11 14:52:54 +07:00
|
|
|
|
//FullName = $"{profile.Prefix}{profile.FirstName} {profile.LastName}", // _userProfileRepository.GetUserFullName(d.KeycloakUserId, AccessToken),
|
2023-11-15 14:29:02 +07:00
|
|
|
|
|
|
|
|
|
|
CheckInDate = d.CheckIn.Date,
|
2023-11-24 14:21:33 +07:00
|
|
|
|
CheckInTime = d.CheckIn.ToString("HH:mm"),
|
|
|
|
|
|
CheckInPOI = d.CheckInPOI,
|
2023-11-15 14:29:02 +07:00
|
|
|
|
CheckInLat = d.CheckInLat,
|
|
|
|
|
|
CheckInLon = d.CheckInLon,
|
2025-12-18 14:08:05 +07:00
|
|
|
|
//CheckInImg = $"{imgUrl}/{d.CheckInImageUrl}",
|
|
|
|
|
|
CheckInImg = await _minIOService.ImagesPathByName(d.CheckInImageUrl),
|
2024-01-10 10:08:34 +07:00
|
|
|
|
CheckInStatus = d.CheckInStatus != null || d.CheckInStatus != "" ? d.CheckInStatus :
|
|
|
|
|
|
DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
|
2023-11-24 14:21:33 +07:00
|
|
|
|
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
|
2024-01-15 10:52:39 +07:00
|
|
|
|
|
|
|
|
|
|
DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
|
|
|
|
|
|
DateTime.Parse($"{d.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
|
|
|
|
|
|
"ABSENT" :
|
2023-11-24 14:21:33 +07:00
|
|
|
|
"LATE" :
|
2024-01-15 10:52:39 +07:00
|
|
|
|
|
2023-11-24 14:21:33 +07:00
|
|
|
|
"NORMAL",
|
|
|
|
|
|
CheckInDescription = d.CheckInRemark ?? "",
|
2024-01-05 16:46:52 +07:00
|
|
|
|
IsLocationCheckIn = d.IsLocationCheckIn,
|
|
|
|
|
|
CheckInLocationName = d.CheckInLocationName ?? "",
|
2023-11-15 14:29:02 +07:00
|
|
|
|
|
|
|
|
|
|
CheckOutDate = d.CheckOut == null ? null : d.CheckOut.Value.Date,
|
2023-11-24 14:21:33 +07:00
|
|
|
|
CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm"),
|
|
|
|
|
|
CheckOutPOI = d.CheckOut == null ? "" : d.CheckOutPOI,
|
2023-11-15 14:29:02 +07:00
|
|
|
|
CheckOutLat = d.CheckOut == null ? null : d.CheckOutLat,
|
|
|
|
|
|
CheckOutLon = d.CheckOut == null ? null : d.CheckOutLon,
|
2025-12-18 14:08:05 +07:00
|
|
|
|
CheckOutImg = d.CheckOut == null ? "" : await _minIOService.ImagesPathByName(d.CheckOutImageUrl),
|
2023-11-24 14:21:33 +07:00
|
|
|
|
|
|
|
|
|
|
CheckOutStatus = d.CheckOut == null ? null :
|
2024-01-10 10:08:34 +07:00
|
|
|
|
d.CheckOutStatus != null || d.CheckOutStatus != "" ? d.CheckOutStatus :
|
2023-11-24 14:21:33 +07:00
|
|
|
|
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
|
|
|
|
|
|
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
|
2024-01-15 10:52:39 +07:00
|
|
|
|
"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",
|
2023-11-24 14:21:33 +07:00
|
|
|
|
CheckOutDescription = d.CheckOutRemark ?? "",
|
2024-01-05 16:46:52 +07:00
|
|
|
|
|
|
|
|
|
|
IsLocationCheckOut = d.IsLocationCheckOut,
|
|
|
|
|
|
CheckOutLocationName = d.CheckOutLocationName ?? ""
|
2023-11-15 14:29:02 +07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return Success(result);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-23 10:35:55 +07:00
|
|
|
|
|
|
|
|
|
|
/// <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)]
|
2023-11-23 15:48:09 +07:00
|
|
|
|
[AllowAnonymous]
|
2025-10-06 15:12:40 +07:00
|
|
|
|
public async Task<ActionResult<ResponseObject>> GetTimeRecordAsync([Required] DateTime startDate, [Required] DateTime endDate, int page = 1, int pageSize = 10, string status = "NORMAL", string keyword = "", string profileType = "ALL", string? sortBy = "", bool? descending = false)
|
2023-11-23 10:35:55 +07:00
|
|
|
|
{
|
2024-09-24 11:53:09 +07:00
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_CHECKIN");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
2023-11-23 10:35:55 +07:00
|
|
|
|
if (startDate.Date > endDate.Date)
|
|
|
|
|
|
{
|
2023-11-24 14:21:33 +07:00
|
|
|
|
return Error(new Exception("วันเริ่มต้นต้องมีค่าน้อยกว่าหรือเท่ากับวันสิ้นสุด"), StatusCodes.Status400BadRequest);
|
2023-11-23 10:35:55 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-07-11 12:44:56 +07:00
|
|
|
|
var profiles = new List<GetProfileByKeycloakIdDto>();
|
2023-11-23 10:35:55 +07:00
|
|
|
|
|
2024-07-11 12:44:56 +07:00
|
|
|
|
//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);
|
|
|
|
|
|
//}
|
2023-11-23 10:35:55 +07:00
|
|
|
|
|
2023-11-24 14:21:33 +07:00
|
|
|
|
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
|
|
|
|
|
|
if (defaultRound == null)
|
2023-11-23 10:35:55 +07:00
|
|
|
|
{
|
2023-11-24 14:21:33 +07:00
|
|
|
|
return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound);
|
2023-11-23 10:35:55 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-18 15:50:42 +07:00
|
|
|
|
//var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty);
|
2024-07-11 12:44:56 +07:00
|
|
|
|
//var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id);
|
|
|
|
|
|
//var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
|
|
|
|
|
|
//var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
|
2023-11-24 14:21:33 +07:00
|
|
|
|
|
2024-07-11 12:44:56 +07:00
|
|
|
|
//var duty = userRound ?? defaultRound;
|
|
|
|
|
|
var duty = defaultRound;
|
2023-11-24 14:21:33 +07:00
|
|
|
|
|
2023-11-23 10:35:55 +07:00
|
|
|
|
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}");
|
|
|
|
|
|
|
2023-11-24 14:21:33 +07:00
|
|
|
|
//var count = await _processUserTimeStampRepository.GetTimeStampHistoryForAdminCountAsync(startDate, endDate);
|
2023-11-23 10:35:55 +07:00
|
|
|
|
|
|
|
|
|
|
var imgUrl = $"{_configuration["MinIO:Endpoint"]}{_configuration["MinIO:BucketName"]}";
|
2025-06-24 14:02:44 +07:00
|
|
|
|
string role = jsonData["result"]?.ToString();
|
|
|
|
|
|
var nodeId = string.Empty;
|
|
|
|
|
|
var profileAdmin = new GetUserOCAllDto();
|
|
|
|
|
|
profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken);
|
|
|
|
|
|
if (role == "NORMAL" || role == "CHILD")
|
|
|
|
|
|
{
|
|
|
|
|
|
nodeId = profileAdmin?.Node == 4
|
|
|
|
|
|
? profileAdmin?.Child4DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 3
|
|
|
|
|
|
? profileAdmin?.Child3DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 2
|
|
|
|
|
|
? profileAdmin?.Child2DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 1
|
|
|
|
|
|
? profileAdmin?.Child1DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 0
|
|
|
|
|
|
? profileAdmin?.RootDnaId
|
|
|
|
|
|
: "";
|
|
|
|
|
|
}
|
2025-12-12 01:36:06 +07:00
|
|
|
|
else if (role == "BROTHER")
|
|
|
|
|
|
{
|
|
|
|
|
|
nodeId = profileAdmin?.Node == 4
|
|
|
|
|
|
? profileAdmin?.Child3DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 3
|
|
|
|
|
|
? profileAdmin?.Child2DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 2
|
|
|
|
|
|
? profileAdmin?.Child1DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 1 || profileAdmin?.Node == 0
|
|
|
|
|
|
? profileAdmin?.RootDnaId
|
|
|
|
|
|
: "";
|
|
|
|
|
|
}
|
2026-02-19 17:01:44 +07:00
|
|
|
|
else if (role == "ROOT" /*|| role == "PARENT"*/)
|
2025-06-24 14:02:44 +07:00
|
|
|
|
{
|
|
|
|
|
|
nodeId = profileAdmin?.RootDnaId;
|
|
|
|
|
|
}
|
2025-10-12 16:51:51 +07:00
|
|
|
|
|
2025-06-24 14:02:44 +07:00
|
|
|
|
//var resultData = await _processUserTimeStampRepository.GetTimeStampHistoryForAdminAsync(startDate, endDate);
|
|
|
|
|
|
var resultData = await _processUserTimeStampRepository.GetTimeStampHistoryForAdminRoleAsync(startDate, endDate, role, nodeId, profileAdmin?.Node);
|
2024-07-10 13:45:45 +07:00
|
|
|
|
var data = new List<CheckInProcessHistoryForAdminDto>();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var d in resultData)
|
|
|
|
|
|
{
|
2024-07-11 14:52:54 +07:00
|
|
|
|
//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);
|
|
|
|
|
|
//}
|
2024-07-11 12:44:56 +07:00
|
|
|
|
|
|
|
|
|
|
//var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(d.KeycloakUserId, AccessToken);
|
|
|
|
|
|
//if (pf == null) continue;
|
2024-07-10 13:45:45 +07:00
|
|
|
|
data.Add(new CheckInProcessHistoryForAdminDto
|
|
|
|
|
|
{
|
|
|
|
|
|
Id = d.Id,
|
2024-08-26 11:58:42 +07:00
|
|
|
|
FullName = $"{d.Prefix ?? ""}{d.FirstName ?? ""} {d.LastName ?? ""}",
|
2025-10-06 15:12:40 +07:00
|
|
|
|
Prefix = d.Prefix ?? "",
|
|
|
|
|
|
FirstName = d.FirstName ?? "",
|
|
|
|
|
|
LastName = d.LastName ?? "",
|
2025-01-27 14:18:22 +07:00
|
|
|
|
ProfileType = d.ProfileType ?? "",
|
|
|
|
|
|
|
2024-07-10 13:45:45 +07:00
|
|
|
|
CheckInDate = d.CheckIn.Date,
|
|
|
|
|
|
CheckInTime = d.CheckIn.ToString("HH:mm"),
|
|
|
|
|
|
CheckInLocation = d.CheckInPOI,
|
|
|
|
|
|
CheckInLat = d.CheckInLat,
|
|
|
|
|
|
CheckInLon = d.CheckInLon,
|
|
|
|
|
|
CheckInStatus = d.CheckInStatus != "" ? d.CheckInStatus :
|
2024-01-08 09:51:00 +07:00
|
|
|
|
DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
|
2023-11-23 10:35:55 +07:00
|
|
|
|
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
|
2024-01-15 10:52:39 +07:00
|
|
|
|
|
|
|
|
|
|
DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
|
|
|
|
|
|
DateTime.Parse($"{d.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
|
|
|
|
|
|
"ABSENT" :
|
|
|
|
|
|
"LATE" :
|
2023-11-23 10:35:55 +07:00
|
|
|
|
"NORMAL",
|
2024-07-10 13:45:45 +07:00
|
|
|
|
CheckInIsLocation = d.IsLocationCheckIn,
|
2024-08-26 11:58:42 +07:00
|
|
|
|
CheckInLocationName = d.CheckInLocationName ?? "",
|
2024-07-10 13:45:45 +07:00
|
|
|
|
//CheckInImageUrl = $"{imgUrl}/{d.CheckInImageUrl}",
|
2023-11-23 10:35:55 +07:00
|
|
|
|
|
2024-08-26 11:58:42 +07:00
|
|
|
|
CheckOutDate = d.CheckOut?.Date,
|
2024-07-10 13:45:45 +07:00
|
|
|
|
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 :
|
2024-01-08 09:51:00 +07:00
|
|
|
|
d.CheckOut == null ? null : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
|
2023-11-23 10:35:55 +07:00
|
|
|
|
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
|
2024-01-15 10:52:39 +07:00
|
|
|
|
"ABSENT" :
|
2023-12-20 15:01:23 +07:00
|
|
|
|
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
|
|
|
|
|
|
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
|
|
|
|
|
|
"ABSENT" :
|
2023-11-23 10:35:55 +07:00
|
|
|
|
"NORMAL",
|
2024-07-10 13:45:45 +07:00
|
|
|
|
CheckOutIsLocation = d.IsLocationCheckOut,
|
2024-08-26 11:58:42 +07:00
|
|
|
|
CheckOutLocationName = d.CheckOutLocationName ?? ""
|
2024-07-10 13:45:45 +07:00
|
|
|
|
//CheckOutImageUrl = d.CheckOut == null ? "" : $"{imgUrl}/{d.CheckOutImageUrl}",
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2023-11-24 14:21:33 +07:00
|
|
|
|
|
2024-07-10 13:45:45 +07:00
|
|
|
|
|
|
|
|
|
|
// 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();
|
2023-11-23 17:04:45 +07:00
|
|
|
|
|
2023-11-24 14:21:33 +07:00
|
|
|
|
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();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-01-27 16:33:00 +07:00
|
|
|
|
if (profileType.Trim().ToUpper() != "ALL")
|
|
|
|
|
|
data = data.Where(x => x.ProfileType == profileType.Trim().ToUpper()).ToList();
|
|
|
|
|
|
|
2025-10-06 15:12:40 +07:00
|
|
|
|
if (!string.IsNullOrWhiteSpace(sortBy))
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (sortBy.ToUpper())
|
|
|
|
|
|
{
|
|
|
|
|
|
case "FULLNAME":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
data = data.OrderByDescending(x => x.Prefix)
|
|
|
|
|
|
.ThenByDescending(x => x.FirstName)
|
|
|
|
|
|
.ThenByDescending(x => x.LastName)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
data = data.OrderBy(x => x.Prefix)
|
|
|
|
|
|
.ThenBy(x => x.FirstName)
|
|
|
|
|
|
.ThenBy(x => x.LastName)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "CHECKINTIME":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
data = data.OrderByDescending(x => x.CheckInTime).ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
data = data.OrderBy(x => x.CheckInTime).ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "CHECKINLOCATION":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
data = data.OrderByDescending(x => x.CheckInLocation)
|
|
|
|
|
|
.ThenByDescending(x => x.CheckInLat)
|
|
|
|
|
|
.ThenByDescending(x => x.CheckInLon)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
data = data.OrderBy(x => x.CheckInLocation)
|
|
|
|
|
|
.ThenBy(x => x.CheckInLat)
|
|
|
|
|
|
.ThenBy(x => x.CheckInLon)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "CHECKOUTTIME":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
data = data.OrderByDescending(x => x.CheckOutTime).ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
data = data.OrderBy(x => x.CheckOutTime).ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "CHECKOUTLOCATION":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
data = data.OrderByDescending(x => x.CheckOutLocation)
|
|
|
|
|
|
.ThenByDescending(x => x.CheckOutLat)
|
|
|
|
|
|
.ThenByDescending(x => x.CheckOutLon)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
data = data.OrderBy(x => x.CheckOutLocation)
|
|
|
|
|
|
.ThenBy(x => x.CheckOutLat)
|
|
|
|
|
|
.ThenBy(x => x.CheckOutLon)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
default: break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-11-24 14:21:33 +07:00
|
|
|
|
var pageData = data
|
|
|
|
|
|
.Skip((page - 1) * pageSize)
|
|
|
|
|
|
.Take(pageSize)
|
|
|
|
|
|
.ToList();
|
2023-11-23 10:35:55 +07:00
|
|
|
|
|
2023-11-24 14:21:33 +07:00
|
|
|
|
return Success(new { data = pageData, total = data.Count });
|
2023-11-23 10:35:55 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-10 14:40:53 +07:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
2023-11-23 15:48:09 +07:00
|
|
|
|
#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)]
|
2024-07-11 13:06:52 +07:00
|
|
|
|
public async Task<ActionResult<ResponseObject>> SearchProfileAsync([FromBody] DTOs.ChangeRound.SearchProfileDto req)
|
2023-11-23 15:48:09 +07:00
|
|
|
|
{
|
2024-09-24 11:53:09 +07:00
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_WORK_ROUND_EDIT");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
2025-06-24 14:02:44 +07:00
|
|
|
|
string role = jsonData["result"]?.ToString();
|
|
|
|
|
|
var nodeId = string.Empty;
|
|
|
|
|
|
var profileAdmin = new GetUserOCAllDto();
|
|
|
|
|
|
profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken);
|
|
|
|
|
|
if (role == "NORMAL" || role == "CHILD")
|
|
|
|
|
|
{
|
|
|
|
|
|
nodeId = profileAdmin?.Node == 4
|
|
|
|
|
|
? profileAdmin?.Child4DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 3
|
|
|
|
|
|
? profileAdmin?.Child3DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 2
|
|
|
|
|
|
? profileAdmin?.Child2DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 1
|
|
|
|
|
|
? profileAdmin?.Child1DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 0
|
|
|
|
|
|
? profileAdmin?.RootDnaId
|
|
|
|
|
|
: "";
|
|
|
|
|
|
}
|
2025-12-12 01:36:06 +07:00
|
|
|
|
else if (role == "BROTHER")
|
|
|
|
|
|
{
|
|
|
|
|
|
nodeId = profileAdmin?.Node == 4
|
|
|
|
|
|
? profileAdmin?.Child3DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 3
|
|
|
|
|
|
? profileAdmin?.Child2DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 2
|
|
|
|
|
|
? profileAdmin?.Child1DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 1 || profileAdmin?.Node == 0
|
|
|
|
|
|
? profileAdmin?.RootDnaId
|
|
|
|
|
|
: "";
|
|
|
|
|
|
}
|
2026-02-19 17:01:44 +07:00
|
|
|
|
else if (role == "ROOT" /*|| role == "PARENT"*/)
|
2025-06-24 14:02:44 +07:00
|
|
|
|
{
|
|
|
|
|
|
nodeId = profileAdmin?.RootDnaId;
|
|
|
|
|
|
}
|
2026-03-23 10:40:54 +07:00
|
|
|
|
var profile = await _userProfileRepository.SearchProfile(req.CitizenId, req.FirstName, req.LastName, AccessToken ?? "", req.Page, req.PageSize, role, nodeId, profileAdmin?.Node, req.SelectedNodeId == null ? null : req.SelectedNodeId.Value.ToString("D"), req.SelectedNode);
|
2023-11-23 15:48:09 +07:00
|
|
|
|
|
2025-10-09 23:21:36 +07:00
|
|
|
|
// Get default round once
|
2023-12-27 09:20:09 +07:00
|
|
|
|
var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync();
|
2023-11-23 15:48:09 +07:00
|
|
|
|
|
|
|
|
|
|
var resultSet = new List<SearchProfileResultDto>();
|
|
|
|
|
|
|
2025-10-09 23:21:36 +07:00
|
|
|
|
// Create dictionaries to cache results and avoid duplicate queries
|
|
|
|
|
|
var effectiveDateCache = new Dictionary<Guid, UserDutyTime?>();
|
|
|
|
|
|
var dutyTimeCache = new Dictionary<Guid, DutyTime?>();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var p in profile.Data)
|
2023-11-23 15:48:09 +07:00
|
|
|
|
{
|
2025-10-09 23:21:36 +07:00
|
|
|
|
// Use cache for effective date
|
|
|
|
|
|
if (!effectiveDateCache.ContainsKey(p.Id))
|
|
|
|
|
|
{
|
|
|
|
|
|
effectiveDateCache[p.Id] = await _userDutyTimeRepository.GetLastEffectRound(p.Id);
|
|
|
|
|
|
}
|
|
|
|
|
|
var effectiveDate = effectiveDateCache[p.Id];
|
|
|
|
|
|
|
2023-12-27 09:20:09 +07:00
|
|
|
|
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
|
2025-10-09 23:21:36 +07:00
|
|
|
|
|
|
|
|
|
|
// Use cache for duty time
|
|
|
|
|
|
DutyTime? userRound = null;
|
|
|
|
|
|
if (roundId != Guid.Empty)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!dutyTimeCache.ContainsKey(roundId))
|
|
|
|
|
|
{
|
|
|
|
|
|
dutyTimeCache[roundId] = await _dutyTimeRepository.GetByIdAsync(roundId);
|
|
|
|
|
|
}
|
|
|
|
|
|
userRound = dutyTimeCache[roundId];
|
|
|
|
|
|
}
|
2023-12-27 09:20:09 +07:00
|
|
|
|
|
|
|
|
|
|
var duty = userRound ?? getDefaultRound;
|
2023-12-18 15:50:42 +07:00
|
|
|
|
|
2025-10-09 23:21:36 +07:00
|
|
|
|
if (duty == null) continue; // Skip if no duty time found
|
|
|
|
|
|
|
2023-11-23 15:48:09 +07:00
|
|
|
|
var res = new SearchProfileResultDto
|
|
|
|
|
|
{
|
|
|
|
|
|
ProfileId = p.Id,
|
2024-05-29 13:33:23 +07:00
|
|
|
|
CitizenId = p.CitizenId ?? "",
|
2024-05-30 09:32:34 +07:00
|
|
|
|
FullName = $"{p.Prefix ?? ""}{p.FirstName ?? ""} {p.LastName ?? ""}",
|
2023-12-27 09:20:09 +07:00
|
|
|
|
StartTimeMorning = duty.StartTimeMorning,
|
|
|
|
|
|
LeaveTimeAfterNoon = duty.EndTimeAfternoon,
|
2026-03-25 15:17:54 +07:00
|
|
|
|
EffectiveDate = effectiveDate?.EffectiveDate?.Date,
|
|
|
|
|
|
Prefix = p.Prefix ?? "",
|
|
|
|
|
|
FirstName = p.FirstName ?? "",
|
|
|
|
|
|
LastName = p.LastName ?? "",
|
|
|
|
|
|
RootDnaId = p.RootDnaId,
|
|
|
|
|
|
Child1DnaId = p.Child1DnaId,
|
|
|
|
|
|
Child2DnaId = p.Child2DnaId,
|
|
|
|
|
|
Child3DnaId = p.Child3DnaId,
|
|
|
|
|
|
Child4DnaId = p.Child4DnaId
|
|
|
|
|
|
|
2023-11-23 15:48:09 +07:00
|
|
|
|
};
|
|
|
|
|
|
resultSet.Add(res);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-09 23:21:36 +07:00
|
|
|
|
return Success(new { data = resultSet, total = profile.Total });
|
2023-11-23 15:48:09 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-23 17:04:45 +07:00
|
|
|
|
/// <summary>
|
2023-11-23 15:48:09 +07:00
|
|
|
|
/// 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)
|
|
|
|
|
|
{
|
2024-09-24 11:53:09 +07:00
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_WORK_ROUND_EDIT");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
2023-11-24 16:52:48 +07:00
|
|
|
|
var currentDate = DateTime.Now.Date;
|
|
|
|
|
|
if (req.EffectiveDate.Date < currentDate)
|
2023-11-23 17:04:45 +07:00
|
|
|
|
{
|
2023-11-24 16:52:48 +07:00
|
|
|
|
return Error(new Exception($"วันที่มีผลต้องมากกว่าหรือเท่ากับวันที่ปัจจุบัน({currentDate.ToString("yyyy-MM-dd")})"), StatusCodes.Status400BadRequest);
|
2023-11-23 17:04:45 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var old = await _userDutyTimeRepository.GetExist(req.ProfileId, req.EffectiveDate);
|
|
|
|
|
|
|
2025-06-20 14:44:53 +07:00
|
|
|
|
var profile = await _userProfileRepository.GetProfileByProfileIdAsync(req.ProfileId, AccessToken);
|
|
|
|
|
|
if (profile == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-23 17:04:45 +07:00
|
|
|
|
if (old != null)
|
|
|
|
|
|
{
|
2023-11-24 14:21:33 +07:00
|
|
|
|
return Error(new Exception("ไม่สามารถทำรายการได้ เนื่องจากมีการกำหนดรอบการทำงานในวันที่นี้ไว้แล้ว"), StatusCodes.Status400BadRequest);
|
2023-11-23 17:04:45 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-23 15:48:09 +07:00
|
|
|
|
var data = new UserDutyTime
|
|
|
|
|
|
{
|
|
|
|
|
|
ProfileId = req.ProfileId,
|
|
|
|
|
|
DutyTimeId = req.RoundId,
|
|
|
|
|
|
EffectiveDate = req.EffectiveDate,
|
|
|
|
|
|
Remark = req.Remark,
|
2025-06-20 14:44:53 +07:00
|
|
|
|
|
|
|
|
|
|
RootDnaId = profile.RootDnaId,
|
|
|
|
|
|
Child1DnaId = profile.Child1DnaId,
|
|
|
|
|
|
Child2DnaId = profile.Child2DnaId,
|
|
|
|
|
|
Child3DnaId = profile.Child3DnaId,
|
|
|
|
|
|
Child4DnaId = profile.Child4DnaId,
|
2023-11-23 15:48:09 +07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
await _userDutyTimeRepository.AddAsync(data);
|
|
|
|
|
|
|
|
|
|
|
|
return Success();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-23 17:04:45 +07:00
|
|
|
|
|
2026-03-23 09:49:17 +07:00
|
|
|
|
[HttpPost("round/multiple")]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
2026-03-25 15:17:54 +07:00
|
|
|
|
public async Task<ActionResult<ResponseObject>> CreateChangeRoundMultipleAsync([FromBody] List<CreateChangeRoundMultipleDto> reqs)
|
2026-03-23 09:49:17 +07:00
|
|
|
|
{
|
|
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_WORK_ROUND_EDIT");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
|
|
|
|
|
var currentDate = DateTime.Now.Date;
|
|
|
|
|
|
|
2026-03-25 15:17:54 +07:00
|
|
|
|
List<UserDutyTime> dataList = new List<UserDutyTime>();
|
|
|
|
|
|
|
2026-03-23 09:49:17 +07:00
|
|
|
|
foreach(var req in reqs)
|
|
|
|
|
|
{
|
2026-03-25 15:17:54 +07:00
|
|
|
|
// var profile = await _userProfileRepository.GetProfileByProfileIdAsync(req.ProfileId, AccessToken);
|
|
|
|
|
|
// if (profile == null)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
|
|
|
|
|
|
// }
|
2026-03-23 09:49:17 +07:00
|
|
|
|
|
|
|
|
|
|
if (req.EffectiveDate.Date < currentDate)
|
|
|
|
|
|
{
|
2026-03-25 15:17:54 +07:00
|
|
|
|
continue; // move to next item if effective date is in the past, not return error
|
|
|
|
|
|
// return Error(new Exception($"กำหนดรอบลงเวลาของ {req.FirstName} {req.LastName} ผิดพลาด เนื่องจากวันที่มีผลต้องมากกว่าหรือเท่ากับวันที่ปัจจุบัน({currentDate.ToString("yyyy-MM-dd")})"), StatusCodes.Status400BadRequest);
|
2026-03-23 09:49:17 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var old = await _userDutyTimeRepository.GetExist(req.ProfileId, req.EffectiveDate);
|
|
|
|
|
|
|
|
|
|
|
|
if (old != null)
|
|
|
|
|
|
{
|
2026-03-25 15:17:54 +07:00
|
|
|
|
continue; // move to next item if already exist, not return error
|
|
|
|
|
|
//return Error(new Exception($"กำหนดรอบลงเวลาของ {req.FirstName} {req.LastName} ผิดพลาด เนื่องจากมีการกำหนดรอบการทำงานในวันที่นี้ไว้แล้ว"), StatusCodes.Status400BadRequest);
|
2026-03-23 09:49:17 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var data = new UserDutyTime
|
|
|
|
|
|
{
|
|
|
|
|
|
ProfileId = req.ProfileId,
|
|
|
|
|
|
DutyTimeId = req.RoundId,
|
|
|
|
|
|
EffectiveDate = req.EffectiveDate,
|
|
|
|
|
|
Remark = req.Remark,
|
|
|
|
|
|
|
2026-03-25 15:17:54 +07:00
|
|
|
|
RootDnaId = req.RootDnaId,
|
|
|
|
|
|
Child1DnaId = req.Child1DnaId,
|
|
|
|
|
|
Child2DnaId = req.Child2DnaId,
|
|
|
|
|
|
Child3DnaId = req.Child3DnaId,
|
|
|
|
|
|
Child4DnaId = req.Child4DnaId,
|
2026-03-23 09:49:17 +07:00
|
|
|
|
};
|
2026-03-25 15:17:54 +07:00
|
|
|
|
dataList.Add(data);
|
2026-03-23 09:49:17 +07:00
|
|
|
|
}
|
2026-03-25 15:17:54 +07:00
|
|
|
|
|
|
|
|
|
|
await _userDutyTimeRepository.AddRangeAsync(dataList);
|
2026-03-23 09:49:17 +07:00
|
|
|
|
return Success();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-23 17:04:45 +07:00
|
|
|
|
/// <summary>
|
2023-11-23 17:21:37 +07:00
|
|
|
|
/// LV1_015 - ประวัติการเปลี่ยนรอบการลงเวลา (ADMIN)
|
2023-11-23 17:04:45 +07:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
2023-11-28 09:05:41 +07:00
|
|
|
|
[HttpGet("round/{id:guid}")]
|
2023-11-23 17:04:45 +07:00
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
2025-10-06 15:12:40 +07:00
|
|
|
|
public async Task<ActionResult<ResponseObject>> GetChangeRoundHistoryByProfileIdAsync(Guid id, int page = 1, int pageSize = 10, string keyword = "", string? sortBy = "", bool? descending = false)
|
2023-11-23 17:04:45 +07:00
|
|
|
|
{
|
2024-10-22 08:20:33 +07:00
|
|
|
|
var getWorkflow = await _permission.GetPermissionAPIWorkflowAsync(id.ToString(), "SYS_WORK_ROUND_EDIT");
|
|
|
|
|
|
if (getWorkflow == false)
|
2024-09-24 11:53:09 +07:00
|
|
|
|
{
|
2024-10-22 08:20:33 +07:00
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("GET", "SYS_WORK_ROUND_EDIT");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
2024-09-24 11:53:09 +07:00
|
|
|
|
}
|
2023-11-23 17:04:45 +07:00
|
|
|
|
var data = await _userDutyTimeRepository.GetListByProfileIdAsync(id);
|
|
|
|
|
|
|
|
|
|
|
|
var resultSet = new List<ChangeRoundHistoryDto>();
|
|
|
|
|
|
|
|
|
|
|
|
if (data != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
resultSet = data
|
|
|
|
|
|
.GroupBy(item => item.ProfileId)
|
|
|
|
|
|
.SelectMany(group => group
|
2025-10-06 15:12:40 +07:00
|
|
|
|
//.OrderBy(item => item.EffectiveDate) // เรียงลำดับตาม property ที่คุณต้องการ
|
2023-11-23 17:04:45 +07:00
|
|
|
|
.Select((item, index) => new ChangeRoundHistoryDto
|
|
|
|
|
|
{
|
|
|
|
|
|
Round = index + 1,
|
|
|
|
|
|
StartTimeMorning = item.DutyTime.StartTimeMorning,
|
|
|
|
|
|
LeaveTimeAfternoon = item.DutyTime.EndTimeAfternoon,
|
|
|
|
|
|
EffectiveDate = item.EffectiveDate.Value,
|
|
|
|
|
|
Remark = item.Remark
|
|
|
|
|
|
}))
|
2025-10-06 15:12:40 +07:00
|
|
|
|
//.Skip((page - 1) * pageSize)
|
|
|
|
|
|
//.Take(pageSize)
|
2023-11-23 17:04:45 +07:00
|
|
|
|
.ToList();
|
2025-10-06 15:12:40 +07:00
|
|
|
|
if (!string.IsNullOrWhiteSpace(sortBy))
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (sortBy.ToUpper())
|
|
|
|
|
|
{
|
|
|
|
|
|
case "ROUNT":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
resultSet = resultSet.OrderByDescending(x => x.Round).ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
resultSet = resultSet.OrderBy(x => x.Round).ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "STARTTIMEMORNIONG":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
resultSet = resultSet.OrderByDescending(x => x.StartTimeMorning).ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
resultSet = resultSet.OrderBy(x => x.StartTimeMorning).ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "EFFECTIVEDATE":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
resultSet = resultSet.OrderByDescending(x => x.EffectiveDate).ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
resultSet = resultSet.OrderBy(x => x.EffectiveDate).ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "REMARK":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
resultSet = resultSet.OrderByDescending(x => x.Remark).ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
resultSet = resultSet.OrderBy(x => x.Remark).ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
default: break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
resultSet = resultSet
|
|
|
|
|
|
.Skip((page - 1) * pageSize)
|
|
|
|
|
|
.Take(pageSize)
|
|
|
|
|
|
.ToList();
|
2023-11-23 17:04:45 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return Success(new { data = resultSet, total = data.Count });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-07-11 10:03:38 +07:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region " เปลี่ยนรอบการทำงาน ลจ. "
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// LV1_006 - เช็คเวลาต้องลงเวลาเข้าหรือออกงาน (USER) ลจ.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
|
|
|
|
|
[HttpPost("emp/search")]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
public async Task<ActionResult<ResponseObject>> SearchEmpProfileAsync([FromBody] DTOs.ChangeRound.SearchProfileDto req)
|
|
|
|
|
|
{
|
2025-12-19 14:23:27 +07:00
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_WORK_ROUND_EDIT_EMP");
|
2025-07-11 10:03:38 +07:00
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
|
|
|
|
|
string role = jsonData["result"]?.ToString();
|
|
|
|
|
|
var nodeId = string.Empty;
|
|
|
|
|
|
var profileAdmin = new GetUserOCAllDto();
|
|
|
|
|
|
profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken);
|
|
|
|
|
|
if (role == "NORMAL" || role == "CHILD")
|
|
|
|
|
|
{
|
|
|
|
|
|
nodeId = profileAdmin?.Node == 4
|
|
|
|
|
|
? profileAdmin?.Child4DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 3
|
|
|
|
|
|
? profileAdmin?.Child3DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 2
|
|
|
|
|
|
? profileAdmin?.Child2DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 1
|
|
|
|
|
|
? profileAdmin?.Child1DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 0
|
|
|
|
|
|
? profileAdmin?.RootDnaId
|
|
|
|
|
|
: "";
|
|
|
|
|
|
}
|
2025-12-12 01:36:06 +07:00
|
|
|
|
else if (role == "BROTHER")
|
|
|
|
|
|
{
|
|
|
|
|
|
nodeId = profileAdmin?.Node == 4
|
|
|
|
|
|
? profileAdmin?.Child3DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 3
|
|
|
|
|
|
? profileAdmin?.Child2DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 2
|
|
|
|
|
|
? profileAdmin?.Child1DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 1 || profileAdmin?.Node == 0
|
|
|
|
|
|
? profileAdmin?.RootDnaId
|
|
|
|
|
|
: "";
|
|
|
|
|
|
}
|
2026-02-19 17:01:44 +07:00
|
|
|
|
else if (role == "ROOT" /*|| role == "PARENT"*/)
|
2025-07-11 10:03:38 +07:00
|
|
|
|
{
|
|
|
|
|
|
nodeId = profileAdmin?.RootDnaId;
|
|
|
|
|
|
}
|
2025-10-09 21:43:25 +07:00
|
|
|
|
var profile = await _userProfileRepository.SearchProfileEmployee(req.CitizenId, req.FirstName, req.LastName, AccessToken ?? "", req.Page, req.PageSize, role, nodeId, profileAdmin?.Node);
|
2025-07-11 10:03:38 +07:00
|
|
|
|
|
2025-10-09 23:21:36 +07:00
|
|
|
|
// Get default round once
|
2025-07-11 10:03:38 +07:00
|
|
|
|
var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync();
|
|
|
|
|
|
|
|
|
|
|
|
var resultSet = new List<SearchProfileResultDto>();
|
|
|
|
|
|
|
2025-10-09 23:21:36 +07:00
|
|
|
|
// Create dictionaries to cache results and avoid duplicate queries
|
|
|
|
|
|
var effectiveDateCache = new Dictionary<Guid, UserDutyTime?>();
|
|
|
|
|
|
var dutyTimeCache = new Dictionary<Guid, DutyTime?>();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var p in profile.Data)
|
2025-07-11 10:03:38 +07:00
|
|
|
|
{
|
2025-10-09 23:21:36 +07:00
|
|
|
|
// Use cache for effective date
|
|
|
|
|
|
if (!effectiveDateCache.ContainsKey(p.Id))
|
|
|
|
|
|
{
|
|
|
|
|
|
effectiveDateCache[p.Id] = await _userDutyTimeRepository.GetLastEffectRound(p.Id);
|
|
|
|
|
|
}
|
|
|
|
|
|
var effectiveDate = effectiveDateCache[p.Id];
|
|
|
|
|
|
|
2025-07-11 10:03:38 +07:00
|
|
|
|
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
|
2025-10-09 23:21:36 +07:00
|
|
|
|
|
|
|
|
|
|
// Use cache for duty time
|
|
|
|
|
|
DutyTime? userRound = null;
|
|
|
|
|
|
if (roundId != Guid.Empty)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!dutyTimeCache.ContainsKey(roundId))
|
|
|
|
|
|
{
|
|
|
|
|
|
dutyTimeCache[roundId] = await _dutyTimeRepository.GetByIdAsync(roundId);
|
|
|
|
|
|
}
|
|
|
|
|
|
userRound = dutyTimeCache[roundId];
|
|
|
|
|
|
}
|
2025-07-11 10:03:38 +07:00
|
|
|
|
|
|
|
|
|
|
var duty = userRound ?? getDefaultRound;
|
|
|
|
|
|
|
2025-10-09 23:21:36 +07:00
|
|
|
|
if (duty == null) continue; // Skip if no duty time found
|
|
|
|
|
|
|
2025-07-11 10:03:38 +07:00
|
|
|
|
var res = new SearchProfileResultDto
|
|
|
|
|
|
{
|
|
|
|
|
|
ProfileId = p.Id,
|
|
|
|
|
|
CitizenId = p.CitizenId ?? "",
|
|
|
|
|
|
FullName = $"{p.Prefix ?? ""}{p.FirstName ?? ""} {p.LastName ?? ""}",
|
|
|
|
|
|
StartTimeMorning = duty.StartTimeMorning,
|
|
|
|
|
|
LeaveTimeAfterNoon = duty.EndTimeAfternoon,
|
2025-10-09 23:21:36 +07:00
|
|
|
|
EffectiveDate = effectiveDate?.EffectiveDate?.Date
|
2025-07-11 10:03:38 +07:00
|
|
|
|
};
|
|
|
|
|
|
resultSet.Add(res);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-09 23:21:36 +07:00
|
|
|
|
return Success(new { data = resultSet, total = profile.Total });
|
2025-07-11 10:03:38 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// LV1_014 - เปลี่ยนรอบการลงเวลา (ADMIN) Employee
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
|
|
|
|
|
[HttpPost("emp/round")]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
public async Task<ActionResult<ResponseObject>> CreateChangeEmpRoundAsync([FromBody] CreateChangeRoundDto req)
|
|
|
|
|
|
{
|
|
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_WORK_ROUND_EDIT");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
|
|
var profile = await _userProfileRepository.GetProfileByProfileIdAsync(req.ProfileId, AccessToken);
|
|
|
|
|
|
if (profile == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
|
|
|
|
|
|
|
RootDnaId = profile.RootDnaId,
|
|
|
|
|
|
Child1DnaId = profile.Child1DnaId,
|
|
|
|
|
|
Child2DnaId = profile.Child2DnaId,
|
|
|
|
|
|
Child3DnaId = profile.Child3DnaId,
|
|
|
|
|
|
Child4DnaId = profile.Child4DnaId,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
await _userDutyTimeRepository.AddAsync(data);
|
|
|
|
|
|
|
|
|
|
|
|
return Success();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// LV1_015 - ประวัติการเปลี่ยนรอบการลงเวลา (ADMIN) Employee
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
|
|
|
|
|
[HttpGet("emp/round/{id:guid}")]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
2025-10-06 15:12:40 +07:00
|
|
|
|
public async Task<ActionResult<ResponseObject>> GetChangeEmpRoundHistoryByProfileIdAsync(Guid id, int page = 1, int pageSize = 10, string keyword = "", string? sortBy = "", bool? descending = false)
|
2025-07-11 10:03:38 +07:00
|
|
|
|
{
|
|
|
|
|
|
var getWorkflow = await _permission.GetPermissionAPIWorkflowAsync(id.ToString(), "SYS_WORK_ROUND_EDIT");
|
|
|
|
|
|
if (getWorkflow == false)
|
|
|
|
|
|
{
|
|
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("GET", "SYS_WORK_ROUND_EDIT");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
var data = await _userDutyTimeRepository.GetListByProfileIdAsync(id);
|
|
|
|
|
|
|
|
|
|
|
|
var resultSet = new List<ChangeRoundHistoryDto>();
|
|
|
|
|
|
|
|
|
|
|
|
if (data != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
resultSet = data
|
|
|
|
|
|
.GroupBy(item => item.ProfileId)
|
|
|
|
|
|
.SelectMany(group => group
|
2025-10-06 15:12:40 +07:00
|
|
|
|
//.OrderBy(item => item.EffectiveDate) // เรียงลำดับตาม property ที่คุณต้องการ
|
2025-07-11 10:03:38 +07:00
|
|
|
|
.Select((item, index) => new ChangeRoundHistoryDto
|
|
|
|
|
|
{
|
|
|
|
|
|
Round = index + 1,
|
|
|
|
|
|
StartTimeMorning = item.DutyTime.StartTimeMorning,
|
|
|
|
|
|
LeaveTimeAfternoon = item.DutyTime.EndTimeAfternoon,
|
|
|
|
|
|
EffectiveDate = item.EffectiveDate.Value,
|
|
|
|
|
|
Remark = item.Remark
|
|
|
|
|
|
}))
|
2025-10-06 15:12:40 +07:00
|
|
|
|
//.Skip((page - 1) * pageSize)
|
|
|
|
|
|
//.Take(pageSize)
|
2025-07-11 10:03:38 +07:00
|
|
|
|
.ToList();
|
2025-10-06 15:12:40 +07:00
|
|
|
|
if (!string.IsNullOrWhiteSpace(sortBy))
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (sortBy.ToUpper())
|
|
|
|
|
|
{
|
|
|
|
|
|
case "ROUNT":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
resultSet = resultSet.OrderByDescending(x => x.Round).ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
resultSet = resultSet.OrderBy(x => x.Round).ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "STARTTIMEMORNIONG":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
resultSet = resultSet.OrderByDescending(x => x.StartTimeMorning).ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
resultSet = resultSet.OrderBy(x => x.StartTimeMorning).ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "EFFECTIVEDATE":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
resultSet = resultSet.OrderByDescending(x => x.EffectiveDate).ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
resultSet = resultSet.OrderBy(x => x.EffectiveDate).ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "REMARK":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
resultSet = resultSet.OrderByDescending(x => x.Remark).ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
resultSet = resultSet.OrderBy(x => x.Remark).ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
default: break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
resultSet = resultSet
|
|
|
|
|
|
.Skip((page - 1) * pageSize)
|
|
|
|
|
|
.Take(pageSize)
|
|
|
|
|
|
.ToList();
|
2025-07-11 10:03:38 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return Success(new { data = resultSet, total = data.Count });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-19 16:13:22 +07:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region " Check Checkout Time "
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// ตรวจสอบว่าเวลาปัจจุบัน ถ้า checkout จะขาดราชการหรือไม่?
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
2026-01-14 16:43:35 +07:00
|
|
|
|
[HttpGet("user/checkout-check/{isSeminar}")]
|
2025-03-19 16:13:22 +07:00
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
2026-01-14 16:43:35 +07:00
|
|
|
|
public async Task<ActionResult<ResponseObject>> CheckoutCheckAsync(string isSeminar = "N")
|
2025-03-19 16:13:22 +07:00
|
|
|
|
{
|
|
|
|
|
|
var time = DateTime.Now;
|
|
|
|
|
|
|
|
|
|
|
|
var userId = UserId != null ? Guid.Parse(UserId) : Guid.Empty;
|
2026-04-17 09:41:52 +07:00
|
|
|
|
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
|
2025-03-19 16:13:22 +07:00
|
|
|
|
if (profile == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception(GlobalMessages.DataNotFound);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync();
|
|
|
|
|
|
if (getDefaultRound == 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 ?? getDefaultRound;
|
|
|
|
|
|
|
2025-03-21 13:01:17 +07:00
|
|
|
|
|
|
|
|
|
|
var lastCheckIn = await _context.Set<UserTimeStamp>()
|
|
|
|
|
|
.Where(u => u.KeycloakUserId == userId)
|
|
|
|
|
|
.Where(d => d.CheckOut == null)
|
|
|
|
|
|
.OrderByDescending(u => u.CheckIn)
|
|
|
|
|
|
.FirstOrDefaultAsync();
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-03-21 21:34:39 +07:00
|
|
|
|
//var endTime = DateTimeOffset.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")}T{duty.EndTimeAfternoon}:00.0000000+07:00").ToLocalTime().DateTime;
|
|
|
|
|
|
|
2025-12-15 10:29:23 +07:00
|
|
|
|
//var endTime = DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")}T{duty.EndTimeAfternoon}:00.0000000+07:00");
|
2026-01-14 16:43:35 +07:00
|
|
|
|
var endTime = isSeminar.Trim().ToUpper() == "Y"
|
2026-01-14 16:51:21 +07:00
|
|
|
|
? DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} 14:30")
|
2026-01-14 16:42:36 +07:00
|
|
|
|
: DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}");
|
2026-01-16 16:19:49 +07:00
|
|
|
|
|
|
|
|
|
|
var endTimeMorning = DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}");
|
2026-01-19 14:27:43 +07:00
|
|
|
|
var endTimeDisplay = endTime;
|
2026-01-16 16:19:49 +07:00
|
|
|
|
|
2025-12-15 10:29:23 +07:00
|
|
|
|
var status = string.Empty;
|
|
|
|
|
|
if(lastCheckIn == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
status = "ABSENT";
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (lastCheckIn.CheckIn.Date < DateTime.Now.Date)
|
|
|
|
|
|
{
|
|
|
|
|
|
status = "NORMAL";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (time < endTime)
|
|
|
|
|
|
{
|
2026-01-12 13:41:23 +07:00
|
|
|
|
|
|
|
|
|
|
//string checkOutStatus = "NORMAL";
|
|
|
|
|
|
var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(userId, time.Date);
|
|
|
|
|
|
if (leaveReq != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var leaveRange = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper();
|
|
|
|
|
|
if (leaveRange == "AFTERNOON" || leaveRange == "ALL")
|
2026-01-16 16:19:49 +07:00
|
|
|
|
{
|
|
|
|
|
|
if(time < endTimeMorning)
|
|
|
|
|
|
{
|
|
|
|
|
|
status = "ABSENT";
|
2026-01-19 14:27:43 +07:00
|
|
|
|
endTimeDisplay = endTimeMorning;
|
2026-01-16 16:19:49 +07:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
status = "NORMAL";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-12 13:41:23 +07:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
status = "ABSENT";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
status = "ABSENT";
|
|
|
|
|
|
}
|
2025-12-15 10:29:23 +07:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
status = "NORMAL";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-03-19 16:13:22 +07:00
|
|
|
|
|
2025-12-15 10:29:23 +07:00
|
|
|
|
//var status = lastCheckIn == null ? "ABSENT" : lastCheckIn.CheckIn.Date < DateTime.Now.Date ? "NORMAL" : time < endTime ? "ABSENT" : "NORMAL";
|
2025-03-19 16:13:22 +07:00
|
|
|
|
|
|
|
|
|
|
return Success(new
|
|
|
|
|
|
{
|
|
|
|
|
|
Status = status,
|
|
|
|
|
|
StatusText = status == "ABSENT" ? "ขาดราชการ" : "ปกติ",
|
2025-03-21 21:34:39 +07:00
|
|
|
|
ServerTime = time,
|
2026-01-19 14:27:43 +07:00
|
|
|
|
EndTime = endTimeDisplay
|
2025-03-19 16:13:22 +07:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-11-24 11:23:18 +07:00
|
|
|
|
#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)
|
|
|
|
|
|
{
|
2023-11-24 14:21:33 +07:00
|
|
|
|
return Error("ไม่สามารถขอลงเวลากรณีพิเศษในวันที่มากกว่าวันที่ปัจจุบันได้", StatusCodes.Status400BadRequest);
|
2023-11-24 11:23:18 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-07-11 14:52:54 +07:00
|
|
|
|
var userId = UserId != null ? Guid.Parse(UserId) : Guid.Empty;
|
2026-04-17 09:41:52 +07:00
|
|
|
|
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
|
2024-07-11 14:52:54 +07:00
|
|
|
|
if (profile == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception(GlobalMessages.DataNotFound);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-24 11:23:18 +07:00
|
|
|
|
var request = new AdditionalCheckRequest
|
|
|
|
|
|
{
|
|
|
|
|
|
KeycloakUserId = UserId != null ? Guid.Parse(UserId) : Guid.Empty,
|
|
|
|
|
|
CheckDate = req.CheckDate,
|
|
|
|
|
|
CheckInEdit = req.CheckInEdit,
|
|
|
|
|
|
CheckOutEdit = req.CheckOutEdit,
|
|
|
|
|
|
Description = req.Description,
|
2024-07-11 14:52:54 +07:00
|
|
|
|
Prefix = profile.Prefix,
|
|
|
|
|
|
FirstName = profile.FirstName,
|
|
|
|
|
|
LastName = profile.LastName,
|
2025-05-16 10:33:38 +07:00
|
|
|
|
|
|
|
|
|
|
// fix issue #1547
|
|
|
|
|
|
POI = req.POI,
|
|
|
|
|
|
Latitude = req.Latitude,
|
|
|
|
|
|
Longitude = req.Longitude,
|
2025-06-20 14:44:53 +07:00
|
|
|
|
|
|
|
|
|
|
// add all Dna Id
|
|
|
|
|
|
RootDnaId = profile.RootDnaId ?? Guid.Empty,
|
|
|
|
|
|
Child1DnaId = profile.Child1DnaId ?? Guid.Empty,
|
|
|
|
|
|
Child2DnaId = profile.Child2DnaId ?? Guid.Empty,
|
|
|
|
|
|
Child3DnaId = profile.Child3DnaId ?? Guid.Empty,
|
|
|
|
|
|
Child4DnaId = profile.Child4DnaId ?? Guid.Empty,
|
2023-11-24 11:23:18 +07:00
|
|
|
|
};
|
|
|
|
|
|
await _additionalCheckRequestRepository.AddAsync(request);
|
2024-01-17 12:42:19 +07:00
|
|
|
|
|
2025-01-23 14:58:04 +07:00
|
|
|
|
|
2023-11-24 11:23:18 +07:00
|
|
|
|
return Success();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-24 12:25:03 +07:00
|
|
|
|
/// <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)]
|
2025-10-06 15:12:40 +07:00
|
|
|
|
public async Task<ActionResult<ResponseObject>> GetAdditionalCheckRequestAsync([Required] int year, [Required] int month, [Required] int page = 1, [Required] int pageSize = 10, string keyword = "", string? sortBy = "", bool? descending = false)
|
2023-11-24 12:25:03 +07:00
|
|
|
|
{
|
2026-04-17 15:35:29 +07:00
|
|
|
|
var jsonData = await _permission.GetPermissionWithActingAPIAsync("LIST", "SYS_CHECKIN_SPECIAL");
|
|
|
|
|
|
//var jsonData = JsonConvert.DeserializeObject<GetPermissionWithActingResultDto>(getPermission);
|
|
|
|
|
|
if (jsonData!.status != 200)
|
2024-09-24 11:53:09 +07:00
|
|
|
|
{
|
2026-04-17 15:35:29 +07:00
|
|
|
|
return Error(jsonData.message, StatusCodes.Status403Forbidden);
|
2024-09-24 11:53:09 +07:00
|
|
|
|
}
|
2026-04-17 15:35:29 +07:00
|
|
|
|
//string role = jsonData["result"]?.ToString();
|
|
|
|
|
|
string role = jsonData.result.privilege;
|
2025-06-24 14:02:44 +07:00
|
|
|
|
var nodeId = string.Empty;
|
|
|
|
|
|
var profileAdmin = new GetUserOCAllDto();
|
|
|
|
|
|
profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken);
|
|
|
|
|
|
if (role == "NORMAL" || role == "CHILD")
|
|
|
|
|
|
{
|
|
|
|
|
|
nodeId = profileAdmin?.Node == 4
|
|
|
|
|
|
? profileAdmin?.Child4DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 3
|
|
|
|
|
|
? profileAdmin?.Child3DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 2
|
|
|
|
|
|
? profileAdmin?.Child2DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 1
|
|
|
|
|
|
? profileAdmin?.Child1DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 0
|
|
|
|
|
|
? profileAdmin?.RootDnaId
|
|
|
|
|
|
: "";
|
|
|
|
|
|
}
|
2025-12-12 01:36:06 +07:00
|
|
|
|
else if (role == "BROTHER")
|
|
|
|
|
|
{
|
|
|
|
|
|
nodeId = profileAdmin?.Node == 4
|
|
|
|
|
|
? profileAdmin?.Child3DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 3
|
|
|
|
|
|
? profileAdmin?.Child2DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 2
|
|
|
|
|
|
? profileAdmin?.Child1DnaId
|
|
|
|
|
|
: profileAdmin?.Node == 1 || profileAdmin?.Node == 0
|
|
|
|
|
|
? profileAdmin?.RootDnaId
|
|
|
|
|
|
: "";
|
|
|
|
|
|
}
|
2026-02-19 17:01:44 +07:00
|
|
|
|
else if (role == "ROOT" /*|| role == "PARENT"*/)
|
2025-06-24 14:02:44 +07:00
|
|
|
|
{
|
|
|
|
|
|
nodeId = profileAdmin?.RootDnaId;
|
|
|
|
|
|
}
|
2025-10-12 16:51:51 +07:00
|
|
|
|
|
2025-06-24 14:02:44 +07:00
|
|
|
|
//var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequests(year, month);
|
2026-01-28 16:24:59 +07:00
|
|
|
|
var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequestsByAdminRole(year, month, role, nodeId, profileAdmin?.Node, keyword);
|
2023-11-24 12:25:03 +07:00
|
|
|
|
|
2026-04-17 15:35:29 +07:00
|
|
|
|
// ถ้ามีการรักษาการ
|
|
|
|
|
|
if (jsonData.result.isAct)
|
|
|
|
|
|
{
|
|
|
|
|
|
var posActs = jsonData.result.posMasterActs;
|
|
|
|
|
|
foreach(var act in posActs)
|
|
|
|
|
|
{
|
|
|
|
|
|
var actRole = act.privilege;
|
|
|
|
|
|
string actNodeId = string.Empty;
|
|
|
|
|
|
int? actNode;
|
|
|
|
|
|
|
|
|
|
|
|
if (role == "NORMAL" || role == "CHILD")
|
|
|
|
|
|
{
|
|
|
|
|
|
actNodeId = act.child4DnaId != null ?
|
|
|
|
|
|
act.child4DnaId.Value.ToString("D") :
|
|
|
|
|
|
act.child3DnaId != null ?
|
|
|
|
|
|
act.child3DnaId.Value.ToString("D") :
|
|
|
|
|
|
act.child2DnaId != null ?
|
|
|
|
|
|
act.child2DnaId.Value.ToString("D") :
|
|
|
|
|
|
act.child1DnaId != null ?
|
|
|
|
|
|
act.child1DnaId.Value.ToString("D") :
|
|
|
|
|
|
act.rootDnaId != null ?
|
|
|
|
|
|
act.rootDnaId.Value.ToString("D") :
|
|
|
|
|
|
"";
|
|
|
|
|
|
actNode = act.child4DnaId != null ?
|
|
|
|
|
|
4 :
|
|
|
|
|
|
act.child3DnaId != null ?
|
|
|
|
|
|
3 :
|
|
|
|
|
|
act.child2DnaId != null ?
|
|
|
|
|
|
2 :
|
|
|
|
|
|
act.child1DnaId != null ?
|
|
|
|
|
|
1 :
|
|
|
|
|
|
act.rootDnaId != null ?
|
|
|
|
|
|
0 :
|
|
|
|
|
|
null;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (role == "BROTHER")
|
|
|
|
|
|
{
|
|
|
|
|
|
actNodeId = act.child3DnaId != null ?
|
|
|
|
|
|
act.child3DnaId.Value.ToString("D") :
|
|
|
|
|
|
act.child2DnaId != null ?
|
|
|
|
|
|
act.child2DnaId.Value.ToString("D") :
|
|
|
|
|
|
act.child1DnaId != null ?
|
|
|
|
|
|
act.rootDnaId!.Value.ToString("D") :
|
|
|
|
|
|
act.rootDnaId != null ?
|
|
|
|
|
|
act.rootDnaId.Value.ToString("D") :
|
|
|
|
|
|
"";
|
|
|
|
|
|
actNode = act.child4DnaId != null ?
|
|
|
|
|
|
4 :
|
|
|
|
|
|
act.child3DnaId != null ?
|
|
|
|
|
|
4 :
|
|
|
|
|
|
act.child2DnaId != null ?
|
|
|
|
|
|
3 :
|
|
|
|
|
|
act.child1DnaId != null ?
|
|
|
|
|
|
2 :
|
|
|
|
|
|
act.rootDnaId != null ?
|
|
|
|
|
|
0 :
|
|
|
|
|
|
null;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (role == "ROOT" /*|| role == "PARENT"*/)
|
|
|
|
|
|
{
|
|
|
|
|
|
actNodeId = act.rootDnaId!.Value.ToString("D");
|
|
|
|
|
|
actNode = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var rawDataAct = await _additionalCheckRequestRepository.GetAdditionalCheckRequestsByAdminRole(year, month, actRole, actNodeId, profileAdmin?.Node, keyword);
|
|
|
|
|
|
if (rawDataAct != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (rawData != null)
|
|
|
|
|
|
rawData = rawData.Union(rawDataAct).ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
rawData = rawDataAct;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-13 17:01:20 +07:00
|
|
|
|
var total = rawData.Count;
|
|
|
|
|
|
|
2023-11-24 12:25:03 +07:00
|
|
|
|
var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync();
|
|
|
|
|
|
if (getDefaultRound == null)
|
|
|
|
|
|
{
|
2023-11-24 14:21:33 +07:00
|
|
|
|
return Error("ไม่พบรอบลงเวลา Default", StatusCodes.Status404NotFound);
|
2023-11-24 12:25:03 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var result = new List<GetAdditionalCheckRequestDto>();
|
|
|
|
|
|
|
2026-01-13 17:01:20 +07:00
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(sortBy))
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (sortBy.ToUpper())
|
|
|
|
|
|
{
|
|
|
|
|
|
case "FULLNAME":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
rawData = rawData.OrderByDescending(x => x.Prefix)
|
|
|
|
|
|
.ThenByDescending(x => x.FirstName)
|
|
|
|
|
|
.ThenByDescending(x => x.LastName)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
rawData = rawData.OrderBy(x => x.Prefix)
|
|
|
|
|
|
.ThenBy(x => x.FirstName)
|
|
|
|
|
|
.ThenBy(x => x.LastName)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "CREATEDAT":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
rawData = rawData.OrderByDescending(x => x.CreatedAt).ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
rawData = rawData.OrderBy(x => x.CreatedAt).ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "CHECKDATE":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
rawData = rawData.OrderByDescending(x => x.CheckDate).ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
rawData = rawData.OrderBy(x => x.CheckDate).ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
// case "STARTTIMEMORNING":
|
|
|
|
|
|
// if (descending == true)
|
|
|
|
|
|
// rawData = rawData.OrderByDescending(x => x.StartTimeMorning).ToList();
|
|
|
|
|
|
// else
|
|
|
|
|
|
// result = result.OrderBy(x => x.StartTimeMorning).ToList();
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case "STARTTIMEAFTERNOON":
|
|
|
|
|
|
// if (descending == true)
|
|
|
|
|
|
// result = result.OrderByDescending(x => x.StartTimeAfternoon).ToList();
|
|
|
|
|
|
// else
|
|
|
|
|
|
// result = result.OrderBy(x => x.StartTimeAfternoon).ToList();
|
|
|
|
|
|
// break;
|
|
|
|
|
|
case "DESCRIPTION":
|
|
|
|
|
|
if (descending == true)
|
|
|
|
|
|
rawData = rawData.OrderByDescending(x => x.Description).ToList();
|
|
|
|
|
|
else
|
|
|
|
|
|
rawData = rawData.OrderBy(x => x.Description).ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
rawData = rawData.OrderBy(x =>
|
|
|
|
|
|
x.Status.Trim().ToLower() == "pending" ? 1 :
|
|
|
|
|
|
x.Status.Trim().ToLower() == "approve" ? 2 : 3).ToList();
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var rawDataPaged = rawData.Skip((page - 1) * pageSize).Take(pageSize)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var data in rawDataPaged)
|
2023-11-24 12:25:03 +07:00
|
|
|
|
{
|
2026-04-17 09:41:52 +07:00
|
|
|
|
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(data.KeycloakUserId, AccessToken);
|
2026-04-01 12:24:00 +07:00
|
|
|
|
UserDutyTime? effectiveDate = null;
|
|
|
|
|
|
if (profile != null)
|
2025-03-19 15:58:11 +07:00
|
|
|
|
{
|
2026-04-01 12:24:00 +07:00
|
|
|
|
effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id);
|
|
|
|
|
|
//return Error($"{data.Id} PF{data.FirstName} {data.LastName} : {GlobalMessages.DataNotFound}", StatusCodes.Status404NotFound);
|
2025-03-19 15:58:11 +07:00
|
|
|
|
}
|
2023-12-18 15:50:42 +07:00
|
|
|
|
//var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty);
|
2025-03-19 15:58:11 +07:00
|
|
|
|
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
|
|
|
|
|
|
var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
|
2023-11-24 12:25:03 +07:00
|
|
|
|
var checkInData = await _userTimeStampRepository.GetTimestampByDateAsync(data.KeycloakUserId, data.CheckDate);
|
|
|
|
|
|
|
2025-03-19 15:58:11 +07:00
|
|
|
|
var duty = userRound ?? getDefaultRound;
|
2024-07-12 14:01:49 +07:00
|
|
|
|
|
2025-03-19 15:58:11 +07:00
|
|
|
|
//var duty = getDefaultRound;
|
2023-11-24 12:25:03 +07:00
|
|
|
|
|
|
|
|
|
|
// create result object to return
|
|
|
|
|
|
var resObj = new GetAdditionalCheckRequestDto
|
|
|
|
|
|
{
|
|
|
|
|
|
Id = data.Id,
|
2024-07-11 14:52:54 +07:00
|
|
|
|
FullName = $"{data.Prefix}{data.FirstName} {data.LastName}",
|
2025-10-06 15:12:40 +07:00
|
|
|
|
Prefix = data.Prefix ?? "",
|
|
|
|
|
|
FirstName = data.FirstName ?? "",
|
|
|
|
|
|
LastName = data.LastName ?? "",
|
2024-07-11 14:52:54 +07:00
|
|
|
|
//FullName = $"{profile.Prefix}{profile.FirstName} {profile.LastName}",
|
2023-11-24 12:25:03 +07:00
|
|
|
|
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}") ?
|
2024-01-15 10:52:39 +07:00
|
|
|
|
|
|
|
|
|
|
DateTime.Parse(checkInData.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
|
|
|
|
|
|
DateTime.Parse($"{checkInData.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
|
|
|
|
|
|
"ABSENT" :
|
2023-11-24 12:25:03 +07:00
|
|
|
|
"LATE" :
|
2024-01-15 10:52:39 +07:00
|
|
|
|
|
2023-11-24 12:25:03 +07:00
|
|
|
|
"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}") ?
|
2024-01-15 10:52:39 +07:00
|
|
|
|
"ABSENT" :
|
|
|
|
|
|
DateTime.Parse(checkInData.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
|
|
|
|
|
|
DateTime.Parse($"{checkInData.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
|
|
|
|
|
|
"ABSENT" :
|
2023-11-24 12:25:03 +07:00
|
|
|
|
"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,
|
|
|
|
|
|
|
2025-05-16 10:33:38 +07:00
|
|
|
|
|
|
|
|
|
|
POI = data.POI,
|
|
|
|
|
|
Latitude = data.Latitude,
|
|
|
|
|
|
Longitude = data.Longitude,
|
|
|
|
|
|
|
2023-11-24 12:25:03 +07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
result.Add(resObj);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-13 17:01:20 +07:00
|
|
|
|
// if (keyword != "")
|
|
|
|
|
|
// {
|
|
|
|
|
|
// result = result.Where(x => x.FullName.Contains(keyword)).ToList();
|
|
|
|
|
|
// }
|
|
|
|
|
|
// if (string.IsNullOrWhiteSpace(sortBy))
|
|
|
|
|
|
// {
|
|
|
|
|
|
// sortBy = "default";
|
|
|
|
|
|
// }
|
|
|
|
|
|
// if (!string.IsNullOrWhiteSpace(sortBy))
|
|
|
|
|
|
// {
|
|
|
|
|
|
// switch (sortBy.ToUpper())
|
|
|
|
|
|
// {
|
|
|
|
|
|
// case "FULLNAME":
|
|
|
|
|
|
// if (descending == true)
|
|
|
|
|
|
// result = result.OrderByDescending(x => x.Prefix)
|
|
|
|
|
|
// .ThenByDescending(x => x.FirstName)
|
|
|
|
|
|
// .ThenByDescending(x => x.LastName)
|
|
|
|
|
|
// .ToList();
|
|
|
|
|
|
// else
|
|
|
|
|
|
// result = result.OrderBy(x => x.Prefix)
|
|
|
|
|
|
// .ThenBy(x => x.FirstName)
|
|
|
|
|
|
// .ThenBy(x => x.LastName)
|
|
|
|
|
|
// .ToList();
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case "CREATEDAT":
|
|
|
|
|
|
// if (descending == true)
|
|
|
|
|
|
// result = result.OrderByDescending(x => x.CreatedAt).ToList();
|
|
|
|
|
|
// else
|
|
|
|
|
|
// result = result.OrderBy(x => x.CreatedAt).ToList();
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case "CHECKDATE":
|
|
|
|
|
|
// if (descending == true)
|
|
|
|
|
|
// result = result.OrderByDescending(x => x.CheckDate).ToList();
|
|
|
|
|
|
// else
|
|
|
|
|
|
// result = result.OrderBy(x => x.CheckDate).ToList();
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case "STARTTIMEMORNING":
|
|
|
|
|
|
// if (descending == true)
|
|
|
|
|
|
// result = result.OrderByDescending(x => x.StartTimeMorning).ToList();
|
|
|
|
|
|
// else
|
|
|
|
|
|
// result = result.OrderBy(x => x.StartTimeMorning).ToList();
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case "STARTTIMEAFTERNOON":
|
|
|
|
|
|
// if (descending == true)
|
|
|
|
|
|
// result = result.OrderByDescending(x => x.StartTimeAfternoon).ToList();
|
|
|
|
|
|
// else
|
|
|
|
|
|
// result = result.OrderBy(x => x.StartTimeAfternoon).ToList();
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case "DESCRIPTION":
|
|
|
|
|
|
// if (descending == true)
|
|
|
|
|
|
// result = result.OrderByDescending(x => x.Description).ToList();
|
|
|
|
|
|
// else
|
|
|
|
|
|
// result = result.OrderBy(x => x.Description).ToList();
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// default:
|
|
|
|
|
|
// result = result.OrderBy(x => x.StatusSort).ToList();
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
// var pageResult = result.Skip((page - 1) * pageSize).Take(pageSize)
|
|
|
|
|
|
// .ToList();
|
|
|
|
|
|
|
|
|
|
|
|
return Success(new { data = result, total = total });
|
2023-11-24 12:25:03 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-24 13:05:22 +07:00
|
|
|
|
/// <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}")]
|
2023-11-28 09:53:38 +07:00
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
2023-11-24 13:05:22 +07:00
|
|
|
|
public async Task<ActionResult<ResponseObject>> ApproveRequestAsync(Guid id, [FromBody] ApproveRequestDto req)
|
|
|
|
|
|
{
|
2024-09-24 11:53:09 +07:00
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_CHECKIN_SPECIAL");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
2023-11-24 13:05:22 +07:00
|
|
|
|
if (req.Reason == null || req.Reason == string.Empty)
|
|
|
|
|
|
{
|
2023-11-24 14:21:33 +07:00
|
|
|
|
return Error("กรุณากรอกเหตุผล", StatusCodes.Status400BadRequest);
|
2023-11-24 13:05:22 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var requestData = await _additionalCheckRequestRepository.GetByIdAsync(id);
|
|
|
|
|
|
if (requestData == null)
|
|
|
|
|
|
{
|
2023-11-24 14:21:33 +07:00
|
|
|
|
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
|
2023-11-24 13:05:22 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
requestData.Status = "APPROVE";
|
|
|
|
|
|
requestData.Comment = req.Reason;
|
|
|
|
|
|
await _additionalCheckRequestRepository.UpdateAsync(requestData);
|
|
|
|
|
|
|
|
|
|
|
|
// change user timestamp
|
|
|
|
|
|
var processTimeStamp = await _processUserTimeStampRepository.GetTimestampByDateAsync(requestData.KeycloakUserId, requestData.CheckDate.Date);
|
|
|
|
|
|
|
2026-04-17 09:41:52 +07:00
|
|
|
|
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(requestData.KeycloakUserId, AccessToken);
|
2024-10-30 13:41:12 +07:00
|
|
|
|
|
2023-11-24 13:05:22 +07:00
|
|
|
|
if (processTimeStamp == null)
|
|
|
|
|
|
{
|
2024-10-30 13:41:12 +07:00
|
|
|
|
|
2024-07-11 14:52:54 +07:00
|
|
|
|
|
2023-11-24 13:05:22 +07:00
|
|
|
|
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,
|
2025-05-16 10:33:38 +07:00
|
|
|
|
CheckInPOI = "",
|
|
|
|
|
|
CheckOutPOI = "",
|
2023-12-21 15:15:03 +07:00
|
|
|
|
CheckInStatus = req.CheckInStatus,
|
2024-07-11 14:52:54 +07:00
|
|
|
|
CheckOutStatus = req.CheckOutStatus,
|
|
|
|
|
|
|
|
|
|
|
|
Prefix = profile.Prefix,
|
|
|
|
|
|
FirstName = profile.FirstName,
|
|
|
|
|
|
LastName = profile.LastName,
|
2023-11-24 13:05:22 +07:00
|
|
|
|
|
2025-01-27 14:18:22 +07:00
|
|
|
|
// Add ข้อมูลจาก profile
|
|
|
|
|
|
CitizenId = profile.CitizenId,
|
|
|
|
|
|
ProfileType = profile.ProfileType,
|
|
|
|
|
|
Root = profile.Root,
|
|
|
|
|
|
RootId = profile.RootId,
|
|
|
|
|
|
Child1 = profile.Child1,
|
|
|
|
|
|
Child1Id = profile.Child1Id,
|
|
|
|
|
|
Child2 = profile.Child2,
|
|
|
|
|
|
Child2Id = profile.Child2Id,
|
|
|
|
|
|
Child3 = profile.Child3,
|
|
|
|
|
|
Child3Id = profile.Child3Id,
|
|
|
|
|
|
Child4 = profile.Child4,
|
|
|
|
|
|
Child4Id = profile.Child4Id,
|
|
|
|
|
|
Gender = profile.Gender,
|
|
|
|
|
|
ProfileId = profile.Id,
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-11-24 13:05:22 +07:00
|
|
|
|
};
|
|
|
|
|
|
|
2023-11-24 16:52:48 +07:00
|
|
|
|
processTimeStamp.EditStatus = "APPROVE";
|
|
|
|
|
|
processTimeStamp.EditReason = req.Reason;
|
|
|
|
|
|
|
2025-06-17 11:02:42 +07:00
|
|
|
|
if (requestData.CheckInEdit)
|
|
|
|
|
|
{
|
|
|
|
|
|
processTimeStamp.CheckInPOI = requestData.POI ?? "";
|
|
|
|
|
|
processTimeStamp.CheckInLat = requestData.Latitude ?? 0;
|
|
|
|
|
|
processTimeStamp.CheckInLon = requestData.Longitude ?? 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (requestData.CheckOutEdit)
|
|
|
|
|
|
{
|
|
|
|
|
|
processTimeStamp.CheckOutPOI = requestData.POI ?? "";
|
|
|
|
|
|
processTimeStamp.CheckOutLat = requestData.Latitude ?? 0;
|
|
|
|
|
|
processTimeStamp.CheckOutLon = requestData.Longitude ?? 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-24 13:05:22 +07:00
|
|
|
|
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;
|
2025-06-17 11:02:42 +07:00
|
|
|
|
//processTimeStamp.CheckInLat = 0;
|
|
|
|
|
|
//processTimeStamp.CheckInLon = 0;
|
|
|
|
|
|
//processTimeStamp.CheckInPOI = "ลงเวลากรณีพิเศษ";
|
2023-12-21 15:15:03 +07:00
|
|
|
|
processTimeStamp.CheckInStatus = req.CheckInStatus;
|
2025-05-16 10:33:38 +07:00
|
|
|
|
|
|
|
|
|
|
processTimeStamp.CheckInPOI = requestData.POI ?? "";
|
|
|
|
|
|
processTimeStamp.CheckInLat = requestData.Latitude ?? 0;
|
|
|
|
|
|
processTimeStamp.CheckInLon = requestData.Longitude ?? 0;
|
2023-11-24 13:05:22 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (requestData.CheckOutEdit)
|
|
|
|
|
|
{
|
|
|
|
|
|
processTimeStamp.CheckOut = DateTime.Parse($"{requestData.CheckDate.Date.ToString("yyyy-MM-dd")} {req.CheckOutTime}");
|
|
|
|
|
|
processTimeStamp.CheckOutRemark = req.Reason;
|
2025-06-17 11:02:42 +07:00
|
|
|
|
//processTimeStamp.CheckOutLat = 0;
|
|
|
|
|
|
//processTimeStamp.CheckOutLon = 0;
|
|
|
|
|
|
//processTimeStamp.CheckOutPOI = "ลงเวลากรณีพิเศษ";
|
2023-12-21 15:15:03 +07:00
|
|
|
|
processTimeStamp.CheckOutStatus = req.CheckOutStatus;
|
2025-05-16 10:33:38 +07:00
|
|
|
|
|
|
|
|
|
|
processTimeStamp.CheckOutPOI = requestData.POI ?? "";
|
|
|
|
|
|
processTimeStamp.CheckOutLat = requestData.Latitude ?? 0;
|
|
|
|
|
|
processTimeStamp.CheckOutLon = requestData.Longitude ?? 0;
|
2023-11-24 13:05:22 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-24 16:52:48 +07:00
|
|
|
|
|
|
|
|
|
|
processTimeStamp.EditStatus = "APPROVE";
|
|
|
|
|
|
processTimeStamp.EditReason = req.Reason;
|
|
|
|
|
|
|
2023-11-24 13:05:22 +07:00
|
|
|
|
await _processUserTimeStampRepository.UpdateAsync(processTimeStamp);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-30 13:41:12 +07:00
|
|
|
|
var recvId = new List<Guid> { profile.Id };
|
|
|
|
|
|
await _notificationRepository.PushNotificationsAsync(recvId.ToArray(), "ลงเวลากรณีพิเศษ", "การขอลงเวลากรณีพิเศษของคุณได้รับการอนุมัติ", "", "", true, false);
|
|
|
|
|
|
|
2023-11-24 13:05:22 +07:00
|
|
|
|
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}")]
|
2023-11-28 09:53:38 +07:00
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
2023-11-24 13:05:22 +07:00
|
|
|
|
public async Task<ActionResult<ResponseObject>> RejectRequestAsync(Guid id, [FromBody] RejectRequestDto req)
|
|
|
|
|
|
{
|
2024-09-24 11:53:09 +07:00
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_CHECKIN_SPECIAL");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
2023-11-24 13:05:22 +07:00
|
|
|
|
if (req.Reason == null || req.Reason == string.Empty)
|
|
|
|
|
|
{
|
2023-11-24 14:21:33 +07:00
|
|
|
|
return Error("กรุณากรอกเหตุผล", StatusCodes.Status400BadRequest);
|
2023-11-24 13:05:22 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var requestData = await _additionalCheckRequestRepository.GetByIdAsync(id);
|
|
|
|
|
|
if (requestData == null)
|
|
|
|
|
|
{
|
2023-11-24 14:21:33 +07:00
|
|
|
|
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
|
2023-11-24 13:05:22 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-24 16:52:48 +07:00
|
|
|
|
// 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-24 13:05:22 +07:00
|
|
|
|
requestData.Status = "REJECT";
|
|
|
|
|
|
requestData.Comment = req.Reason;
|
|
|
|
|
|
await _additionalCheckRequestRepository.UpdateAsync(requestData);
|
|
|
|
|
|
|
2026-04-17 09:41:52 +07:00
|
|
|
|
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(requestData.KeycloakUserId, AccessToken);
|
2024-10-30 13:41:12 +07:00
|
|
|
|
|
|
|
|
|
|
var recvId = new List<Guid> { profile.Id };
|
|
|
|
|
|
await _notificationRepository.PushNotificationsAsync(recvId.ToArray(), "ลงเวลากรณีพิเศษ", "การขอลงเวลากรณีพิเศษของคุณไม่ได้รับการอนุมัติ", "", "", true, false);
|
|
|
|
|
|
|
2023-11-24 13:05:22 +07:00
|
|
|
|
return Success();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-11-28 09:53:38 +07:00
|
|
|
|
/// <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)
|
|
|
|
|
|
{
|
2024-10-22 08:20:33 +07:00
|
|
|
|
var getWorkflow = await _permission.GetPermissionAPIWorkflowAsync(id.ToString(), "SYS_CHECKIN");
|
|
|
|
|
|
if (getWorkflow == false)
|
2024-09-24 11:53:09 +07:00
|
|
|
|
{
|
2024-10-22 08:20:33 +07:00
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("GET", "SYS_CHECKIN");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
2024-09-24 11:53:09 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-28 09:53:38 +07:00
|
|
|
|
var imgUrl = $"{_configuration["MinIO:Endpoint"]}{_configuration["MinIO:BucketName"]}";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var d = (await _userTimeStampRepository.GetTimeStampById(id));
|
|
|
|
|
|
if (d == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception(GlobalMessages.DataNotFound);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-04-17 09:41:52 +07:00
|
|
|
|
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(d.KeycloakUserId, AccessToken);
|
2023-11-28 09:53:38 +07:00
|
|
|
|
if (profile == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
|
|
|
|
|
|
if (defaultRound == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-18 15:50:42 +07:00
|
|
|
|
//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);
|
2023-11-28 09:53:38 +07:00
|
|
|
|
|
|
|
|
|
|
var duty = userRound ?? defaultRound;
|
|
|
|
|
|
|
|
|
|
|
|
var result = new CheckInDetailForAdminDto
|
|
|
|
|
|
{
|
|
|
|
|
|
Id = d.Id,
|
2024-07-11 14:52:54 +07:00
|
|
|
|
FullName = $"{d.Prefix}{d.FirstName} {d.LastName}",
|
|
|
|
|
|
//FullName = _userProfileRepository.GetUserFullName(d.KeycloakUserId, AccessToken),
|
2023-11-28 09:53:38 +07:00
|
|
|
|
|
|
|
|
|
|
CheckInDate = d.CheckIn.Date,
|
|
|
|
|
|
CheckInTime = d.CheckIn.ToString("HH:mm"),
|
|
|
|
|
|
CheckInPOI = d.CheckInPOI,
|
|
|
|
|
|
CheckInLat = d.CheckInLat,
|
|
|
|
|
|
CheckInLon = d.CheckInLon,
|
2026-01-14 10:25:31 +07:00
|
|
|
|
// CheckInImg = $"{imgUrl}/{d.CheckInImageUrl}",
|
|
|
|
|
|
CheckInImg = await _minIOService.ImagesPathByName(d.CheckInImageUrl),
|
2023-11-28 09:53:38 +07:00
|
|
|
|
CheckInStatus = DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
|
|
|
|
|
|
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
|
2024-01-15 10:52:39 +07:00
|
|
|
|
|
|
|
|
|
|
DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
|
|
|
|
|
|
DateTime.Parse($"{d.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
|
|
|
|
|
|
"ABSENT" :
|
2023-11-28 09:53:38 +07:00
|
|
|
|
"LATE" :
|
2024-01-15 10:52:39 +07:00
|
|
|
|
|
2023-11-28 09:53:38 +07:00
|
|
|
|
"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,
|
2026-01-14 10:25:31 +07:00
|
|
|
|
CheckOutImg = d.CheckOut == null ? "" : await _minIOService.ImagesPathByName(d.CheckOutImageUrl),
|
2023-11-28 09:53:38 +07:00
|
|
|
|
|
|
|
|
|
|
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}") ?
|
2024-01-15 10:52:39 +07:00
|
|
|
|
"ABSENT" :
|
|
|
|
|
|
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
|
|
|
|
|
|
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
|
|
|
|
|
|
"ABSENT" :
|
2023-11-28 09:53:38 +07:00
|
|
|
|
"NORMAL",
|
|
|
|
|
|
CheckOutDescription = d.CheckOutRemark ?? "",
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return Success(result);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-29 06:07:57 +07:00
|
|
|
|
|
|
|
|
|
|
/// <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)
|
|
|
|
|
|
{
|
2026-04-17 09:41:52 +07:00
|
|
|
|
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(data.KeycloakUserId, AccessToken);
|
2023-11-29 06:07:57 +07:00
|
|
|
|
if (profile == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
|
|
|
|
|
|
}
|
2023-12-18 15:50:42 +07:00
|
|
|
|
//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);
|
|
|
|
|
|
|
2025-06-17 14:19:52 +07:00
|
|
|
|
var checkInData = await _userTimeStampRepository.GetTimestampByDateAsync(data.KeycloakUserId, data.CheckDate);
|
|
|
|
|
|
//var checkInData = await _processUserTimeStampRepository.GetTimestampByDateAsync(data.KeycloakUserId, data.CheckDate);
|
2023-11-29 06:07:57 +07:00
|
|
|
|
|
|
|
|
|
|
var duty = userRound ?? getDefaultRound;
|
|
|
|
|
|
|
2025-06-17 14:19:52 +07:00
|
|
|
|
DateTime? resultCheckInDate, resultCheckOutDate, resultCheckInDateAndTime, resultCheckOutDateAndTime;
|
2023-12-15 09:59:03 +07:00
|
|
|
|
string resultCheckInTime, resultCheckOutTime;
|
2025-06-17 14:19:52 +07:00
|
|
|
|
string resultCheckInLocation = "", resultCheckOutLocation = "";
|
2023-12-15 09:59:03 +07:00
|
|
|
|
|
|
|
|
|
|
if (data.CheckInEdit)
|
|
|
|
|
|
{
|
|
|
|
|
|
resultCheckInDate = data.CheckDate.Date;
|
|
|
|
|
|
resultCheckInTime = duty.StartTimeMorning;
|
2025-06-17 14:19:52 +07:00
|
|
|
|
resultCheckInLocation = data.POI ?? "";
|
2023-12-15 09:59:03 +07:00
|
|
|
|
}
|
|
|
|
|
|
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;
|
2025-06-17 14:19:52 +07:00
|
|
|
|
resultCheckOutLocation = data.POI ?? "";
|
2023-12-15 09:59:03 +07:00
|
|
|
|
}
|
|
|
|
|
|
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");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-06-17 14:19:52 +07:00
|
|
|
|
|
2025-07-07 12:01:34 +07:00
|
|
|
|
resultCheckInDateAndTime = resultCheckInDate is null ? null : DateTime.Parse($"{resultCheckInDate.Value.Date.ToString("yyyy-MM-dd")} {resultCheckInTime}");
|
|
|
|
|
|
resultCheckOutDateAndTime = resultCheckOutDate is null ? null : DateTime.Parse($"{resultCheckOutDate.Value.Date.ToString("yyyy-MM-dd")} {resultCheckOutTime}");
|
2025-06-17 14:19:52 +07:00
|
|
|
|
|
|
|
|
|
|
|
2023-11-29 06:07:57 +07:00
|
|
|
|
// create result object to return
|
|
|
|
|
|
var resObj = new GetAdditionalCheckRequestHistoryDto
|
|
|
|
|
|
{
|
|
|
|
|
|
Id = data.Id,
|
|
|
|
|
|
|
2023-12-15 09:59:03 +07:00
|
|
|
|
CheckInDate = resultCheckInDate,
|
|
|
|
|
|
CheckOutDate = resultCheckOutDate,
|
|
|
|
|
|
|
|
|
|
|
|
CheckInTime = resultCheckInTime,
|
|
|
|
|
|
CheckOutTime = resultCheckOutTime,
|
2023-11-29 06:07:57 +07:00
|
|
|
|
|
2025-06-17 14:19:52 +07:00
|
|
|
|
CheckInStatus = resultCheckInDateAndTime == null ? null :
|
|
|
|
|
|
DateTime.Parse(resultCheckInDateAndTime.Value.ToString("yyyy-MM-dd HH:mm")) >
|
|
|
|
|
|
DateTime.Parse($"{resultCheckInDate.Value.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
|
|
|
|
|
|
DateTime.Parse(resultCheckInDateAndTime.Value.ToString("yyyy-MM-dd HH:mm")) >
|
|
|
|
|
|
DateTime.Parse($"{resultCheckInDate.Value.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
|
2024-01-15 10:52:39 +07:00
|
|
|
|
"ABSENT" :
|
2023-11-29 06:07:57 +07:00
|
|
|
|
"LATE" :
|
|
|
|
|
|
"NORMAL",
|
|
|
|
|
|
|
2025-07-07 12:01:34 +07:00
|
|
|
|
CheckOutStatus = resultCheckInDateAndTime == null ? null :
|
|
|
|
|
|
resultCheckOutDateAndTime == null ? null :
|
2025-06-17 14:19:52 +07:00
|
|
|
|
DateTime.Parse(resultCheckOutDateAndTime.Value.ToString("yyyy-MM-dd HH:mm")) <
|
|
|
|
|
|
DateTime.Parse($"{resultCheckInDate.Value.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
|
2024-01-15 10:52:39 +07:00
|
|
|
|
"ABSENT" :
|
2025-06-17 14:19:52 +07:00
|
|
|
|
DateTime.Parse(resultCheckOutDateAndTime.Value.ToString("yyyy-MM-dd HH:mm")) <
|
|
|
|
|
|
DateTime.Parse($"{resultCheckInDate.Value.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
|
2024-01-15 10:52:39 +07:00
|
|
|
|
"ABSENT" :
|
2023-11-29 06:07:57 +07:00
|
|
|
|
"NORMAL",
|
|
|
|
|
|
|
2025-06-17 14:19:52 +07:00
|
|
|
|
//CheckInLocation = checkInData == null ? "" : checkInData.CheckInPOI,
|
|
|
|
|
|
//CheckOutLocation = checkInData == null ? "" : checkInData.CheckOutPOI ?? "",
|
|
|
|
|
|
|
|
|
|
|
|
CheckInLocation = resultCheckInLocation,
|
|
|
|
|
|
CheckOutLocation = resultCheckOutLocation,
|
2023-11-29 06:07:57 +07:00
|
|
|
|
|
|
|
|
|
|
EditReason = data.Comment ?? "",
|
|
|
|
|
|
EditStatus = data.Status,
|
|
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
result.Add(resObj);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-13 17:01:20 +07:00
|
|
|
|
|
2023-11-29 06:07:57 +07:00
|
|
|
|
|
|
|
|
|
|
var pageResult = result.Skip((page - 1) * pageSize).Take(pageSize)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
return Success(new { data = pageResult, total = result.Count });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-23 15:48:09 +07:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
2024-01-09 10:25:18 +07:00
|
|
|
|
#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)
|
|
|
|
|
|
{
|
2024-10-22 08:20:33 +07:00
|
|
|
|
var getWorkflow = await _permission.GetPermissionAPIWorkflowAsync(id.ToString(), "SYS_WORK_ROUND_EDIT");
|
|
|
|
|
|
if (getWorkflow == false)
|
2024-09-24 11:53:09 +07:00
|
|
|
|
{
|
2024-10-22 08:20:33 +07:00
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("GET", "SYS_WORK_ROUND_EDIT");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
2024-09-24 11:53:09 +07:00
|
|
|
|
}
|
2024-01-09 10:25:18 +07:00
|
|
|
|
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>
|
2024-01-09 15:32:40 +07:00
|
|
|
|
[HttpPut("admin/work/{id:guid}")]
|
2024-01-09 10:25:18 +07:00
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
public async Task<ActionResult<ResponseObject>> UpdateCalendarByProfileAsync(Guid id, [FromBody] UpdateCalendarDto req)
|
|
|
|
|
|
{
|
2024-09-24 11:53:09 +07:00
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_WORK_ROUND_EDIT");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
2025-07-11 10:03:38 +07:00
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
|
|
|
|
|
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_023 - แสดงปฏิทินวันทำงานรายคน (ADMIN) Employee
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
|
|
|
|
|
[HttpGet("admin/emp/work/{id:guid}")]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
public async Task<ActionResult<ResponseObject>> GetCalendarEmpByProfileAsync(Guid id)
|
|
|
|
|
|
{
|
|
|
|
|
|
var getWorkflow = await _permission.GetPermissionAPIWorkflowAsync(id.ToString(), "SYS_WORK_ROUND_EDIT");
|
|
|
|
|
|
if (getWorkflow == false)
|
|
|
|
|
|
{
|
|
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("GET", "SYS_WORK_ROUND_EDIT");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
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/emp/work/{id:guid}")]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
public async Task<ActionResult<ResponseObject>> UpdateEmpCalendarByProfileAsync(Guid id, [FromBody] UpdateCalendarDto req)
|
|
|
|
|
|
{
|
|
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_WORK_ROUND_EDIT");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
2024-09-24 11:53:09 +07:00
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
2024-01-09 10:25:18 +07:00
|
|
|
|
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();
|
|
|
|
|
|
}
|
2024-01-09 14:48:07 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#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)]
|
2024-01-09 15:32:40 +07:00
|
|
|
|
public async Task<ActionResult<ResponseObject>> EditCheckInStatusAsync(Guid id, [FromBody] EditCheckInStatusDto req)
|
2024-01-09 14:48:07 +07:00
|
|
|
|
{
|
2024-09-24 11:53:09 +07:00
|
|
|
|
var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_CHECKIN");
|
|
|
|
|
|
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
|
|
|
|
|
|
if (jsonData["status"]?.ToString() != "200")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
|
|
|
|
|
|
}
|
2024-01-09 14:48:07 +07:00
|
|
|
|
var data = await _processUserTimeStampRepository.GetByIdAsync(id);
|
|
|
|
|
|
if (data == null)
|
|
|
|
|
|
return Error(GlobalMessages.DataNotFound);
|
2026-01-23 20:35:54 +07:00
|
|
|
|
|
2026-01-26 12:04:58 +07:00
|
|
|
|
//if (data.CheckInStatus == "NORMAL" || data.CheckOutStatus == "NORMAL")
|
|
|
|
|
|
|
2026-01-28 11:47:10 +07:00
|
|
|
|
//var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
|
|
|
|
|
|
// แก้เป็นมาใช้งาน KeycloakUserId แทน
|
2026-04-17 09:41:52 +07:00
|
|
|
|
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(data.KeycloakUserId, AccessToken);
|
2026-01-26 12:04:58 +07:00
|
|
|
|
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
|
|
|
|
|
|
if (defaultRound == null)
|
2026-01-23 20:35:54 +07:00
|
|
|
|
{
|
2026-01-26 12:04:58 +07:00
|
|
|
|
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;
|
|
|
|
|
|
if (req.CheckInStatus == "NORMAL")
|
|
|
|
|
|
{
|
|
|
|
|
|
if(data.CheckInLocationName == "ไปประชุม / อบรม / สัมมนา")
|
2026-01-23 20:35:54 +07:00
|
|
|
|
{
|
2026-01-26 12:04:58 +07:00
|
|
|
|
data.CheckIn = DateTime.Parse($"{data.CheckIn.Date.ToString("yyyy-MM-dd")} 10:30");
|
2026-01-23 20:35:54 +07:00
|
|
|
|
}
|
2026-01-26 12:04:58 +07:00
|
|
|
|
else
|
2026-01-23 20:35:54 +07:00
|
|
|
|
{
|
2026-01-26 12:04:58 +07:00
|
|
|
|
data.CheckIn = DateTime.Parse($"{data.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}");
|
2026-01-23 20:35:54 +07:00
|
|
|
|
}
|
2026-01-26 12:04:58 +07:00
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
if (req.CheckOutStatus == "NORMAL" )
|
|
|
|
|
|
{
|
|
|
|
|
|
var checkOutTime = data.CheckOut != null ? data.CheckOut.Value : data.CheckIn;
|
|
|
|
|
|
var oldCheckOutTime = data.CheckOut != null ? data.CheckOut.Value : DateTime.Now;
|
|
|
|
|
|
var roundCheckOutTime = DateTime.Now;
|
|
|
|
|
|
if(data.CheckOutLocationName == "ไปประชุม / อบรม / สัมมนา")
|
2026-01-23 20:35:54 +07:00
|
|
|
|
{
|
2026-01-26 12:04:58 +07:00
|
|
|
|
roundCheckOutTime = DateTime.Parse($"{checkOutTime.Date.ToString("yyyy-MM-dd")} 14:30");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
roundCheckOutTime = DateTime.Parse($"{checkOutTime.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (oldCheckOutTime < roundCheckOutTime)
|
|
|
|
|
|
{
|
|
|
|
|
|
data.CheckOut = roundCheckOutTime;
|
2026-01-23 20:35:54 +07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-26 12:04:58 +07:00
|
|
|
|
|
2024-01-09 14:48:07 +07:00
|
|
|
|
data.CheckInStatus = req.CheckInStatus;
|
2024-01-10 10:08:34 +07:00
|
|
|
|
data.CheckOutStatus = req.CheckOutStatus;
|
2024-01-09 14:48:07 +07:00
|
|
|
|
data.EditReason = req.Reason;
|
2024-01-09 10:25:18 +07:00
|
|
|
|
|
2024-01-09 14:48:07 +07:00
|
|
|
|
await _processUserTimeStampRepository.UpdateAsync(data);
|
2024-01-09 10:25:18 +07:00
|
|
|
|
|
2024-01-09 14:48:07 +07:00
|
|
|
|
return Success();
|
2024-01-09 10:25:18 +07:00
|
|
|
|
}
|
2024-01-09 14:48:07 +07:00
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2024-08-30 11:12:45 +07:00
|
|
|
|
#region " รายการลารายบุคคล "
|
|
|
|
|
|
|
2024-03-08 13:22:22 +07:00
|
|
|
|
/// <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)
|
|
|
|
|
|
{
|
2026-04-17 09:41:52 +07:00
|
|
|
|
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(id, AccessToken);
|
2024-03-08 13:22:22 +07:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-09 14:48:07 +07:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
2026-03-30 09:52:27 +07:00
|
|
|
|
#region " Process - Leave and Absence "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// สร้าง Task สำหรับ Process ข้อมูลวันลาและขาดราชการ (ADMIN)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
|
|
|
|
|
[HttpPost("admin/leave-task/process")]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
public async Task<ActionResult<ResponseObject>> CreateProcessTaskAsync([FromBody] CreateLeaveProcessJobDto req)
|
|
|
|
|
|
{
|
|
|
|
|
|
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
|
|
|
|
|
|
|
2026-04-17 09:41:52 +07:00
|
|
|
|
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
|
2026-03-30 09:52:27 +07:00
|
|
|
|
if (profile == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var task = new LeaveProcessJobStatus
|
|
|
|
|
|
{
|
|
|
|
|
|
RootDnaId = profile.RootDnaId ?? Guid.Empty,
|
|
|
|
|
|
CreatedUserId = profile.Keycloak?.ToString("D") ?? "",
|
|
|
|
|
|
CreatedFullName = profile.FirstName + " " + profile.LastName,
|
|
|
|
|
|
CreatedAt = DateTime.Now,
|
|
|
|
|
|
Status = "PENDING",
|
|
|
|
|
|
StartDate = req.StartDate,
|
|
|
|
|
|
EndDate = req.EndDate
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
await _leaveProcessJobStatusRepository.AddAsync(task);
|
|
|
|
|
|
|
|
|
|
|
|
return Success();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 09:46:44 +07:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// แสดงรายการ Task สำหรับ Process ข้อมูลวันลาและขาดราชการ (ADMIN)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
|
|
|
|
|
[HttpGet("admin/leave-task/process")]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
public async Task<ActionResult<ResponseObject>> GetProcessTaskAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
|
|
|
|
|
|
|
|
|
|
|
|
var tasks = await _leaveProcessJobStatusRepository.GetByUserIdAsync(userId);
|
|
|
|
|
|
|
|
|
|
|
|
var result = tasks.Select(t => new
|
|
|
|
|
|
{
|
|
|
|
|
|
t.Id,
|
|
|
|
|
|
t.CreatedFullName,
|
|
|
|
|
|
t.CreatedAt,
|
|
|
|
|
|
t.Status,
|
|
|
|
|
|
t.StartDate,
|
|
|
|
|
|
t.EndDate,
|
|
|
|
|
|
t.ProcessingDate,
|
|
|
|
|
|
t.CompletedDate,
|
|
|
|
|
|
t.ErrorMessage
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return Success(result);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// แสดงรายการ Task สำหรับ Process ข้อมูลวันลาและขาดราชการ (ADMIN)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
|
|
|
|
|
[HttpGet("admin/leave-task/process/{id:guid}")]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
public async Task<ActionResult<ResponseObject>> GetProcessTaskByIdAsync(Guid id)
|
|
|
|
|
|
{
|
|
|
|
|
|
var task = await _leaveProcessJobStatusRepository.GetByTaskIdAsync(id);
|
|
|
|
|
|
|
|
|
|
|
|
if (task == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var result = new
|
|
|
|
|
|
{
|
|
|
|
|
|
task.Id,
|
|
|
|
|
|
task.CreatedFullName,
|
|
|
|
|
|
task.CreatedAt,
|
|
|
|
|
|
task.Status,
|
|
|
|
|
|
task.StartDate,
|
|
|
|
|
|
task.EndDate,
|
|
|
|
|
|
task.ProcessingDate,
|
|
|
|
|
|
task.CompletedDate,
|
|
|
|
|
|
task.ErrorMessage
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return Success(result);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// ลบ Task สำหรับ Process ข้อมูลวันลาและขาดราชการ (ADMIN)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
|
|
|
|
|
[HttpDelete("admin/leave-task/process/{id:guid}")]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
public async Task<ActionResult<ResponseObject>> DeleteProcessTaskByIdAsync(Guid id)
|
|
|
|
|
|
{
|
|
|
|
|
|
var task = await _leaveProcessJobStatusRepository.GetByTaskIdAsync(id);
|
|
|
|
|
|
|
|
|
|
|
|
if (task == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await _leaveProcessJobStatusRepository.DeleteAsync(task);
|
|
|
|
|
|
|
|
|
|
|
|
return Success();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// อัปเดต Task สำหรับ Process ข้อมูลวันลาและขาดราชการ (ADMIN)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// </returns>
|
|
|
|
|
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
|
|
|
|
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
|
|
|
|
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
|
|
|
|
|
[HttpPut("admin/leave-task/process/{id:guid}")]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
|
|
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
|
|
|
|
public async Task<ActionResult<ResponseObject>> UpdateProcessTaskByIdAsync(Guid id,[FromBody] CreateLeaveProcessJobDto req)
|
|
|
|
|
|
{
|
|
|
|
|
|
var task = await _leaveProcessJobStatusRepository.GetByTaskIdAsync(id);
|
|
|
|
|
|
|
|
|
|
|
|
if (task == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(task.Status != "PENDING")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Error("ไม่สามารถแก้ไขได้เนื่องจาก Task อยู่ในสถานะกำลังดำเนินการหรือดำเนินการเสร็จสิ้นแล้ว");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
task.StartDate = req.StartDate;
|
|
|
|
|
|
task.EndDate = req.EndDate;
|
|
|
|
|
|
|
|
|
|
|
|
await _leaveProcessJobStatusRepository.UpdateAsync(task);
|
|
|
|
|
|
|
|
|
|
|
|
return Success();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-30 09:52:27 +07:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
2024-08-30 11:12:45 +07:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
2023-11-09 14:35:08 +07:00
|
|
|
|
}
|
2024-01-09 10:34:02 +07:00
|
|
|
|
}
|