From 759a51ab58648665d580dfd130ec62353291a490 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 30 Mar 2026 15:53:33 +0700 Subject: [PATCH] Enhance LeaveProcessJobStatusRepository with detailed processing logic and add new methods in UserProfileRepository for fetching officer and employee profiles by RootDnaId --- .../LeaveProcessJobStatusRepository.cs | 268 +++++++++++++++++- .../Repositories/UserProfileRepository.cs | 69 +++++ BMA.EHR.Insignia/appsettings.json | 17 +- 3 files changed, 345 insertions(+), 9 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs index 9543820f..b57183d2 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs @@ -2,7 +2,13 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using System.Text.Json; +using System.IO; using BMA.EHR.Application.Common.Interfaces; +using BMA.EHR.Application.Repositories.Leaves.LeaveRequests; +using BMA.EHR.Application.Repositories.MetaData; +using BMA.EHR.Application.Responses.Profiles; +using BMA.EHR.Domain.Extensions; using BMA.EHR.Domain.Models.Leave.TimeAttendants; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; @@ -14,15 +20,35 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants #region " Fields " private readonly ILeaveDbContext _dbContext; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly UserProfileRepository _userProfileRepository; + private readonly HolidayRepository _holidayRepository; + private readonly DutyTimeRepository _dutyTimeRepository; + private readonly UserDutyTimeRepository _userDutyTimeRepository; + private readonly ProcessUserTimeStampRepository _processUserTimeStampRepository; + private readonly LeaveRequestRepository _leaveRequestRepository; #endregion #region " Constructor and Destructor " public LeaveProcessJobStatusRepository(ILeaveDbContext dbContext, - IHttpContextAccessor httpContextAccessor) : base(dbContext, httpContextAccessor) + IHttpContextAccessor httpContextAccessor, + UserProfileRepository userProfileRepository, + HolidayRepository holidayRepository, + DutyTimeRepository dutyTimeRepository, + UserDutyTimeRepository userDutyTimeRepository, + ProcessUserTimeStampRepository processUserTimeStampRepository, + LeaveRequestRepository leaveRequestRepository) : base(dbContext, httpContextAccessor) { _dbContext = dbContext; + _httpContextAccessor = httpContextAccessor; + _userProfileRepository = userProfileRepository; + _holidayRepository = holidayRepository; + _leaveRequestRepository = leaveRequestRepository; + _dutyTimeRepository = dutyTimeRepository; + _userDutyTimeRepository = userDutyTimeRepository; + _processUserTimeStampRepository = processUserTimeStampRepository; } #endregion @@ -123,9 +149,214 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants return job!; } - public async Task ProcessTaskAsync(Guid id, CancellationToken cancellationToken = default) + public async Task ProcessTaskAsync(Guid rootDnaId, DateTime? startDate, DateTime? endDate) { - Console.WriteLine($"กำลังประมวลผลงานที่มี RootDnaId: {id}"); + + var profiles = new List(); + var dateStart = startDate?.Date ?? DateTime.Now.Date; + var dateEnd = endDate?.Date ?? DateTime.Now.Date; + + var holidays = await _holidayRepository.GetHolidayAsync(dateStart, dateEnd); + var weekend = _holidayRepository.GetWeekEnd(dateStart, dateEnd); + var excludeDates = holidays.Union(weekend).ToList(); + + var dateList = new List(); + for (DateTime i = dateStart; i <= dateEnd; i = i.AddDays(1)) + { + if (holidays.Contains(i)) + { + var d = await _holidayRepository.GetHolidayAsync(i); + dateList.Add(new LoopDate + { + date = i, + isHoliday = true, + isWeekEnd = false, + dateRemark = d + }); + } + else if (weekend.Contains(i)) + { + dateList.Add(new LoopDate + { + date = i, + isHoliday = true, + isWeekEnd = false, + dateRemark = "วันหยุด" + }); + } + else + { + dateList.Add(new LoopDate + { + date = i, + isHoliday = false, + isWeekEnd = false, + dateRemark = "" + }); + } + } + + var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); + if (defaultRound == null) + { + throw new Exception("ไม่พบรอบการลงเวลา Default"); + } + + var employees = new List(); + + foreach (var dd in dateList.Where(x => !x.isHoliday && !x.isWeekEnd)) + { + profiles = await _userProfileRepository.GetAllOfficerByRootDnaId(rootDnaId.ToString(),dd.date); + foreach (var p in profiles) + { + var count = 1; + var keycloakUserId = p.Keycloak ?? Guid.Empty; + + var timeStamps = await _processUserTimeStampRepository.GetTimestampByDateAsync(keycloakUserId, dd.date); + + var fullName = $"{p.Prefix}{p.FirstName} {p.LastName}"; + + var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id, dd.date); + var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; + var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); + + var duty = userRound ?? defaultRound; + + // check วันลาของแต่ละคน + var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(keycloakUserId, dd.date); + var remarkStr = string.Empty; + + if (leaveReq != null) + { + switch (leaveReq.Type.Code.ToUpper()) + { + case "LV-001": + case "LV-002": + case "LV-005": + remarkStr += leaveReq.Type.Name; + var leaveRange = leaveReq.LeaveRange == null ? "" : leaveReq.LeaveRange.ToUpper(); + if (leaveRange == "MORNING") + remarkStr += "ครึ่งวันเช้า"; + else if (leaveRange == "AFTERNOON") + remarkStr += "ครึ่งวันบ่าย"; + + + // var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + // if (leaveRangeEnd == "MORNING") + // remarkStr += "ครึ่งวันเช้า"; + // else if (leaveRangeEnd == "AFTERNOON") + // remarkStr += "ครึ่งวันบ่าย"; + + var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + if (leaveRange != leaveRangeEnd) + { + if (leaveRangeEnd == "MORNING") + remarkStr += " - ครึ่งวันเช้า"; + else if (leaveRangeEnd == "AFTERNOON") + remarkStr += " - ครึ่งวันบ่าย"; + } + break; + default: + remarkStr += leaveReq.Type.Name; + break; + } + } + else + { + if (timeStamps == null) + { + if (dd.date <= DateTime.Now.Date) + { + remarkStr = "ขาดราชการ"; + if (dd.isHoliday == true) + { + remarkStr = $"วันหยุด ({dd.dateRemark})"; + } + else if (dd.isWeekEnd) + { + remarkStr = dd.dateRemark; + } + } + else remarkStr = ""; + } + else + { + // check status ของการลงเวลา + if (timeStamps.CheckOut != null) + { + if (timeStamps.CheckOutStatus == "ABSENT") + remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckOut ? $" (นอกสถานที่:{timeStamps.CheckOutLocationName})".Trim() : ""); + else if (timeStamps.CheckInStatus == "ABSENT") + remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + else if (timeStamps.CheckInStatus == "LATE") + { + remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + //lateTotal += 1; + } + else + remarkStr = !timeStamps.IsLocationCheckIn ? $" นอกสถานที่:{timeStamps.CheckInLocationName}".Trim() : ""; + } + else + { + if (timeStamps.CheckInStatus == "ABSENT") + remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + else if (timeStamps.CheckInStatus == "LATE") + { + remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + //lateTotal += 1; + } + else + remarkStr = !timeStamps.IsLocationCheckIn ? $" นอกสถานที่:{timeStamps.CheckInLocationName}".Trim() : ""; + } + } + } + + var emp = new DateResultReport + { + no = count, + fullName = fullName, + dutyTimeName = $"{duty.StartTimeMorning} - {duty.EndTimeAfternoon} น.", + checkInLocation = timeStamps == null ? "" : timeStamps.CheckInPOI, + checkInTime = timeStamps == null ? "" : $"{timeStamps.CheckIn.ToString("HH:mm")} น.", + checkOutLocation = timeStamps == null ? "" : timeStamps.CheckOutPOI ?? "", + checkOutTime = timeStamps == null ? "" : + timeStamps.CheckOut != null ? + $"{timeStamps.CheckOut.Value.ToString("HH:mm")} น." : + "", + remark = remarkStr, + checkInDate = timeStamps == null ? dd.date.Date.ToThaiFullDate2() : timeStamps.CheckIn.Date.ToThaiFullDate2(), + checkedOutDate = timeStamps == null ? dd.date.Date.ToThaiFullDate2() : + timeStamps.CheckOut != null ? + timeStamps.CheckOut.Value.ToThaiFullDate2() : + "", + checkInTimeRaw = timeStamps == null ? dd.date.Date : timeStamps?.CheckIn, + checkOutTimeRaw = timeStamps == null ? dd.date.Date : timeStamps?.CheckOut != null ? timeStamps?.CheckOut : null, + }; + + employees.Add(emp); + count++; + } + + // Write employees to JSON file + var fileName = $"employees_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; + var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Exports", fileName); + + // Ensure directory exists + var directory = Path.GetDirectoryName(filePath); + if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + var jsonOptions = new JsonSerializerOptions + { + WriteIndented = true, + Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + var jsonContent = JsonSerializer.Serialize(employees, jsonOptions); + await File.WriteAllTextAsync(filePath, jsonContent); + } } public async Task ProcessPendingJobsAsync() @@ -142,7 +373,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants await UpdateToProcessingAsync(job.Id); // ทำงานที่ต้องการที่นี่ (เช่น เรียก API, ประมวลผลข้อมูล ฯลฯ) - await ProcessTaskAsync(job.RootDnaId); + await ProcessTaskAsync(job.RootDnaId,job.StartDate, job.EndDate); // อัปเดตสถานะเป็น Completed await UpdateToCompletedAsync(job.Id); @@ -157,4 +388,33 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants #endregion } + + class LoopDate + { + public DateTime date { get; set; } + + public bool isHoliday { get; set; } + + public bool isWeekEnd { get; set; } + + public string dateRemark { get; set; } + + } + + class DateResultReport + { + public int no { get; set; } + public string fullName { get; set; } + public string dutyTimeName { get; set; } + public string checkInLocation { get; set; } + public string checkInTime { get; set; } + public string checkOutLocation { get; set; } + public string checkOutTime { get; set; } + public string remark { get; set; } + public string checkInDate { get; set; } + public string checkedOutDate { get; set; } + public DateTime? checkInTimeRaw { get; set; } + public DateTime? checkOutTimeRaw { get; set; } + } + } \ No newline at end of file diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index ba1f70c3..4ae8bcbe 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -764,6 +764,75 @@ namespace BMA.EHR.Application.Repositories } } + public async Task> GetAllOfficerByRootDnaId(string? rootDnaId, DateTime date) + { + try + { + var apiPath = $"{_configuration["API"]}/org/unauthorize/officer-list"; + var apiKey = _configuration["API_KEY"]; + var body = new + { + reqNode = 0, + reqNodeId = rootDnaId, + date = date + }; + //Console.WriteLine(body); + + var profiles = new List(); + + var apiResult = await PostExternalAPIAsync(apiPath, "", body, apiKey); + if (apiResult != null) + { + var raw = JsonConvert.DeserializeObject(apiResult); + if (raw != null) + return raw.Result; + else + return new List(); + } + else + return new List(); + } + catch + { + throw; + } + } + + public async Task> GetAllEmployeeByRootDnaId(string? rootDnaId, DateTime date) + { + try + { + var apiPath = $"{_configuration["API"]}/org/unauthorize/employee-list"; + var apiKey = _configuration["API_KEY"]; + var body = new + { + reqNode = 0, + reqNodeId = rootDnaId, + startDate = date, + endDate = date + }; + //Console.WriteLine(body); + + var profiles = new List(); + + var apiResult = await PostExternalAPIAsync(apiPath, "", body, apiKey); + if (apiResult != null) + { + var raw = JsonConvert.DeserializeObject(apiResult); + if (raw != null) + return raw.Result; + else + return new List(); + } + else + return new List(); + } + catch + { + throw; + } + } + public async Task> GetProfileByAdminRolev4(string? accessToken, int? node, string? nodeId, string role, string? revisionId, int? reqNode, string? reqNodeId, DateTime? startDate, DateTime? endDate) { try diff --git a/BMA.EHR.Insignia/appsettings.json b/BMA.EHR.Insignia/appsettings.json index 4f3d3faf..4e160482 100644 --- a/BMA.EHR.Insignia/appsettings.json +++ b/BMA.EHR.Insignia/appsettings.json @@ -31,10 +31,11 @@ //"DisciplineConnection": "server=hrms.chin.in.th;user=root;password=ey2qVVyyqGYw8CyA7h8X72559r2Ad84K;port=53636;database=hrms_discipline;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;" }, "Jwt": { - //"Key": "j7C9RO_p4nRtuwCH4z9Db_A_6We42tkD_p4lZtDrezc", - //"Issuer": "https://hrms-id.chin.in.th/realms/hrms" - "Key": "HP-FnQMUj9msHMSD3T9HtdEnphAKoCJLEl85CIqROFI", - "Issuer": "https://id.frappet.synology.me/realms/hrms" + //"Key": "HP-FnQMUj9msHMSD3T9HtdEnphAKoCJLEl85CIqROFI", + "Key": "j7C9RO_p4nRtuwCH4z9Db_A_6We42tkD_p4lZtDrezc", + "Issuer": "https://hrmsbkk-id.case-collection.com/realms/hrms" + //"Key": "xY2VR-EFvvNPsMs39u8ooVBWQL6mPwrNJOh3koJFTgU", + //"Issuer": "https://hrms-id.bangkok.go.th/realms/hrms" }, "EPPlus": { "ExcelPackage": { @@ -55,11 +56,17 @@ "Node": { "API": "https://bma-ehr.frappet.synology.me/api/v1/probation" }, - "API": "https://bma-ehr.frappet.synology.me/api/v1", "RabbitMQ": { "URL": "localhost", "UserName": "frappet", "Password": "FPTadmin2357" }, + "Domain": "https://hrmsbkk.case-collection.com", + "APIPROBATION": "https://hrmsbkk.case-collection.com/api/v1/probation", + "API": "https://hrmsbkk.case-collection.com/api/v1", + "APIV2": "https://hrmsbkk.case-collection.com/api/v2", + "VITE_URL_MGT": "https://hrmsbkk-mgt.case-collection.com", + //"API": "https://bma-ehr.frappet.synology.me/api/v1", + //"API": "https://bma-hrms.bangkok.go.th/api/v1", "API_KEY": "fKRL16yyEgbyTEJdsMw2h64tGSCmkW685PRtM3CygzX1JOSdptT9UJtpgWwKM8FybRTJups3GTFwj27ZRvlPdIkv3XgCoVJaD5LmR06ozuEPvCCRSdp2WFthg08V5xHc56fTPfZLpr1VmXrhd6dvYhHIqKkQUJR02Rlkss11cLRWEQOssEFVA4xdu2J5DIRO1EM5m7wRRvEwcDB4mYRXD9HH52SMq6iYqUWEWsMwLdbk7QW9yYESUEuzMW5gWrb6vIeWZxJV5bTz1PcWUyR7eO9Fyw1F5DiQYc9JgzTC1mW7cv31fEtTtrfbJYKIb5EbWilqIEUKC6A0UKBDDek35ML0006cqRVm0pvdOH6jeq7VQyYrhdXe59dBEyhYGUIfozoVBvW7Up4QBuOMjyPjSqJPlMBKwaseptfrblxQV1AOOivSBpf1ZcQyOZ8JktRtKUDSuXsmG0lsXwFlI3JCeSHdpVdgZWFYcJPegqfrB6KotR02t9AVkpLs1ZWrixwz" }