using BMA.EHR.Application.Repositories; using BMA.EHR.Application.Repositories.Commands; using BMA.EHR.Application.Repositories.Leaves.LeaveRequests; using BMA.EHR.Application.Repositories.Leaves.TimeAttendants; using BMA.EHR.Application.Repositories.MessageQueue; using BMA.EHR.Application.Responses.Profiles; using BMA.EHR.Domain.Common; using BMA.EHR.Domain.Models.Leave.TimeAttendants; using BMA.EHR.Domain.Shared; using BMA.EHR.Infrastructure.Persistence; using BMA.EHR.Leave.Service.DTOs.AdditionalCheck; using BMA.EHR.Leave.Service.DTOs.Calendar; using BMA.EHR.Leave.Service.DTOs.ChangeRound; using BMA.EHR.Leave.Service.DTOs.CheckIn; using BMA.EHR.Leave.Service.DTOs.DutyTime; using BMA.EHR.Leave.Service.DTOs.LeaveRequest; using iTextSharp.text; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.ObjectPool; using Nest; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using RabbitMQ.Client; using RabbitMQ.Client.Events; using Swashbuckle.AspNetCore.Annotations; using System.ComponentModel.DataAnnotations; using System.Diagnostics; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using SearchProfileResultDto = BMA.EHR.Leave.Service.DTOs.ChangeRound.SearchProfileResultDto; namespace BMA.EHR.Leave.Service.Controllers { [Route("api/v{version:apiVersion}/leave")] [ApiVersion("1.0")] [ApiController] [Produces("application/json")] [Authorize] [SwaggerTag("API ระบบลงเวลาและการลา")] public class LeaveController : BaseController { #region " Fields " private readonly DutyTimeRepository _dutyTimeRepository; private readonly LeaveDbContext _context; private readonly IHttpContextAccessor _httpContextAccessor; private readonly IWebHostEnvironment _hostingEnvironment; private readonly IConfiguration _configuration; private readonly UserProfileRepository _userProfileRepository; private readonly UserTimeStampRepository _userTimeStampRepository; private readonly MinIOService _minIOService; private readonly ProcessUserTimeStampRepository _processUserTimeStampRepository; private readonly UserDutyTimeRepository _userDutyTimeRepository; private readonly AdditionalCheckRequestRepository _additionalCheckRequestRepository; private readonly LeaveRequestRepository _leaveRequestRepository; private readonly UserCalendarRepository _userCalendarRepository; private readonly PermissionRepository _permission; private readonly CheckInJobStatusRepository _checkInJobStatusRepository; private readonly CommandRepository _commandRepository; private readonly NotificationRepository _notificationRepository; private readonly string _bucketName = "check-in"; private readonly ObjectPool _objectPool; private readonly string _fakeCheckInQueue = "fake-bma-checkin-queue"; private readonly string _realCheckInQueue = "bma-checkin-queue"; private readonly HttpClient _httpClient; #endregion #region " Constuctor and Destructor " public LeaveController(DutyTimeRepository dutyTimeRepository, LeaveDbContext context, IHttpContextAccessor httpContextAccessor, IWebHostEnvironment hostingEnvironment, IConfiguration configuration, UserProfileRepository userProfileRepository, UserTimeStampRepository userTimeStampRepository, MinIOService minIOService, ProcessUserTimeStampRepository processUserTimeStampRepository, UserDutyTimeRepository userDutyTimeRepository, AdditionalCheckRequestRepository additionalCheckRequestRepository, UserCalendarRepository userCalendarRepository, CommandRepository commandRepository, LeaveRequestRepository leaveRequestRepository, ObjectPool objectPool, PermissionRepository permission, NotificationRepository notificationRepository, CheckInJobStatusRepository checkInJobStatusRepository, HttpClient httpClient) { _dutyTimeRepository = dutyTimeRepository; _context = context; _httpContextAccessor = httpContextAccessor; _hostingEnvironment = hostingEnvironment; _configuration = configuration; _userProfileRepository = userProfileRepository; _userTimeStampRepository = userTimeStampRepository; _minIOService = minIOService; _processUserTimeStampRepository = processUserTimeStampRepository; _userDutyTimeRepository = userDutyTimeRepository; _additionalCheckRequestRepository = additionalCheckRequestRepository; _userCalendarRepository = userCalendarRepository; _commandRepository = commandRepository; _leaveRequestRepository = leaveRequestRepository; _notificationRepository = notificationRepository; _checkInJobStatusRepository = checkInJobStatusRepository; _objectPool = objectPool; _permission = permission; _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); } #endregion #region " Properties " private string? UserId => _httpContextAccessor?.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; private string? FullName => _httpContextAccessor?.HttpContext?.User?.FindFirst("name")?.Value; private bool? PlacementAdmin => _httpContextAccessor?.HttpContext?.User?.IsInRole("placement1"); private string? AccessToken => _httpContextAccessor?.HttpContext?.Request.Headers["Authorization"]; private Guid OcId { get { if (UserId != null || UserId != "") return _userProfileRepository.GetUserOCId(Guid.Parse(UserId!), AccessToken); else return Guid.Empty; } } #endregion #region " Methods " #region " Duty Time รอบการทำงาน " /// /// LV1_004 - ข้อมูลทั้งหมดของรอบการปฏิบัติงาน (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("duty-time")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> GetAllAsync() { var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_WORK_ROUND"); var jsonData = JsonConvert.DeserializeObject(getPermission); if (jsonData["status"]?.ToString() != "200") { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } var data = await _dutyTimeRepository.GetAllAsync(); return Success(data); } /// /// ข้อมูลของรอบการปฏิบัติงาน (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("duty-time/{id:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> GetByIdAsync(Guid id) { var data = await _dutyTimeRepository.GetByIdAsync(id); return Success(data); } /// /// LV1_001 - สร้างรอบการปฏิบัติงาน (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPost("duty-time")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> PostAsync([FromBody] CreateDutyTimeDto data) { var getPermission = await _permission.GetPermissionAPIAsync("CREATE", "SYS_WORK_ROUND"); var jsonData = JsonConvert.DeserializeObject(getPermission); if (jsonData["status"]?.ToString() != "200") { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } // validate var startMorning = TimeOnly.Parse(data.StartTimeMorning); var endMorning = TimeOnly.Parse(data.EndTimeMorning); var startAfternoon = TimeOnly.Parse(data.StartTimeAfternoon); var endAfternoon = TimeOnly.Parse(data.EndTimeAfternoon); if (startMorning >= endMorning) { throw new Exception(GlobalMessages.StartTimeGreaterEnd); } if (startAfternoon >= endAfternoon) { throw new Exception(GlobalMessages.StartTimeGreaterEnd); } var oldData = await _dutyTimeRepository.GetAllAsync(); if (oldData == null || oldData.Count == 0) { var inserted = new DutyTime { Id = Guid.NewGuid(), Description = data.Description ?? "", StartTimeMorning = data.StartTimeMorning, EndTimeMorning = data.EndTimeMorning, StartTimeAfternoon = data.StartTimeAfternoon, EndTimeAfternoon = data.EndTimeAfternoon, IsActive = data.IsActive, IsDefault = true, }; var ret = await _dutyTimeRepository.AddAsync(inserted); return Success(ret); } else { if (data.IsDefault) { foreach (var d in oldData) { d.IsDefault = false; await _dutyTimeRepository.UpdateAsync(d); } } var inserted = new DutyTime { Id = Guid.NewGuid(), Description = data.Description ?? "", StartTimeMorning = data.StartTimeMorning, EndTimeMorning = data.EndTimeMorning, StartTimeAfternoon = data.StartTimeAfternoon, EndTimeAfternoon = data.EndTimeAfternoon, IsActive = data.IsActive, IsDefault = data.IsDefault, }; var ret = await _dutyTimeRepository.AddAsync(inserted); return Success(ret); } } /// /// LV1_002 - แก้ไขรอบการปฏิบัติงาน (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPut("duty-time/{id:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> PutAsync(Guid id, [FromBody] UpdateDutyTimeDto data) { var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_WORK_ROUND"); var jsonData = JsonConvert.DeserializeObject(getPermission); if (jsonData["status"]?.ToString() != "200") { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } var oldData = await _dutyTimeRepository.GetByIdAsync(id); if (oldData == null) { throw new Exception(GlobalMessages.DataNotFound); } else { var oldDataList = await _dutyTimeRepository.GetAllAsync(); if (data.IsDefault) { foreach (var d in oldDataList) { d.IsDefault = false; await _dutyTimeRepository.UpdateAsync(d); } } oldData.Description = data.Description ?? ""; oldData.IsDefault = data.IsDefault; oldData.IsActive = data.IsActive; if (!data.IsActive) { // ลบรายการที่เคยผูกไว้ทั้งหมด var userDutyTimes = await _context.UserDutyTimes.Where(x => x.DutyTimeId == oldData.Id).ToListAsync(); _context.UserDutyTimes.RemoveRange(userDutyTimes); await _context.SaveChangesAsync(); } await _dutyTimeRepository.UpdateAsync(oldData); return Success(oldData); } } /// /// LV1_003 - ลบรอบการปฏิบัติงาน (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpDelete("duty-time/{id:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> DeleteAsync(Guid id) { var getPermission = await _permission.GetPermissionAPIAsync("DELETE", "SYS_WORK_ROUND"); var jsonData = JsonConvert.DeserializeObject(getPermission); if (jsonData["status"]?.ToString() != "200") { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } var oldData = await _dutyTimeRepository.GetByIdAsync(id); if (oldData == null) { throw new Exception(GlobalMessages.DataNotFound); } else { var inUseRound = await _userDutyTimeRepository.GetFirstInUseRound(oldData.Id); //if (inUseRound != null || oldData.IsActive || oldData.IsDefault) if (inUseRound != null) { throw new Exception("ไม่สามารถลบรอบการปฏิบัติงานที่ยังใช้งานอยู่ได้"); } if (oldData.IsDefault) { throw new Exception("ไม่สามารถลบรอบการปฏิบัติงานที่ตั้งค่า Default ได้"); } await _dutyTimeRepository.DeleteAsync(oldData); return Success(); } } /// /// LV1_012 - ข้อมูลทั้งหมดของรอบการปฏิบัติงานที่ active (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("round")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> GetAllActiveAsync() { var getPermission = await _permission.GetPermissionAPIAsync("GET", "SYS_WORK_ROUND_EDIT"); var jsonData = JsonConvert.DeserializeObject(getPermission); if (jsonData["status"]?.ToString() != "200") { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } var data = await _dutyTimeRepository.GetAllActiveAsync(); return Success(data); } #endregion #region " Check-In Check-Out ลงเวลา " /// /// LV1_006 - เช็คเวลาต้องลงเวลาเข้าหรือออกงาน (USER) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("check-time")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> CheckTimeAsync() { var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); var data = await _userTimeStampRepository.GetLastRecord(userId); var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); 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; // TODO : รอดุึงรอบที่ผูกกับ user //var duty = await _dutyTimeRepository.GetDefaultAsync(); CheckInResultDto ret; if (data == null) { ret = new CheckInResultDto { StartTimeMorning = duty == null ? "00:00" : duty.StartTimeMorning, EndTimeMorning = duty == null ? "00:00" : duty.EndTimeMorning, StartTimeAfternoon = duty == null ? "00:00" : duty.StartTimeAfternoon, EndTimeAfternoon = duty == null ? "00:00" : duty.EndTimeAfternoon, Description = duty == null ? "-" : duty.Description, CheckInTime = null, CheckInId = null, }; } else { if (data.CheckOut != null) { // fix issue SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921 var cur_date = DateTime.Now.Date; // ถ้า check-in + check-out ไปแล้ว if (data.CheckIn.Date == cur_date && data.CheckOut.Value.Date == cur_date) { return Error("คุณได้ทำการลงเวลาเข้าและออกเรียบร้อยแล้ว คุณจะสามารถลงเวลาได้อีกครั้งในวันถัดไป"); } else { ret = new CheckInResultDto { StartTimeMorning = duty == null ? "00:00" : duty.StartTimeMorning, EndTimeMorning = duty == null ? "00:00" : duty.EndTimeMorning, StartTimeAfternoon = duty == null ? "00:00" : duty.StartTimeAfternoon, EndTimeAfternoon = duty == null ? "00:00" : duty.EndTimeAfternoon, Description = duty == null ? "-" : duty.Description, CheckInTime = null, CheckInId = null, }; } } else { ret = new CheckInResultDto { StartTimeMorning = duty == null ? "00:00" : duty.StartTimeMorning, EndTimeMorning = duty == null ? "00:00" : duty.EndTimeMorning, StartTimeAfternoon = duty == null ? "00:00" : duty.StartTimeAfternoon, EndTimeAfternoon = duty == null ? "00:00" : duty.EndTimeAfternoon, Description = duty == null ? "-" : duty.Description, CheckInTime = data.CheckIn, CheckInId = data.Id, }; } } return Success(ret); } /// /// LV1_005 - ลงเวลาเข้า-ออกงาน (USER) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPost("check-in"), DisableRequestSizeLimit] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> CheckInAsync([FromForm] CheckTimeDto data) { // prepare data and convert request body and send to queue var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); var currentDate = DateTime.Now; // ตรวจสอบว่ามีงานที่กำลัง 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) { // ตรวจสอบว่างานที่มีอยู่ถูกสร้างเมื่อไหร่ ถ้าเกิน 2 นาทีให้สร้างใหม่ได้ var timeDiff = (currentDate - sameTypeJob.CreatedDate).TotalMinutes; if (timeDiff < 2) { return Error($"มีงาน {checkType} กำลังดำเนินการอยู่ กรุณารอสักครู่", StatusCodes.Status409Conflict); } } } var checkFileBytes = new byte[0]; // fix issue : ระบบลงเวลาปฏิบัติงาน>>รูปภาพไม่แสดงในฝั่งของ Admin #804 if (data.Img != null && data.Img.Length > 0) { var formFile = data.Img; using (var memoryStream = new MemoryStream()) { await formFile.CopyToAsync(memoryStream); checkFileBytes = memoryStream.ToArray(); } } // add task id for check in queue string taskId = Guid.NewGuid().ToString(); var checkData = new CheckTimeDtoRB { UserId = userId, CurrentDate = currentDate, CheckInId = data.CheckInId, TaskId = Guid.Parse(taskId), 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 ?? "" }; var channel = _objectPool.Get(); CheckInJobStatus? jobStatus = null; try { var queue = _configuration["Rabbit:Queue"] ?? "basic-queue"; channel.QueueDeclare(queue: queue, durable: true, exclusive: false, autoDelete: false, arguments: null); var serializedObject = JsonConvert.SerializeObject(checkData); var body = Encoding.UTF8.GetBytes(serializedObject); var properties = channel.CreateBasicProperties(); properties.Persistent = true; properties.MessageId = taskId; // บันทึกสถานะงานก่อนส่งไป RabbitMQ jobStatus = new CheckInJobStatus { 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); // ส่งไป RabbitMQ channel.BasicPublish(exchange: "", routingKey: queue, basicProperties: properties, body: body); return Success(new { date = currentDate, taskId = taskId, keycloakId = userId }); } catch (Exception ex) { // ถ้าส่งไป queue ไม่สำเร็จ ให้ลบ job status ที่สร้างไว้ออก if (jobStatus != null) { try { await _checkInJobStatusRepository.DeleteAsync(jobStatus); } catch { // Ignore delete error } } throw new Exception($"ไม่สามารถส่งงานไปยัง Queue ได้: {ex.Message}"); } finally { _objectPool.Return(channel); } } /// /// ตรวจสอบสถานะงาน check-in ด้วย Task ID /// /// Task ID ที่ได้จากการเรียก CheckInAsync /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// ไม่พบข้อมูลงาน /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("job-status/{taskId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> GetJobStatusAsync(Guid taskId) { var jobStatus = await _checkInJobStatusRepository.GetByTaskIdAsync(taskId); if (jobStatus == null) { return Error("ไม่พบข้อมูลงาน", StatusCodes.Status404NotFound); } 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 }; return Success(result); } /// /// ดึงรายการงานที่ยัง pending หรือ processing ของผู้ใช้ /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("pending-jobs")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> GetPendingJobsAsync() { var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); var jobs = await _checkInJobStatusRepository.GetPendingOrProcessingJobsAsync(userId); 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 }); } [HttpGet("check-status")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> 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() ?? 0; // // Step 2: วนลูปดึง message ทีละ 100 งาน // int batchSize = 100; // var allMessages = new List(); // 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); // Step 3: ค้นหา taskIds ที่อยู่ใน messages ทั้งหมด //var foundTasks = allMessages.FirstOrDefault(x => x.Contains(userId.ToString("D"))); return Success(new { keycloakId = userId, InQueue = (jobs != null && jobs.Count > 0) }); } catch (Exception ex) { return Error(ex, ex.Message); } finally { //_objectPool.Return(channel); } } /// /// Fake Check in /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPost("fake-check-in"), DisableRequestSizeLimit] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] [AllowAnonymous] public ActionResult FakeCheckIn([FromBody] FakeCheckTimeDto data) { var currentDate = DateTime.Now; var channel = _objectPool.Get(); try { channel.QueueDeclare(queue: _fakeCheckInQueue, durable: true, exclusive: false, autoDelete: false, arguments: null); // Create Task ID string taskId = Guid.NewGuid().ToString(); var properties = channel.CreateBasicProperties(); properties.Persistent = true; properties.MessageId = taskId; // แนบ Message ID เพื่อใช้ตรวจสอบ var serializedObject = JsonConvert.SerializeObject(data); var body = Encoding.UTF8.GetBytes(serializedObject); channel.BasicPublish(exchange: "", routingKey: _fakeCheckInQueue, basicProperties: properties, body: body); 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> 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() ?? 0; // Step 2: วนลูปดึง message ทีละ 100 งาน int batchSize = 100; var allMessages = new List(); 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); } finally { _objectPool.Return(channel); } } /// /// Check in Processing /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPost("process-check-in"), DisableRequestSizeLimit] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] [AllowAnonymous] public async Task> ProcessCheckInAsync([FromBody] CheckTimeDtoRB data) { var userId = data.UserId ?? Guid.Empty; var taskId = data.TaskId ?? Guid.Empty; try { // อัปเดตสถานะเป็น PROCESSING if (taskId != Guid.Empty) { await _checkInJobStatusRepository.UpdateToProcessingAsync(taskId); } var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, data.Token); if (profile == null) return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); if (data.CheckInFileName == "no-file") throw new Exception(GlobalMessages.NoFileToUpload); var currentDate = data.CurrentDate ?? DateTime.Now; var check_status = data.CheckInId == null ? "check-in-picture" : "check-out-picture"; var fileName = $"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{check_status}/{data.CheckInFileName}"; using (var ms = new MemoryStream(data.CheckInFileBytes ?? new byte[0])) { await _minIOService.UploadFileAsync(fileName, ms); } var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { return Error("ไม่พบรอบการลงเวลาทำงาน Default", StatusCodes.Status404NotFound); } var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id, currentDate); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); // TODO : รอดุึงรอบที่ผูกกับ user var duty = userRound ?? defaultRound; // create check in object if (data.CheckInId == null) { // validate duplicate check in var currentCheckIn = await _userTimeStampRepository.GetTimestampByDateAsync(userId, currentDate); if (currentCheckIn != null) { return Error(new Exception("ไม่สามารถลงเวลาได้ เนื่องจากมีการลงเวลาในวันนี้แล้ว!"), StatusCodes.Status400BadRequest); } var checkin = new UserTimeStamp { KeycloakUserId = userId, CheckInLat = data.Lat, CheckInLon = data.Lon, IsLocationCheckIn = data.IsLocation, CheckInLocationName = data.LocationName, CheckInPOI = data.POI, CheckInRemark = data.Remark, CheckInImageUrl = fileName, CheckIn = currentDate, Prefix = profile.Prefix, FirstName = profile.FirstName, LastName = profile.LastName, 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, }; var startTime = ""; if (!data.IsLocation && data.LocationName == "ไปประชุม / อบรม / สัมมนา") { //startTime = "09:30"; startTime = "10:30"; } else startTime = duty.StartTimeMorning; 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"; } } 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"; } // 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, }; await _userTimeStampRepository.AddAsync(checkin); await _processUserTimeStampRepository.AddAsync(checkin_process); } else { var checkout = await _userTimeStampRepository.GetByIdAsync(data.CheckInId.Value); var currentCheckInProcess = await _processUserTimeStampRepository.GetTimestampByDateAsync(userId, checkout.CheckIn.Date); var checkout_process = await _processUserTimeStampRepository.GetByIdAsync(currentCheckInProcess.Id); if (checkout != null) { checkout.CheckOutLat = data.Lat; checkout.CheckOutLon = data.Lon; checkout.IsLocationCheckOut = data.IsLocation; checkout.CheckOutLocationName = data.LocationName; checkout.CheckOutPOI = data.POI; checkout.CheckOutRemark = data.Remark; checkout.CheckOutImageUrl = fileName; checkout.CheckOut = currentDate; await _userTimeStampRepository.UpdateAsync(checkout); } else { return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); } var endTime = ""; if (!data.IsLocation && data.LocationName == "ไปประชุม / อบรม / สัมมนา") { //startTime = "09:30"; endTime = "14:30"; } else endTime = duty.EndTimeAfternoon; var endTimeMorning = duty.EndTimeMorning; 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") { if(DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning}")) checkOutStatus = "ABSENT"; else checkOutStatus = "NORMAL"; } else { // fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921 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")} {duty.EndTimeMorning}") ? "ABSENT" : "NORMAL"; } } else { // fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921 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")} {duty.EndTimeMorning}") ? "ABSENT" : "NORMAL"; } if (checkout_process != null) { checkout_process.CheckOutLat = data.Lat; checkout_process.CheckOutLon = data.Lon; checkout_process.IsLocationCheckOut = data.IsLocation; checkout_process.CheckOutLocationName = data.LocationName; checkout_process.CheckOutPOI = data.POI; checkout_process.CheckOutRemark = data.Remark; checkout_process.CheckOutImageUrl = fileName; checkout_process.CheckOut = currentDate; checkout_process.CheckOutStatus = checkOutStatus; await _processUserTimeStampRepository.UpdateAsync(checkout_process); } else { return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); } } // อัปเดตสถานะเป็น COMPLETED if (taskId != Guid.Empty) { var additionalData = JsonConvert.SerializeObject(new { CheckInType = data.CheckInId == null ? "check-in" : "check-out", FileName = fileName, ProcessedDate = currentDate }); await _checkInJobStatusRepository.UpdateToCompletedAsync(taskId, additionalData); } var checkInType = data.CheckInId == null ? "check-in" : "check-out"; return Success(new { user = $"{profile.FirstName} {profile.LastName}", date = currentDate, type = checkInType }); ; } catch (Exception ex) { // อัปเดตสถานะเป็น FAILED if (taskId != Guid.Empty) { await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, ex.Message); } throw; } } /// /// LV1_005 - ลงเวลาเข้า-ออกงาน (USER) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPost("check-in-old"), DisableRequestSizeLimit] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> CheckInOldAsync([FromForm] CheckTimeDto data) { var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); if (profile == null) return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); if (data.Img == null) throw new Exception(GlobalMessages.NoFileToUpload); var currentDate = DateTime.Now; var fileName = $"{_bucketName}/{UserId}/{DateTime.Now.ToString("dd-MM-yyyy")}/{data.Img.FileName}"; using (var ms = new MemoryStream()) { data.Img.CopyTo(ms); await _minIOService.UploadFileAsync(fileName, ms); } var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { return Error("ไม่พบรอบการลงเวลาทำงาน Default", StatusCodes.Status404NotFound); } var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); // TODO : รอดุึงรอบที่ผูกกับ user var duty = userRound ?? defaultRound; // create check in object if (data.CheckInId == null) { // validate duplicate check in var currentCheckIn = await _userTimeStampRepository.GetTimestampByDateAsync(Guid.Parse(UserId), currentDate); if (currentCheckIn != null) { return Error(new Exception("ไม่สามารถลงเวลาได้ เนื่องจากมีการลงเวลาในวันนี้แล้ว!"), StatusCodes.Status400BadRequest); } var checkin = new UserTimeStamp { KeycloakUserId = UserId != null ? Guid.Parse(UserId) : Guid.Empty, CheckInLat = data.Lat, CheckInLon = data.Lon, IsLocationCheckIn = data.IsLocation, CheckInLocationName = data.LocationName, CheckInPOI = data.POI, CheckInRemark = data.Remark, CheckInImageUrl = fileName, CheckIn = currentDate, Prefix = profile.Prefix, FirstName = profile.FirstName, LastName = profile.LastName, }; var checkInStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ? DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "LATE" : "NORMAL"; // process - รอทำใน queue var checkin_process = new ProcessUserTimeStamp { KeycloakUserId = UserId != null ? Guid.Parse(UserId) : Guid.Empty, CheckInLat = data.Lat, CheckInLon = data.Lon, IsLocationCheckIn = data.IsLocation, CheckInLocationName = data.LocationName, CheckInPOI = data.POI, CheckInRemark = data.Remark, CheckInImageUrl = fileName, CheckIn = currentDate, CheckInStatus = checkInStatus, Prefix = profile.Prefix, FirstName = profile.FirstName, LastName = profile.LastName, }; await _userTimeStampRepository.AddAsync(checkin); await _processUserTimeStampRepository.AddAsync(checkin_process); } else { var checkout = await _userTimeStampRepository.GetByIdAsync(data.CheckInId.Value); var currentCheckInProcess = await _processUserTimeStampRepository.GetTimestampByDateAsync(Guid.Parse(UserId), checkout.CheckIn.Date); var checkout_process = await _processUserTimeStampRepository.GetByIdAsync(currentCheckInProcess.Id); if (checkout != null) { checkout.CheckOutLat = data.Lat; checkout.CheckOutLon = data.Lon; checkout.IsLocationCheckOut = data.IsLocation; checkout.CheckOutLocationName = data.LocationName; checkout.CheckOutPOI = data.POI; checkout.CheckOutRemark = data.Remark; checkout.CheckOutImageUrl = fileName; checkout.CheckOut = currentDate; await _userTimeStampRepository.UpdateAsync(checkout); } else { return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); } var checkOutStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? "ABSENT" : DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "NORMAL"; if (checkout_process != null) { checkout_process.CheckOutLat = data.Lat; checkout_process.CheckOutLon = data.Lon; checkout_process.IsLocationCheckOut = data.IsLocation; checkout_process.CheckOutLocationName = data.LocationName; checkout_process.CheckOutPOI = data.POI; checkout_process.CheckOutRemark = data.Remark; checkout_process.CheckOutImageUrl = fileName; checkout_process.CheckOut = currentDate; checkout_process.CheckOutStatus = checkOutStatus; await _processUserTimeStampRepository.UpdateAsync(checkout_process); } else { return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); } } return Success(new { date = currentDate }); } /// /// LV1_007 - ประวัติการลงเวลา (USER) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("check-in/history")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> CheckInHistoryAsync(int year, int page = 1, int pageSize = 10, string keyword = "") { var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { return Error("ไม่พบรอบการลงเวลาทำงาน Default", StatusCodes.Status404NotFound); } var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); // TODO : รอดุึงรอบที่ผูกกับ user var duty = userRound ?? defaultRound; var checkin_base = DateTime.Parse($"{DateTime.Now.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}"); var checkout_base = DateTime.Parse($"{DateTime.Now.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); // var test = await _processUserTimeStampRepository.GetTimeStampHistoryAsync(userId, year); // return Success(test); var data = (await _processUserTimeStampRepository.GetTimeStampHistoryAsync(userId, year)) .Select(d => new CheckInHistoryDto { CheckInId = d.Id, CheckInDate = d.CheckIn.Date, CheckInTime = d.CheckIn.ToString("HH:mm:ss"), CheckInLocation = d.CheckInPOI, CheckInStatus = d.CheckInStatus != null || d.CheckInStatus != "" ? d.CheckInStatus : DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ? "LATE" : "NORMAL", CheckInIsLocation = d.IsLocationCheckIn, CheckInLocationName = d.CheckInLocationName, CheckOutDate = d.CheckOut == null ? null : d.CheckOut.Value.Date, CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm:ss"), CheckOutLocation = d.CheckOutPOI ?? "", CheckOutStatus = d.CheckOut == null ? null : d.CheckOutStatus != null || d.CheckOutStatus != "" ? d.CheckOutStatus : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? "LATE" : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "NORMAL", CheckOutIsLocation = d.IsLocationCheckOut, CheckOutLocationName = d.CheckOutLocationName, IsEdit = _processUserTimeStampRepository.IsEditRequest(userId, d.CheckIn.Date) //IsEdit = (d.EditStatus != null && d.EditStatus != "") //EditReason = d.EditReason ?? "",ß //EditStatus = d.EditStatus ?? "" }) .ToList(); if (keyword != "") { data = data.Where(x => (x.CheckInLocationName!.Contains(keyword) || x.CheckInLocation!.Contains(keyword) || x.CheckOutLocationName!.Contains(keyword) || x.CheckOutLocation!.Contains(keyword))) .ToList(); } var pageData = data .Skip((page - 1) * pageSize) .Take(pageSize) .ToList(); return Success(new { data = pageData, total = data.Count }); } /// /// LV1_010 - รายการลงเวลาปฏิบัติงาน (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("log-record")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> LogRecordAsync([Required] DateTime startDate, [Required] DateTime endDate, int page = 1, int pageSize = 10, string keyword = "", string profileType = "ALL", string? sortBy = "", bool? descending = false) { var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_CHECKIN"); var jsonData = JsonConvert.DeserializeObject(getPermission); if (jsonData["status"]?.ToString() != "200") { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } if (startDate.Date > endDate.Date) { return Error(new Exception("วันเริ่มต้นต้องมีค่าน้อยกว่าหรือเท่ากับวันสิ้นสุด"), StatusCodes.Status400BadRequest); } //var count = await _userTimeStampRepository.CountRecordAsync(); var imgUrl = $"{_configuration["MinIO:Endpoint"]}{_configuration["MinIO:BucketName"]}"; 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 : ""; } 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 : ""; } else if (role == "ROOT" || role == "PARENT") { nodeId = profileAdmin?.RootDnaId; } //var data = (await _userTimeStampRepository.GetTimeStampHistoryForAdminAsync(startDate, endDate)) var data = (await _userTimeStampRepository.GetTimeStampHistoryForAdminRoleAsync(startDate, endDate, role, nodeId, profileAdmin?.Node)) .Select(d => new CheckInHistoryForAdminDto { Id = d.Id, //FullName = _userProfileRepository.GetUserFullName(d.KeycloakUserId, AccessToken), FullName = $"{d.Prefix ?? ""}{d.FirstName ?? ""} {d.LastName ?? ""}", Prefix = d.Prefix ?? "", FirstName = d.FirstName ?? "", LastName = d.LastName ?? "", ProfileType = d.ProfileType ?? "", CheckInDate = d.CheckIn.Date, CheckInTime = d.CheckIn.ToString("HH:mm:ss"), CheckInLocation = d.CheckInPOI, CheckInLat = d.CheckInLat, CheckInLon = d.CheckInLon, CheckInImage = $"{imgUrl}/{d.CheckInImageUrl}", // add from new specification IsLocationCheckIn = d.IsLocationCheckIn, CheckInLocationName = d.CheckInLocationName ?? "", CheckOutDate = d.CheckOut?.Date, CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm:ss"), CheckOutLocation = d.CheckOut == null ? "" : d.CheckOutPOI, CheckOutLat = d.CheckOut == null ? null : d.CheckOutLat, CheckOutLon = d.CheckOut == null ? null : d.CheckOutLon, CheckOutImage = d.CheckOut == null ? "" : $"{imgUrl}/{d.CheckOutImageUrl}", // add from new specification IsLocationCheckOut = d.IsLocationCheckOut, CheckOutLocationName = d.CheckOutLocationName ?? "" }) .ToList(); if (keyword != "") { data = data.Where(x => x.FullName.Contains(keyword)).ToList(); } if (profileType.Trim().ToUpper() != "ALL") data = data.Where(x => x.ProfileType == profileType.Trim().ToUpper()).ToList(); 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; } } var pageData = data .Skip((page - 1) * pageSize) .Take(pageSize) .ToList(); return Success(new { data = pageData, total = data.Count }); } /// /// LV1_011 - รายละเอียดการลงเวลาปฎิบัติงานรายบุคคล Tabรายากรลงเวลาที่ประมวลผลแล้ว (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("time-record/{id:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> GetTimeRecordAsync([Required] Guid id) { var getWorkflow = await _permission.GetPermissionAPIWorkflowAsync(id.ToString(), "SYS_CHECKIN"); if (getWorkflow == false) { var getPermission = await _permission.GetPermissionAPIAsync("GET", "SYS_CHECKIN"); var jsonData = JsonConvert.DeserializeObject(getPermission); if (jsonData["status"]?.ToString() != "200") { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } } var imgUrl = $"{_configuration["MinIO:Endpoint"]}{_configuration["MinIO:BucketName"]}"; var d = (await _processUserTimeStampRepository.GetTimeStampById(id)); if (d == null) { throw new Exception(GlobalMessages.DataNotFound); } else { var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(d.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound); } //var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty); var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); var duty = userRound ?? defaultRound; var result = new CheckInDetailForAdminDto { Id = d.Id, FullName = $"{d.Prefix}{d.FirstName} {d.LastName}", ProfileType = (d.ProfileType != "" || d.ProfileType != null) ? d.ProfileType : (profile.ProfileType ?? ""), //FullName = $"{profile.Prefix}{profile.FirstName} {profile.LastName}", // _userProfileRepository.GetUserFullName(d.KeycloakUserId, AccessToken), CheckInDate = d.CheckIn.Date, CheckInTime = d.CheckIn.ToString("HH:mm"), CheckInPOI = d.CheckInPOI, CheckInLat = d.CheckInLat, CheckInLon = d.CheckInLon, //CheckInImg = $"{imgUrl}/{d.CheckInImageUrl}", CheckInImg = await _minIOService.ImagesPathByName(d.CheckInImageUrl), CheckInStatus = d.CheckInStatus != null || d.CheckInStatus != "" ? d.CheckInStatus : DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ? DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{d.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "LATE" : "NORMAL", CheckInDescription = d.CheckInRemark ?? "", IsLocationCheckIn = d.IsLocationCheckIn, CheckInLocationName = d.CheckInLocationName ?? "", CheckOutDate = d.CheckOut == null ? null : d.CheckOut.Value.Date, CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm"), CheckOutPOI = d.CheckOut == null ? "" : d.CheckOutPOI, CheckOutLat = d.CheckOut == null ? null : d.CheckOutLat, CheckOutLon = d.CheckOut == null ? null : d.CheckOutLon, CheckOutImg = d.CheckOut == null ? "" : await _minIOService.ImagesPathByName(d.CheckOutImageUrl), CheckOutStatus = d.CheckOut == null ? null : d.CheckOutStatus != null || d.CheckOutStatus != "" ? d.CheckOutStatus : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? "ABSENT" : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "NORMAL", CheckOutDescription = d.CheckOutRemark ?? "", IsLocationCheckOut = d.IsLocationCheckOut, CheckOutLocationName = d.CheckOutLocationName ?? "" }; return Success(result); } } /// /// LV1_009 - รายการลงเวลาปฏิบัติงานที่ประมวลผลแล้ว (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("time-record")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] [AllowAnonymous] public async Task> 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) { var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_CHECKIN"); var jsonData = JsonConvert.DeserializeObject(getPermission); if (jsonData["status"]?.ToString() != "200") { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } if (startDate.Date > endDate.Date) { return Error(new Exception("วันเริ่มต้นต้องมีค่าน้อยกว่าหรือเท่ากับวันสิ้นสุด"), StatusCodes.Status400BadRequest); } var profiles = new List(); //var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); //var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); //if (profile == null) //{ // return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); //} var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound); } //var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty); //var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); //var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; //var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); //var duty = userRound ?? defaultRound; var duty = defaultRound; var checkin_base = DateTime.Parse($"{DateTime.Now.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}"); var checkout_base = DateTime.Parse($"{DateTime.Now.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); //var count = await _processUserTimeStampRepository.GetTimeStampHistoryForAdminCountAsync(startDate, endDate); var imgUrl = $"{_configuration["MinIO:Endpoint"]}{_configuration["MinIO:BucketName"]}"; 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 : ""; } 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 : ""; } else if (role == "ROOT" || role == "PARENT") { nodeId = profileAdmin?.RootDnaId; } //var resultData = await _processUserTimeStampRepository.GetTimeStampHistoryForAdminAsync(startDate, endDate); var resultData = await _processUserTimeStampRepository.GetTimeStampHistoryForAdminRoleAsync(startDate, endDate, role, nodeId, profileAdmin?.Node); var data = new List(); foreach (var d in resultData) { //var pf = profiles.FirstOrDefault(x => x.Keycloak == d.KeycloakUserId); //if (pf == null) //{ // pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(d.KeycloakUserId, AccessToken); // if (pf == null) // continue; // else // profiles.Add(pf); //} //var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(d.KeycloakUserId, AccessToken); //if (pf == null) continue; data.Add(new CheckInProcessHistoryForAdminDto { Id = d.Id, FullName = $"{d.Prefix ?? ""}{d.FirstName ?? ""} {d.LastName ?? ""}", Prefix = d.Prefix ?? "", FirstName = d.FirstName ?? "", LastName = d.LastName ?? "", ProfileType = d.ProfileType ?? "", 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?.Date, CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm"), CheckOutLocation = d.CheckOut == null ? "" : d.CheckOutPOI, CheckOutLat = d.CheckOut == null ? null : d.CheckOutLat, CheckOutLon = d.CheckOut == null ? null : d.CheckOutLon, CheckOutStatus = d.CheckOutStatus != "" ? d.CheckOutStatus : d.CheckOut == null ? null : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? "ABSENT" : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "NORMAL", CheckOutIsLocation = d.IsLocationCheckOut, CheckOutLocationName = d.CheckOutLocationName ?? "" //CheckOutImageUrl = d.CheckOut == null ? "" : $"{imgUrl}/{d.CheckOutImageUrl}", }); } // var data = (await _processUserTimeStampRepository.GetTimeStampHistoryForAdminAsync(startDate, endDate)) // .Select(d => new CheckInProcessHistoryForAdminDto // { // Id = d.Id, // FullName = _userProfileRepository.GetUserFullName(d.KeycloakUserId, AccessToken), // CheckInDate = d.CheckIn.Date, // CheckInTime = d.CheckIn.ToString("HH:mm"), // CheckInLocation = d.CheckInPOI, // CheckInLat = d.CheckInLat, // CheckInLon = d.CheckInLon, // CheckInStatus = d.CheckInStatus != "" ? d.CheckInStatus : // DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) > // DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ? // DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) > // DateTime.Parse($"{d.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? // "ABSENT" : // "LATE" : // "NORMAL", // CheckInIsLocation = d.IsLocationCheckIn, // CheckInLocationName = d.CheckInLocationName, // //CheckInImageUrl = $"{imgUrl}/{d.CheckInImageUrl}", // CheckOutDate = d.CheckOut == null ? null : d.CheckOut.Value.Date, // CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm"), // CheckOutLocation = d.CheckOut == null ? "" : d.CheckOutPOI, // CheckOutLat = d.CheckOut == null ? null : d.CheckOutLat, // CheckOutLon = d.CheckOut == null ? null : d.CheckOutLon, // CheckOutStatus = d.CheckOutStatus != "" ? d.CheckOutStatus : // d.CheckOut == null ? null : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < // DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? // "ABSENT" : // DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < // DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? // "ABSENT" : // "NORMAL", // CheckOutIsLocation = d.IsLocationCheckOut, // CheckOutLocationName = d.CheckOutLocationName // //CheckOutImageUrl = d.CheckOut == null ? "" : $"{imgUrl}/{d.CheckOutImageUrl}", // }) // .ToList(); if (keyword != "") { data = data.Where(x => x.FullName.Contains(keyword)).ToList(); } if (status.Trim().ToUpper() != "ALL") { data = data.Where(x => x.CheckInStatus == status || x.CheckOutStatus == status).ToList(); } if (profileType.Trim().ToUpper() != "ALL") data = data.Where(x => x.ProfileType == profileType.Trim().ToUpper()).ToList(); 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; } } var pageData = data .Skip((page - 1) * pageSize) .Take(pageSize) .ToList(); return Success(new { data = pageData, total = data.Count }); } #endregion #region " เปลี่ยนรอบการทำงาน " /// /// LV1_006 - เช็คเวลาต้องลงเวลาเข้าหรือออกงาน (USER) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPost("search")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> SearchProfileAsync([FromBody] DTOs.ChangeRound.SearchProfileDto req) { var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_WORK_ROUND_EDIT"); var jsonData = JsonConvert.DeserializeObject(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 : ""; } 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 : ""; } else if (role == "ROOT" || role == "PARENT") { nodeId = profileAdmin?.RootDnaId; } var profile = await _userProfileRepository.SearchProfile(req.CitizenId, req.FirstName, req.LastName, AccessToken ?? "", req.Page, req.PageSize, role, nodeId, profileAdmin?.Node); // Get default round once var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync(); var resultSet = new List(); // Create dictionaries to cache results and avoid duplicate queries var effectiveDateCache = new Dictionary(); var dutyTimeCache = new Dictionary(); foreach (var p in profile.Data) { // Use cache for effective date if (!effectiveDateCache.ContainsKey(p.Id)) { effectiveDateCache[p.Id] = await _userDutyTimeRepository.GetLastEffectRound(p.Id); } var effectiveDate = effectiveDateCache[p.Id]; var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; // 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]; } var duty = userRound ?? getDefaultRound; if (duty == null) continue; // Skip if no duty time found var res = new SearchProfileResultDto { ProfileId = p.Id, CitizenId = p.CitizenId ?? "", FullName = $"{p.Prefix ?? ""}{p.FirstName ?? ""} {p.LastName ?? ""}", StartTimeMorning = duty.StartTimeMorning, LeaveTimeAfterNoon = duty.EndTimeAfternoon, EffectiveDate = effectiveDate?.EffectiveDate?.Date }; resultSet.Add(res); } return Success(new { data = resultSet, total = profile.Total }); } /// /// LV1_014 - เปลี่ยนรอบการลงเวลา (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPost("round")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> CreateChangeRoundAsync([FromBody] CreateChangeRoundDto req) { var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_WORK_ROUND_EDIT"); var jsonData = JsonConvert.DeserializeObject(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(); } /// /// LV1_015 - ประวัติการเปลี่ยนรอบการลงเวลา (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("round/{id:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> GetChangeRoundHistoryByProfileIdAsync(Guid id, int page = 1, int pageSize = 10, string keyword = "", string? sortBy = "", bool? descending = false) { 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(getPermission); if (jsonData["status"]?.ToString() != "200") { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } } var data = await _userDutyTimeRepository.GetListByProfileIdAsync(id); var resultSet = new List(); if (data != null) { resultSet = data .GroupBy(item => item.ProfileId) .SelectMany(group => group //.OrderBy(item => item.EffectiveDate) // เรียงลำดับตาม property ที่คุณต้องการ .Select((item, index) => new ChangeRoundHistoryDto { Round = index + 1, StartTimeMorning = item.DutyTime.StartTimeMorning, LeaveTimeAfternoon = item.DutyTime.EndTimeAfternoon, EffectiveDate = item.EffectiveDate.Value, Remark = item.Remark })) //.Skip((page - 1) * pageSize) //.Take(pageSize) .ToList(); 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(); } return Success(new { data = resultSet, total = data.Count }); } #endregion #region " เปลี่ยนรอบการทำงาน ลจ. " /// /// LV1_006 - เช็คเวลาต้องลงเวลาเข้าหรือออกงาน (USER) ลจ. /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPost("emp/search")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> SearchEmpProfileAsync([FromBody] DTOs.ChangeRound.SearchProfileDto req) { var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_WORK_ROUND_EDIT_EMP"); var jsonData = JsonConvert.DeserializeObject(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 : ""; } 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 : ""; } else if (role == "ROOT" || role == "PARENT") { nodeId = profileAdmin?.RootDnaId; } var profile = await _userProfileRepository.SearchProfileEmployee(req.CitizenId, req.FirstName, req.LastName, AccessToken ?? "", req.Page, req.PageSize, role, nodeId, profileAdmin?.Node); // Get default round once var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync(); var resultSet = new List(); // Create dictionaries to cache results and avoid duplicate queries var effectiveDateCache = new Dictionary(); var dutyTimeCache = new Dictionary(); foreach (var p in profile.Data) { // Use cache for effective date if (!effectiveDateCache.ContainsKey(p.Id)) { effectiveDateCache[p.Id] = await _userDutyTimeRepository.GetLastEffectRound(p.Id); } var effectiveDate = effectiveDateCache[p.Id]; var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; // 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]; } var duty = userRound ?? getDefaultRound; if (duty == null) continue; // Skip if no duty time found var res = new SearchProfileResultDto { ProfileId = p.Id, CitizenId = p.CitizenId ?? "", FullName = $"{p.Prefix ?? ""}{p.FirstName ?? ""} {p.LastName ?? ""}", StartTimeMorning = duty.StartTimeMorning, LeaveTimeAfterNoon = duty.EndTimeAfternoon, EffectiveDate = effectiveDate?.EffectiveDate?.Date }; resultSet.Add(res); } return Success(new { data = resultSet, total = profile.Total }); } /// /// LV1_014 - เปลี่ยนรอบการลงเวลา (ADMIN) Employee /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPost("emp/round")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> CreateChangeEmpRoundAsync([FromBody] CreateChangeRoundDto req) { var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_WORK_ROUND_EDIT"); var jsonData = JsonConvert.DeserializeObject(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(); } /// /// LV1_015 - ประวัติการเปลี่ยนรอบการลงเวลา (ADMIN) Employee /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("emp/round/{id:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> GetChangeEmpRoundHistoryByProfileIdAsync(Guid id, int page = 1, int pageSize = 10, string keyword = "", string? sortBy = "", bool? descending = false) { 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(getPermission); if (jsonData["status"]?.ToString() != "200") { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } } var data = await _userDutyTimeRepository.GetListByProfileIdAsync(id); var resultSet = new List(); if (data != null) { resultSet = data .GroupBy(item => item.ProfileId) .SelectMany(group => group //.OrderBy(item => item.EffectiveDate) // เรียงลำดับตาม property ที่คุณต้องการ .Select((item, index) => new ChangeRoundHistoryDto { Round = index + 1, StartTimeMorning = item.DutyTime.StartTimeMorning, LeaveTimeAfternoon = item.DutyTime.EndTimeAfternoon, EffectiveDate = item.EffectiveDate.Value, Remark = item.Remark })) //.Skip((page - 1) * pageSize) //.Take(pageSize) .ToList(); 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(); } return Success(new { data = resultSet, total = data.Count }); } #endregion #region " Check Checkout Time " /// /// ตรวจสอบว่าเวลาปัจจุบัน ถ้า checkout จะขาดราชการหรือไม่? /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("user/checkout-check/{isSeminar}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> CheckoutCheckAsync(string isSeminar = "N") { var time = DateTime.Now; var userId = UserId != null ? Guid.Parse(UserId) : Guid.Empty; var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); 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; var lastCheckIn = await _context.Set() .Where(u => u.KeycloakUserId == userId) .Where(d => d.CheckOut == null) .OrderByDescending(u => u.CheckIn) .FirstOrDefaultAsync(); //var endTime = DateTimeOffset.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")}T{duty.EndTimeAfternoon}:00.0000000+07:00").ToLocalTime().DateTime;  //var endTime = DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")}T{duty.EndTimeAfternoon}:00.0000000+07:00"); var endTime = isSeminar.Trim().ToUpper() == "Y" ? DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} 14:30") : DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); var endTimeMorning = DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}"); var endTimeDisplay = endTime; var status = string.Empty; if(lastCheckIn == null) { status = "ABSENT"; } else if (lastCheckIn.CheckIn.Date < DateTime.Now.Date) { status = "NORMAL"; } else { if (time < endTime) { //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") { if(time < endTimeMorning) { status = "ABSENT"; endTimeDisplay = endTimeMorning; } else { status = "NORMAL"; } } else { status = "ABSENT"; } } else { status = "ABSENT"; } } else { status = "NORMAL"; } } //var status = lastCheckIn == null ? "ABSENT" : lastCheckIn.CheckIn.Date < DateTime.Now.Date ? "NORMAL" : time < endTime ? "ABSENT" : "NORMAL"; return Success(new { Status = status, StatusText = status == "ABSENT" ? "ขาดราชการ" : "ปกติ", ServerTime = time, EndTime = endTimeDisplay }); } #endregion #region " ขอลงเวลาเป็นกรณีพิเศษ " /// /// LV1_017 - เพิ่มลงเวลากรณีพิเศษ (USER) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPost("user/edit")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> CreateAdditionalCheckRequestAsync([FromBody] CreateAdditionalCheckRequestDto req) { if (req.CheckDate.Date > DateTime.Now.Date) { return Error("ไม่สามารถขอลงเวลากรณีพิเศษในวันที่มากกว่าวันที่ปัจจุบันได้", StatusCodes.Status400BadRequest); } var userId = UserId != null ? Guid.Parse(UserId) : Guid.Empty; var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); } var request = new AdditionalCheckRequest { KeycloakUserId = UserId != null ? Guid.Parse(UserId) : Guid.Empty, CheckDate = req.CheckDate, CheckInEdit = req.CheckInEdit, CheckOutEdit = req.CheckOutEdit, Description = req.Description, Prefix = profile.Prefix, FirstName = profile.FirstName, LastName = profile.LastName, // fix issue #1547 POI = req.POI, Latitude = req.Latitude, Longitude = req.Longitude, // 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, }; await _additionalCheckRequestRepository.AddAsync(request); return Success(); } /// /// LV1_018 - รายการลงเวลากรณีพิเศษ (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("admin/edit")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> GetAdditionalCheckRequestAsync([Required] int year, [Required] int month, [Required] int page = 1, [Required] int pageSize = 10, string keyword = "", string? sortBy = "", bool? descending = false) { var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_CHECKIN_SPECIAL"); var jsonData = JsonConvert.DeserializeObject(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 : ""; } 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 : ""; } else if (role == "ROOT" || role == "PARENT") { nodeId = profileAdmin?.RootDnaId; } //var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequests(year, month); var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequestsByAdminRole(year, month, role, nodeId, profileAdmin?.Node); var total = rawData.Count; var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (getDefaultRound == null) { return Error("ไม่พบรอบลงเวลา Default", StatusCodes.Status404NotFound); } var result = new List(); 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) { var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(data.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } //var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty); var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); var checkInData = await _userTimeStampRepository.GetTimestampByDateAsync(data.KeycloakUserId, data.CheckDate); var duty = userRound ?? getDefaultRound; //var duty = getDefaultRound; // create result object to return var resObj = new GetAdditionalCheckRequestDto { Id = data.Id, FullName = $"{data.Prefix}{data.FirstName} {data.LastName}", Prefix = data.Prefix ?? "", FirstName = data.FirstName ?? "", LastName = data.LastName ?? "", //FullName = $"{profile.Prefix}{profile.FirstName} {profile.LastName}", CreatedAt = data.CreatedAt, CheckDate = data.CheckDate, CheckInEdit = data.CheckInEdit, CheckOutEdit = data.CheckOutEdit, CheckInTime = checkInData == null ? "00:00" : checkInData.CheckIn.ToString("HH:mm"), CheckOutTime = checkInData == null ? "00:00" : checkInData.CheckOut == null ? "00:00" : checkInData.CheckOut.Value.ToString("HH:mm"), CheckInStatus = checkInData == null ? null : DateTime.Parse(checkInData.CheckIn.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{checkInData.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ? DateTime.Parse(checkInData.CheckIn.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{checkInData.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "LATE" : "NORMAL", CheckOutStatus = checkInData == null ? null : checkInData.CheckOut == null ? null : DateTime.Parse(checkInData.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{checkInData.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? "ABSENT" : DateTime.Parse(checkInData.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{checkInData.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "NORMAL", StartTimeMorning = duty.StartTimeMorning, EndTimeMorning = duty.EndTimeMorning, StartTimeAfternoon = duty.StartTimeAfternoon, EndTimeAfternoon = duty.EndTimeAfternoon, Reason = data.Comment ?? "", Status = data.Status, Description = data.Description, StatusSort = data.Status.Trim().ToLower() == "pending" ? 1 : data.Status.Trim().ToLower() == "approve" ? 2 : 3, POI = data.POI, Latitude = data.Latitude, Longitude = data.Longitude, }; result.Add(resObj); } // 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 }); } /// /// LV1_019 - อนุมัติลงเวลากรณีพิเศษ (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPut("admin/edit/approve/{id:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> ApproveRequestAsync(Guid id, [FromBody] ApproveRequestDto req) { var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_CHECKIN_SPECIAL"); var jsonData = JsonConvert.DeserializeObject(getPermission); if (jsonData["status"]?.ToString() != "200") { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } if (req.Reason == null || req.Reason == string.Empty) { return Error("กรุณากรอกเหตุผล", StatusCodes.Status400BadRequest); } var requestData = await _additionalCheckRequestRepository.GetByIdAsync(id); if (requestData == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } requestData.Status = "APPROVE"; requestData.Comment = req.Reason; await _additionalCheckRequestRepository.UpdateAsync(requestData); // change user timestamp var processTimeStamp = await _processUserTimeStampRepository.GetTimestampByDateAsync(requestData.KeycloakUserId, requestData.CheckDate.Date); var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(requestData.KeycloakUserId, AccessToken); if (processTimeStamp == null) { processTimeStamp = new ProcessUserTimeStamp { KeycloakUserId = requestData.KeycloakUserId, CheckIn = DateTime.Parse($"{requestData.CheckDate.Date.ToString("yyyy-MM-dd")} {req.CheckInTime}"), CheckOut = DateTime.Parse($"{requestData.CheckDate.Date.ToString("yyyy-MM-dd")} {req.CheckOutTime}"), CheckInRemark = req.Reason, CheckOutRemark = req.Reason, CheckInLat = 0, CheckInLon = 0, CheckOutLat = 0, CheckOutLon = 0, CheckInPOI = "", CheckOutPOI = "", CheckInStatus = req.CheckInStatus, CheckOutStatus = req.CheckOutStatus, Prefix = profile.Prefix, FirstName = profile.FirstName, LastName = profile.LastName, // 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, }; processTimeStamp.EditStatus = "APPROVE"; processTimeStamp.EditReason = req.Reason; 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; } await _processUserTimeStampRepository.AddAsync(processTimeStamp); } else { if (requestData.CheckInEdit) { processTimeStamp.CheckIn = DateTime.Parse($"{requestData.CheckDate.Date.ToString("yyyy-MM-dd")} {req.CheckInTime}"); processTimeStamp.CheckInRemark = req.Reason; //processTimeStamp.CheckInLat = 0; //processTimeStamp.CheckInLon = 0; //processTimeStamp.CheckInPOI = "ลงเวลากรณีพิเศษ"; processTimeStamp.CheckInStatus = req.CheckInStatus; processTimeStamp.CheckInPOI = requestData.POI ?? ""; processTimeStamp.CheckInLat = requestData.Latitude ?? 0; processTimeStamp.CheckInLon = requestData.Longitude ?? 0; } if (requestData.CheckOutEdit) { processTimeStamp.CheckOut = DateTime.Parse($"{requestData.CheckDate.Date.ToString("yyyy-MM-dd")} {req.CheckOutTime}"); processTimeStamp.CheckOutRemark = req.Reason; //processTimeStamp.CheckOutLat = 0; //processTimeStamp.CheckOutLon = 0; //processTimeStamp.CheckOutPOI = "ลงเวลากรณีพิเศษ"; processTimeStamp.CheckOutStatus = req.CheckOutStatus; processTimeStamp.CheckOutPOI = requestData.POI ?? ""; processTimeStamp.CheckOutLat = requestData.Latitude ?? 0; processTimeStamp.CheckOutLon = requestData.Longitude ?? 0; } processTimeStamp.EditStatus = "APPROVE"; processTimeStamp.EditReason = req.Reason; await _processUserTimeStampRepository.UpdateAsync(processTimeStamp); } var recvId = new List { profile.Id }; await _notificationRepository.PushNotificationsAsync(recvId.ToArray(), "ลงเวลากรณีพิเศษ", "การขอลงเวลากรณีพิเศษของคุณได้รับการอนุมัติ", "", "", true, false); return Success(); } /// /// LV1_020 - ไม่อนุมัติลงเวลากรณีพิเศษ (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPut("admin/edit/reject/{id:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> RejectRequestAsync(Guid id, [FromBody] RejectRequestDto req) { var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_CHECKIN_SPECIAL"); var jsonData = JsonConvert.DeserializeObject(getPermission); if (jsonData["status"]?.ToString() != "200") { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } if (req.Reason == null || req.Reason == string.Empty) { return Error("กรุณากรอกเหตุผล", StatusCodes.Status400BadRequest); } var requestData = await _additionalCheckRequestRepository.GetByIdAsync(id); if (requestData == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } // change user timestamp var processTimeStamp = await _processUserTimeStampRepository.GetTimestampByDateAsync(requestData.KeycloakUserId, requestData.CheckDate.Date); if (processTimeStamp != null) { processTimeStamp.EditStatus = "REJECT"; processTimeStamp.EditReason = req.Reason; await _processUserTimeStampRepository.UpdateAsync(processTimeStamp); } requestData.Status = "REJECT"; requestData.Comment = req.Reason; await _additionalCheckRequestRepository.UpdateAsync(requestData); var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(requestData.KeycloakUserId, AccessToken); var recvId = new List { profile.Id }; await _notificationRepository.PushNotificationsAsync(recvId.ToArray(), "ลงเวลากรณีพิเศษ", "การขอลงเวลากรณีพิเศษของคุณไม่ได้รับการอนุมัติ", "", "", true, false); return Success(); } /// /// LV1_021 - รายละเอียดการลงเวลาปฎิบัติงานรายบุคคล Tabรายการลงเวลา (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("log-record/{id:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> GetLogRecordAsync([Required] Guid id) { var getWorkflow = await _permission.GetPermissionAPIWorkflowAsync(id.ToString(), "SYS_CHECKIN"); if (getWorkflow == false) { var getPermission = await _permission.GetPermissionAPIAsync("GET", "SYS_CHECKIN"); var jsonData = JsonConvert.DeserializeObject(getPermission); if (jsonData["status"]?.ToString() != "200") { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } } var imgUrl = $"{_configuration["MinIO:Endpoint"]}{_configuration["MinIO:BucketName"]}"; var d = (await _userTimeStampRepository.GetTimeStampById(id)); if (d == null) { throw new Exception(GlobalMessages.DataNotFound); } else { var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(d.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound); } //var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty); var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); var duty = userRound ?? defaultRound; var result = new CheckInDetailForAdminDto { Id = d.Id, FullName = $"{d.Prefix}{d.FirstName} {d.LastName}", //FullName = _userProfileRepository.GetUserFullName(d.KeycloakUserId, AccessToken), CheckInDate = d.CheckIn.Date, CheckInTime = d.CheckIn.ToString("HH:mm"), CheckInPOI = d.CheckInPOI, CheckInLat = d.CheckInLat, CheckInLon = d.CheckInLon, // CheckInImg = $"{imgUrl}/{d.CheckInImageUrl}", CheckInImg = await _minIOService.ImagesPathByName(d.CheckInImageUrl), CheckInStatus = DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ? DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{d.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "LATE" : "NORMAL", CheckInDescription = d.CheckInRemark ?? "", CheckOutDate = d.CheckOut == null ? null : d.CheckOut.Value.Date, CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm"), CheckOutPOI = d.CheckOut == null ? "" : d.CheckOutPOI, CheckOutLat = d.CheckOut == null ? null : d.CheckOutLat, CheckOutLon = d.CheckOut == null ? null : d.CheckOutLon, CheckOutImg = d.CheckOut == null ? "" : await _minIOService.ImagesPathByName(d.CheckOutImageUrl), CheckOutStatus = d.CheckOut == null ? null : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? "ABSENT" : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "NORMAL", CheckOutDescription = d.CheckOutRemark ?? "", }; return Success(result); } } /// /// LV1_022 - ประวัติการยื่นขอลงเวลาพิเศษ (USER) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("edit/history")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> 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(); foreach (var data in rawData) { var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(data.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } //var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty); var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); var checkInData = await _userTimeStampRepository.GetTimestampByDateAsync(data.KeycloakUserId, data.CheckDate); //var checkInData = await _processUserTimeStampRepository.GetTimestampByDateAsync(data.KeycloakUserId, data.CheckDate); var duty = userRound ?? getDefaultRound; DateTime? resultCheckInDate, resultCheckOutDate, resultCheckInDateAndTime, resultCheckOutDateAndTime; string resultCheckInTime, resultCheckOutTime; string resultCheckInLocation = "", resultCheckOutLocation = ""; if (data.CheckInEdit) { resultCheckInDate = data.CheckDate.Date; resultCheckInTime = duty.StartTimeMorning; resultCheckInLocation = data.POI ?? ""; } 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; resultCheckOutLocation = data.POI ?? ""; } 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"); } 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}"); // create result object to return var resObj = new GetAdditionalCheckRequestHistoryDto { Id = data.Id, CheckInDate = resultCheckInDate, CheckOutDate = resultCheckOutDate, CheckInTime = resultCheckInTime, CheckOutTime = resultCheckOutTime, 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}") ? "ABSENT" : "LATE" : "NORMAL", CheckOutStatus = resultCheckInDateAndTime == null ? null : resultCheckOutDateAndTime == null ? null : DateTime.Parse(resultCheckOutDateAndTime.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{resultCheckInDate.Value.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? "ABSENT" : DateTime.Parse(resultCheckOutDateAndTime.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{resultCheckInDate.Value.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "NORMAL", //CheckInLocation = checkInData == null ? "" : checkInData.CheckInPOI, //CheckOutLocation = checkInData == null ? "" : checkInData.CheckOutPOI ?? "", CheckInLocation = resultCheckInLocation, CheckOutLocation = resultCheckOutLocation, EditReason = data.Comment ?? "", EditStatus = data.Status, }; result.Add(resObj); } var pageResult = result.Skip((page - 1) * pageSize).Take(pageSize) .ToList(); return Success(new { data = pageResult, total = result.Count }); } #endregion #region " ปฏิทินการทำงานของ ขรก. " /// /// LV1_023 - แสดงปฏิทินวันทำงานรายคน (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("admin/work/{id:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> GetCalendarByProfileAsync(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(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 }); } /// /// LV1_024 - บันทึกแก้ไขปฏิทินวันทำงาน (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPut("admin/work/{id:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> UpdateCalendarByProfileAsync(Guid id, [FromBody] UpdateCalendarDto req) { var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_WORK_ROUND_EDIT"); var jsonData = JsonConvert.DeserializeObject(getPermission); 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 " ปฏิทินการทำงานของ ลจ. " /// /// LV1_023 - แสดงปฏิทินวันทำงานรายคน (ADMIN) Employee /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("admin/emp/work/{id:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> 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(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 }); } /// /// LV1_024 - บันทึกแก้ไขปฏิทินวันทำงาน (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPut("admin/emp/work/{id:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> UpdateEmpCalendarByProfileAsync(Guid id, [FromBody] UpdateCalendarDto req) { var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_WORK_ROUND_EDIT"); var jsonData = JsonConvert.DeserializeObject(getPermission); 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 " แก้ไขสถานะการลงเวลา " /// /// LV1_025 - บันทึกแก้ไขสถานะการเข้า-ออกงาน (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPut("admin/edit/checkin/{id:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> EditCheckInStatusAsync(Guid id, [FromBody] EditCheckInStatusDto req) { var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_CHECKIN"); var jsonData = JsonConvert.DeserializeObject(getPermission); if (jsonData["status"]?.ToString() != "200") { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } var data = await _processUserTimeStampRepository.GetByIdAsync(id); if (data == null) return Error(GlobalMessages.DataNotFound); data.CheckInStatus = req.CheckInStatus; data.CheckOutStatus = req.CheckOutStatus; data.EditReason = req.Reason; await _processUserTimeStampRepository.UpdateAsync(data); return Success(); } #endregion #region " รายการลารายบุคคล " /// /// LV1_026 - รายการลารายบุคคล (ADMIN) /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPut("admin/summary/keycloak/{id:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> GetLeaveSummaryByProfileAsync(Guid id, [FromBody] GetLeaveSummaryDto req) { var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(id, AccessToken); var thisYear = DateTime.Now.Year; var startDate = req.StartDate; var endDate = req.EndDate; var leaveDayCount = await _leaveRequestRepository.GetSumApproveLeaveByRangeForUser(id, startDate, endDate); var timeStamps = await _processUserTimeStampRepository.GetTimeStampHistoryByRangeForUserAsync(id, startDate, endDate); var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound); } var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); var duty = userRound ?? defaultRound; var processTimeStamps = timeStamps .Select(d => new { d.Id, CheckInStatus = DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ? "LATE" : "NORMAL", CheckOutStatus = d.CheckOut == null ? "" : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? "LATE" : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "NORMAL", }); var absentCount = processTimeStamps.Count(x => x.CheckOutStatus == "ABSENT"); var lateCount = processTimeStamps.Count(x => x.CheckInStatus == "LATE"); return Success(new { leave = leaveDayCount, late = lateCount }); } #endregion #endregion } }