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.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants { public class LeaveProcessJobStatusRepository: GenericLeaveRepository { #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; private readonly IConfiguration _configuration; private readonly IWebHostEnvironment _env; #endregion #region " Constructor and Destructor " public LeaveProcessJobStatusRepository(ILeaveDbContext dbContext, IHttpContextAccessor httpContextAccessor, UserProfileRepository userProfileRepository, HolidayRepository holidayRepository, DutyTimeRepository dutyTimeRepository, UserDutyTimeRepository userDutyTimeRepository, ProcessUserTimeStampRepository processUserTimeStampRepository, LeaveRequestRepository leaveRequestRepository, IConfiguration configuration, IWebHostEnvironment env) : base(dbContext, httpContextAccessor) { _dbContext = dbContext; _httpContextAccessor = httpContextAccessor; _userProfileRepository = userProfileRepository; _holidayRepository = holidayRepository; _configuration = configuration; _leaveRequestRepository = leaveRequestRepository; _dutyTimeRepository = dutyTimeRepository; _userDutyTimeRepository = userDutyTimeRepository; _processUserTimeStampRepository = processUserTimeStampRepository; _env = env; } #endregion #region " Methods " /// /// ดึงข้อมูล Job Status จาก TaskId /// public async Task GetByTaskIdAsync(Guid id) { var data = await _dbContext.Set() .Where(x => x.Id == id) .FirstOrDefaultAsync(); return data; } /// /// ดึงข้อมูล Job Status จาก UserId และสถานะ /// public async Task> GetByUserIdAndStatusAsync(Guid userId, string status) { var data = await _dbContext.Set() .Where(x => x.CreatedUserId == userId.ToString("D") && x.Status == status) .OrderByDescending(x => x.CreatedDate) .ToListAsync(); return data; } /// /// ดึงข้อมูล Job Status จาก UserId /// public async Task> GetByUserIdAsync(Guid userId) { var data = await _dbContext.Set() .Where(x => x.CreatedUserId == userId.ToString("D")) .OrderByDescending(x => x.CreatedDate) .ToListAsync(); return data; } /// /// ดึงข้อมูล Job Status ที่ยัง pending หรือ processing /// public async Task> GetPendingOrProcessingJobsAsync(Guid userId) { var data = await _dbContext.Set() .Where(x => x.CreatedUserId == userId.ToString("D") && (x.Status == "PENDING" || x.Status == "PROCESSING")) //.OrderByDescending(x => x.CreatedDate) .ToListAsync(); return data; } public async Task> GetPendingJobsAsync() { var data = await _dbContext.Set() .Where(x => x.Status == "PENDING") .ToListAsync(); return data; } /// /// อัปเดตสถานะเป็น Processing /// public async Task UpdateToProcessingAsync(Guid id) { var job = await GetByTaskIdAsync(id); if (job != null) { job.Status = "PROCESSING"; job.ProcessingDate = DateTime.Now; await UpdateAsync(job); } return job!; } /// /// อัปเดตสถานะเป็น Completed /// public async Task UpdateToCompletedAsync(Guid id, string? additionalData = null) { var job = await GetByTaskIdAsync(id); if (job != null) { job.Status = "COMPLETED"; job.CompletedDate = DateTime.Now; await UpdateAsync(job); } return job!; } /// /// อัปเดตสถานะเป็น Failed /// public async Task UpdateToFailedAsync(Guid id, string errorMessage) { var job = await GetByTaskIdAsync(id); if (job != null) { job.Status = "FAILED"; job.CompletedDate = DateTime.Now; job.ErrorMessage = errorMessage; await UpdateAsync(job); } return job!; } public async Task ProcessTaskAsync(Guid rootDnaId, DateTime? startDate, DateTime? endDate) { 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; var status = string.Empty; var stampType = string.Empty; var stampAmount = 0.0; 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(leaveReq.LeaveStartDate.Date == leaveReq.LeaveEndDate.Date) { 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 += " - ครึ่งวันบ่าย"; } } else { if(dd.date == leaveReq.LeaveStartDate.Date) { if (leaveRange == "MORNING") remarkStr += "ครึ่งวันเช้า"; else if (leaveRange == "AFTERNOON") remarkStr += "ครึ่งวันบ่าย"; } else if(dd.date == leaveReq.LeaveEndDate.Date) { var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); if (leaveRangeEnd == "MORNING") remarkStr += "ครึ่งวันเช้า"; else if (leaveRangeEnd == "AFTERNOON") remarkStr += "ครึ่งวันบ่าย"; else remarkStr += "เต็มวัน"; } else { remarkStr += "เต็มวัน"; } } break; default: remarkStr += leaveReq.Type.Name; break; } status = "LEAVE"; if(leaveReq.LeaveStartDate.Date == dd.date) { stampType = leaveReq.LeaveRange ?? ""; stampAmount = leaveReq.LeaveRange != "ALL" ? 0.5 : 1; } else if(leaveReq.LeaveEndDate.Date == dd.date) { stampAmount = leaveReq.LeaveRangeEnd != "ALL" ? 0.5 : 1; stampType = leaveReq.LeaveRangeEnd ?? ""; } else stampAmount = leaveReq.LeaveRange != "ALL" || leaveReq.LeaveRangeEnd != "ALL" ? 0.5 : 1; if(stampType == "ALL") stampType = "FULL_DAY"; } else { if (timeStamps == null) { if (dd.date <= DateTime.Now.Date) { remarkStr = "ขาดราชการ"; status = "ABSENT"; stampType = "FULL_DAY"; stampAmount = 1; if (dd.isHoliday == true) { remarkStr = $"วันหยุด ({dd.dateRemark})"; status = "HOLIDAY"; } else if (dd.isWeekEnd) { remarkStr = dd.dateRemark; status = "WEEKEND"; } } else remarkStr = ""; } else { // check status ของการลงเวลา if (timeStamps.CheckOut != null) { if (timeStamps.CheckOutStatus == "ABSENT") { remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckOut ? $" (นอกสถานที่:{timeStamps.CheckOutLocationName})".Trim() : ""); status = "ABSENT"; stampType = "FULL_DAY"; stampAmount = 1; } else if (timeStamps.CheckInStatus == "ABSENT") { remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); status = "ABSENT"; stampType = "FULL_DAY"; stampAmount = 1; } else if (timeStamps.CheckInStatus == "LATE") { remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); status = "LATE"; stampType = "FULL_DAY"; stampAmount = 1; //lateTotal += 1; } else remarkStr = !timeStamps.IsLocationCheckIn ? $" นอกสถานที่:{timeStamps.CheckInLocationName}".Trim() : ""; } else { if (timeStamps.CheckInStatus == "ABSENT") { status = "ABSENT"; stampType = "FULL_DAY"; stampAmount = 1; remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); } else if (timeStamps.CheckInStatus == "LATE") { status = "LATE"; stampType = "FULL_DAY"; stampAmount = 1; remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); //lateTotal += 1; } else remarkStr = !timeStamps.IsLocationCheckIn ? $" นอกสถานที่:{timeStamps.CheckInLocationName}".Trim() : ""; } } } var emp = new DateResultReport { profileId = p.Id.ToString(), stampDate = dd.date, stampType = stampType, stampAmount = stampAmount, remark = remarkStr, status = status }; 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); } //call api var apiPath = $"{_configuration["API"]}/org/unauthorize/profile/absent-late/batch"; var apiKey = _configuration["API_KEY"]; var body = new { records = employees.Where(x => x.status == "ABSENT" || x.status == "LATE").ToList() }; var apiResult = await PostExternalAPIAsync(apiPath, AccessToken ?? "", body, apiKey); if(apiResult == "") { throw new Exception($"เรียก API {apiPath} ไม่สำเร็จ"); } } public async Task ProcessEmpTaskAsync(Guid rootDnaId, DateTime? startDate, DateTime? endDate) { 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.GetAllEmployeeByRootDnaId(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; var status = string.Empty; var stampType = string.Empty; var stampAmount = 0.0; 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(leaveReq.LeaveStartDate.Date == leaveReq.LeaveEndDate.Date) { 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 += " - ครึ่งวันบ่าย"; } } else { if(dd.date == leaveReq.LeaveStartDate.Date) { if (leaveRange == "MORNING") remarkStr += "ครึ่งวันเช้า"; else if (leaveRange == "AFTERNOON") remarkStr += "ครึ่งวันบ่าย"; } else if(dd.date == leaveReq.LeaveEndDate.Date) { var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); if (leaveRangeEnd == "MORNING") remarkStr += "ครึ่งวันเช้า"; else if (leaveRangeEnd == "AFTERNOON") remarkStr += "ครึ่งวันบ่าย"; } else { remarkStr += "เต็มวัน"; } } break; default: remarkStr += leaveReq.Type.Name; break; } status = "LEAVE"; if(leaveReq.LeaveStartDate.Date == dd.date) { stampType = leaveReq.LeaveRange ?? ""; stampAmount = leaveReq.LeaveRange != "ALL" ? 0.5 : 1; } else if(leaveReq.LeaveEndDate.Date == dd.date) { stampAmount = leaveReq.LeaveRangeEnd != "ALL" ? 0.5 : 1; stampType = leaveReq.LeaveRangeEnd ?? ""; } else stampAmount = leaveReq.LeaveRange != "ALL" || leaveReq.LeaveRangeEnd != "ALL" ? 0.5 : 1; if(stampType == "ALL") stampType = "FULL_DAY"; //stampAmount = leaveReq.LeaveRange != "ALL" || leaveReq.LeaveRangeEnd != "ALL" ? 0.5 : 1; } else { if (timeStamps == null) { if (dd.date <= DateTime.Now.Date) { remarkStr = "ขาดราชการ"; status = "ABSENT"; stampType = "FULL_DAY"; stampAmount = 1; if (dd.isHoliday == true) { remarkStr = $"วันหยุด ({dd.dateRemark})"; status = "HOLIDAY"; } else if (dd.isWeekEnd) { remarkStr = dd.dateRemark; status = "WEEKEND"; } } else remarkStr = ""; } else { // check status ของการลงเวลา if (timeStamps.CheckOut != null) { if (timeStamps.CheckOutStatus == "ABSENT") { remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckOut ? $" (นอกสถานที่:{timeStamps.CheckOutLocationName})".Trim() : ""); status = "ABSENT"; stampType = "FULL_DAY"; stampAmount = 1; } else if (timeStamps.CheckInStatus == "ABSENT") { remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); status = "ABSENT"; stampType = "FULL_DAY"; stampAmount = 1; } else if (timeStamps.CheckInStatus == "LATE") { remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); status = "LATE"; stampType = "FULL_DAY"; stampAmount = 1; //lateTotal += 1; } else remarkStr = !timeStamps.IsLocationCheckIn ? $" นอกสถานที่:{timeStamps.CheckInLocationName}".Trim() : ""; } else { if (timeStamps.CheckInStatus == "ABSENT") { status = "ABSENT"; stampType = "FULL_DAY"; stampAmount = 1; remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); } else if (timeStamps.CheckInStatus == "LATE") { status = "LATE"; stampType = "FULL_DAY"; stampAmount = 1; remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); //lateTotal += 1; } else remarkStr = !timeStamps.IsLocationCheckIn ? $" นอกสถานที่:{timeStamps.CheckInLocationName}".Trim() : ""; } } } var emp = new DateResultReport { profileId = p.Id.ToString(), stampDate = dd.date, stampType = stampType, stampAmount = stampAmount, remark = remarkStr, status = status }; employees.Add(emp); count++; } // Write employees to JSON file // var fileName = $"employees_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; // var filePath = Path.Combine(_env.ContentRootPath, "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); // Console.WriteLine($"Writing file to: {filePath}"); // await File.WriteAllTextAsync(filePath, jsonContent); // Console.WriteLine($"File written successfully: {fileName}"); } // call api var apiPath = $"{_configuration["API"]}/org/unauthorize/profile-employee/absent-late/batch"; var apiKey = _configuration["API_KEY"]; var body = new { records = employees.Where(x => x.status == "ABSENT" || x.status == "LATE").ToList() }; var apiResult = await PostExternalAPIAsync(apiPath, AccessToken ?? "", body, apiKey); if(apiResult == "") { throw new Exception($"เรียก API {apiPath} ไม่สำเร็จ"); } } public async Task ProcessPendingJobsAsync() { var pendingJobs = await GetPendingJobsAsync(); Console.WriteLine($"พบงานที่ค้างอยู่ในสถานะ PENDING จำนวน {pendingJobs.Count} งาน"); foreach (var job in pendingJobs) { try { // อัปเดตสถานะเป็น Processing await UpdateToProcessingAsync(job.Id); // ทำงานที่ต้องการที่นี่ (เช่น เรียก API, ประมวลผลข้อมูล ฯลฯ) await ProcessTaskAsync(job.RootDnaId,job.StartDate, job.EndDate); await ProcessEmpTaskAsync(job.RootDnaId,job.StartDate, job.EndDate); // อัปเดตสถานะเป็น Completed await UpdateToCompletedAsync(job.Id); } catch (Exception ex) { // หากเกิดข้อผิดพลาด อัปเดตสถานะเป็น Failed พร้อมข้อความแสดงข้อผิดพลาด await UpdateToFailedAsync(job.Id, ex.Message); } } } #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 string? profileId { get; set; } public DateTime stampDate { get; set; } public string stampType { get; set; } public double stampAmount { get; set; } public string remark { get; set; } public string status { get; set; } } }