using System.Drawing; using System.Net.Http.Headers; using System.Net.Http.Json; using BMA.EHR.Application.Common.Interfaces; using BMA.EHR.Application.Messaging; using BMA.EHR.Application.Responses.Leaves; using BMA.EHR.Domain.Models.HR; using BMA.EHR.Domain.Models.Leave.Commons; using BMA.EHR.Domain.Models.Leave.Requests; using BMA.EHR.Domain.Models.Notifications; using BMA.EHR.Domain.Shared; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests { public class LeaveRequestRepository : GenericLeaveRepository { #region " Fields " private readonly ILeaveDbContext _dbContext; private readonly IHttpContextAccessor _httpContextAccessor; private readonly OrganizationCommonRepository _organizationCommonRepository; private readonly UserProfileRepository _userProfileRepository; private readonly IConfiguration _configuration; private readonly EmailSenderService _emailSenderService; private readonly IApplicationDBContext _appDbContext; private readonly string URL = string.Empty; #endregion #region " Constructor and Destuctor " public LeaveRequestRepository(ILeaveDbContext dbContext, IHttpContextAccessor httpContextAccessor, OrganizationCommonRepository organizationCommonRepository, UserProfileRepository userProfileRepository, IConfiguration configuration, EmailSenderService emailSenderService, IApplicationDBContext appDbContext) : base(dbContext, httpContextAccessor) { _dbContext = dbContext; _httpContextAccessor = httpContextAccessor; _organizationCommonRepository = organizationCommonRepository; _userProfileRepository = userProfileRepository; _configuration = configuration; _emailSenderService = emailSenderService; _appDbContext = appDbContext; URL = (_configuration["API"]).Replace("/api/v1", ""); } #endregion #region " Properties " protected Guid UserOrganizationId { get { if (UserId != null || UserId != "") return _userProfileRepository.GetUserOCId(Guid.Parse(UserId!), AccessToken); else return Guid.Empty; } } #endregion #region " Methods " #region " Overrides " public async Task GetByIdWithTrackingAsync(Guid id) { var data = await _dbContext.Set().AsQueryable() //.AsNoTracking() .Include(x => x.Approvers) .Include(x => x.LeaveDocument) .ThenInclude(x => x.Document) .Include(x => x.LeaveDraftDocument) .Include(x => x.LeaveCancelDocument) .Include(x => x.Type) .FirstOrDefaultAsync(x => x.Id == id); return data; } public override async Task GetByIdAsync(Guid id) { var data = await _dbContext.Set().AsQueryable() .AsNoTracking() .Include(x => x.Approvers) .Include(x => x.LeaveDocument) .ThenInclude(x => x.Document) .Include(x => x.LeaveDraftDocument) .Include(x => x.LeaveCancelDocument) .Include(x => x.Type) .FirstOrDefaultAsync(x => x.Id == id); return data; } public override async Task AddAsync(LeaveRequest entity) { if (entity.LeaveCancelDocument != null) _dbContext.Attatch(entity.LeaveCancelDocument); if (entity.LeaveDraftDocument != null) _dbContext.Attatch(entity.LeaveDraftDocument); if (entity.LeaveDocument != null) { foreach (var d in entity.LeaveDocument) { _dbContext.Attatch(d); } } if (entity.Type != null) _dbContext.Attatch(entity.Type); return await base.AddAsync(entity); } public async Task UpdateWithTrackingAsync(LeaveRequest entity) { // detach //_dbContext.Detach(entity); if (entity.LeaveCancelDocument != null) _dbContext.Attatch(entity.LeaveCancelDocument); if (entity.LeaveDraftDocument != null) _dbContext.Attatch(entity.LeaveDraftDocument); if (entity.LeaveDocument != null) { foreach (var d in entity.LeaveDocument) { _dbContext.Attatch(d); } } if (entity.Type != null) { //_dbContext.Attatch(entity.Type); //_dbContext.Detach(entity.Type); } return await base.UpdateAsync(entity); } public override async Task UpdateAsync(LeaveRequest entity) { // detach //_dbContext.Detach(entity); if (entity.LeaveCancelDocument != null) _dbContext.Attatch(entity.LeaveCancelDocument); if (entity.LeaveDraftDocument != null) _dbContext.Attatch(entity.LeaveDraftDocument); if (entity.LeaveDocument != null) { foreach (var d in entity.LeaveDocument) { _dbContext.Attatch(d); } } if (entity.Type != null) { _dbContext.Attatch(entity.Type); //_dbContext.Detach(entity.Type); } return await base.UpdateAsync(entity); } #endregion public async Task> GetLeaveRequestByYearAsync(int year) { var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.LeaveStartDate.Year == year) .Where(x => x.LeaveStatus != "REJECT" && x.LeaveStatus != "DELETE") .ToListAsync(); return data; } public async Task> GetLeaveRequestByUserIdAsync(Guid keycloakUserId, int year, Guid type, string status) { var rawData = _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.KeycloakUserId == keycloakUserId); if (year != 0) rawData = rawData.Where(x => x.LeaveStartDate.Year == year); if (type != Guid.Empty) rawData = rawData.Where(x => x.Type.Id == type); if (status.Trim().ToUpper() != "ALL") rawData = rawData.Where(x => x.LeaveStatus == status); return await rawData.ToListAsync(); } public async Task> GetLeaveRequestForAdminAsync(int year, Guid type, string status, DateTime startDate, DateTime endDate) { var rawData = _dbContext.Set().AsNoTracking() .Include(x => x.Type) .Where(x => x.LeaveStatus != "DRAFT") .OrderByDescending(x => x.CreatedAt) .AsQueryable(); if (year != 0) rawData = rawData.Where(x => x.LeaveStartDate.Year == year); if (type != Guid.Empty) rawData = rawData.Where(x => x.Type.Id == type); if (status.Trim().ToUpper() != "ALL") rawData = rawData.Where(x => x.LeaveStatus == status); if (startDate != DateTime.MinValue) rawData = rawData.Where(x => x.LeaveStartDate >= startDate); if (endDate != DateTime.MinValue) rawData = rawData.Where(x => x.LeaveEndDate <= endDate); return await rawData.ToListAsync(); } public async Task GetRestDayTotalByYearForUserAsync(Guid keycloakUserId, int year) { var startFiscalDate = new DateTime(year - 1, 10, 1); var endFiscalDate = new DateTime(year, 9, 30); var leaveType = await _dbContext.Set().AsQueryable().AsNoTracking() .FirstOrDefaultAsync(l => l.Code.Trim().ToUpper() == "LV-005"); if (leaveType == null) { throw new Exception("ไม่พบข้อมูลประเภทการลาพักผ่อน โปรดติดต่อผู้ดูและระบบ"); } var data = _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.Type.Id == leaveType.Id) //.Where(x => x.LeaveStartDate.Year == year) .Where(x => x.LeaveStartDate.Date >= startFiscalDate && x.LeaveStartDate.Date <= endFiscalDate) .Where(x => x.LeaveStatus == "APPROVE") .Sum(x => x.LeaveTotal); return data; } public async Task GetSumLeaveByTypeForUserAsync(Guid keycloakUserId, Guid leaveTypeId, int year) { var startFiscalDate = new DateTime(year - 1, 10, 1); var endFiscalDate = new DateTime(year, 9, 30); var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.Type.Id == leaveTypeId) //.Where(x => x.LeaveStartDate.Year == year) .Where(x => x.LeaveStartDate.Date >= startFiscalDate && x.LeaveStartDate.Date <= endFiscalDate) .Where(x => x.LeaveStatus == "APPROVE") //.Where(x => x.LeaveStatus != "REJECT" && x.LeaveStatus != "DELETE") .ToListAsync(); return data.Sum(x => x.LeaveTotal); } //public async Task GetSumApproveLeaveByTypeForUserAsync(Guid keycloakUserId, Guid leaveTypeId, int year) //{ // var data = await _dbContext.Set().AsQueryable().AsNoTracking() // .Include(x => x.Type) // .Where(x => x.KeycloakUserId == keycloakUserId) // .Where(x => x.Type.Id == leaveTypeId) // .Where(x => x.LeaveStartDate.Year == year) // .Where(x => x.LeaveStatus == "APPROVE") // .ToListAsync(); // return data.Sum(x => x.LeaveTotal); //} public async Task GetLeaveLastByTypeForUserAsync(Guid keycloakUserId, Guid leaveTypeId) { var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.Type.Id == leaveTypeId) .Where(x => x.LeaveStatus == "APPROVE") //.Where(x => x.LeaveStatus != "REJECT" && x.LeaveStatus != "DELETE") .OrderByDescending(x => x.LeaveStartDate.Date) .Select(x => x.LeaveStartDate.Date) .FirstOrDefaultAsync(); return data; } public async Task GetLastLeaveRequestByTypeForUserAsync(Guid keycloakUserId, Guid leaveTypeId) { var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.Type.Id == leaveTypeId) .Where(x => x.LeaveStatus == "APPROVE") //.Where(x => x.LeaveStatus != "REJECT" && x.LeaveStatus != "DELETE") .OrderByDescending(x => x.LeaveStartDate.Date) .FirstOrDefaultAsync(); return data; } public async Task> GetCancelLeaveRequestForAdminAsync(int year, Guid type, string status) { var rawData = _dbContext.Set().AsNoTracking() .Include(x => x.Type) .Where(x => x.LeaveStatus == "DELETE") .AsQueryable(); if (year != 0) rawData = rawData.Where(x => x.LeaveStartDate.Year == year); if (type != Guid.Empty) rawData = rawData.Where(x => x.Type.Id == type); if (status.Trim().ToUpper() != "ALL") rawData = rawData.Where(x => x.LeaveCancelStatus == status); return await rawData.ToListAsync(); } public async Task ApproveCancelLeaveRequestAsync(LeaveRequest data, string Reason) { try { var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken ?? ""); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); } data.LeaveCancelStatus = "APPROVE"; data.LeaveCancelComment = Reason; // Send Noti var noti = new Notification { Body = $"การขอยกเลิกใบลาของคุณได้รับการอนุมัติ", ReceiverUserId = profile.Id, Type = "", Payload = "", }; _appDbContext.Set().Add(noti); await _appDbContext.SaveChangesAsync(); return data; } catch { throw; } } public async Task ApproveCancelLeaveRequestAsync(Guid id, string Reason) { var rawData = await GetByIdAsync(id); if (rawData == null) { throw new Exception(GlobalMessages.DataNotFound); } var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken ?? ""); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); } rawData.LeaveCancelStatus = "APPROVE"; rawData.LeaveCancelComment = Reason; await UpdateAsync(rawData); // TODO: remove วันลา // Send Noti var noti = new Notification { Body = $"การขอยกเลิกใบลาของคุณได้รับการอนุมัติ", ReceiverUserId = profile.Id, Type = "", Payload = "", }; _appDbContext.Set().Add(noti); await _appDbContext.SaveChangesAsync(); } public async Task RejectCancelLeaveRequestAsync(Guid id, string Reason) { var rawData = await GetByIdAsync(id); if (rawData == null) { throw new Exception(GlobalMessages.DataNotFound); } var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken ?? ""); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); } rawData.LeaveCancelStatus = "REJECT"; rawData.LeaveCancelComment = Reason; // fix issue : ระบบลา (User) >> กรณีขอยกเลิกการลา แต่ผู้บังคับบัญชาไม่อนุมัติ (สถานะการลาไม่อัปเดต) #846 if (rawData.ApproveStep == "st4") { rawData.LeaveStatus = "APPROVE"; } else { rawData.LeaveStatus = "NEW"; } await UpdateAsync(rawData); // fix issue : SIT ระบบบันทึกการลา>>รายการลา (กรณีขอยกเลิกการลา) #935 // TODO: remove วันลา //var leaveType = await _appDbContext.Set() // .FirstOrDefaultAsync(x => x.Name == rawData.Type.Name); // insert to profile leave //var profileLeave = await _appDbContext.Set() // .Where(x => x.TypeLeave.Id == leaveType.Id) // .Where(x => x.ProfileId == profile.Id) // .Where(x => x.DateStartLeave == rawData.LeaveStartDate && x.DateEndLeave == rawData.LeaveEndDate) // .FirstOrDefaultAsync(); //if (profileLeave != null) //{ // _appDbContext.Set().Remove(profileLeave); // await _appDbContext.SaveChangesAsync(); //} // Send Noti var noti = new Notification { Body = $"การขอยกเลิกใบลาของคุณไม่ได้รับการอนุมัติ \r\nเนืองจาก {Reason}", ReceiverUserId = profile.Id, Type = "", Payload = "", }; _appDbContext.Set().Add(noti); await _appDbContext.SaveChangesAsync(); } public async Task SendToOfficerAsync(Guid id) { var rawData = await GetByIdAsync(id); if (rawData == null) { throw new Exception(GlobalMessages.DataNotFound); } var isDuplicate = await CheckDuplicateLeave(rawData.KeycloakUserId, rawData.LeaveStartDate.Date, rawData.LeaveEndDate.Date, rawData.LeaveRange ?? "ALL"); if (isDuplicate) { throw new Exception("ไม่สามารถขอลาในช่วงเวลาเดียวกันได้ เนื่องจากมีการขอลาในช่วงเวลาดังกล่าวแล้ว"); } rawData.LeaveStatus = "NEW"; //rawData.ApproveStep = "st2"; await UpdateAsync(rawData); } public async Task OfficerApproveLeaveRequest(Guid id) { var rawData = await GetByIdAsync(id); if (rawData == null) { throw new Exception(GlobalMessages.DataNotFound); } rawData.LeaveStatus = "PENDING"; rawData.ApproveStep = "st2"; await UpdateAsync(rawData); // TODO: Send notification to 1st Commander var firstCommander = rawData.Approvers .Where(x => x.ApproveType!.ToUpper() == "COMMANDER") .OrderBy(x => x.Seq) .FirstOrDefault(); // Send Notification var noti1 = new Notification { Body = $"การขอลาของคุณ {rawData.FirstName} {rawData.LastName} รอรับการอนุมัติจากคุณ", ReceiverUserId = firstCommander!.ProfileId, Type = "", Payload = $"{URL}/leave/detail/{id}", }; _appDbContext.Set().Add(noti1); await _appDbContext.SaveChangesAsync(); } public async Task CommanderApproveLeaveRequest(Guid id, string reason) { // Get UserId from token var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); var rawData = await GetByIdAsync(id); if (rawData == null) { throw new Exception(GlobalMessages.DataNotFound); } if (rawData.ApproveStep != "st2") { throw new Exception("คำขอนี้ยังไม่ได้อยู่ในขั้นตอนที่สามารถอนุมัติได้ ไม่สามารถทำรายการได้"); } // check commander approve var approvers = rawData.Approvers.Where(x => x.ApproveType!.ToUpper() == "COMMANDER").OrderBy(x => x.Seq).ToList(); var approver = approvers.FirstOrDefault(x => x.KeycloakId == userId); if (approver == null) { throw new Exception("คุณไม่มีสิทธิ์อนุมัติการลาในขั้นตอนนี้"); } // check prev approver มี action แล้วหรือไม่? var prevApprover = approvers.FirstOrDefault(x => x.Seq == approver.Seq - 1); if (prevApprover != null) { if (prevApprover.ApproveStatus == "PENDING") { throw new Exception("ไม่สามารถทำการอนุมัติได้ เนื่องจากยังอยู่ระหว่างการพิจารณาโดยผู้บังคับบัญชารายก่อนหน้า"); } } var maxSeq = approvers.Max(x => x.Seq); approver.ApproveStatus = "APPROVE"; approver.Comment = reason; if (approver.Seq != maxSeq) { var nextApprover = approvers.FirstOrDefault(x => x.Seq == approver.Seq + 1); // Send Noti var noti = new Notification { Body = $"การขอลาของคุณ {rawData.FirstName} {rawData.LastName} รอรับการอนุมัติจากคุณ", ReceiverUserId = nextApprover!.ProfileId, Type = "", Payload = $"{URL}/leave/detail/{id}", }; _appDbContext.Set().Add(noti); await _appDbContext.SaveChangesAsync(); rawData.LeaveStatus = "PENDING"; await UpdateAsync(rawData); } else { rawData.LeaveStatus = "PENDING"; rawData.LeaveComment = reason; rawData.ApproveStep = "st3"; await UpdateAsync(rawData); // TODO: Send notification to 1st Approver var firstCommander = rawData.Approvers .Where(x => x.ApproveType!.ToUpper() == "APPROVER") .OrderBy(x => x.Seq) .FirstOrDefault(); // Send Notification var noti1 = new Notification { Body = $"การขอลาของคุณ {rawData.FirstName} {rawData.LastName} รอรับการอนุมัติจากคุณ", ReceiverUserId = firstCommander!.ProfileId, Type = "", Payload = $"{URL}/leave/detail/{id}", }; _appDbContext.Set().Add(noti1); await _appDbContext.SaveChangesAsync(); } } public async Task CommanderRejectLeaveRequest(Guid id, string reason) { // Get UserId from token var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); var rawData = await GetByIdAsync(id); if (rawData == null) { throw new Exception(GlobalMessages.DataNotFound); } if (rawData.ApproveStep != "st2") { throw new Exception("คำขอนี้ยังไม่ได้อยู่ในขั้นตอนที่สามารถอนุมัติได้ ไม่สามารถทำรายการได้"); } // check commander approve var approvers = rawData.Approvers.Where(x => x.ApproveType!.ToUpper() == "COMMANDER").OrderBy(x => x.Seq).ToList(); var approver = approvers.FirstOrDefault(x => x.KeycloakId == userId); if (approver == null) { throw new Exception("คุณไม่มีสิทธิ์อนุมัติการลาในขั้นตอนนี้"); } // check prev approver มี action แล้วหรือไม่? var prevApprover = approvers.FirstOrDefault(x => x.Seq == approver.Seq - 1); if (prevApprover != null) { if (prevApprover.ApproveStatus == "PENDING") { throw new Exception("ไม่สามารถทำการอนุมัติได้ เนื่องจากยังอยู่ระหว่างการพิจารณาโดยผู้บังคับบัญชารายก่อนหน้า"); } } var maxSeq = approvers.Max(x => x.Seq); approver.ApproveStatus = "REJECT"; approver.Comment = reason; if (approver.Seq != maxSeq) { var nextApprover = approvers.FirstOrDefault(x => x.Seq == approver.Seq + 1); // Send Noti var noti = new Notification { Body = $"การขอลาของคุณ {rawData.FirstName} {rawData.LastName} รอรับการอนุมัติจากคุณ", ReceiverUserId = nextApprover!.ProfileId, Type = "", Payload = $"{URL}/leave/detail/{id}", }; _appDbContext.Set().Add(noti); await _appDbContext.SaveChangesAsync(); rawData.LeaveStatus = "PENDING"; await UpdateAsync(rawData); } else { rawData.LeaveStatus = "PENDING"; rawData.LeaveComment = reason; rawData.ApproveStep = "st3"; await UpdateAsync(rawData); // TODO: Send notification to 1st Approver var firstCommander = rawData.Approvers .Where(x => x.ApproveType!.ToUpper() == "APPROVER") .OrderBy(x => x.Seq) .FirstOrDefault(); // Send Notification var noti1 = new Notification { Body = $"การขอลาของคุณ {rawData.FirstName} {rawData.LastName} รอรับการอนุมัติจากคุณ", ReceiverUserId = firstCommander!.ProfileId, Type = "", Payload = $"{URL}/leave/detail/{id}", }; _appDbContext.Set().Add(noti1); await _appDbContext.SaveChangesAsync(); } } public async Task ApproveLeaveRequest(Guid id, string reason) { // Get UserId from token var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); var rawData = await GetByIdAsync(id); if (rawData == null) { throw new Exception(GlobalMessages.DataNotFound); } if (rawData.ApproveStep != "st3") { throw new Exception("คำขอนี้ยังไม่ได้อยู่ในขั้นตอนที่สามารถอนุมัติได้ ไม่สามารถทำรายการได้"); } // check commander approve var approvers = rawData.Approvers.Where(x => x.ApproveType!.ToUpper() == "APPROVER").OrderBy(x => x.Seq).ToList(); var approver = approvers.FirstOrDefault(x => x.KeycloakId == userId); if (approver == null) { throw new Exception("คุณไม่มีสิทธิ์อนุมัติการลาในขั้นตอนนี้"); } // check prev approver มี action แล้วหรือไม่? var prevApprover = approvers.FirstOrDefault(x => x.Seq == approver.Seq - 1); if (prevApprover != null) { if (prevApprover.ApproveStatus == "PENDING") { throw new Exception("ไม่สามารถทำการอนุมัติได้ เนื่องจากยังอยู่ระหว่างการพิจารณาโดยผู้บังคับบัญชารายก่อนหน้า"); } } var maxSeq = approvers.Max(x => x.Seq); approver.ApproveStatus = "APPROVE"; approver.Comment = reason; if (approver.Seq != maxSeq) { var nextApprover = approvers.FirstOrDefault(x => x.Seq == approver.Seq + 1); // Send Noti var noti1 = new Notification { Body = $"การขอลาของคุณ {rawData.FirstName} {rawData.LastName} รอรับการอนุมัติจากคุณ", ReceiverUserId = nextApprover!.ProfileId, Type = "", Payload = "", }; _appDbContext.Set().Add(noti1); await _appDbContext.SaveChangesAsync(); await UpdateAsync(rawData); } else { var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); } rawData.LeaveStatus = "APPROVE"; rawData.LeaveDirectorComment = reason; rawData.ApproveStep = "st4"; await UpdateAsync(rawData); var _baseAPI = _configuration["API"]; var apiUrlSalary = string.Empty; if (profile.ProfileType == "OFFICER") { apiUrlSalary = $"{_baseAPI}/org/profile/leave"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken.Replace("Bearer ", "")); client.DefaultRequestHeaders.Add("api_key", _configuration["API_KEY"]); var _res = await client.PostAsJsonAsync(apiUrlSalary, new { profileId = profile.Id, leaveTypeId = rawData?.Type?.Id ?? null, dateLeaveStart = rawData.LeaveStartDate, dateLeaveEnd = rawData.LeaveEndDate, totalLeave = 0,//หน้า fe ไม่ได้ใช้ leaveCount = 0,//หน้า fe ไม่ได้ใช้ leaveDays = rawData.LeaveTotal, status = "approve", reason = rawData.LeaveDetail, }); // var _result = await _res.Content.ReadAsStringAsync(); } } else if (profile.ProfileType == "EMPLOYEE") { apiUrlSalary = $"{_baseAPI}/org/profile-employee/leave"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken.Replace("Bearer ", "")); client.DefaultRequestHeaders.Add("api_key", _configuration["API_KEY"]); var _res = await client.PostAsJsonAsync(apiUrlSalary, new { profileEmployeeId = profile.Id, leaveTypeId = rawData?.Type?.Id ?? null, dateLeaveStart = rawData.LeaveStartDate, dateLeaveEnd = rawData.LeaveEndDate, totalLeave = 0, leaveCount = 0, leaveDays = rawData.LeaveTotal, status = "approve", reason = rawData.LeaveDetail, }); } } else { throw new Exception("ไม่สามารถทำรายการได้"); } await _appDbContext.SaveChangesAsync(); // insert to process timestamp // Send Noti var noti = new Notification { Body = $"การขอลาของคุณได้รับการอนุมัติ", ReceiverUserId = profile.Id, Type = "", Payload = "", }; _appDbContext.Set().Add(noti); await _appDbContext.SaveChangesAsync(); } } public async Task RejectLeaveRequest(Guid id, string reason) { // Get UserId from token var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); var rawData = await GetByIdAsync(id); if (rawData == null) { throw new Exception(GlobalMessages.DataNotFound); } if (rawData.ApproveStep != "st3") { throw new Exception("คำขอนี้ยังไม่ได้อยู่ในขั้นตอนที่สามารถอนุมัติได้ ไม่สามารถทำรายการได้"); } // check commander approve var approvers = rawData.Approvers.Where(x => x.ApproveType!.ToUpper() == "APPROVER").OrderBy(x => x.Seq).ToList(); var approver = approvers.FirstOrDefault(x => x.KeycloakId == userId); if (approver == null) { throw new Exception("คุณไม่มีสิทธิ์อนุมัติการลาในขั้นตอนนี้"); } // check prev approver มี action แล้วหรือไม่? var prevApprover = approvers.FirstOrDefault(x => x.Seq == approver.Seq - 1); if (prevApprover != null) { if (prevApprover.ApproveStatus == "PENDING") { throw new Exception("ไม่สามารถทำการอนุมัติได้ เนื่องจากยังอยู่ระหว่างการพิจารณาโดยผู้บังคับบัญชารายก่อนหน้า"); } } var maxSeq = approvers.Max(x => x.Seq); approver.ApproveStatus = "REJECT"; approver.Comment = reason; if (approver.Seq != maxSeq) { var nextApprover = approvers.FirstOrDefault(x => x.Seq == approver.Seq + 1); // Send Noti var noti1 = new Notification { Body = $"การขอลาของคุณ {rawData.FirstName} {rawData.LastName} รอรับการอนุมัติจากคุณ", ReceiverUserId = nextApprover!.ProfileId, Type = "", Payload = "", }; _appDbContext.Set().Add(noti1); await _appDbContext.SaveChangesAsync(); await UpdateAsync(rawData); } else { var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); } rawData.LeaveStatus = "REJECT"; rawData.LeaveDirectorComment = reason; rawData.ApproveStep = "st5"; await UpdateAsync(rawData); // Send Noti var noti = new Notification { Body = $"การขอลาของคุณไม่ได้รับการอนุมัติ \r\nเนื่องจาก{reason}", ReceiverUserId = profile.Id, Type = "", Payload = "", }; _appDbContext.Set().Add(noti); await _appDbContext.SaveChangesAsync(); } } public async Task> GetSumSendLeaveAsync(int year) { var startFiscalDate = new DateTime(year - 1, 10, 1); var endFiscalDate = new DateTime(year, 9, 30); var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) //.Where(x => x.LeaveStartDate.Year == year) .Where(x => x.LeaveStartDate.Date >= startFiscalDate && x.LeaveStartDate.Date <= endFiscalDate) .Where(x => x.LeaveStatus == "NEW") // fix issue : #729 .ToListAsync(); var res = (from d in data group d by new { d.KeycloakUserId, LeaveTypeId = d.Type.Id, LeaveTypeCode = d.Type.Code } into grp select new GetSumApproveLeaveByTypeDto { KeycloakUserId = grp.Key.KeycloakUserId, LeaveTypeId = grp.Key.LeaveTypeId, LeaveTypeCode = grp.Key.LeaveTypeCode, SumLeaveDay = grp.Sum(x => x.LeaveTotal) }) .ToList(); return res; } public async Task> GetSumApproveLeaveAsync(int year) { // fix issue : #729 var startFiscalDate = new DateTime(year - 1, 10, 1); var endFiscalDate = new DateTime(year, 9, 30); var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) //.Where(x => x.LeaveStartDate.Year == year) .Where(x => x.LeaveStartDate.Date >= startFiscalDate && x.LeaveStartDate.Date <= endFiscalDate) // fix issue : #729 .Where(x => x.LeaveStatus == "APPROVE") .ToListAsync(); var res = (from d in data group d by new { d.KeycloakUserId, LeaveTypeId = d.Type.Id, LeaveTypeCode = d.Type.Code } into grp select new GetSumApproveLeaveByTypeDto { KeycloakUserId = grp.Key.KeycloakUserId, LeaveTypeId = grp.Key.LeaveTypeId, LeaveTypeCode = grp.Key.LeaveTypeCode, SumLeaveDay = grp.Sum(x => x.LeaveTotal) }) .ToList(); return res; } public async Task> GetSumRejectLeaveAsync(int year) { // fix issue : #729 var startFiscalDate = new DateTime(year - 1, 10, 1); var endFiscalDate = new DateTime(year, 9, 30); var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) //.Where(x => x.LeaveStartDate.Year == year) .Where(x => x.LeaveStartDate.Date >= startFiscalDate && x.LeaveStartDate.Date <= endFiscalDate) // fix issue : #729 .Where(x => x.LeaveStatus == "REJECT") .ToListAsync(); var res = (from d in data group d by new { d.KeycloakUserId, LeaveTypeId = d.Type.Id, LeaveTypeCode = d.Type.Code } into grp select new GetSumApproveLeaveByTypeDto { KeycloakUserId = grp.Key.KeycloakUserId, LeaveTypeId = grp.Key.LeaveTypeId, LeaveTypeCode = grp.Key.LeaveTypeCode, SumLeaveDay = grp.Sum(x => x.LeaveTotal) }) .ToList(); return res; } public async Task> GetSumDeleteLeaveAsync(int year) { // fix issue : #729 var startFiscalDate = new DateTime(year - 1, 10, 1); var endFiscalDate = new DateTime(year, 9, 30); var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) //.Where(x => x.LeaveStartDate.Year == year) .Where(x => x.LeaveStartDate.Date >= startFiscalDate && x.LeaveStartDate.Date <= endFiscalDate) // fix issue : #729 .Where(x => x.LeaveStatus == "DELETE") .ToListAsync(); var res = (from d in data group d by new { d.KeycloakUserId, LeaveTypeId = d.Type.Id, LeaveTypeCode = d.Type.Code } into grp select new GetSumApproveLeaveByTypeDto { KeycloakUserId = grp.Key.KeycloakUserId, LeaveTypeId = grp.Key.LeaveTypeId, LeaveTypeCode = grp.Key.LeaveTypeCode, SumLeaveDay = grp.Sum(x => x.LeaveTotal) }) .ToList(); return res; } public async Task GetSumSendLeaveByTypeForUserAsync(Guid keycloakUserId, Guid leaveTypeId, int year) { var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.Type.Id == leaveTypeId) .Where(x => x.LeaveStartDate.Year == year) .ToListAsync(); return data.Sum(x => x.LeaveTotal); } public async Task GetSumApproveLeaveByTypeForUserAsync(Guid keycloakUserId, Guid leaveTypeId, int year) { var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.Type.Id == leaveTypeId) .Where(x => x.LeaveStartDate.Year == year) .Where(x => x.LeaveStatus == "APPROVE") .ToListAsync(); if (data.Count > 0) return data.Sum(x => x.LeaveTotal); else return 0.0; } public async Task GetSumApproveLeaveByRangeForUser(Guid keycloakUserId, DateTime startDate, DateTime endDate) { var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) .Where(x => x.LeaveStatus == "APPROVE") .ToListAsync(); if (data.Count > 0) return data.Sum(x => x.LeaveTotal); else return 0; } public async Task> GetSumApproveLeaveByTypeAndRange(DateTime startDate, DateTime endDate) { var data = await _dbContext.Set().AsQueryable() .Include(x => x.Type) .Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) .Where(x => x.LeaveStatus == "APPROVE").ToListAsync(); var res = (from d in data group d by new { d.KeycloakUserId, LeaveTypeId = d.Type.Id, LeaveTypeCode = d.Type.Code } into grp select new GetSumApproveLeaveByTypeDto { KeycloakUserId = grp.Key.KeycloakUserId, LeaveTypeId = grp.Key.LeaveTypeId, LeaveTypeCode = grp.Key.LeaveTypeCode, SumLeaveDay = grp.Sum(x => x.LeaveTotal) }) .ToList(); return res; } public async Task> GetSumApproveLeaveByRootAndRange(DateTime startDate, DateTime endDate, string type, string role, string? nodeId, int? node) { // var _nodeId = Guid.Parse(nodeId); var data = new List(); if (role == "OWNER" || role == "CHILD") { data = await _dbContext.Set().AsQueryable() .Include(x => x.Type) .Where(x => x.ProfileType == type.Trim().ToUpper()) .Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) .Where(x => node == 4 ? x.Child4Id == Guid.Parse(nodeId) : (node == 3 ? x.Child3Id == Guid.Parse(nodeId) : (node == 2 ? x.Child2Id == Guid.Parse(nodeId) : (node == 1 ? x.Child1Id == Guid.Parse(nodeId) : (node == 0 ? x.RootId == Guid.Parse(nodeId) : (node == null ? true : true)))))) .Where(x => x.LeaveStatus == "APPROVE").ToListAsync(); } else { data = await _dbContext.Set().AsQueryable() .Include(x => x.Type) .Where(x => x.ProfileType == type.Trim().ToUpper()) .Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) .Where(x => node == 4 ? x.Child4Id == Guid.Parse(nodeId) : (node == 3 ? x.Child3Id == Guid.Parse(nodeId) : (node == 2 ? x.Child2Id == Guid.Parse(nodeId) : (node == 1 ? x.Child1Id == Guid.Parse(nodeId) : (node == 0 ? x.RootId == Guid.Parse(nodeId) : (node == null ? true : true)))))) .Where(x => node == 0 ? x.Child1Id == null : (node == 1 ? x.Child2Id == null : (node == 2 ? x.Child3Id == null : (node == 3 ? x.Child4Id == null : true)))) .Where(x => x.LeaveStatus == "APPROVE").ToListAsync(); } var res = (from d in data group d by new { d.Root, d.Child1, d.Child2, d.Child3, d.Child4 } into grp orderby grp.Key.Root, grp.Key.Child1, grp.Key.Child2, grp.Key.Child3, grp.Key.Child4 select new GetSumApproveLeaveByRootDto { Root = $"{grp.Key.Root}{(!string.IsNullOrEmpty(grp.Key.Child1) ? "/" + grp.Key.Child1 : "")}{(!string.IsNullOrEmpty(grp.Key.Child2) ? "/" + grp.Key.Child2 : "")}{(!string.IsNullOrEmpty(grp.Key.Child3) ? "/" + grp.Key.Child3 : "")}{(!string.IsNullOrEmpty(grp.Key.Child4) ? "/" + grp.Key.Child4 : "")}", SumLeaveDay = grp.Sum(x => x.LeaveTotal), sickDayCountMale = grp.Where(x => x.Gender == "ชาย" && x.LeaveTypeCode == "LV-001").Sum(x => x.LeaveTotal), maternityDayCountMale = grp.Where(x => x.Gender == "ชาย" && x.LeaveTypeCode == "LV-002").Sum(x => x.LeaveTotal), wifeDayCountMale = grp.Where(x => x.Gender == "ชาย" && x.LeaveTypeCode == "LV-003").Sum(x => x.LeaveTotal), personalDayCountMale = grp.Where(x => x.Gender == "ชาย" && x.LeaveTypeCode == "LV-004").Sum(x => x.LeaveTotal), restDayCountMale = grp.Where(x => x.Gender == "ชาย" && x.LeaveTypeCode == "LV-005").Sum(x => x.LeaveTotal), ordainDayCountMale = grp.Where(x => x.Gender == "ชาย" && x.LeaveTypeCode == "LV-006").Sum(x => x.LeaveTotal), absentDayCountMale = grp.Where(x => x.Gender == "ชาย" && x.LeaveTypeCode == "LV-007").Sum(x => x.LeaveTotal), studyDayCountMale = grp.Where(x => x.Gender == "ชาย" && x.LeaveTypeCode == "LV-008").Sum(x => x.LeaveTotal), agencyDayCountMale = grp.Where(x => x.Gender == "ชาย" && x.LeaveTypeCode == "LV-009").Sum(x => x.LeaveTotal), coupleDayCountMale = grp.Where(x => x.Gender == "ชาย" && x.LeaveTypeCode == "LV-010").Sum(x => x.LeaveTotal), therapyDayCountMale = grp.Where(x => x.Gender == "ชาย" && x.LeaveTypeCode == "LV-011").Sum(x => x.LeaveTotal), sickDayCountFemale = grp.Where(x => x.Gender == "หญิง" && x.LeaveTypeCode == "LV-001").Sum(x => x.LeaveTotal), maternityDayCountFemale = grp.Where(x => x.Gender == "หญิง" && x.LeaveTypeCode == "LV-002").Sum(x => x.LeaveTotal), wifeDayCountFemale = grp.Where(x => x.Gender == "หญิง" && x.LeaveTypeCode == "LV-003").Sum(x => x.LeaveTotal), personalDayCountFemale = grp.Where(x => x.Gender == "หญิง" && x.LeaveTypeCode == "LV-004").Sum(x => x.LeaveTotal), restDayCountFemale = grp.Where(x => x.Gender == "หญิง" && x.LeaveTypeCode == "LV-005").Sum(x => x.LeaveTotal), ordainDayCountFemale = grp.Where(x => x.Gender == "หญิง" && x.LeaveTypeCode == "LV-006").Sum(x => x.LeaveTotal), absentDayCountFemale = grp.Where(x => x.Gender == "หญิง" && x.LeaveTypeCode == "LV-007").Sum(x => x.LeaveTotal), studyDayCountFemale = grp.Where(x => x.Gender == "หญิง" && x.LeaveTypeCode == "LV-008").Sum(x => x.LeaveTotal), agencyDayCountFemale = grp.Where(x => x.Gender == "หญิง" && x.LeaveTypeCode == "LV-009").Sum(x => x.LeaveTotal), coupleDayCountFemale = grp.Where(x => x.Gender == "หญิง" && x.LeaveTypeCode == "LV-010").Sum(x => x.LeaveTotal), therapyDayCountFemale = grp.Where(x => x.Gender == "หญิง" && x.LeaveTypeCode == "LV-011").Sum(x => x.LeaveTotal), sickDayCountNo = grp.Where(x => x.Gender != "ชาย" && x.Gender != "หญิง" && x.LeaveTypeCode == "LV-001").Sum(x => x.LeaveTotal), maternityDayCountNo = grp.Where(x => x.Gender != "ชาย" && x.Gender != "หญิง" && x.LeaveTypeCode == "LV-002").Sum(x => x.LeaveTotal), wifeDayCountNo = grp.Where(x => x.Gender != "ชาย" && x.Gender != "หญิง" && x.LeaveTypeCode == "LV-003").Sum(x => x.LeaveTotal), personalDayCountNo = grp.Where(x => x.Gender != "ชาย" && x.Gender != "หญิง" && x.LeaveTypeCode == "LV-004").Sum(x => x.LeaveTotal), restDayCountNo = grp.Where(x => x.Gender != "ชาย" && x.Gender != "หญิง" && x.LeaveTypeCode == "LV-005").Sum(x => x.LeaveTotal), ordainDayCountNo = grp.Where(x => x.Gender != "ชาย" && x.Gender != "หญิง" && x.LeaveTypeCode == "LV-006").Sum(x => x.LeaveTotal), absentDayCountNo = grp.Where(x => x.Gender != "ชาย" && x.Gender != "หญิง" && x.LeaveTypeCode == "LV-007").Sum(x => x.LeaveTotal), studyDayCountNo = grp.Where(x => x.Gender != "ชาย" && x.Gender != "หญิง" && x.LeaveTypeCode == "LV-008").Sum(x => x.LeaveTotal), agencyDayCountNo = grp.Where(x => x.Gender != "ชาย" && x.Gender != "หญิง" && x.LeaveTypeCode == "LV-009").Sum(x => x.LeaveTotal), coupleDayCountNo = grp.Where(x => x.Gender != "ชาย" && x.Gender != "หญิง" && x.LeaveTypeCode == "LV-010").Sum(x => x.LeaveTotal), therapyDayCountNo = grp.Where(x => x.Gender != "ชาย" && x.Gender != "หญิง" && x.LeaveTypeCode == "LV-011").Sum(x => x.LeaveTotal), }) .ToList(); return res; } public async Task> GetCountApproveLeaveByTypeAndRange(DateTime startDate, DateTime endDate) { var data = await _dbContext.Set().AsQueryable() .Include(x => x.Type) .Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) .Where(x => x.LeaveStatus == "APPROVE").ToListAsync(); var res = (from d in data group d by new { d.KeycloakUserId, LeaveTypeId = d.Type.Id, LeaveTypeCode = d.Type.Code } into grp select new GetCountApproveLeaveByTypeDto { KeycloakUserId = grp.Key.KeycloakUserId, LeaveTypeId = grp.Key.LeaveTypeId, LeaveTypeCode = grp.Key.LeaveTypeCode, CountLeave = grp.Count() }) .ToList(); return res; } public async Task GetSumApproveLeaveByTypeAndRangeForUser(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate) { var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.Type.Id == leaveTypeId) .Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) .Where(x => x.LeaveStatus == "APPROVE") .ToListAsync(); if (data.Count > 0) return data.Sum(x => x.LeaveTotal); else return 0; } public async Task GetCountApproveLeaveByTypeAndRangeForUser(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate) { var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.Type.Id == leaveTypeId) .Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) .Where(x => x.LeaveStatus == "APPROVE") .ToListAsync(); return data.Count; } public async Task GetSumRejectLeaveByTypeForUserAsync(Guid keycloakUserId, Guid leaveTypeId, int year) { var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.Type.Id == leaveTypeId) .Where(x => x.LeaveStartDate.Year == year) .Where(x => x.LeaveStatus == "REJECT") .ToListAsync(); if (data.Count > 0) return data.Sum(x => x.LeaveTotal); else return 0; } public async Task GetSumDeleteLeaveByTypeForUserAsync(Guid keycloakUserId, Guid leaveTypeId, int year) { var data = await _dbContext.Set().AsQueryable() .Include(x => x.Type) .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.Type.Id == leaveTypeId) .Where(x => x.LeaveStartDate.Year == year) .Where(x => x.LeaveStatus == "DELETE") .ToListAsync(); if (data.Count > 0) return data.Sum(x => x.LeaveTotal); else return 0; } public async Task CheckDuplicateLeave(Guid keycloakUserId, DateTime startDate, DateTime endDate, string range) { var leaveStatus = new List() { "NEW", "PENDING", "APPROVE" }; if (range == "ALL") { var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) //.Where(x => x.LeaveRange == "ALL") .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.LeaveStartDate.Date == startDate.Date || x.LeaveEndDate.Date == endDate.Date) .Where(x => leaveStatus.Contains(x.LeaveStatus)) .ToListAsync(); return data.Count > 0; } else { var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.LeaveRange == range) .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.LeaveStartDate.Date == startDate.Date || x.LeaveEndDate.Date == endDate.Date) .Where(x => leaveStatus.Contains(x.LeaveStatus)) .ToListAsync(); return data.Count > 0; } } public async Task DeleteLeaveDocumentAsync(Guid Id) { var doc = await _dbContext.Set() .Where(x => x.Id == Id) .FirstOrDefaultAsync(); if (doc != null) { _dbContext.Set().Remove(doc); await _dbContext.SaveChangesAsync(); } } public async Task GetLeavePeriodAsync(Guid keycloakUserId, DateTime date) { var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.LeaveStatus == "APPROVE") .Where(x => x.LeaveStartDate.Date <= date.Date && x.LeaveEndDate >= date.Date) .FirstOrDefaultAsync(); return data; } #endregion } }