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 Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.ObjectPool; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using RabbitMQ.Client; using RabbitMQ.Client.Events; using Swashbuckle.AspNetCore.Annotations; using System.ComponentModel.DataAnnotations; 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 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, 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; _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; 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.GetProfileByKeycloakIdAsync(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; 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(); } } var checkData = new CheckTimeDtoRB { UserId = userId, CurrentDate = currentDate, CheckInId = data.CheckInId, Lat = data.Lat, Lon = data.Lon, POI = data.POI, IsLocation = data.IsLocation, LocationName = data.LocationName, Remark = data.Remark, CheckInFileName = data.Img == null ? "no-file" : data.Img.FileName, CheckInFileBytes = checkFileBytes, Token = AccessToken ?? "" }; var channel = _objectPool.Get(); 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); // add task id for check in queue string taskId = Guid.NewGuid().ToString(); var properties = channel.CreateBasicProperties(); properties.Persistent = true; properties.MessageId = userId.ToString("D");// ระบบลงเวลาต้องมีการเช็คสถานะใน rabbitMQ ด้วยว่ามีการรอรันอยู่ไหม ลงเวลาเข้า/ออกงาน #894 channel.BasicPublish(exchange: "", routingKey: queue, basicProperties: properties, body: body); return Success(new { date = currentDate, taskId = taskId, keycloakId = userId }); } finally { _objectPool.Return(channel); } } [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())); } // Step 3: ค้นหา taskIds ที่อยู่ใน messages ทั้งหมด var foundTasks = allMessages.FirstOrDefault(x => x.Contains(userId.ToString("D"))); return Success(new { keycloakId = userId, InQueue = foundTasks != null }); } 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 profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, data.Token); if (profile == null) return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); if (data.CheckInFileName == "no-file") throw new Exception(GlobalMessages.NoFileToUpload); var currentDate = data.CurrentDate ?? DateTime.Now; var 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); 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"; } else startTime = duty.StartTimeMorning; var 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); } // fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921 var 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" : "ABSENT" : DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "NORMAL"; if (checkout_process != null) { checkout_process.CheckOutLat = data.Lat; checkout_process.CheckOutLon = data.Lon; checkout_process.IsLocationCheckOut = data.IsLocation; checkout_process.CheckOutLocationName = data.LocationName; checkout_process.CheckOutPOI = data.POI; checkout_process.CheckOutRemark = data.Remark; checkout_process.CheckOutImageUrl = fileName; checkout_process.CheckOut = currentDate; checkout_process.CheckOutStatus = checkOutStatus; await _processUserTimeStampRepository.UpdateAsync(checkout_process); } else { return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); } } var checkInType = data.CheckInId == null ? "check-in" : "check-out"; return Success(new { user = $"{profile.FirstName} {profile.LastName}", date = currentDate, type = checkInType }); ; } /// /// 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.GetProfileByKeycloakIdAsync(userId, AccessToken); if (profile == null) return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); if (data.Img == null) throw new Exception(GlobalMessages.NoFileToUpload); var currentDate = DateTime.Now; var fileName = $"{_bucketName}/{UserId}/{DateTime.Now.ToString("dd-MM-yyyy")}/{data.Img.FileName}"; using (var ms = new MemoryStream()) { data.Img.CopyTo(ms); await _minIOService.UploadFileAsync(fileName, ms); } var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { return Error("ไม่พบรอบการลงเวลาทำงาน Default", StatusCodes.Status404NotFound); } var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); // TODO : รอดุึงรอบที่ผูกกับ user var duty = userRound ?? defaultRound; // create check in object if (data.CheckInId == null) { // validate duplicate check in var currentCheckIn = await _userTimeStampRepository.GetTimestampByDateAsync(Guid.Parse(UserId), currentDate); if (currentCheckIn != null) { return Error(new Exception("ไม่สามารถลงเวลาได้ เนื่องจากมีการลงเวลาในวันนี้แล้ว!"), StatusCodes.Status400BadRequest); } var checkin = new UserTimeStamp { KeycloakUserId = UserId != null ? Guid.Parse(UserId) : Guid.Empty, CheckInLat = data.Lat, CheckInLon = data.Lon, IsLocationCheckIn = data.IsLocation, CheckInLocationName = data.LocationName, CheckInPOI = data.POI, CheckInRemark = data.Remark, CheckInImageUrl = fileName, CheckIn = currentDate, Prefix = profile.Prefix, FirstName = profile.FirstName, LastName = profile.LastName, }; var checkInStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ? DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "LATE" : "NORMAL"; // process - รอทำใน queue var checkin_process = new ProcessUserTimeStamp { KeycloakUserId = UserId != null ? Guid.Parse(UserId) : Guid.Empty, CheckInLat = data.Lat, CheckInLon = data.Lon, IsLocationCheckIn = data.IsLocation, CheckInLocationName = data.LocationName, CheckInPOI = data.POI, CheckInRemark = data.Remark, CheckInImageUrl = fileName, CheckIn = currentDate, CheckInStatus = checkInStatus, Prefix = profile.Prefix, FirstName = profile.FirstName, LastName = profile.LastName, }; await _userTimeStampRepository.AddAsync(checkin); await _processUserTimeStampRepository.AddAsync(checkin_process); } else { var checkout = await _userTimeStampRepository.GetByIdAsync(data.CheckInId.Value); var currentCheckInProcess = await _processUserTimeStampRepository.GetTimestampByDateAsync(Guid.Parse(UserId), checkout.CheckIn.Date); var checkout_process = await _processUserTimeStampRepository.GetByIdAsync(currentCheckInProcess.Id); if (checkout != null) { checkout.CheckOutLat = data.Lat; checkout.CheckOutLon = data.Lon; checkout.IsLocationCheckOut = data.IsLocation; checkout.CheckOutLocationName = data.LocationName; checkout.CheckOutPOI = data.POI; checkout.CheckOutRemark = data.Remark; checkout.CheckOutImageUrl = fileName; checkout.CheckOut = currentDate; await _userTimeStampRepository.UpdateAsync(checkout); } else { return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); } var checkOutStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? "ABSENT" : DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "NORMAL"; if (checkout_process != null) { checkout_process.CheckOutLat = data.Lat; checkout_process.CheckOutLon = data.Lon; checkout_process.IsLocationCheckOut = data.IsLocation; checkout_process.CheckOutLocationName = data.LocationName; checkout_process.CheckOutPOI = data.POI; checkout_process.CheckOutRemark = data.Remark; checkout_process.CheckOutImageUrl = fileName; checkout_process.CheckOut = currentDate; checkout_process.CheckOutStatus = checkOutStatus; await _processUserTimeStampRepository.UpdateAsync(checkout_process); } else { return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); } } return Success(new { date = currentDate }); } /// /// 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.GetProfileByKeycloakIdAsync(userId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { return Error("ไม่พบรอบการลงเวลาทำงาน Default", StatusCodes.Status404NotFound); } var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); // TODO : รอดุึงรอบที่ผูกกับ user var duty = userRound ?? defaultRound; var checkin_base = DateTime.Parse($"{DateTime.Now.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}"); var checkout_base = DateTime.Parse($"{DateTime.Now.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); var data = (await _processUserTimeStampRepository.GetTimeStampHistoryAsync(userId, year)) .Select(d => new CheckInHistoryDto { CheckInId = d.Id, CheckInDate = d.CheckIn.Date, CheckInTime = d.CheckIn.ToString("HH:mm:ss"), CheckInLocation = d.CheckInPOI, CheckInStatus = d.CheckInStatus != null || d.CheckInStatus != "" ? d.CheckInStatus : DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ? "LATE" : "NORMAL", CheckInIsLocation = d.IsLocationCheckIn, CheckInLocationName = d.CheckInLocationName, CheckOutDate = d.CheckOut == null ? null : d.CheckOut.Value.Date, CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm:ss"), CheckOutLocation = d.CheckOutPOI ?? "", CheckOutStatus = d.CheckOut == null ? null : d.CheckOutStatus != null || d.CheckOutStatus != "" ? d.CheckOutStatus : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? "LATE" : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "NORMAL", CheckOutIsLocation = d.IsLocationCheckOut, CheckOutLocationName = d.CheckOutLocationName, IsEdit = _processUserTimeStampRepository.IsEditRequest(userId, d.CheckIn.Date) //IsEdit = (d.EditStatus != null && d.EditStatus != "") //EditReason = d.EditReason ?? "",ß //EditStatus = d.EditStatus ?? "" }) .ToList(); if (keyword != "") { data = data.Where(x => (x.CheckInLocation.Contains(keyword) || x.CheckOutLocation.Contains(keyword))).ToList(); } var pageData = data .Skip((page - 1) * pageSize) .Take(pageSize) .ToList(); return Success(new { data = pageData, total = data.Count }); } /// /// 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") { 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"]}"; var data = (await _userTimeStampRepository.GetTimeStampHistoryForAdminAsync(startDate, endDate)) .Select(d => new CheckInHistoryForAdminDto { Id = d.Id, //FullName = _userProfileRepository.GetUserFullName(d.KeycloakUserId, AccessToken), FullName = $"{d.Prefix ?? ""}{d.FirstName ?? ""} {d.LastName ?? ""}", 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(); 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.GetProfileByKeycloakIdAsync(d.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound); } //var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty); var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); var duty = userRound ?? defaultRound; var result = new CheckInDetailForAdminDto { Id = d.Id, FullName = $"{d.Prefix}{d.FirstName} {d.LastName}", 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}", CheckInStatus = d.CheckInStatus != null || d.CheckInStatus != "" ? d.CheckInStatus : DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ? DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{d.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "LATE" : "NORMAL", CheckInDescription = d.CheckInRemark ?? "", IsLocationCheckIn = d.IsLocationCheckIn, CheckInLocationName = d.CheckInLocationName ?? "", CheckOutDate = d.CheckOut == null ? null : d.CheckOut.Value.Date, CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm"), CheckOutPOI = d.CheckOut == null ? "" : d.CheckOutPOI, CheckOutLat = d.CheckOut == null ? null : d.CheckOutLat, CheckOutLon = d.CheckOut == null ? null : d.CheckOutLon, CheckOutImg = d.CheckOut == null ? "" : $"{imgUrl}/{d.CheckOutImageUrl}", CheckOutStatus = d.CheckOut == null ? null : d.CheckOutStatus != null || d.CheckOutStatus != "" ? d.CheckOutStatus : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? "ABSENT" : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "NORMAL", CheckOutDescription = d.CheckOutRemark ?? "", IsLocationCheckOut = d.IsLocationCheckOut, CheckOutLocationName = d.CheckOutLocationName ?? "" }; return Success(result); } } /// /// 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") { 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"]}"; var resultData = await _processUserTimeStampRepository.GetTimeStampHistoryForAdminAsync(startDate, endDate); 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 ?? ""}", 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(); 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); } var profile = await _userProfileRepository.SearchProfile(req.CitizenId, req.FirstName, req.LastName, AccessToken ?? ""); var pagedProfile = profile.Skip((req.Page - 1) * req.PageSize).Take(req.PageSize).ToList(); var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync(); var resultSet = new List(); foreach (var p in pagedProfile) { var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); var duty = userRound ?? getDefaultRound; var res = new SearchProfileResultDto { ProfileId = p.Id, CitizenId = p.CitizenId ?? "", FullName = $"{p.Prefix ?? ""}{p.FirstName ?? ""} {p.LastName ?? ""}", StartTimeMorning = duty.StartTimeMorning, LeaveTimeAfterNoon = duty.EndTimeAfternoon, EffectiveDate = effectiveDate == null ? null : effectiveDate.EffectiveDate.Value.Date }; resultSet.Add(res); } return Success(new { data = resultSet, total = profile.Count }); } /// /// 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 = "") { 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(); } return Success(new { data = resultSet, total = data.Count }); } #endregion #region " Check Checkout Time " /// /// ตรวจสอบว่าเวลาปัจจุบัน ถ้า checkout จะขาดราชการหรือไม่? /// /// /// /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpGet("user/checkout-check")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> CheckoutCheckAsync() { var time = DateTime.Now; var userId = UserId != null ? Guid.Parse(UserId) : Guid.Empty; var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); } var 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 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 = endTime }); } #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.GetProfileByKeycloakIdAsync(userId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); } var request = new AdditionalCheckRequest { KeycloakUserId = UserId != null ? Guid.Parse(UserId) : Guid.Empty, CheckDate = req.CheckDate, CheckInEdit = req.CheckInEdit, CheckOutEdit = req.CheckOutEdit, Description = req.Description, Prefix = profile.Prefix, FirstName = profile.FirstName, LastName = profile.LastName, // 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 = "") { 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); } var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequests(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.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } //var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty); var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); var checkInData = await _userTimeStampRepository.GetTimestampByDateAsync(data.KeycloakUserId, data.CheckDate); var duty = userRound ?? getDefaultRound; //var duty = getDefaultRound; // create result object to return var resObj = new GetAdditionalCheckRequestDto { Id = data.Id, FullName = $"{data.Prefix}{data.FirstName} {data.LastName}", //FullName = $"{profile.Prefix}{profile.FirstName} {profile.LastName}", CreatedAt = data.CreatedAt, CheckDate = data.CheckDate, CheckInEdit = data.CheckInEdit, CheckOutEdit = data.CheckOutEdit, CheckInTime = checkInData == null ? "00:00" : checkInData.CheckIn.ToString("HH:mm"), CheckOutTime = checkInData == null ? "00:00" : checkInData.CheckOut == null ? "00:00" : checkInData.CheckOut.Value.ToString("HH:mm"), CheckInStatus = checkInData == null ? null : DateTime.Parse(checkInData.CheckIn.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{checkInData.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ? DateTime.Parse(checkInData.CheckIn.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{checkInData.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "LATE" : "NORMAL", CheckOutStatus = checkInData == null ? null : checkInData.CheckOut == null ? null : DateTime.Parse(checkInData.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{checkInData.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? "ABSENT" : DateTime.Parse(checkInData.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{checkInData.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "NORMAL", StartTimeMorning = duty.StartTimeMorning, EndTimeMorning = duty.EndTimeMorning, StartTimeAfternoon = duty.StartTimeAfternoon, EndTimeAfternoon = duty.EndTimeAfternoon, Reason = data.Comment ?? "", Status = data.Status, Description = data.Description, StatusSort = data.Status.Trim().ToLower() == "pending" ? 1 : data.Status.Trim().ToLower() == "approve" ? 2 : 3, POI = data.POI, Latitude = data.Latitude, Longitude = data.Longitude, }; result.Add(resObj); } if (keyword != "") { result = result.Where(x => x.FullName.Contains(keyword)).ToList(); } var pageResult = result.Skip((page - 1) * pageSize).Take(pageSize) .OrderBy(x => x.StatusSort) .ToList(); return Success(new { data = pageResult, total = result.Count }); } /// /// 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.GetProfileByKeycloakIdAsync(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.GetProfileByKeycloakIdAsync(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.GetProfileByKeycloakIdAsync(d.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound); } //var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty); var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); var duty = userRound ?? defaultRound; var result = new CheckInDetailForAdminDto { Id = d.Id, FullName = $"{d.Prefix}{d.FirstName} {d.LastName}", //FullName = _userProfileRepository.GetUserFullName(d.KeycloakUserId, AccessToken), CheckInDate = d.CheckIn.Date, CheckInTime = d.CheckIn.ToString("HH:mm"), CheckInPOI = d.CheckInPOI, CheckInLat = d.CheckInLat, CheckInLon = d.CheckInLon, CheckInImg = $"{imgUrl}/{d.CheckInImageUrl}", CheckInStatus = DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ? DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{d.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "LATE" : "NORMAL", CheckInDescription = d.CheckInRemark ?? "", CheckOutDate = d.CheckOut == null ? null : d.CheckOut.Value.Date, CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm"), CheckOutPOI = d.CheckOut == null ? "" : d.CheckOutPOI, CheckOutLat = d.CheckOut == null ? null : d.CheckOutLat, CheckOutLon = d.CheckOut == null ? null : d.CheckOutLon, CheckOutImg = d.CheckOut == null ? "" : $"{imgUrl}/{d.CheckOutImageUrl}", CheckOutStatus = d.CheckOut == null ? null : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? "ABSENT" : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "NORMAL", CheckOutDescription = d.CheckOutRemark ?? "", }; return Success(result); } } /// /// 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.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } //var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty); var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); var checkInData = await _userTimeStampRepository.GetTimestampByDateAsync(data.KeycloakUserId, data.CheckDate); //var 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 = DateTime.Parse($"{resultCheckInDate.Value.Date.ToString("yyyy-MM-dd")} {resultCheckInTime}"); resultCheckOutDateAndTime = 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 = resultCheckInDate == null ? null : resultCheckOutDate == 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); } if (keyword != "") { result = result.Where(x => x.EditReason!.Contains(keyword)).ToList(); } var pageResult = result.Skip((page - 1) * pageSize).Take(pageSize) .ToList(); return Success(new { data = pageResult, total = result.Count }); } #endregion #region " ปฏิทินการทำงานของ ขรก. " /// /// 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_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.GetProfileByKeycloakIdAsync(id, AccessToken); var thisYear = DateTime.Now.Year; var startDate = req.StartDate; var endDate = req.EndDate; var leaveDayCount = await _leaveRequestRepository.GetSumApproveLeaveByRangeForUser(id, startDate, endDate); var timeStamps = await _processUserTimeStampRepository.GetTimeStampHistoryByRangeForUserAsync(id, startDate, endDate); var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound); } var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); var duty = userRound ?? defaultRound; var processTimeStamps = timeStamps .Select(d => new { d.Id, CheckInStatus = DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ? "LATE" : "NORMAL", CheckOutStatus = d.CheckOut == null ? "" : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? "LATE" : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : "NORMAL", }); var absentCount = processTimeStamps.Count(x => x.CheckOutStatus == "ABSENT"); var lateCount = processTimeStamps.Count(x => x.CheckInStatus == "LATE"); return Success(new { leave = leaveDayCount, late = lateCount }); } #endregion #endregion } }