420 lines
No EOL
19 KiB
C#
420 lines
No EOL
19 KiB
C#
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;
|
|
|
|
namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants
|
|
{
|
|
public class LeaveProcessJobStatusRepository: GenericLeaveRepository<Guid, LeaveProcessJobStatus>
|
|
{
|
|
#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,
|
|
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
|
|
|
|
#region " Methods "
|
|
|
|
/// <summary>
|
|
/// ดึงข้อมูล Job Status จาก TaskId
|
|
/// </summary>
|
|
public async Task<LeaveProcessJobStatus?> GetByTaskIdAsync(Guid id)
|
|
{
|
|
var data = await _dbContext.Set<LeaveProcessJobStatus>()
|
|
.Where(x => x.Id == id)
|
|
.FirstOrDefaultAsync();
|
|
|
|
return data;
|
|
}
|
|
|
|
/// <summary>
|
|
/// ดึงข้อมูล Job Status จาก UserId และสถานะ
|
|
/// </summary>
|
|
public async Task<List<LeaveProcessJobStatus>> GetByUserIdAndStatusAsync(Guid userId, string status)
|
|
{
|
|
var data = await _dbContext.Set<LeaveProcessJobStatus>()
|
|
.Where(x => x.CreatedUserId == userId.ToString("D") && x.Status == status)
|
|
.OrderByDescending(x => x.CreatedDate)
|
|
.ToListAsync();
|
|
|
|
return data;
|
|
}
|
|
|
|
/// <summary>
|
|
/// ดึงข้อมูล Job Status ที่ยัง pending หรือ processing
|
|
/// </summary>
|
|
public async Task<List<LeaveProcessJobStatus>> GetPendingOrProcessingJobsAsync(Guid userId)
|
|
{
|
|
var data = await _dbContext.Set<LeaveProcessJobStatus>()
|
|
.Where(x => x.CreatedUserId == userId.ToString("D") &&
|
|
(x.Status == "PENDING" || x.Status == "PROCESSING"))
|
|
//.OrderByDescending(x => x.CreatedDate)
|
|
.ToListAsync();
|
|
|
|
return data;
|
|
}
|
|
|
|
public async Task<List<LeaveProcessJobStatus>> GetPendingJobsAsync()
|
|
{
|
|
var data = await _dbContext.Set<LeaveProcessJobStatus>()
|
|
.Where(x => x.Status == "PENDING")
|
|
.ToListAsync();
|
|
|
|
return data;
|
|
}
|
|
|
|
/// <summary>
|
|
/// อัปเดตสถานะเป็น Processing
|
|
/// </summary>
|
|
public async Task<LeaveProcessJobStatus> UpdateToProcessingAsync(Guid id)
|
|
{
|
|
var job = await GetByTaskIdAsync(id);
|
|
if (job != null)
|
|
{
|
|
job.Status = "PROCESSING";
|
|
job.ProcessingDate = DateTime.Now;
|
|
await UpdateAsync(job);
|
|
}
|
|
return job!;
|
|
}
|
|
|
|
/// <summary>
|
|
/// อัปเดตสถานะเป็น Completed
|
|
/// </summary>
|
|
public async Task<LeaveProcessJobStatus> 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!;
|
|
}
|
|
|
|
/// <summary>
|
|
/// อัปเดตสถานะเป็น Failed
|
|
/// </summary>
|
|
public async Task<LeaveProcessJobStatus> 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<GetProfileByKeycloakIdRootDto>();
|
|
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<LoopDate>();
|
|
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<DateResultReport>();
|
|
|
|
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()
|
|
{
|
|
|
|
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);
|
|
|
|
// อัปเดตสถานะเป็น 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 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; }
|
|
}
|
|
|
|
} |