From 71966eb4e99548054b529313cecc7ea350a552c0 Mon Sep 17 00:00:00 2001 From: harid Date: Fri, 19 Jun 2026 09:22:33 +0700 Subject: [PATCH 01/18] Linear Flow (RetirementService) #224 --- .../Controllers/RetirementOtherController.cs | 94 ++++++------ .../Controllers/RetirementOutController.cs | 62 ++++---- .../Controllers/RetirementResignController.cs | 136 ++++++++++-------- .../RetirementResignEmployeeController.cs | 100 +++++++------ 4 files changed, 213 insertions(+), 179 deletions(-) diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs index 697af6eb..4880f3b6 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs @@ -846,9 +846,6 @@ namespace BMA.EHR.Retirement.Service.Controllers var data = await _context.RetirementOthers .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); - // Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); - await _context.SaveChangesAsync(); string? _null = null; var resultData = (from p in data @@ -912,24 +909,32 @@ namespace BMA.EHR.Retirement.Service.Controllers orgChild4New = p.child4 }).ToList(); - var baseAPIOrg = _configuration["API"]; - var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave"; - using (var client = new HttpClient()) - { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - data = resultData, - }); - // // var _result = await _res.Content.ReadAsStringAsync(); - // // if (_res.IsSuccessStatusCode) - // // { - // // data.ForEach(profile => profile.Status = "DONE"); - // // await _context.SaveChangesAsync(); - // // } - } - return Success(); + #region Old: Circular Flow + // var baseAPIOrg = _configuration["API"]; + // var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData, + // }); + // // // var _result = await _res.Content.ReadAsStringAsync(); + // // // if (_res.IsSuccessStatusCode) + // // // { + // // // data.ForEach(profile => profile.Status = "DONE"); + // // // await _context.SaveChangesAsync(); + // // // } + // } + #endregion + + // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + data.ForEach(profile => profile.Status = "DONE"); + await _context.SaveChangesAsync(); + + // Return resultData for Node to process directly (Linear Flow) + return Success(resultData); } /// @@ -1098,9 +1103,6 @@ namespace BMA.EHR.Retirement.Service.Controllers var data = await _context.RetirementOthers .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); - // Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); - await _context.SaveChangesAsync(); string? _null = null; var resultData = (from p in data @@ -1164,24 +1166,32 @@ namespace BMA.EHR.Retirement.Service.Controllers orgChild4New = p.child4 }).ToList(); - var baseAPIOrg = _configuration["API"]; - var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave"; - using (var client = new HttpClient()) - { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - data = resultData, - }); - // // var _result = await _res.Content.ReadAsStringAsync(); - // // if (_res.IsSuccessStatusCode) - // // { - // // data.ForEach(profile => profile.Status = "DONE"); - // // await _context.SaveChangesAsync(); - // // } - } - return Success(); + #region Old: Circular Flow + // var baseAPIOrg = _configuration["API"]; + // var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData, + // }); + // // // var _result = await _res.Content.ReadAsStringAsync(); + // // // if (_res.IsSuccessStatusCode) + // // // { + // // // data.ForEach(profile => profile.Status = "DONE"); + // // // await _context.SaveChangesAsync(); + // // // } + // } + #endregion + + // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + data.ForEach(profile => profile.Status = "DONE"); + await _context.SaveChangesAsync(); + + // Return resultData for Node to process directly (Linear Flow) + return Success(resultData); } } } diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs index a8c9d8e2..ae528316 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs @@ -642,10 +642,6 @@ namespace BMA.EHR.Retirement.Service.Controllers var data = await _context.RetirementOuts .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); - // Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); - await _context.SaveChangesAsync(); - var resultData = (from p in data join r in req.refIds on p.Id.ToString() equals r.refId @@ -688,31 +684,39 @@ namespace BMA.EHR.Retirement.Service.Controllers resignId = p.Id, }).ToList(); - var baseAPIOrg = _configuration["API"]; - var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave"; - if (data.Count > 0) - { - if (data[0].profileType == "EMPLOYEE") - { - apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-employee-leave"; - } - } - using (var client = new HttpClient()) - { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - data = resultData, - }); - // // var _result = await _res.Content.ReadAsStringAsync(); - // // if (_res.IsSuccessStatusCode) - // // { - // // data.ForEach(profile => profile.Status = "DONE"); - // // await _context.SaveChangesAsync(); - // // } - } - return Success(); + #region Old: Circular Flow + // var baseAPIOrg = _configuration["API"]; + // var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave"; + // if (data.Count > 0) + // { + // if (data[0].profileType == "EMPLOYEE") + // { + // apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-employee-leave"; + // } + // } + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData, + // }); + // // // var _result = await _res.Content.ReadAsStringAsync(); + // // // if (_res.IsSuccessStatusCode) + // // // { + // // // data.ForEach(profile => profile.Status = "DONE"); + // // // await _context.SaveChangesAsync(); + // // // } + // } + #endregion + + // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + data.ForEach(profile => profile.Status = "DONE"); + await _context.SaveChangesAsync(); + + // Return resultData for Node to process directly (Linear Flow) + return Success(resultData); } } } diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs index 03871f9f..1b434380 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs @@ -2907,10 +2907,6 @@ namespace BMA.EHR.Retirement.Service.Controllers var data = await _context.RetirementResigns .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); - // Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); - await _context.SaveChangesAsync(); - var resultData = (from p in data join r in req.refIds on p.Id.ToString() equals r.refId @@ -2955,24 +2951,32 @@ namespace BMA.EHR.Retirement.Service.Controllers remark = r.remark, }).ToList(); - var baseAPIOrg = _configuration["API"]; - var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave"; - using (var client = new HttpClient()) - { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - data = resultData, - }); - // // var _result = await _res.Content.ReadAsStringAsync(); - // // if (_res.IsSuccessStatusCode) - // // { - // // data.ForEach(profile => profile.Status = "DONE"); - // // await _context.SaveChangesAsync(); - // // } - } - return Success(); + #region Old: Circular Flow + // var baseAPIOrg = _configuration["API"]; + // var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData, + // }); + // // // var _result = await _res.Content.ReadAsStringAsync(); + // // // if (_res.IsSuccessStatusCode) + // // // { + // // // data.ForEach(profile => profile.Status = "DONE"); + // // // await _context.SaveChangesAsync(); + // // // } + // } + #endregion + + // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + data.ForEach(profile => profile.Status = "DONE"); + await _context.SaveChangesAsync(); + + // Return resultData for Node to process directly (Linear Flow) + return Success(resultData); } /// @@ -3102,10 +3106,6 @@ namespace BMA.EHR.Retirement.Service.Controllers var data = await _context.RetirementResigns .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); - // Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); - await _context.SaveChangesAsync(); - var resultData = (from p in data join r in req.refIds on p.Id.ToString() equals r.refId @@ -3149,18 +3149,26 @@ namespace BMA.EHR.Retirement.Service.Controllers remark = r.remark, }).ToList(); - var baseAPIOrg = _configuration["API"]; - var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave"; - using (var client = new HttpClient()) - { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - data = resultData, - }); - } - return Success(); + #region Old: Circular Flow + // var baseAPIOrg = _configuration["API"]; + // var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData, + // }); + // } + #endregion + + // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + data.ForEach(profile => profile.Status = "DONE"); + await _context.SaveChangesAsync(); + + // Return resultData for Node to process directly (Linear Flow) + return Success(resultData); } /// @@ -3298,11 +3306,6 @@ namespace BMA.EHR.Retirement.Service.Controllers .Include(x => x.RetirementResign) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); - // Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); - data.ForEach(profile => profile.RetirementResign.Status = "CANCEL"); - await _context.SaveChangesAsync(); - string? _null = null; var resultData = (from p in data join r in req.refIds @@ -3346,7 +3349,8 @@ namespace BMA.EHR.Retirement.Service.Controllers resignId = p.RetirementResign.Id, }).ToList(); - var baseAPIOrg = _configuration["API"]; + #region Old: Circular Flow + // var baseAPIOrg = _configuration["API"]; //var reportDone = false; //if (data.Where(profile => profile.Status == "DONE").Any()) //{ @@ -3354,23 +3358,23 @@ namespace BMA.EHR.Retirement.Service.Controllers //} //if (reportDone == true) //{ - var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave"; - using (var client = new HttpClient()) - { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - data = resultData, - }); - // // var _result = await _res.Content.ReadAsStringAsync(); - // // if (_res.IsSuccessStatusCode) - // // { - // // data.ForEach(profile => profile.Status = "DONE"); - // // data.ForEach(profile => profile.RetirementResign.Status = "CANCEL"); - // // await _context.SaveChangesAsync(); - // // } - } + // var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData, + // }); + // // // var _result = await _res.Content.ReadAsStringAsync(); + // // // if (_res.IsSuccessStatusCode) + // // // { + // // // data.ForEach(profile => profile.Status = "DONE"); + // // // data.ForEach(profile => profile.RetirementResign.Status = "CANCEL"); + // // // await _context.SaveChangesAsync(); + // // // } + // } //} //else //{ @@ -3392,7 +3396,15 @@ namespace BMA.EHR.Retirement.Service.Controllers // } // } //} - return Success(); + #endregion + + // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + data.ForEach(profile => profile.Status = "DONE"); + data.ForEach(profile => profile.RetirementResign.Status = "CANCEL"); + await _context.SaveChangesAsync(); + + // Return resultData for Node to process directly (Linear Flow) + return Success(resultData); } #region 33-แบบฟอร์มหนังสือขอลาออกจากราชการ diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs index 064220d4..4449099b 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs @@ -2405,10 +2405,6 @@ namespace BMA.EHR.Retirement.Service.Controllers var data = await _context.RetirementResignEmployees .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); - // Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); - await _context.SaveChangesAsync(); - var resultData = (from p in data join r in req.refIds on p.Id.ToString() equals r.refId @@ -2451,24 +2447,32 @@ namespace BMA.EHR.Retirement.Service.Controllers resignId = p.Id, }).ToList(); - var baseAPIOrg = _configuration["API"]; - var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-employee-leave"; - using (var client = new HttpClient()) - { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - data = resultData, - }); - // // var _result = await _res.Content.ReadAsStringAsync(); - // // if (_res.IsSuccessStatusCode) - // // { - // // data.ForEach(profile => profile.Status = "DONE"); - // // await _context.SaveChangesAsync(); - // // } - } - return Success(); + #region Old: Circular Flow + // var baseAPIOrg = _configuration["API"]; + // var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-employee-leave"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData, + // }); + // // // var _result = await _res.Content.ReadAsStringAsync(); + // // // if (_res.IsSuccessStatusCode) + // // // { + // // // data.ForEach(profile => profile.Status = "DONE"); + // // // await _context.SaveChangesAsync(); + // // // } + // } + #endregion + + // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + data.ForEach(profile => profile.Status = "DONE"); + await _context.SaveChangesAsync(); + + // Return resultData for Node to process directly (Linear Flow) + return Success(resultData); } /// @@ -2604,11 +2608,6 @@ namespace BMA.EHR.Retirement.Service.Controllers .Include(x => x.RetirementResignEmployee) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); - // Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); - data.ForEach(profile => profile.RetirementResignEmployee.Status = "CANCEL"); - await _context.SaveChangesAsync(); - string? _null = null; var resultData = (from p in data join r in req.refIds @@ -2650,7 +2649,8 @@ namespace BMA.EHR.Retirement.Service.Controllers resignId = p.RetirementResignEmployee.Id }).ToList(); - var baseAPIOrg = _configuration["API"]; + #region Old: Circular Flow + // var baseAPIOrg = _configuration["API"]; //var reportDone = false; //if (data.Where(profile => profile.Status == "DONE").Any()) //{ @@ -2659,23 +2659,23 @@ namespace BMA.EHR.Retirement.Service.Controllers //if (reportDone == true) //{ - var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-employee-leave"; - using (var client = new HttpClient()) - { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - data = resultData, - }); - // // var _result = await _res.Content.ReadAsStringAsync(); - // // if (_res.IsSuccessStatusCode) - // // { - // // data.ForEach(profile => profile.Status = "DONE"); - // // data.ForEach(profile => profile.RetirementResignEmployee.Status = "CANCEL"); - // // await _context.SaveChangesAsync(); - // // } - } + // var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-employee-leave"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData, + // }); + // // // var _result = await _res.Content.ReadAsStringAsync(); + // // // if (_res.IsSuccessStatusCode) + // // // { + // // // data.ForEach(profile => profile.Status = "DONE"); + // // // data.ForEach(profile => profile.RetirementResignEmployee.Status = "CANCEL"); + // // // await _context.SaveChangesAsync(); + // // // } + // } //} //else //{ @@ -2697,7 +2697,15 @@ namespace BMA.EHR.Retirement.Service.Controllers // } // } //} - return Success(); + #endregion + + // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + data.ForEach(profile => profile.Status = "DONE"); + data.ForEach(profile => profile.RetirementResignEmployee.Status = "CANCEL"); + await _context.SaveChangesAsync(); + + // Return resultData for Node to process directly (Linear Flow) + return Success(resultData); } /// From 2cdae3578e5a4c64b9e7f16e2b07026fa1172bd6 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 19 Jun 2026 10:51:18 +0700 Subject: [PATCH 02/18] issue #2551 --- .../LeaveRequests/LeaveBeginingRepository.cs | 126 +- ...eginning ProfileId Year TypeId.Designer.cs | 1816 +++++++++++++++++ ...ex LeaveBeginning ProfileId Year TypeId.cs | 28 + .../LeaveDb/LeaveDbContextModelSnapshot.cs | 4 + .../Persistence/LeaveDbContext.cs | 13 + .../Controllers/LeaveRequestController.cs | 24 + BMA.EHR.Leave/Program.cs | 25 +- 7 files changed, 1996 insertions(+), 40 deletions(-) create mode 100644 BMA.EHR.Infrastructure/Migrations/LeaveDb/20260619032119_Add Unique Index LeaveBeginning ProfileId Year TypeId.Designer.cs create mode 100644 BMA.EHR.Infrastructure/Migrations/LeaveDb/20260619032119_Add Unique Index LeaveBeginning ProfileId Year TypeId.cs diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs index 35b18090..7cf4dee7 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs @@ -9,6 +9,7 @@ using BMA.EHR.Domain.Shared; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; +using System.Collections.Concurrent; namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests { @@ -23,6 +24,12 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests private readonly IConfiguration _configuration; private readonly EmailSenderService _emailSenderService; + /// + /// Keyed locks to serialize get-or-create for LeaveBeginning rows by (ProfileId, LeaveYear, LeaveTypeId). + /// Prevents duplicate inserts when concurrent requests (e.g. UI calling /user/check twice) hit the same key. + /// + private static readonly ConcurrentDictionary _getOrAddLocks = new(); + #endregion #region " Constructor and Destuctor " @@ -121,6 +128,27 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests await _dbContext.SaveChangesAsync(); } + public async Task ProcessEarlyLeaveRequest(int year) + { + // Get Early Leave Request + var leaveReq = await _dbContext.Set() + .Include(x => x.Type) + .Where(x => x.LeaveStatus == "APPROVE") + .Where(x => x.LeaveStartDate.Year == year || x.LeaveEndDate.Year == year) + .ToListAsync(); + + foreach (var leave in leaveReq) + { + await GetByYearAndTypeIdForUserAsync(year, leave.Type.Id, leave.KeycloakUserId); + } + } + + public async Task ProcessEarlyLeaveRequestSchedule() + { + int year = DateTime.Now.Year; + await ProcessEarlyLeaveRequest(year); + } + public async Task GetByYearAndTypeIdForUserAsync(int year, Guid typeId, Guid userId) { // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); @@ -134,17 +162,13 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests var leaveType = await _dbContext.Set().FirstOrDefaultAsync(x => x.Id == typeId); - var data = await _dbContext.Set() - .Include(x => x.LeaveType) - .FirstOrDefaultAsync(x => x.LeaveYear == year && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); - - if (data == null) + LeaveBeginning Factory() { var limit = 0.0; - var prev = await _dbContext.Set() + var prev = _dbContext.Set() .Include(x => x.LeaveType) - .FirstOrDefaultAsync(x => x.LeaveYear == year - 1 && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); + .FirstOrDefault(x => x.LeaveYear == year - 1 && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); var prevRemain = 0.0; if (prev != null) @@ -170,7 +194,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests limit = 0.0; } - data = new LeaveBeginning + return new LeaveBeginning { LeaveYear = year, LeaveTypeId = typeId, @@ -186,12 +210,9 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests Child3DnaId = pf.Child3DnaId, Child4DnaId = pf.Child4DnaId }; - - _dbContext.Set().Add(data); - await _dbContext.SaveChangesAsync(); } - return data; + return await GetOrAddForUserAsync(year, typeId, pf.Id, Factory); } public async Task GetByYearAndTypeIdForUser(int year, Guid typeId, GetProfileByKeycloakIdDto? pf) @@ -200,17 +221,13 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests var leaveType = await _dbContext.Set().FirstOrDefaultAsync(x => x.Id == typeId); - var data = await _dbContext.Set() - .Include(x => x.LeaveType) - .FirstOrDefaultAsync(x => x.LeaveYear == year && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); - - if (data == null) + LeaveBeginning Factory() { var limit = 0.0; - var prev = await _dbContext.Set() + var prev = _dbContext.Set() .Include(x => x.LeaveType) - .FirstOrDefaultAsync(x => x.LeaveYear == year - 1 && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); + .FirstOrDefault(x => x.LeaveYear == year - 1 && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); var prevRemain = 0.0; if (prev != null) @@ -236,7 +253,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests limit = 0.0; } - data = new LeaveBeginning + return new LeaveBeginning { LeaveYear = year, LeaveTypeId = typeId, @@ -252,12 +269,9 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests Child3DnaId = pf.Child3DnaId, Child4DnaId = pf.Child4DnaId }; - - _dbContext.Set().Add(data); - await _dbContext.SaveChangesAsync(); } - return data; + return await GetOrAddForUserAsync(year, typeId, pf.Id, Factory); } public async Task GetByYearAndTypeIdForUser2Async(int year, Guid typeId, Guid userId) @@ -273,17 +287,13 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests var leaveType = await _dbContext.Set().FirstOrDefaultAsync(x => x.Id == typeId); - var data = await _dbContext.Set() - .Include(x => x.LeaveType) - .FirstOrDefaultAsync(x => x.LeaveYear == year && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); - - if (data == null) + LeaveBeginning Factory() { var limit = 0.0; - var prev = await _dbContext.Set() + var prev = _dbContext.Set() .Include(x => x.LeaveType) - .FirstOrDefaultAsync(x => x.LeaveYear == year - 1 && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); + .FirstOrDefault(x => x.LeaveYear == year - 1 && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); var prevRemain = 0.0; if (prev != null) @@ -309,7 +319,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests limit = 0.0; } - data = new LeaveBeginning + return new LeaveBeginning { LeaveYear = year, LeaveTypeId = typeId, @@ -325,17 +335,59 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests Child3DnaId = pf.Child3DnaId, Child4DnaId = pf.Child4DnaId }; - - _dbContext.Set().Add(data); - await _dbContext.SaveChangesAsync(); } - return data; + return await GetOrAddForUserAsync(year, typeId, pf.Id, Factory); + } + + /// + /// Get-or-create a LeaveBeginning row for (ProfileId, LeaveYear, LeaveTypeId) with concurrency protection. + /// Uses a keyed SemaphoreSlim to serialize within-process requests, and re-queries after acquiring the lock. + /// If a cross-process insert wins (unique index violation), the duplicate key exception is caught and the row + /// created by the winner is returned. + /// + private async Task GetOrAddForUserAsync(int year, Guid typeId, Guid profileId, Func factory) + { + var key = $"{profileId}_{year}_{typeId}"; + var semaphore = _getOrAddLocks.GetOrAdd(key, _ => new SemaphoreSlim(1, 1)); + await semaphore.WaitAsync(); + try + { + // Re-query inside the lock — another thread may have created it while we waited. + var existing = await _dbContext.Set() + .Include(x => x.LeaveType) + .FirstOrDefaultAsync(x => x.LeaveYear == year && x.LeaveTypeId == typeId && x.ProfileId == profileId); + if (existing != null) + { + return existing; + } + + var entity = factory(); + _dbContext.Set().Add(entity); + try + { + await _dbContext.SaveChangesAsync(); + return entity; + } + catch (DbUpdateException) + { + // Cross-process/cross-server race hit the unique index (IX_LeaveBeginnings_ProfileId_LeaveYear_LeaveTypeId). + // Detach the failed insert and return the row created by the winner. + _dbContext.Detach(entity); + var winner = await _dbContext.Set() + .Include(x => x.LeaveType) + .FirstOrDefaultAsync(x => x.LeaveYear == year && x.LeaveTypeId == typeId && x.ProfileId == profileId); + return winner; + } + } + finally + { + semaphore.Release(); + } } public async Task> GetAllByYearAndTypeAsync(int year, Guid typeId, List userIdList) { - var updateList = new List(); var result = new List(); diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260619032119_Add Unique Index LeaveBeginning ProfileId Year TypeId.Designer.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260619032119_Add Unique Index LeaveBeginning ProfileId Year TypeId.Designer.cs new file mode 100644 index 00000000..d2d336f9 --- /dev/null +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260619032119_Add Unique Index LeaveBeginning ProfileId Year TypeId.Designer.cs @@ -0,0 +1,1816 @@ +// +using System; +using BMA.EHR.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BMA.EHR.Infrastructure.Migrations.LeaveDb +{ + [DbContext(typeof(LeaveDbContext))] + [Migration("20260619032119_Add Unique Index LeaveBeginning ProfileId Year TypeId")] + partial class AddUniqueIndexLeaveBeginningProfileIdYearTypeId + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Documents.Document", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedDate") + .HasColumnType("datetime(6)"); + + b.Property("Detail") + .IsRequired() + .HasColumnType("text"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("FileSize") + .HasColumnType("int"); + + b.Property("FileType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("ObjectRefId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Document"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Code") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รหัสประเภทการลา"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Limit") + .HasColumnType("int") + .HasComment("จำนวนวันลาสูงสุดประจำปี"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อประเภทการลา"); + + b.HasKey("Id"); + + b.ToTable("LeaveTypes"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveBeginning", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("BeginningLeaveCount") + .HasColumnType("int") + .HasComment("จำนวนครั้งที่ลายกมา"); + + b.Property("BeginningLeaveDays") + .HasColumnType("double") + .HasComment("จำนวนวันลายกมา"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveCount") + .HasColumnType("int") + .HasComment("จำนวนครั้งที่ลาสะสม"); + + b.Property("LeaveDays") + .HasColumnType("double") + .HasComment("จำนวนวันลาทั้งหมด"); + + b.Property("LeaveDaysUsed") + .HasColumnType("double") + .HasComment("จำนวนวันลาที่ใช้ไป"); + + b.Property("LeaveTypeId") + .HasColumnType("char(36)") + .HasComment("รหัสประเภทการลา"); + + b.Property("LeaveYear") + .HasColumnType("int") + .HasComment("ปีงบประมาณ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)") + .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("LeaveTypeId"); + + b.HasIndex("ProfileId", "LeaveYear", "LeaveTypeId") + .IsUnique() + .HasDatabaseName("IX_LeaveBeginnings_ProfileId_LeaveYear_LeaveTypeId"); + + b.ToTable("LeaveBeginnings"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("DocumentId") + .HasColumnType("char(36)"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveRequestId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("DocumentId"); + + b.HasIndex("LeaveRequestId"); + + b.ToTable("LeaveDocuments"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("AbsentDayAt") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AbsentDayGetIn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AbsentDayLocation") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AbsentDayRegistorDate") + .HasColumnType("datetime(6)"); + + b.Property("AbsentDaySummon") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Amount") + .HasColumnType("double"); + + b.Property("ApproveStep") + .HasColumnType("longtext") + .HasComment("step การอนุมัติ st1 = จทน.อนุมัตื,st2 = ผู้บังคับบัญชา อนุมัติ "); + + b.Property("BirthDate") + .HasColumnType("datetime(6)"); + + b.Property("CancelLeaveWrote") + .HasColumnType("longtext") + .HasComment("เขียนที่ (ขอยกเลิก)"); + + b.Property("Child1") + .HasColumnType("longtext"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child1Id") + .HasColumnType("char(36)"); + + b.Property("Child2") + .HasColumnType("longtext"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2Id") + .HasColumnType("char(36)"); + + b.Property("Child3") + .HasColumnType("longtext"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3Id") + .HasColumnType("char(36)"); + + b.Property("Child4") + .HasColumnType("longtext"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4Id") + .HasColumnType("char(36)"); + + b.Property("CitizenId") + .HasColumnType("longtext"); + + b.Property("CommanderPosition") + .HasColumnType("longtext"); + + b.Property("CoupleDayCountryHistory") + .HasColumnType("longtext"); + + b.Property("CoupleDayEndDateHistory") + .HasColumnType("datetime(6)"); + + b.Property("CoupleDayLevel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayLevelCountry") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayPosition") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayStartDateHistory") + .HasColumnType("datetime(6)"); + + b.Property("CoupleDaySumTotalHistory") + .HasColumnType("longtext"); + + b.Property("CoupleDayTotalHistory") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("DateAppoint") + .HasColumnType("datetime(6)"); + + b.Property("DateSendLeave") + .HasColumnType("datetime(6)"); + + b.Property("Dear") + .HasColumnType("longtext") + .HasComment("เรียนใคร"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("Gender") + .HasColumnType("longtext"); + + b.Property("HajjDayStatus") + .HasColumnType("tinyint(1)"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveAddress") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานที่ติดต่อขณะลา"); + + b.Property("LeaveBirthDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveCancelComment") + .HasColumnType("longtext") + .HasComment("เหตุผลในการขอยกเลิก"); + + b.Property("LeaveCancelDocumentId") + .HasColumnType("char(36)"); + + b.Property("LeaveCancelStatus") + .HasColumnType("longtext") + .HasComment("สถานะของคำขอยกเลิก"); + + b.Property("LeaveComment") + .HasColumnType("longtext") + .HasComment("ความเห็นของผู้บังคับบัญชา"); + + b.Property("LeaveDetail") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รายละเอียดการลา"); + + b.Property("LeaveDirectorComment") + .HasColumnType("longtext") + .HasComment("ความเห็นของผู้อำนวยการสำนัก"); + + b.Property("LeaveDraftDocumentId") + .HasColumnType("char(36)"); + + b.Property("LeaveEndDate") + .HasColumnType("datetime(6)") + .HasComment("วัน เดือน ปีสิ้นสุดลา"); + + b.Property("LeaveGovernmentDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveLast") + .HasColumnType("datetime(6)"); + + b.Property("LeaveNumber") + .IsRequired() + .HasColumnType("longtext") + .HasComment("หมายเลขที่ติดต่อขณะลา"); + + b.Property("LeaveRange") + .HasColumnType("longtext") + .HasComment("ช่วงของการลาของวันเริ่ม เช่น ลาทั้งวัน ครึ่งวันเช้า ครึ่งวันบ่าย"); + + b.Property("LeaveRangeEnd") + .HasColumnType("longtext") + .HasComment("ช่วงของการลาของวันสิ้นสุด เช่น ลาทั้งวัน ครึ่งวันเช้า ครึ่งวันบ่าย"); + + b.Property("LeaveSalary") + .HasColumnType("int"); + + b.Property("LeaveSalaryText") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LeaveStartDate") + .HasColumnType("datetime(6)") + .HasComment("วัน เดือน ปีเริ่มต้นลา"); + + b.Property("LeaveStatus") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะของคำร้อง"); + + b.Property("LeaveSubTypeName") + .HasColumnType("longtext"); + + b.Property("LeaveTotal") + .HasColumnType("double"); + + b.Property("LeaveTypeCode") + .HasColumnType("longtext") + .HasComment("code ของประเภทการลา"); + + b.Property("LeaveWrote") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เขียนที่"); + + b.Property("OrdainDayBuddhistLentAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayBuddhistLentName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayLocationAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayLocationName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayLocationNumber") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayOrdination") + .HasColumnType("datetime(6)"); + + b.Property("OrdainDayStatus") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationName") + .HasColumnType("longtext") + .HasComment("สังกัดผู้ยื่นขอ"); + + b.Property("PositionLevelName") + .HasColumnType("longtext") + .HasComment("ระดับผู้ยื่นขอ"); + + b.Property("PositionName") + .HasColumnType("longtext") + .HasComment("ตำแหน่งผู้ยื่นขอ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("ProfileType") + .HasColumnType("longtext"); + + b.Property("RestDayCurrentTotal") + .HasColumnType("double"); + + b.Property("RestDayOldTotal") + .HasColumnType("double"); + + b.Property("Root") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("RootId") + .HasColumnType("char(36)"); + + b.Property("StudyDayCountry") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayDegreeLevel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayScholarship") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDaySubject") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayTrainingName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayTrainingSubject") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayUniversityName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TypeId") + .HasColumnType("char(36)"); + + b.Property("WifeDayDateBorn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("WifeDayName") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("LeaveCancelDocumentId"); + + b.HasIndex("LeaveDraftDocumentId"); + + b.HasIndex("TypeId"); + + b.ToTable("LeaveRequests"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequestApprover", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("ApproveStatus") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApproveType") + .HasColumnType("longtext"); + + b.Property("Comment") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsAct") + .HasColumnType("tinyint(1)"); + + b.Property("KeyId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("KeycloakId") + .HasColumnType("char(36)"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveRequestId") + .HasColumnType("char(36)"); + + b.Property("OrganizationName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สังกัด"); + + b.Property("PosExecutiveName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ตำแหน่งทางการบริหาร"); + + b.Property("PositionLevelName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ประเภทระดับตำแหน่ง"); + + b.Property("PositionName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PositionSign") + .HasColumnType("longtext") + .HasComment("ตำแหน่งใต้ลายเช็นต์"); + + b.Property("Prefix") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("Seq") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("LeaveRequestId"); + + b.ToTable("LeaveRequestApprovers"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.AdditionalCheckRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CheckDate") + .HasColumnType("datetime(6)") + .HasComment("*วันที่ลงเวลา"); + + b.Property("CheckInEdit") + .HasColumnType("tinyint(1)") + .HasComment("*ขอลงเวลาช่วงเช้า"); + + b.Property("CheckOutEdit") + .HasColumnType("tinyint(1)") + .HasComment("*ขอลงเวลาช่วงบ่าย"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Comment") + .HasColumnType("longtext") + .HasComment("หมายเหตุในการการอนุมัติ/ไม่อนุมัติ"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext") + .HasComment("*หมายเหตุขอลงเวลาพิเศษ"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak ที่ร้องขอ"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Latitude") + .HasColumnType("double"); + + b.Property("Longitude") + .HasColumnType("double"); + + b.Property("POI") + .HasColumnType("longtext"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะการอนุมัติ"); + + b.HasKey("Id"); + + b.ToTable("AdditionalCheckRequests"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.CheckInJobStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("AdditionalData") + .HasColumnType("longtext") + .HasComment("ข้อมูลเพิ่มเติม (JSON)"); + + b.Property("CheckInId") + .HasColumnType("char(36)") + .HasComment("CheckInId สำหรับ Check-Out"); + + b.Property("CheckType") + .HasColumnType("longtext") + .HasComment("ประเภทการลงเวลา: CHECK_IN, CHECK_OUT"); + + b.Property("CompletedDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่เสร็จสิ้นการประมวลผล"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่สร้างงาน"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("ErrorMessage") + .HasColumnType("longtext") + .HasComment("ข้อความแสดงข้อผิดพลาด"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProcessingDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่เริ่มประมวลผล"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED"); + + b.Property("TaskId") + .HasColumnType("char(36)") + .HasComment("Task ID สำหรับติดตามสถานะงาน"); + + b.HasKey("Id"); + + b.ToTable("CheckInJobStatuses"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext") + .HasComment("คำอธิบาย"); + + b.Property("EndTimeAfternoon") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาออกงานช่วงบ่าย"); + + b.Property("EndTimeMorning") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาออกงานช่วงเช้า"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)") + .HasComment("สถานะการเปิดใช้งาน (เปิด/ปิด)"); + + b.Property("IsDefault") + .HasColumnType("tinyint(1)") + .HasComment("สถานะว่ารอบใดเป็นค่า Default ของข้าราชการ (สำหรับทุกคนที่ยังไม่ได้ทำการเลือกรอบ)"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("StartTimeAfternoon") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาเข้างานช่วงบ่าย"); + + b.Property("StartTimeMorning") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาเข้างานช่วงเช้า"); + + b.HasKey("Id"); + + b.ToTable("DutyTimes"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.LeaveProcessJobStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CompletedDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่เสร็จสิ้นการประมวลผล"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่สร้างงาน"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("EndDate") + .HasColumnType("datetime(6)") + .HasComment("วันสิ้นสุด"); + + b.Property("ErrorMessage") + .HasColumnType("longtext") + .HasComment("ข้อความแสดงข้อผิดพลาด"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProcessingDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่เริ่มประมวลผล"); + + b.Property("RootDnaId") + .HasColumnType("char(36)") + .HasComment("รหัส Root DNA Id"); + + b.Property("StartDate") + .HasColumnType("datetime(6)") + .HasComment("วันเริ่มต้น"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED"); + + b.HasKey("Id"); + + b.ToTable("LeaveProcessJobStatuses"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.ProcessUserTimeStamp", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CheckIn") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา เข้างาน"); + + b.Property("CheckInImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-In"); + + b.Property("CheckInLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-In"); + + b.Property("CheckInLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-In"); + + b.Property("CheckInLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-In"); + + b.Property("CheckInPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-In"); + + b.Property("CheckInRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-In"); + + b.Property("CheckInStatus") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะ Check-In"); + + b.Property("CheckOut") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา ออกงาน"); + + b.Property("CheckOutImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-Out"); + + b.Property("CheckOutLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-Out"); + + b.Property("CheckOutLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-Out"); + + b.Property("CheckOutLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-Out"); + + b.Property("CheckOutPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-Out"); + + b.Property("CheckOutRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-Out"); + + b.Property("CheckOutStatus") + .HasColumnType("longtext") + .HasComment("สถานะ Check-Out"); + + b.Property("Child1") + .HasColumnType("longtext"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child1Id") + .HasColumnType("char(36)"); + + b.Property("Child2") + .HasColumnType("longtext"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2Id") + .HasColumnType("char(36)"); + + b.Property("Child3") + .HasColumnType("longtext"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3Id") + .HasColumnType("char(36)"); + + b.Property("Child4") + .HasColumnType("longtext"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4Id") + .HasColumnType("char(36)"); + + b.Property("CitizenId") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("EditReason") + .HasColumnType("longtext") + .HasComment("เหตุผลการอนุมัติ/ไม่อนุมัติขอลงเวลาพิเศษ"); + + b.Property("EditStatus") + .HasColumnType("longtext") + .HasComment("สถานะการของลงเวลาพิเศษ"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("Gender") + .HasColumnType("longtext"); + + b.Property("IsLocationCheckIn") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-In"); + + b.Property("IsLocationCheckOut") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-Out"); + + b.Property("IsProcess") + .HasColumnType("tinyint(1)") + .HasComment("นำไปประมวลผลแล้วหรือยัง"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("ProfileType") + .HasColumnType("longtext"); + + b.Property("Root") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("RootId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("ProcessUserTimeStamps"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserCalendar", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Calendar") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ปฏิทินการทำงานของ ขรก ปกติ หรือ 6 วันต่อสัปดาห์"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProfileId") + .HasColumnType("char(36)") + .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); + + b.HasKey("Id"); + + b.ToTable("UserCalendars"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserDutyTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("DutyTimeId") + .HasColumnType("char(36)") + .HasComment("รหัสรอบการลงเวลา"); + + b.Property("EffectiveDate") + .HasColumnType("datetime(6)") + .HasComment("วันที่มีผล"); + + b.Property("IsProcess") + .HasColumnType("tinyint(1)") + .HasComment("ทำการประมวลผลแล้วหรือยัง"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProfileId") + .HasColumnType("char(36)") + .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); + + b.Property("Remark") + .HasColumnType("longtext") + .HasComment("หมายเหตุ"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("DutyTimeId"); + + b.ToTable("UserDutyTimes"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserTimeStamp", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CheckIn") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา เข้างาน"); + + b.Property("CheckInImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-In"); + + b.Property("CheckInLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-In"); + + b.Property("CheckInLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-In"); + + b.Property("CheckInLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-In"); + + b.Property("CheckInPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-In"); + + b.Property("CheckInRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-In"); + + b.Property("CheckOut") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา ออกงาน"); + + b.Property("CheckOutImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-Out"); + + b.Property("CheckOutLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-Out"); + + b.Property("CheckOutLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-Out"); + + b.Property("CheckOutLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-Out"); + + b.Property("CheckOutPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-Out"); + + b.Property("CheckOutRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-Out"); + + b.Property("Child1") + .HasColumnType("longtext"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child1Id") + .HasColumnType("char(36)"); + + b.Property("Child2") + .HasColumnType("longtext"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2Id") + .HasColumnType("char(36)"); + + b.Property("Child3") + .HasColumnType("longtext"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3Id") + .HasColumnType("char(36)"); + + b.Property("Child4") + .HasColumnType("longtext"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4Id") + .HasColumnType("char(36)"); + + b.Property("CitizenId") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("Gender") + .HasColumnType("longtext"); + + b.Property("IsLocationCheckIn") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-In"); + + b.Property("IsLocationCheckOut") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-Out"); + + b.Property("IsProcess") + .HasColumnType("tinyint(1)") + .HasComment("นำไปประมวลผลแล้วหรือยัง"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("ProfileType") + .HasColumnType("longtext"); + + b.Property("Root") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("RootId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("UserTimeStamps"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveBeginning", b => + { + b.HasOne("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveDocument", b => + { + b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "Document") + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", "LeaveRequest") + .WithMany("LeaveDocument") + .HasForeignKey("LeaveRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Document"); + + b.Navigation("LeaveRequest"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => + { + b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "LeaveCancelDocument") + .WithMany() + .HasForeignKey("LeaveCancelDocumentId"); + + b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "LeaveDraftDocument") + .WithMany() + .HasForeignKey("LeaveDraftDocumentId"); + + b.HasOne("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", "Type") + .WithMany() + .HasForeignKey("TypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LeaveCancelDocument"); + + b.Navigation("LeaveDraftDocument"); + + b.Navigation("Type"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequestApprover", b => + { + b.HasOne("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", "LeaveRequest") + .WithMany("Approvers") + .HasForeignKey("LeaveRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LeaveRequest"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserDutyTime", b => + { + b.HasOne("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", "DutyTime") + .WithMany() + .HasForeignKey("DutyTimeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DutyTime"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => + { + b.Navigation("Approvers"); + + b.Navigation("LeaveDocument"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260619032119_Add Unique Index LeaveBeginning ProfileId Year TypeId.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260619032119_Add Unique Index LeaveBeginning ProfileId Year TypeId.cs new file mode 100644 index 00000000..deabdd48 --- /dev/null +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260619032119_Add Unique Index LeaveBeginning ProfileId Year TypeId.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BMA.EHR.Infrastructure.Migrations.LeaveDb +{ + /// + public partial class AddUniqueIndexLeaveBeginningProfileIdYearTypeId : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateIndex( + name: "IX_LeaveBeginnings_ProfileId_LeaveYear_LeaveTypeId", + table: "LeaveBeginnings", + columns: new[] { "ProfileId", "LeaveYear", "LeaveTypeId" }, + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_LeaveBeginnings_ProfileId_LeaveYear_LeaveTypeId", + table: "LeaveBeginnings"); + } + } +} diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs index 99694a78..053f6d99 100644 --- a/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs @@ -226,6 +226,10 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.HasIndex("LeaveTypeId"); + b.HasIndex("ProfileId", "LeaveYear", "LeaveTypeId") + .IsUnique() + .HasDatabaseName("IX_LeaveBeginnings_ProfileId_LeaveYear_LeaveTypeId"); + b.ToTable("LeaveBeginnings"); }); diff --git a/BMA.EHR.Infrastructure/Persistence/LeaveDbContext.cs b/BMA.EHR.Infrastructure/Persistence/LeaveDbContext.cs index 10064197..115a2ffc 100644 --- a/BMA.EHR.Infrastructure/Persistence/LeaveDbContext.cs +++ b/BMA.EHR.Infrastructure/Persistence/LeaveDbContext.cs @@ -61,5 +61,18 @@ namespace BMA.EHR.Infrastructure.Persistence { base.Entry(entity).State = EntityState.Detached; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + // Composite unique index on the natural key for LeaveBeginning. + // Prevents duplicate rows when concurrent requests (e.g. UI calling /user/check twice) + // race through the get-or-create flow in LeaveBeginningRepository. + modelBuilder.Entity() + .HasIndex(b => new { b.ProfileId, b.LeaveYear, b.LeaveTypeId }) + .HasDatabaseName("IX_LeaveBeginnings_ProfileId_LeaveYear_LeaveTypeId") + .IsUnique(); + } } } diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 5fbca2cc..85c19b79 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -1016,6 +1016,30 @@ namespace BMA.EHR.Leave.Service.Controllers }); } + /// + /// ทดสอบประมวลผล beginning + /// + /// + /// + [HttpGet("process-beginning/{year:int}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [AllowAnonymous] + public async Task> ProcessBeginningByYearAsync([FromRoute] int year) + { + try + { + await _leaveBeginningRepository.ProcessEarlyLeaveRequest(year); + return Success(); + } + catch (Exception e) + { + return Error(e); + } + } + + /// /// LV2_003 - เช็คการยืนขอลา (USER) /// diff --git a/BMA.EHR.Leave/Program.cs b/BMA.EHR.Leave/Program.cs index 3dd83203..5a396430 100644 --- a/BMA.EHR.Leave/Program.cs +++ b/BMA.EHR.Leave/Program.cs @@ -18,6 +18,7 @@ using System.Text; using Hangfire; using Hangfire.MySql; using System.Transactions; +using BMA.EHR.Application.Repositories.Leaves.LeaveRequests; using BMA.EHR.Leave.Service.Filters; using Hangfire.Common; using BMA.EHR.Application.Repositories.Leaves.TimeAttendants; @@ -194,10 +195,28 @@ app.UseHangfireDashboard("/hangfire", new DashboardOptions() var manager = new RecurringJobManager(); if (manager != null) { - manager.AddOrUpdate("ปรับปรุงรอบการลงเวลาทำงาน", Job.FromExpression(x => x.UpdateUserDutyTime()), "0 1 * * *", bangkokTimeZone); + manager.AddOrUpdate("ปรับปรุงรอบการลงเวลาทำงาน", Job.FromExpression(x => x.UpdateUserDutyTime()), "0 1 * * *", + new RecurringJobOptions + { + TimeZone = bangkokTimeZone, + QueueName = "leave" + }); // ทำความสะอาดข้อมูล CheckIn Job Status ที่เก่ากว่า 30 วัน - รันทุกวันเวลา 02:00 น. - manager.AddOrUpdate("ทำความสะอาดข้อมูล CheckIn Job Status", Job.FromExpression(x => x.CleanupOldJobsAsync(30)), "0 2 * * *", bangkokTimeZone); - + manager.AddOrUpdate("ทำความสะอาดข้อมูล CheckIn Job Status", Job.FromExpression(x => x.CleanupOldJobsAsync(30)), "0 2 * * *", + new RecurringJobOptions + { + TimeZone = bangkokTimeZone, + QueueName = "leave" + }); + + manager.AddOrUpdate("Proceess Beginning สำหรับการลาล่วงหน้า", Job.FromExpression(x => x.ProcessEarlyLeaveRequestSchedule()), "0 1 1 10 *", + new RecurringJobOptions + { + TimeZone = bangkokTimeZone, + QueueName = "leave" + }); + + // ตรวจสอบและ mark งาน CheckIn ที่ค้างเกิน 30 นาทีเป็น FAILED - รันทุก 15 นาที // manager.AddOrUpdate("ตรวจสอบงาน CheckIn ที่ค้างเกินเวลา", Job.FromExpression(x => x.MarkStaleJobsAsFailedAsync(30)), "*/15 * * * *", // new RecurringJobOptions From 6843e3ff3f4e8aa2632f35170aad684c21d4acf5 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 19 Jun 2026 11:11:46 +0700 Subject: [PATCH 03/18] fix #2551 Error --- ...eginning ProfileId Year TypeId.Designer.cs | 1816 ----------------- ...ex LeaveBeginning ProfileId Year TypeId.cs | 28 - .../LeaveDb/LeaveDbContextModelSnapshot.cs | 32 +- .../Persistence/LeaveDbContext.cs | 13 - 4 files changed, 14 insertions(+), 1875 deletions(-) delete mode 100644 BMA.EHR.Infrastructure/Migrations/LeaveDb/20260619032119_Add Unique Index LeaveBeginning ProfileId Year TypeId.Designer.cs delete mode 100644 BMA.EHR.Infrastructure/Migrations/LeaveDb/20260619032119_Add Unique Index LeaveBeginning ProfileId Year TypeId.cs diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260619032119_Add Unique Index LeaveBeginning ProfileId Year TypeId.Designer.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260619032119_Add Unique Index LeaveBeginning ProfileId Year TypeId.Designer.cs deleted file mode 100644 index d2d336f9..00000000 --- a/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260619032119_Add Unique Index LeaveBeginning ProfileId Year TypeId.Designer.cs +++ /dev/null @@ -1,1816 +0,0 @@ -// -using System; -using BMA.EHR.Infrastructure.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace BMA.EHR.Infrastructure.Migrations.LeaveDb -{ - [DbContext(typeof(LeaveDbContext))] - [Migration("20260619032119_Add Unique Index LeaveBeginning ProfileId Year TypeId")] - partial class AddUniqueIndexLeaveBeginningProfileIdYearTypeId - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.9") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Documents.Document", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedDate") - .HasColumnType("datetime(6)"); - - b.Property("Detail") - .IsRequired() - .HasColumnType("text"); - - b.Property("FileName") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("varchar(255)"); - - b.Property("FileSize") - .HasColumnType("int"); - - b.Property("FileType") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.Property("ObjectRefId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.ToTable("Document"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)") - .HasColumnOrder(0) - .HasComment("PrimaryKey") - .HasAnnotation("Relational:JsonPropertyName", "id"); - - b.Property("Code") - .IsRequired() - .HasColumnType("longtext") - .HasComment("รหัสประเภทการลา"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(100) - .HasComment("สร้างข้อมูลเมื่อ"); - - b.Property("CreatedFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(104) - .HasComment("ชื่อ User ที่สร้างข้อมูล"); - - b.Property("CreatedUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(101) - .HasComment("User Id ที่สร้างข้อมูล"); - - b.Property("LastUpdateFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(105) - .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdateUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(103) - .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(102) - .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); - - b.Property("Limit") - .HasColumnType("int") - .HasComment("จำนวนวันลาสูงสุดประจำปี"); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext") - .HasComment("ชื่อประเภทการลา"); - - b.HasKey("Id"); - - b.ToTable("LeaveTypes"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveBeginning", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)") - .HasColumnOrder(0) - .HasComment("PrimaryKey") - .HasAnnotation("Relational:JsonPropertyName", "id"); - - b.Property("BeginningLeaveCount") - .HasColumnType("int") - .HasComment("จำนวนครั้งที่ลายกมา"); - - b.Property("BeginningLeaveDays") - .HasColumnType("double") - .HasComment("จำนวนวันลายกมา"); - - b.Property("Child1DnaId") - .HasColumnType("char(36)"); - - b.Property("Child2DnaId") - .HasColumnType("char(36)"); - - b.Property("Child3DnaId") - .HasColumnType("char(36)"); - - b.Property("Child4DnaId") - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(100) - .HasComment("สร้างข้อมูลเมื่อ"); - - b.Property("CreatedFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(104) - .HasComment("ชื่อ User ที่สร้างข้อมูล"); - - b.Property("CreatedUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(101) - .HasComment("User Id ที่สร้างข้อมูล"); - - b.Property("FirstName") - .HasColumnType("longtext"); - - b.Property("LastName") - .HasColumnType("longtext"); - - b.Property("LastUpdateFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(105) - .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdateUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(103) - .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(102) - .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); - - b.Property("LeaveCount") - .HasColumnType("int") - .HasComment("จำนวนครั้งที่ลาสะสม"); - - b.Property("LeaveDays") - .HasColumnType("double") - .HasComment("จำนวนวันลาทั้งหมด"); - - b.Property("LeaveDaysUsed") - .HasColumnType("double") - .HasComment("จำนวนวันลาที่ใช้ไป"); - - b.Property("LeaveTypeId") - .HasColumnType("char(36)") - .HasComment("รหัสประเภทการลา"); - - b.Property("LeaveYear") - .HasColumnType("int") - .HasComment("ปีงบประมาณ"); - - b.Property("Prefix") - .HasColumnType("longtext"); - - b.Property("ProfileId") - .HasColumnType("char(36)") - .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); - - b.Property("RootDnaId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("LeaveTypeId"); - - b.HasIndex("ProfileId", "LeaveYear", "LeaveTypeId") - .IsUnique() - .HasDatabaseName("IX_LeaveBeginnings_ProfileId_LeaveYear_LeaveTypeId"); - - b.ToTable("LeaveBeginnings"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveDocument", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)") - .HasColumnOrder(0) - .HasComment("PrimaryKey") - .HasAnnotation("Relational:JsonPropertyName", "id"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(100) - .HasComment("สร้างข้อมูลเมื่อ"); - - b.Property("CreatedFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(104) - .HasComment("ชื่อ User ที่สร้างข้อมูล"); - - b.Property("CreatedUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(101) - .HasComment("User Id ที่สร้างข้อมูล"); - - b.Property("DocumentId") - .HasColumnType("char(36)"); - - b.Property("LastUpdateFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(105) - .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdateUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(103) - .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(102) - .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); - - b.Property("LeaveRequestId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("DocumentId"); - - b.HasIndex("LeaveRequestId"); - - b.ToTable("LeaveDocuments"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)") - .HasColumnOrder(0) - .HasComment("PrimaryKey") - .HasAnnotation("Relational:JsonPropertyName", "id"); - - b.Property("AbsentDayAt") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("AbsentDayGetIn") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("AbsentDayLocation") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("AbsentDayRegistorDate") - .HasColumnType("datetime(6)"); - - b.Property("AbsentDaySummon") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Amount") - .HasColumnType("double"); - - b.Property("ApproveStep") - .HasColumnType("longtext") - .HasComment("step การอนุมัติ st1 = จทน.อนุมัตื,st2 = ผู้บังคับบัญชา อนุมัติ "); - - b.Property("BirthDate") - .HasColumnType("datetime(6)"); - - b.Property("CancelLeaveWrote") - .HasColumnType("longtext") - .HasComment("เขียนที่ (ขอยกเลิก)"); - - b.Property("Child1") - .HasColumnType("longtext"); - - b.Property("Child1DnaId") - .HasColumnType("char(36)"); - - b.Property("Child1Id") - .HasColumnType("char(36)"); - - b.Property("Child2") - .HasColumnType("longtext"); - - b.Property("Child2DnaId") - .HasColumnType("char(36)"); - - b.Property("Child2Id") - .HasColumnType("char(36)"); - - b.Property("Child3") - .HasColumnType("longtext"); - - b.Property("Child3DnaId") - .HasColumnType("char(36)"); - - b.Property("Child3Id") - .HasColumnType("char(36)"); - - b.Property("Child4") - .HasColumnType("longtext"); - - b.Property("Child4DnaId") - .HasColumnType("char(36)"); - - b.Property("Child4Id") - .HasColumnType("char(36)"); - - b.Property("CitizenId") - .HasColumnType("longtext"); - - b.Property("CommanderPosition") - .HasColumnType("longtext"); - - b.Property("CoupleDayCountryHistory") - .HasColumnType("longtext"); - - b.Property("CoupleDayEndDateHistory") - .HasColumnType("datetime(6)"); - - b.Property("CoupleDayLevel") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("CoupleDayLevelCountry") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("CoupleDayName") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("CoupleDayPosition") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("CoupleDayStartDateHistory") - .HasColumnType("datetime(6)"); - - b.Property("CoupleDaySumTotalHistory") - .HasColumnType("longtext"); - - b.Property("CoupleDayTotalHistory") - .HasColumnType("longtext"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(100) - .HasComment("สร้างข้อมูลเมื่อ"); - - b.Property("CreatedFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(104) - .HasComment("ชื่อ User ที่สร้างข้อมูล"); - - b.Property("CreatedUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(101) - .HasComment("User Id ที่สร้างข้อมูล"); - - b.Property("DateAppoint") - .HasColumnType("datetime(6)"); - - b.Property("DateSendLeave") - .HasColumnType("datetime(6)"); - - b.Property("Dear") - .HasColumnType("longtext") - .HasComment("เรียนใคร"); - - b.Property("FirstName") - .HasColumnType("longtext"); - - b.Property("Gender") - .HasColumnType("longtext"); - - b.Property("HajjDayStatus") - .HasColumnType("tinyint(1)"); - - b.Property("KeycloakUserId") - .HasColumnType("char(36)"); - - b.Property("LastName") - .HasColumnType("longtext"); - - b.Property("LastUpdateFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(105) - .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdateUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(103) - .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(102) - .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); - - b.Property("LeaveAddress") - .IsRequired() - .HasColumnType("longtext") - .HasComment("สถานที่ติดต่อขณะลา"); - - b.Property("LeaveBirthDate") - .HasColumnType("datetime(6)"); - - b.Property("LeaveCancelComment") - .HasColumnType("longtext") - .HasComment("เหตุผลในการขอยกเลิก"); - - b.Property("LeaveCancelDocumentId") - .HasColumnType("char(36)"); - - b.Property("LeaveCancelStatus") - .HasColumnType("longtext") - .HasComment("สถานะของคำขอยกเลิก"); - - b.Property("LeaveComment") - .HasColumnType("longtext") - .HasComment("ความเห็นของผู้บังคับบัญชา"); - - b.Property("LeaveDetail") - .IsRequired() - .HasColumnType("longtext") - .HasComment("รายละเอียดการลา"); - - b.Property("LeaveDirectorComment") - .HasColumnType("longtext") - .HasComment("ความเห็นของผู้อำนวยการสำนัก"); - - b.Property("LeaveDraftDocumentId") - .HasColumnType("char(36)"); - - b.Property("LeaveEndDate") - .HasColumnType("datetime(6)") - .HasComment("วัน เดือน ปีสิ้นสุดลา"); - - b.Property("LeaveGovernmentDate") - .HasColumnType("datetime(6)"); - - b.Property("LeaveLast") - .HasColumnType("datetime(6)"); - - b.Property("LeaveNumber") - .IsRequired() - .HasColumnType("longtext") - .HasComment("หมายเลขที่ติดต่อขณะลา"); - - b.Property("LeaveRange") - .HasColumnType("longtext") - .HasComment("ช่วงของการลาของวันเริ่ม เช่น ลาทั้งวัน ครึ่งวันเช้า ครึ่งวันบ่าย"); - - b.Property("LeaveRangeEnd") - .HasColumnType("longtext") - .HasComment("ช่วงของการลาของวันสิ้นสุด เช่น ลาทั้งวัน ครึ่งวันเช้า ครึ่งวันบ่าย"); - - b.Property("LeaveSalary") - .HasColumnType("int"); - - b.Property("LeaveSalaryText") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("LeaveStartDate") - .HasColumnType("datetime(6)") - .HasComment("วัน เดือน ปีเริ่มต้นลา"); - - b.Property("LeaveStatus") - .IsRequired() - .HasColumnType("longtext") - .HasComment("สถานะของคำร้อง"); - - b.Property("LeaveSubTypeName") - .HasColumnType("longtext"); - - b.Property("LeaveTotal") - .HasColumnType("double"); - - b.Property("LeaveTypeCode") - .HasColumnType("longtext") - .HasComment("code ของประเภทการลา"); - - b.Property("LeaveWrote") - .IsRequired() - .HasColumnType("longtext") - .HasComment("เขียนที่"); - - b.Property("OrdainDayBuddhistLentAddress") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("OrdainDayBuddhistLentName") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("OrdainDayLocationAddress") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("OrdainDayLocationName") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("OrdainDayLocationNumber") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("OrdainDayOrdination") - .HasColumnType("datetime(6)"); - - b.Property("OrdainDayStatus") - .HasColumnType("tinyint(1)"); - - b.Property("OrganizationName") - .HasColumnType("longtext") - .HasComment("สังกัดผู้ยื่นขอ"); - - b.Property("PositionLevelName") - .HasColumnType("longtext") - .HasComment("ระดับผู้ยื่นขอ"); - - b.Property("PositionName") - .HasColumnType("longtext") - .HasComment("ตำแหน่งผู้ยื่นขอ"); - - b.Property("Prefix") - .HasColumnType("longtext"); - - b.Property("ProfileId") - .HasColumnType("char(36)"); - - b.Property("ProfileType") - .HasColumnType("longtext"); - - b.Property("RestDayCurrentTotal") - .HasColumnType("double"); - - b.Property("RestDayOldTotal") - .HasColumnType("double"); - - b.Property("Root") - .HasColumnType("longtext"); - - b.Property("RootDnaId") - .HasColumnType("char(36)"); - - b.Property("RootId") - .HasColumnType("char(36)"); - - b.Property("StudyDayCountry") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("StudyDayDegreeLevel") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("StudyDayScholarship") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("StudyDaySubject") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("StudyDayTrainingName") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("StudyDayTrainingSubject") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("StudyDayUniversityName") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("TypeId") - .HasColumnType("char(36)"); - - b.Property("WifeDayDateBorn") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("WifeDayName") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("LeaveCancelDocumentId"); - - b.HasIndex("LeaveDraftDocumentId"); - - b.HasIndex("TypeId"); - - b.ToTable("LeaveRequests"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequestApprover", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)") - .HasColumnOrder(0) - .HasComment("PrimaryKey") - .HasAnnotation("Relational:JsonPropertyName", "id"); - - b.Property("ApproveStatus") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("ApproveType") - .HasColumnType("longtext"); - - b.Property("Comment") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(100) - .HasComment("สร้างข้อมูลเมื่อ"); - - b.Property("CreatedFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(104) - .HasComment("ชื่อ User ที่สร้างข้อมูล"); - - b.Property("CreatedUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(101) - .HasComment("User Id ที่สร้างข้อมูล"); - - b.Property("FirstName") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IsAct") - .HasColumnType("tinyint(1)"); - - b.Property("KeyId") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("KeycloakId") - .HasColumnType("char(36)"); - - b.Property("LastName") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("LastUpdateFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(105) - .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdateUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(103) - .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(102) - .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); - - b.Property("LeaveRequestId") - .HasColumnType("char(36)"); - - b.Property("OrganizationName") - .IsRequired() - .HasColumnType("longtext") - .HasComment("สังกัด"); - - b.Property("PosExecutiveName") - .IsRequired() - .HasColumnType("longtext") - .HasComment("ตำแหน่งทางการบริหาร"); - - b.Property("PositionLevelName") - .IsRequired() - .HasColumnType("longtext") - .HasComment("ประเภทระดับตำแหน่ง"); - - b.Property("PositionName") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("PositionSign") - .HasColumnType("longtext") - .HasComment("ตำแหน่งใต้ลายเช็นต์"); - - b.Property("Prefix") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("ProfileId") - .HasColumnType("char(36)"); - - b.Property("Seq") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.HasIndex("LeaveRequestId"); - - b.ToTable("LeaveRequestApprovers"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.AdditionalCheckRequest", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)") - .HasColumnOrder(0) - .HasComment("PrimaryKey") - .HasAnnotation("Relational:JsonPropertyName", "id"); - - b.Property("CheckDate") - .HasColumnType("datetime(6)") - .HasComment("*วันที่ลงเวลา"); - - b.Property("CheckInEdit") - .HasColumnType("tinyint(1)") - .HasComment("*ขอลงเวลาช่วงเช้า"); - - b.Property("CheckOutEdit") - .HasColumnType("tinyint(1)") - .HasComment("*ขอลงเวลาช่วงบ่าย"); - - b.Property("Child1DnaId") - .HasColumnType("char(36)"); - - b.Property("Child2DnaId") - .HasColumnType("char(36)"); - - b.Property("Child3DnaId") - .HasColumnType("char(36)"); - - b.Property("Child4DnaId") - .HasColumnType("char(36)"); - - b.Property("Comment") - .HasColumnType("longtext") - .HasComment("หมายเหตุในการการอนุมัติ/ไม่อนุมัติ"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(100) - .HasComment("สร้างข้อมูลเมื่อ"); - - b.Property("CreatedFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(104) - .HasComment("ชื่อ User ที่สร้างข้อมูล"); - - b.Property("CreatedUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(101) - .HasComment("User Id ที่สร้างข้อมูล"); - - b.Property("Description") - .IsRequired() - .HasColumnType("longtext") - .HasComment("*หมายเหตุขอลงเวลาพิเศษ"); - - b.Property("FirstName") - .HasColumnType("longtext"); - - b.Property("KeycloakUserId") - .HasColumnType("char(36)") - .HasComment("รหัส User ของ Keycloak ที่ร้องขอ"); - - b.Property("LastName") - .HasColumnType("longtext"); - - b.Property("LastUpdateFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(105) - .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdateUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(103) - .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(102) - .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); - - b.Property("Latitude") - .HasColumnType("double"); - - b.Property("Longitude") - .HasColumnType("double"); - - b.Property("POI") - .HasColumnType("longtext"); - - b.Property("Prefix") - .HasColumnType("longtext"); - - b.Property("RootDnaId") - .HasColumnType("char(36)"); - - b.Property("Status") - .IsRequired() - .HasColumnType("longtext") - .HasComment("สถานะการอนุมัติ"); - - b.HasKey("Id"); - - b.ToTable("AdditionalCheckRequests"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.CheckInJobStatus", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)") - .HasColumnOrder(0) - .HasComment("PrimaryKey") - .HasAnnotation("Relational:JsonPropertyName", "id"); - - b.Property("AdditionalData") - .HasColumnType("longtext") - .HasComment("ข้อมูลเพิ่มเติม (JSON)"); - - b.Property("CheckInId") - .HasColumnType("char(36)") - .HasComment("CheckInId สำหรับ Check-Out"); - - b.Property("CheckType") - .HasColumnType("longtext") - .HasComment("ประเภทการลงเวลา: CHECK_IN, CHECK_OUT"); - - b.Property("CompletedDate") - .HasColumnType("datetime(6)") - .HasComment("วันเวลาที่เสร็จสิ้นการประมวลผล"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(100) - .HasComment("สร้างข้อมูลเมื่อ"); - - b.Property("CreatedDate") - .HasColumnType("datetime(6)") - .HasComment("วันเวลาที่สร้างงาน"); - - b.Property("CreatedFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(104) - .HasComment("ชื่อ User ที่สร้างข้อมูล"); - - b.Property("CreatedUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(101) - .HasComment("User Id ที่สร้างข้อมูล"); - - b.Property("ErrorMessage") - .HasColumnType("longtext") - .HasComment("ข้อความแสดงข้อผิดพลาด"); - - b.Property("KeycloakUserId") - .HasColumnType("char(36)") - .HasComment("รหัส User ของ Keycloak"); - - b.Property("LastUpdateFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(105) - .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdateUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(103) - .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(102) - .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); - - b.Property("ProcessingDate") - .HasColumnType("datetime(6)") - .HasComment("วันเวลาที่เริ่มประมวลผล"); - - b.Property("Status") - .IsRequired() - .HasColumnType("longtext") - .HasComment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED"); - - b.Property("TaskId") - .HasColumnType("char(36)") - .HasComment("Task ID สำหรับติดตามสถานะงาน"); - - b.HasKey("Id"); - - b.ToTable("CheckInJobStatuses"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)") - .HasColumnOrder(0) - .HasComment("PrimaryKey") - .HasAnnotation("Relational:JsonPropertyName", "id"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(100) - .HasComment("สร้างข้อมูลเมื่อ"); - - b.Property("CreatedFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(104) - .HasComment("ชื่อ User ที่สร้างข้อมูล"); - - b.Property("CreatedUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(101) - .HasComment("User Id ที่สร้างข้อมูล"); - - b.Property("Description") - .IsRequired() - .HasColumnType("longtext") - .HasComment("คำอธิบาย"); - - b.Property("EndTimeAfternoon") - .IsRequired() - .HasColumnType("longtext") - .HasComment("เวลาออกงานช่วงบ่าย"); - - b.Property("EndTimeMorning") - .IsRequired() - .HasColumnType("longtext") - .HasComment("เวลาออกงานช่วงเช้า"); - - b.Property("IsActive") - .HasColumnType("tinyint(1)") - .HasComment("สถานะการเปิดใช้งาน (เปิด/ปิด)"); - - b.Property("IsDefault") - .HasColumnType("tinyint(1)") - .HasComment("สถานะว่ารอบใดเป็นค่า Default ของข้าราชการ (สำหรับทุกคนที่ยังไม่ได้ทำการเลือกรอบ)"); - - b.Property("LastUpdateFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(105) - .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdateUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(103) - .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(102) - .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); - - b.Property("StartTimeAfternoon") - .IsRequired() - .HasColumnType("longtext") - .HasComment("เวลาเข้างานช่วงบ่าย"); - - b.Property("StartTimeMorning") - .IsRequired() - .HasColumnType("longtext") - .HasComment("เวลาเข้างานช่วงเช้า"); - - b.HasKey("Id"); - - b.ToTable("DutyTimes"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.LeaveProcessJobStatus", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)") - .HasColumnOrder(0) - .HasComment("PrimaryKey") - .HasAnnotation("Relational:JsonPropertyName", "id"); - - b.Property("CompletedDate") - .HasColumnType("datetime(6)") - .HasComment("วันเวลาที่เสร็จสิ้นการประมวลผล"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(100) - .HasComment("สร้างข้อมูลเมื่อ"); - - b.Property("CreatedDate") - .HasColumnType("datetime(6)") - .HasComment("วันเวลาที่สร้างงาน"); - - b.Property("CreatedFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(104) - .HasComment("ชื่อ User ที่สร้างข้อมูล"); - - b.Property("CreatedUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(101) - .HasComment("User Id ที่สร้างข้อมูล"); - - b.Property("EndDate") - .HasColumnType("datetime(6)") - .HasComment("วันสิ้นสุด"); - - b.Property("ErrorMessage") - .HasColumnType("longtext") - .HasComment("ข้อความแสดงข้อผิดพลาด"); - - b.Property("LastUpdateFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(105) - .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdateUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(103) - .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(102) - .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); - - b.Property("ProcessingDate") - .HasColumnType("datetime(6)") - .HasComment("วันเวลาที่เริ่มประมวลผล"); - - b.Property("RootDnaId") - .HasColumnType("char(36)") - .HasComment("รหัส Root DNA Id"); - - b.Property("StartDate") - .HasColumnType("datetime(6)") - .HasComment("วันเริ่มต้น"); - - b.Property("Status") - .IsRequired() - .HasColumnType("longtext") - .HasComment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED"); - - b.HasKey("Id"); - - b.ToTable("LeaveProcessJobStatuses"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.ProcessUserTimeStamp", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)") - .HasColumnOrder(0) - .HasComment("PrimaryKey") - .HasAnnotation("Relational:JsonPropertyName", "id"); - - b.Property("CheckIn") - .HasColumnType("datetime(6)") - .HasComment("วัน เวลา เข้างาน"); - - b.Property("CheckInImageUrl") - .IsRequired() - .HasColumnType("longtext") - .HasComment("รูปถ่ายสถานที่ Check-In"); - - b.Property("CheckInLat") - .HasColumnType("double") - .HasComment("พิกัดละติจูด Check-In"); - - b.Property("CheckInLocationName") - .HasColumnType("longtext") - .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-In"); - - b.Property("CheckInLon") - .HasColumnType("double") - .HasComment("พิกัดลองจิจูด Check-In"); - - b.Property("CheckInPOI") - .IsRequired() - .HasColumnType("longtext") - .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-In"); - - b.Property("CheckInRemark") - .HasColumnType("longtext") - .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-In"); - - b.Property("CheckInStatus") - .IsRequired() - .HasColumnType("longtext") - .HasComment("สถานะ Check-In"); - - b.Property("CheckOut") - .HasColumnType("datetime(6)") - .HasComment("วัน เวลา ออกงาน"); - - b.Property("CheckOutImageUrl") - .IsRequired() - .HasColumnType("longtext") - .HasComment("รูปถ่ายสถานที่ Check-Out"); - - b.Property("CheckOutLat") - .HasColumnType("double") - .HasComment("พิกัดละติจูด Check-Out"); - - b.Property("CheckOutLocationName") - .HasColumnType("longtext") - .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-Out"); - - b.Property("CheckOutLon") - .HasColumnType("double") - .HasComment("พิกัดลองจิจูด Check-Out"); - - b.Property("CheckOutPOI") - .IsRequired() - .HasColumnType("longtext") - .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-Out"); - - b.Property("CheckOutRemark") - .HasColumnType("longtext") - .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-Out"); - - b.Property("CheckOutStatus") - .HasColumnType("longtext") - .HasComment("สถานะ Check-Out"); - - b.Property("Child1") - .HasColumnType("longtext"); - - b.Property("Child1DnaId") - .HasColumnType("char(36)"); - - b.Property("Child1Id") - .HasColumnType("char(36)"); - - b.Property("Child2") - .HasColumnType("longtext"); - - b.Property("Child2DnaId") - .HasColumnType("char(36)"); - - b.Property("Child2Id") - .HasColumnType("char(36)"); - - b.Property("Child3") - .HasColumnType("longtext"); - - b.Property("Child3DnaId") - .HasColumnType("char(36)"); - - b.Property("Child3Id") - .HasColumnType("char(36)"); - - b.Property("Child4") - .HasColumnType("longtext"); - - b.Property("Child4DnaId") - .HasColumnType("char(36)"); - - b.Property("Child4Id") - .HasColumnType("char(36)"); - - b.Property("CitizenId") - .HasColumnType("longtext"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(100) - .HasComment("สร้างข้อมูลเมื่อ"); - - b.Property("CreatedFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(104) - .HasComment("ชื่อ User ที่สร้างข้อมูล"); - - b.Property("CreatedUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(101) - .HasComment("User Id ที่สร้างข้อมูล"); - - b.Property("EditReason") - .HasColumnType("longtext") - .HasComment("เหตุผลการอนุมัติ/ไม่อนุมัติขอลงเวลาพิเศษ"); - - b.Property("EditStatus") - .HasColumnType("longtext") - .HasComment("สถานะการของลงเวลาพิเศษ"); - - b.Property("FirstName") - .HasColumnType("longtext"); - - b.Property("Gender") - .HasColumnType("longtext"); - - b.Property("IsLocationCheckIn") - .HasColumnType("tinyint(1)") - .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-In"); - - b.Property("IsLocationCheckOut") - .HasColumnType("tinyint(1)") - .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-Out"); - - b.Property("IsProcess") - .HasColumnType("tinyint(1)") - .HasComment("นำไปประมวลผลแล้วหรือยัง"); - - b.Property("KeycloakUserId") - .HasColumnType("char(36)") - .HasComment("รหัส User ของ Keycloak"); - - b.Property("LastName") - .HasColumnType("longtext"); - - b.Property("LastUpdateFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(105) - .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdateUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(103) - .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(102) - .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); - - b.Property("Prefix") - .HasColumnType("longtext"); - - b.Property("ProfileId") - .HasColumnType("char(36)"); - - b.Property("ProfileType") - .HasColumnType("longtext"); - - b.Property("Root") - .HasColumnType("longtext"); - - b.Property("RootDnaId") - .HasColumnType("char(36)"); - - b.Property("RootId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.ToTable("ProcessUserTimeStamps"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserCalendar", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)") - .HasColumnOrder(0) - .HasComment("PrimaryKey") - .HasAnnotation("Relational:JsonPropertyName", "id"); - - b.Property("Calendar") - .IsRequired() - .HasColumnType("longtext") - .HasComment("ปฏิทินการทำงานของ ขรก ปกติ หรือ 6 วันต่อสัปดาห์"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(100) - .HasComment("สร้างข้อมูลเมื่อ"); - - b.Property("CreatedFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(104) - .HasComment("ชื่อ User ที่สร้างข้อมูล"); - - b.Property("CreatedUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(101) - .HasComment("User Id ที่สร้างข้อมูล"); - - b.Property("LastUpdateFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(105) - .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdateUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(103) - .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(102) - .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); - - b.Property("ProfileId") - .HasColumnType("char(36)") - .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); - - b.HasKey("Id"); - - b.ToTable("UserCalendars"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserDutyTime", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)") - .HasColumnOrder(0) - .HasComment("PrimaryKey") - .HasAnnotation("Relational:JsonPropertyName", "id"); - - b.Property("Child1DnaId") - .HasColumnType("char(36)"); - - b.Property("Child2DnaId") - .HasColumnType("char(36)"); - - b.Property("Child3DnaId") - .HasColumnType("char(36)"); - - b.Property("Child4DnaId") - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(100) - .HasComment("สร้างข้อมูลเมื่อ"); - - b.Property("CreatedFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(104) - .HasComment("ชื่อ User ที่สร้างข้อมูล"); - - b.Property("CreatedUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(101) - .HasComment("User Id ที่สร้างข้อมูล"); - - b.Property("DutyTimeId") - .HasColumnType("char(36)") - .HasComment("รหัสรอบการลงเวลา"); - - b.Property("EffectiveDate") - .HasColumnType("datetime(6)") - .HasComment("วันที่มีผล"); - - b.Property("IsProcess") - .HasColumnType("tinyint(1)") - .HasComment("ทำการประมวลผลแล้วหรือยัง"); - - b.Property("LastUpdateFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(105) - .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdateUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(103) - .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(102) - .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); - - b.Property("ProfileId") - .HasColumnType("char(36)") - .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); - - b.Property("Remark") - .HasColumnType("longtext") - .HasComment("หมายเหตุ"); - - b.Property("RootDnaId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("DutyTimeId"); - - b.ToTable("UserDutyTimes"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserTimeStamp", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)") - .HasColumnOrder(0) - .HasComment("PrimaryKey") - .HasAnnotation("Relational:JsonPropertyName", "id"); - - b.Property("CheckIn") - .HasColumnType("datetime(6)") - .HasComment("วัน เวลา เข้างาน"); - - b.Property("CheckInImageUrl") - .IsRequired() - .HasColumnType("longtext") - .HasComment("รูปถ่ายสถานที่ Check-In"); - - b.Property("CheckInLat") - .HasColumnType("double") - .HasComment("พิกัดละติจูด Check-In"); - - b.Property("CheckInLocationName") - .HasColumnType("longtext") - .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-In"); - - b.Property("CheckInLon") - .HasColumnType("double") - .HasComment("พิกัดลองจิจูด Check-In"); - - b.Property("CheckInPOI") - .IsRequired() - .HasColumnType("longtext") - .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-In"); - - b.Property("CheckInRemark") - .HasColumnType("longtext") - .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-In"); - - b.Property("CheckOut") - .HasColumnType("datetime(6)") - .HasComment("วัน เวลา ออกงาน"); - - b.Property("CheckOutImageUrl") - .IsRequired() - .HasColumnType("longtext") - .HasComment("รูปถ่ายสถานที่ Check-Out"); - - b.Property("CheckOutLat") - .HasColumnType("double") - .HasComment("พิกัดละติจูด Check-Out"); - - b.Property("CheckOutLocationName") - .HasColumnType("longtext") - .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-Out"); - - b.Property("CheckOutLon") - .HasColumnType("double") - .HasComment("พิกัดลองจิจูด Check-Out"); - - b.Property("CheckOutPOI") - .IsRequired() - .HasColumnType("longtext") - .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-Out"); - - b.Property("CheckOutRemark") - .HasColumnType("longtext") - .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-Out"); - - b.Property("Child1") - .HasColumnType("longtext"); - - b.Property("Child1DnaId") - .HasColumnType("char(36)"); - - b.Property("Child1Id") - .HasColumnType("char(36)"); - - b.Property("Child2") - .HasColumnType("longtext"); - - b.Property("Child2DnaId") - .HasColumnType("char(36)"); - - b.Property("Child2Id") - .HasColumnType("char(36)"); - - b.Property("Child3") - .HasColumnType("longtext"); - - b.Property("Child3DnaId") - .HasColumnType("char(36)"); - - b.Property("Child3Id") - .HasColumnType("char(36)"); - - b.Property("Child4") - .HasColumnType("longtext"); - - b.Property("Child4DnaId") - .HasColumnType("char(36)"); - - b.Property("Child4Id") - .HasColumnType("char(36)"); - - b.Property("CitizenId") - .HasColumnType("longtext"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(100) - .HasComment("สร้างข้อมูลเมื่อ"); - - b.Property("CreatedFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(104) - .HasComment("ชื่อ User ที่สร้างข้อมูล"); - - b.Property("CreatedUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(101) - .HasComment("User Id ที่สร้างข้อมูล"); - - b.Property("FirstName") - .HasColumnType("longtext"); - - b.Property("Gender") - .HasColumnType("longtext"); - - b.Property("IsLocationCheckIn") - .HasColumnType("tinyint(1)") - .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-In"); - - b.Property("IsLocationCheckOut") - .HasColumnType("tinyint(1)") - .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-Out"); - - b.Property("IsProcess") - .HasColumnType("tinyint(1)") - .HasComment("นำไปประมวลผลแล้วหรือยัง"); - - b.Property("KeycloakUserId") - .HasColumnType("char(36)") - .HasComment("รหัส User ของ Keycloak"); - - b.Property("LastName") - .HasColumnType("longtext"); - - b.Property("LastUpdateFullName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)") - .HasColumnOrder(105) - .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdateUserId") - .IsRequired() - .HasMaxLength(40) - .HasColumnType("varchar(40)") - .HasColumnOrder(103) - .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); - - b.Property("LastUpdatedAt") - .HasColumnType("datetime(6)") - .HasColumnOrder(102) - .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); - - b.Property("Prefix") - .HasColumnType("longtext"); - - b.Property("ProfileId") - .HasColumnType("char(36)"); - - b.Property("ProfileType") - .HasColumnType("longtext"); - - b.Property("Root") - .HasColumnType("longtext"); - - b.Property("RootDnaId") - .HasColumnType("char(36)"); - - b.Property("RootId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.ToTable("UserTimeStamps"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveBeginning", b => - { - b.HasOne("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", "LeaveType") - .WithMany() - .HasForeignKey("LeaveTypeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("LeaveType"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveDocument", b => - { - b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "Document") - .WithMany() - .HasForeignKey("DocumentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", "LeaveRequest") - .WithMany("LeaveDocument") - .HasForeignKey("LeaveRequestId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Document"); - - b.Navigation("LeaveRequest"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => - { - b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "LeaveCancelDocument") - .WithMany() - .HasForeignKey("LeaveCancelDocumentId"); - - b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "LeaveDraftDocument") - .WithMany() - .HasForeignKey("LeaveDraftDocumentId"); - - b.HasOne("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", "Type") - .WithMany() - .HasForeignKey("TypeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("LeaveCancelDocument"); - - b.Navigation("LeaveDraftDocument"); - - b.Navigation("Type"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequestApprover", b => - { - b.HasOne("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", "LeaveRequest") - .WithMany("Approvers") - .HasForeignKey("LeaveRequestId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("LeaveRequest"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserDutyTime", b => - { - b.HasOne("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", "DutyTime") - .WithMany() - .HasForeignKey("DutyTimeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("DutyTime"); - }); - - modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => - { - b.Navigation("Approvers"); - - b.Navigation("LeaveDocument"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260619032119_Add Unique Index LeaveBeginning ProfileId Year TypeId.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260619032119_Add Unique Index LeaveBeginning ProfileId Year TypeId.cs deleted file mode 100644 index deabdd48..00000000 --- a/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260619032119_Add Unique Index LeaveBeginning ProfileId Year TypeId.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace BMA.EHR.Infrastructure.Migrations.LeaveDb -{ - /// - public partial class AddUniqueIndexLeaveBeginningProfileIdYearTypeId : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateIndex( - name: "IX_LeaveBeginnings_ProfileId_LeaveYear_LeaveTypeId", - table: "LeaveBeginnings", - columns: new[] { "ProfileId", "LeaveYear", "LeaveTypeId" }, - unique: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_LeaveBeginnings_ProfileId_LeaveYear_LeaveTypeId", - table: "LeaveBeginnings"); - } - } -} diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs index 053f6d99..929783f7 100644 --- a/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs @@ -50,7 +50,7 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.HasKey("Id"); - b.ToTable("Document"); + b.ToTable("Document", (string)null); }); modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", b => @@ -116,7 +116,7 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.HasKey("Id"); - b.ToTable("LeaveTypes"); + b.ToTable("LeaveTypes", (string)null); }); modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveBeginning", b => @@ -226,11 +226,7 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.HasIndex("LeaveTypeId"); - b.HasIndex("ProfileId", "LeaveYear", "LeaveTypeId") - .IsUnique() - .HasDatabaseName("IX_LeaveBeginnings_ProfileId_LeaveYear_LeaveTypeId"); - - b.ToTable("LeaveBeginnings"); + b.ToTable("LeaveBeginnings", (string)null); }); modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveDocument", b => @@ -292,7 +288,7 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.HasIndex("LeaveRequestId"); - b.ToTable("LeaveDocuments"); + b.ToTable("LeaveDocuments", (string)null); }); modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => @@ -671,7 +667,7 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.HasIndex("TypeId"); - b.ToTable("LeaveRequests"); + b.ToTable("LeaveRequests", (string)null); }); modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequestApprover", b => @@ -790,7 +786,7 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.HasIndex("LeaveRequestId"); - b.ToTable("LeaveRequestApprovers"); + b.ToTable("LeaveRequestApprovers", (string)null); }); modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.AdditionalCheckRequest", b => @@ -905,7 +901,7 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.HasKey("Id"); - b.ToTable("AdditionalCheckRequests"); + b.ToTable("AdditionalCheckRequests", (string)null); }); modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.CheckInJobStatus", b => @@ -998,7 +994,7 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.HasKey("Id"); - b.ToTable("CheckInJobStatuses"); + b.ToTable("CheckInJobStatuses", (string)null); }); modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", b => @@ -1083,7 +1079,7 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.HasKey("Id"); - b.ToTable("DutyTimes"); + b.ToTable("DutyTimes", (string)null); }); modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.LeaveProcessJobStatus", b => @@ -1168,7 +1164,7 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.HasKey("Id"); - b.ToTable("LeaveProcessJobStatuses"); + b.ToTable("LeaveProcessJobStatuses", (string)null); }); modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.ProcessUserTimeStamp", b => @@ -1379,7 +1375,7 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.HasKey("Id"); - b.ToTable("ProcessUserTimeStamps"); + b.ToTable("ProcessUserTimeStamps", (string)null); }); modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserCalendar", b => @@ -1440,7 +1436,7 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.HasKey("Id"); - b.ToTable("UserCalendars"); + b.ToTable("UserCalendars", (string)null); }); modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserDutyTime", b => @@ -1529,7 +1525,7 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.HasIndex("DutyTimeId"); - b.ToTable("UserDutyTimes"); + b.ToTable("UserDutyTimes", (string)null); }); modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserTimeStamp", b => @@ -1723,7 +1719,7 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.HasKey("Id"); - b.ToTable("UserTimeStamps"); + b.ToTable("UserTimeStamps", (string)null); }); modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveBeginning", b => diff --git a/BMA.EHR.Infrastructure/Persistence/LeaveDbContext.cs b/BMA.EHR.Infrastructure/Persistence/LeaveDbContext.cs index 115a2ffc..10064197 100644 --- a/BMA.EHR.Infrastructure/Persistence/LeaveDbContext.cs +++ b/BMA.EHR.Infrastructure/Persistence/LeaveDbContext.cs @@ -61,18 +61,5 @@ namespace BMA.EHR.Infrastructure.Persistence { base.Entry(entity).State = EntityState.Detached; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - - // Composite unique index on the natural key for LeaveBeginning. - // Prevents duplicate rows when concurrent requests (e.g. UI calling /user/check twice) - // race through the get-or-create flow in LeaveBeginningRepository. - modelBuilder.Entity() - .HasIndex(b => new { b.ProfileId, b.LeaveYear, b.LeaveTypeId }) - .HasDatabaseName("IX_LeaveBeginnings_ProfileId_LeaveYear_LeaveTypeId") - .IsUnique(); - } } } From 030098c0b9e90ddb95c7cd78c7ffef504225a3e7 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 19 Jun 2026 11:50:36 +0700 Subject: [PATCH 04/18] fix fiscal Year #2551 --- .../LeaveRequests/LeaveBeginingRepository.cs | 84 ++++++++++++++++++- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs index 7cf4dee7..c98ca11e 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs @@ -130,16 +130,19 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task ProcessEarlyLeaveRequest(int year) { - // Get Early Leave Request + // Get Early Leave Request (กรองตามปีงบประมาณ: 1 ต.ค. (year-1) – 30 ก.ย. (year)) + var fiscalStart = new DateTime(year - 1, 10, 1); + var fiscalEnd = new DateTime(year, 9, 30); + var leaveReq = await _dbContext.Set() .Include(x => x.Type) .Where(x => x.LeaveStatus == "APPROVE") - .Where(x => x.LeaveStartDate.Year == year || x.LeaveEndDate.Year == year) + .Where(x => x.LeaveStartDate.Date <= fiscalEnd && x.LeaveEndDate.Date >= fiscalStart) .ToListAsync(); foreach (var leave in leaveReq) { - await GetByYearAndTypeIdForUserAsync(year, leave.Type.Id, leave.KeycloakUserId); + await GetByYearAndTypeIdForUserWithUpdateAsync(year, leave.Type.Id, leave.KeycloakUserId); } } @@ -215,6 +218,81 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests return await GetOrAddForUserAsync(year, typeId, pf.Id, Factory); } + public async Task GetByYearAndTypeIdForUserWithUpdateAsync(int year, Guid typeId, Guid userId) + { + // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); + if (pf == null) + { + throw new Exception(GlobalMessages.DataNotFound); + } + + var govAge = (pf?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); + + var leaveType = await _dbContext.Set().FirstOrDefaultAsync(x => x.Id == typeId); + + + var limit = 0.0; + + var prev = _dbContext.Set() + .Include(x => x.LeaveType) + .FirstOrDefault(x => x.LeaveYear == year - 1 && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); + + var prevRemain = 0.0; + if (prev != null) + { + prevRemain = prev.LeaveDays - (prev.LeaveDaysUsed ?? 0.0); + } + + if (govAge >= 180) + { + if (govAge >= 3650) + { + limit = 10 + prevRemain; + if (limit > 30) limit = 30; + } + else + { + limit = 10 + prevRemain; + if (limit > 20) limit = 20; + } + } + else + { + limit = 0.0; + } + + var data = await _dbContext.Set() + .Where(x => x.LeaveYear == year && x.LeaveTypeId == typeId && x.ProfileId == pf.Id) + .FirstOrDefaultAsync(); + + if (data != null) + { + data.LeaveDays = leaveType?.Code == "LV-005" ? limit : 0; + await _dbContext.SaveChangesAsync(); + } + + // return new LeaveBeginning + // { + // LeaveYear = year, + // LeaveTypeId = typeId, + // ProfileId = pf.Id, + // Prefix = pf.Prefix, + // FirstName = pf.FirstName, + // LastName = pf.LastName, + // LeaveDaysUsed = 0, + // LeaveDays = leaveType?.Code == "LV-005" ? limit : 0, + // RootDnaId = pf.RootDnaId, + // Child1DnaId = pf.Child1DnaId, + // Child2DnaId = pf.Child2DnaId, + // Child3DnaId = pf.Child3DnaId, + // Child4DnaId = pf.Child4DnaId + // }; + return data; + + } + + public async Task GetByYearAndTypeIdForUser(int year, Guid typeId, GetProfileByKeycloakIdDto? pf) { var govAge = (pf?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); From 5d090fa7bdd89fda778b9be4b23dc8b2d8f67e68 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 22 Jun 2026 10:18:21 +0700 Subject: [PATCH 05/18] #2551 --- .../LeaveRequests/LeaveBeginingRepository.cs | 9 +++- .../LeaveRequests/LeaveRequestRepository.cs | 51 ++++++++++++++++--- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs index c98ca11e..c2ba8abb 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs @@ -172,11 +172,15 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests var prev = _dbContext.Set() .Include(x => x.LeaveType) .FirstOrDefault(x => x.LeaveYear == year - 1 && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); + + // คำนวณปีงบประมาณจาก startDate (ปีงบประมาณเริ่ม 1 ต.ค. และสิ้นสุด 30 ก.ย.) + var isCurrentYear = DateTime.Now.Year == year; + var prevRemain = 0.0; if (prev != null) { - prevRemain = prev.LeaveDays - (prev.LeaveDaysUsed ?? 0.0); + prevRemain = isCurrentYear ? prev.LeaveDays - (prev.LeaveDaysUsed ?? 0.0) : 0.0; } if (govAge >= 180) @@ -239,6 +243,9 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests .FirstOrDefault(x => x.LeaveYear == year - 1 && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); var prevRemain = 0.0; + + + if (prev != null) { prevRemain = prev.LeaveDays - (prev.LeaveDaysUsed ?? 0.0); diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs index 3070ee88..05737cfa 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs @@ -1937,12 +1937,17 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task GetSumApproveLeaveTotalByTypeAndRangeForUser2(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate) { + // คำนวณปีงบประมาณจาก startDate (ปีงบประมาณเริ่ม 1 ต.ค. และสิ้นสุด 30 ก.ย.) + var fiscalYear = startDate.Month >= 10 ? startDate.Year + 1 : startDate.Year; + var fiscalStart = new DateTime(fiscalYear - 1, 10, 1); + var fiscalEnd = new DateTime(fiscalYear, 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.DateSendLeave ?? x.CreatedAt) >= startDate && (x.DateSendLeave ??x.CreatedAt) <= endDate)) - //.Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) + .Where(x => ((x.DateSendLeave ?? x.CreatedAt) >= startDate && (x.DateSendLeave ?? x.CreatedAt) <= endDate)) + .Where(x => x.LeaveStartDate.Date >= fiscalStart.Date && x.LeaveStartDate.Date <= fiscalEnd.Date) .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") .ToListAsync(); @@ -1954,12 +1959,17 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task GetSumApproveLeaveTotalByTypeAndRangeForUserBefore(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate) { + // คำนวณปีงบประมาณจาก startDate (ปีงบประมาณเริ่ม 1 ต.ค. และสิ้นสุด 30 ก.ย.) + var fiscalYear = startDate.Month >= 10 ? startDate.Year + 1 : startDate.Year; + var fiscalStart = new DateTime(fiscalYear - 1, 10, 1); + var fiscalEnd = new DateTime(fiscalYear, 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.DateSendLeave ?? x.CreatedAt) >= startDate && (x.DateSendLeave ?? x.CreatedAt) < endDate)) - //.Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) + .Where(x => x.LeaveStartDate.Date >= fiscalStart.Date && x.LeaveStartDate.Date <= fiscalEnd.Date) .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") .ToListAsync(); @@ -1971,12 +1981,17 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task GetSumApproveLeaveTotalByTypeAndRangeForUserByProfile(Guid profileId, Guid leaveTypeId, DateTime startDate, DateTime endDate) { + // คำนวณปีงบประมาณจาก startDate (ปีงบประมาณเริ่ม 1 ต.ค. และสิ้นสุด 30 ก.ย.) + var fiscalYear = startDate.Month >= 10 ? startDate.Year + 1 : startDate.Year; + var fiscalStart = new DateTime(fiscalYear - 1, 10, 1); + var fiscalEnd = new DateTime(fiscalYear, 9, 30); + var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.ProfileId == profileId) .Where(x => x.Type.Id == leaveTypeId) .Where(x => ((x.DateSendLeave ?? x.CreatedAt) >= startDate && (x.DateSendLeave ??x.CreatedAt) <= endDate)) - //.Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) + .Where(x => x.LeaveStartDate.Date >= fiscalStart.Date && x.LeaveStartDate.Date <= fiscalEnd.Date) .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") .ToListAsync(); @@ -1988,12 +2003,17 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task GetSumApproveLeaveCountByTypeAndRangeForUserByProfile(Guid profileId, Guid leaveTypeId, DateTime startDate, DateTime endDate) { + // คำนวณปีงบประมาณจาก startDate (ปีงบประมาณเริ่ม 1 ต.ค. และสิ้นสุด 30 ก.ย.) + var fiscalYear = startDate.Month >= 10 ? startDate.Year + 1 : startDate.Year; + var fiscalStart = new DateTime(fiscalYear - 1, 10, 1); + var fiscalEnd = new DateTime(fiscalYear, 9, 30); + var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.ProfileId == profileId) .Where(x => x.Type.Id == leaveTypeId) .Where(x => ((x.DateSendLeave ?? x.CreatedAt) >= startDate && (x.DateSendLeave ??x.CreatedAt) <= endDate)) - //.Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) + .Where(x => x.LeaveStartDate.Date >= fiscalStart.Date && x.LeaveStartDate.Date <= fiscalEnd.Date) .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") .ToListAsync(); @@ -2002,12 +2022,17 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task GetSumApproveLeaveCountByTypeAndRangeForUser2(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate) { + // คำนวณปีงบประมาณจาก startDate (ปีงบประมาณเริ่ม 1 ต.ค. และสิ้นสุด 30 ก.ย.) + var fiscalYear = startDate.Month >= 10 ? startDate.Year + 1 : startDate.Year; + var fiscalStart = new DateTime(fiscalYear - 1, 10, 1); + var fiscalEnd = new DateTime(fiscalYear, 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.DateSendLeave ?? x.CreatedAt) >= startDate && (x.DateSendLeave ??x.CreatedAt) <= endDate)) - //.Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) + .Where(x => x.LeaveStartDate.Date >= fiscalStart.Date && x.LeaveStartDate.Date <= fiscalEnd.Date) .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") .ToListAsync(); @@ -2024,13 +2049,18 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests /// public async Task GetSumDraftLeaveTotalByTypeAndRangeForUser2(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate) { + // คำนวณปีงบประมาณจาก startDate (ปีงบประมาณเริ่ม 1 ต.ค. และสิ้นสุด 30 ก.ย.) + var fiscalYear = startDate.Month >= 10 ? startDate.Year + 1 : startDate.Year; + var fiscalStart = new DateTime(fiscalYear - 1, 10, 1); + var fiscalEnd = new DateTime(fiscalYear, 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.DateSendLeave ?? x.CreatedAt).Date >= startDate && (x.DateSendLeave ?? x.CreatedAt).Date < endDate)) - //.Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) + .Where(x => x.LeaveStartDate.Date >= fiscalStart.Date && x.LeaveStartDate.Date <= fiscalEnd.Date) .Where(x => x.LeaveStatus == "DRAFT") .ToListAsync(); @@ -2050,12 +2080,17 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests /// public async Task GetSumNewLeaveTotalByTypeAndRangeForUser2(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate) { + // คำนวณปีงบประมาณจาก startDate (ปีงบประมาณเริ่ม 1 ต.ค. และสิ้นสุด 30 ก.ย.) + var fiscalYear = startDate.Month >= 10 ? startDate.Year + 1 : startDate.Year; + var fiscalStart = new DateTime(fiscalYear - 1, 10, 1); + var fiscalEnd = new DateTime(fiscalYear, 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.DateSendLeave ?? x.CreatedAt) >= startDate && (x.DateSendLeave ??x.CreatedAt) < endDate)) - //.Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) + .Where(x => x.LeaveStartDate.Date >= fiscalStart.Date && x.LeaveStartDate.Date <= fiscalEnd.Date) .Where(x => (x.LeaveStatus == "NEW" || x.LeaveStatus == "PENDING")) .ToListAsync(); From fcea84bdb8bb0ea626e423ac9de3c22dcedb9f91 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 22 Jun 2026 10:54:59 +0700 Subject: [PATCH 06/18] fix fiscal start and end date #2551 --- .../Leaves/LeaveRequests/LeaveBeginingRepository.cs | 10 ++++++++-- BMA.EHR.Leave/Controllers/LeaveReportController.cs | 6 +++++- BMA.EHR.Leave/Controllers/LeaveRequestController.cs | 10 +++++++--- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs index c2ba8abb..add1361b 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs @@ -313,11 +313,14 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests var prev = _dbContext.Set() .Include(x => x.LeaveType) .FirstOrDefault(x => x.LeaveYear == year - 1 && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); + + // คำนวณปีงบประมาณจาก startDate (ปีงบประมาณเริ่ม 1 ต.ค. และสิ้นสุด 30 ก.ย.) + var isCurrentYear = DateTime.Now.Year == year; var prevRemain = 0.0; if (prev != null) { - prevRemain = prev.LeaveDays - (prev.LeaveDaysUsed ?? 0.0); + prevRemain = isCurrentYear ? prev.LeaveDays - (prev.LeaveDaysUsed ?? 0.0) : 0.0; } if (govAge >= 180) @@ -379,11 +382,14 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests var prev = _dbContext.Set() .Include(x => x.LeaveType) .FirstOrDefault(x => x.LeaveYear == year - 1 && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); + + // คำนวณปีงบประมาณจาก startDate (ปีงบประมาณเริ่ม 1 ต.ค. และสิ้นสุด 30 ก.ย.) + var isCurrentYear = DateTime.Now.Year == year; var prevRemain = 0.0; if (prev != null) { - prevRemain = prev.LeaveDays - (prev.LeaveDaysUsed ?? 0.0); + prevRemain = isCurrentYear ? prev.LeaveDays - (prev.LeaveDaysUsed ?? 0.0) : 0.0; } if (govAge >= 180) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 1b76947c..0bd07075 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -155,6 +155,10 @@ namespace BMA.EHR.Leave.Service.Controllers var lastLeaveRequest = await _leaveRequestRepository.GetLastLeaveRequestByTypeForUserAsync2(data.KeycloakUserId, data.Type.Id, data.CreatedAt); + + var fiscalYear = data.LeaveStartDate.Month >= 10 ? data.LeaveStartDate.Year + 1 : data.LeaveStartDate.Year; + var fiscalStart = new DateTime(fiscalYear - 1, 10, 1); + var fiscalEnd = new DateTime(fiscalYear, 9, 30); var startFiscalYear = (new DateTime(data.LeaveStartDate.Year - 1, 10, 1)).Date; var endFiscalYear = (data.DateSendLeave ?? data.CreatedAt); @@ -164,7 +168,7 @@ namespace BMA.EHR.Leave.Service.Controllers if (toDay >= new DateTime(toDay.Year, 10, 1) && toDay <= new DateTime(toDay.Year, 12, 31)) thisYear = thisYear + 1; var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUser2Async(thisYear, data.Type.Id, data.KeycloakUserId); - var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUserBefore(data.KeycloakUserId, data.Type.Id, startFiscalYear, endFiscalYear); + var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUserBefore(data.KeycloakUserId, data.Type.Id, fiscalStart, endFiscalYear); if (leaveData != null) { sumLeave += leaveData.BeginningLeaveDays; diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 85c19b79..c6eac2ef 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -2903,16 +2903,20 @@ namespace BMA.EHR.Leave.Service.Controllers orgName += $" {rawData.Root}"; var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUser2Async(thisYear, rawData.Type.Id, rawData.KeycloakUserId); + + var fiscalYear = rawData.LeaveStartDate.Month >= 10 ? rawData.LeaveStartDate.Year + 1 : rawData.LeaveStartDate.Year; + var fiscalStart = new DateTime(fiscalYear - 1, 10, 1); + var fiscalEnd = new DateTime(fiscalYear, 9, 30); var startFiscalYear = new DateTime(rawData.LeaveStartDate.Year - 1, 10, 1); var endFiscalYear = rawData.DateSendLeave ?? rawData.CreatedAt; var endFiscalYear2 = new DateTime(rawData.LeaveStartDate.Year, 9, 30); //var endFiscalYear3 = rawData.DateSendLeave ?? rawData.CreatedAt; - var leaveSummary = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, startFiscalYear, endFiscalYear); + var leaveSummary = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, fiscalStart, endFiscalYear); // วันลาแบบร่างและที่ยื่นลาไปแล้ว - var leaveDraftSummary = await _leaveRequestRepository.GetSumDraftLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, startFiscalYear, endFiscalYear2); - var leaveWaitingSummary = await _leaveRequestRepository.GetSumNewLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, startFiscalYear, endFiscalYear2); + var leaveDraftSummary = await _leaveRequestRepository.GetSumDraftLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, fiscalStart, fiscalEnd); + var leaveWaitingSummary = await _leaveRequestRepository.GetSumNewLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, fiscalStart, fiscalEnd); //var leaveSummary = leaveData == null ? 0.0 : leaveData.LeaveDaysUsed; if (leaveData != null) From be1f6dd84e9a63261635997135c57b8842d3a6ef Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 22 Jun 2026 11:10:29 +0700 Subject: [PATCH 07/18] #2551 --- BMA.EHR.Leave/Controllers/LeaveReportController.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 0bd07075..764ba1a5 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -338,21 +338,25 @@ namespace BMA.EHR.Leave.Service.Controllers var fullName = $"{profile!.Prefix}{profile!.FirstName} {profile!.LastName}"; var startFiscalYear = new DateTime(data.LeaveStartDate.Year - 1, 10, 1); - var endFiscalYear = data.CreatedAt; + var endFiscalYear = (data.DateSendLeave ?? data.CreatedAt); + + var fiscalYear = data.LeaveStartDate.Month >= 10 ? data.LeaveStartDate.Year + 1 : data.LeaveStartDate.Year; + var fiscalStart = new DateTime(fiscalYear - 1, 10, 1); + var fiscalEnd = new DateTime(fiscalYear, 9, 30); var thisYear = data.LeaveStartDate.Year; var toDay = data.LeaveStartDate.Date; if (toDay >= new DateTime(toDay.Year, 10, 1) && toDay <= new DateTime(toDay.Year, 12, 31)) thisYear = thisYear + 1; - var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUser2Async(thisYear, data.Type.Id, data.KeycloakUserId); + var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUser2Async(fiscalYear, data.Type.Id, data.KeycloakUserId); //var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUser2Async(thisYear, rawData.Type.Id, rawData.KeycloakUserId); //var leaveSummary = leaveData == null ? 0.0 : leaveData.LeaveDaysUsed; //var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser(data.KeycloakUserId, data.Type.Id, startFiscalYear, endFiscalYear); - var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUserBefore(data.KeycloakUserId, data.Type.Id, startFiscalYear, endFiscalYear); + var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUserBefore(data.KeycloakUserId, data.Type.Id, fiscalStart, endFiscalYear); if (leaveData != null) { sumLeave += leaveData.BeginningLeaveDays; From baf828ac854fcad6c7151326fafc900a7ee67932 Mon Sep 17 00:00:00 2001 From: harid Date: Mon, 22 Jun 2026 13:21:33 +0700 Subject: [PATCH 08/18] fix #2571 --- .../Controllers/RetirementOtherController.cs | 6 +----- BMA.EHR.Retirement.Service/Requests/OrgRequest.cs | 1 + 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs index 4880f3b6..868aac4f 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs @@ -449,11 +449,7 @@ namespace BMA.EHR.Retirement.Service.Controllers retirementOther.PositionLevelOld = org.result.posLevelName; retirementOther.PositionTypeOld = org.result.posTypeName; retirementOther.PositionNumberOld = org.result.posNo; - retirementOther.OrganizationOld = (org.result.child4 == null ? "" : org.result.child4 + "\n") + - (org.result.child3 == null ? "" : org.result.child3 + "\n") + - (org.result.child2 == null ? "" : org.result.child2 + "\n") + - (org.result.child1 == null ? "" : org.result.child1 + "\n") + - (org.result.root == null ? "" : org.result.root); + retirementOther.OrganizationOld = org.result.org ?? ""; retirementOther.OrganizationPositionOld = org.result.position + "\n" + (retirementOther.PositionExecutiveOld == null ? "" : (retirementOther.positionExecutiveField == null ? retirementOther.PositionExecutiveOld + "\n" : retirementOther.PositionExecutiveOld + "(" + retirementOther.positionExecutiveField + ")" + "\n")) + retirementOther.OrganizationOld; diff --git a/BMA.EHR.Retirement.Service/Requests/OrgRequest.cs b/BMA.EHR.Retirement.Service/Requests/OrgRequest.cs index d68ed4a1..0dda38be 100644 --- a/BMA.EHR.Retirement.Service/Requests/OrgRequest.cs +++ b/BMA.EHR.Retirement.Service/Requests/OrgRequest.cs @@ -54,5 +54,6 @@ namespace BMA.EHR.Retirement.Service.Requests public DateTime? leaveDate { get; set; } public string? education { get; set; } public double? salary { get; set; } + public string? org { get; set; } } } \ No newline at end of file From f4f56b1c21ee9b608ec75eb87d14fef4ce40844a Mon Sep 17 00:00:00 2001 From: harid Date: Mon, 22 Jun 2026 16:36:06 +0700 Subject: [PATCH 09/18] =?UTF-8?q?api=20=E0=B8=A5=E0=B8=9A=E0=B8=82?= =?UTF-8?q?=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=20noti?= =?UTF-8?q?=20#1599?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MessageQueue/NotificationRepository.cs | 38 +++++++++++++++++++ .../Controllers/MessageController.cs | 25 ++++++++++++ 2 files changed, 63 insertions(+) diff --git a/BMA.EHR.Application/Repositories/MessageQueue/NotificationRepository.cs b/BMA.EHR.Application/Repositories/MessageQueue/NotificationRepository.cs index a9d5ceb7..9a8d71e3 100644 --- a/BMA.EHR.Application/Repositories/MessageQueue/NotificationRepository.cs +++ b/BMA.EHR.Application/Repositories/MessageQueue/NotificationRepository.cs @@ -187,6 +187,44 @@ namespace BMA.EHR.Application.Repositories.MessageQueue } } + private async Task GetMyProfileIdAsync() + { + var apiUrl = $"{_configuration["API"]}/org/dotnet/get-profileId"; + var response = await GetExternalAPIAsync(apiUrl, AccessToken!, _configuration["API_KEY"]!); + + if (string.IsNullOrWhiteSpace(response)) + return string.Empty; + + var org = JsonConvert.DeserializeObject(response); + if (org == null || org.result == null) + return string.Empty; + + return org.result.profileId ?? string.Empty; + } + + public async Task DeleteAllMyNotificationsAsync() + { + try + { + var profileId = await GetMyProfileIdAsync(); + if (string.IsNullOrEmpty(profileId)) + return 0; + + var notifications = await _dbContext.Set() + .Where(x => x.ReceiverUserId == Guid.Parse(profileId)) + .Where(x => x.DeleteDate == null) + .ToListAsync(); + + _dbContext.Set().RemoveRange(notifications); + await _dbContext.SaveChangesAsync(); + return notifications.Count; + } + catch + { + throw; + } + } + public async Task PushNotificationAsync(Guid ReceiverUserId, string Subject, string Body, string Payload = "", string NotiLink = "", bool IsSendInbox = false, bool IsSendMail = false) { try diff --git a/BMA.EHR.Placement.Service/Controllers/MessageController.cs b/BMA.EHR.Placement.Service/Controllers/MessageController.cs index c4434eb1..9c8ffdfd 100644 --- a/BMA.EHR.Placement.Service/Controllers/MessageController.cs +++ b/BMA.EHR.Placement.Service/Controllers/MessageController.cs @@ -184,6 +184,31 @@ namespace BMA.EHR.Placement.Service.Controllers } } + /// + /// ลบ Notification ทั้งหมดของ user ที่ login (Hard delete) + /// + /// จำนวนรายการที่ถูกลบ + /// เมื่อทำการลบข้อมูลจาก Relational Database สำเร็จ + /// ไม่ได้ Login เข้าระบบ + /// เมื่อเกิดข้อผิดพลาดในการทำงาน + [HttpDelete("my-notifications")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> PermanentDeleteAllMyNotificationsAsync() + { + try + { + var affectedRows = await _notificationRepository.DeleteAllMyNotificationsAsync(); + + return Success(affectedRows); + } + catch + { + throw; + } + } + #endregion } From bc29952e836600d0ea25c966e8b055089d489509 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 22 Jun 2026 22:22:11 +0700 Subject: [PATCH 10/18] fix #2551 --- .../LeaveRequests/LeaveRequestRepository.cs | 82 ++++++------------- .../Controllers/LeaveBeginningController.cs | 4 +- .../Controllers/LeaveReportController.cs | 6 +- .../Controllers/LeaveRequestController.cs | 26 ++++-- 4 files changed, 50 insertions(+), 68 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs index 05737cfa..6363e258 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs @@ -1935,19 +1935,17 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests return 0; } - public async Task GetSumApproveLeaveTotalByTypeAndRangeForUser2(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate) + public async Task GetSumApproveLeaveTotalByTypeAndRangeForUser2(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate, DateTime sendLeaveDate) { - // คำนวณปีงบประมาณจาก startDate (ปีงบประมาณเริ่ม 1 ต.ค. และสิ้นสุด 30 ก.ย.) - var fiscalYear = startDate.Month >= 10 ? startDate.Year + 1 : startDate.Year; - var fiscalStart = new DateTime(fiscalYear - 1, 10, 1); - var fiscalEnd = new DateTime(fiscalYear, 9, 30); - + // startDate/endDate คือขอบเขตปีงบประมาณ (fiscalStart/fiscalEnd) ที่ caller ส่งมา + // ใช้ LeaveStartDate เป็นหลักในการ filter เพื่อให้กรณียื่นลาล่วงหน้าข้ามปีงบประมาณ + // ถูกนับในปีงบประมาณของวันลาจริง (ไม่ใช้วันที่ยื่นลา) 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.DateSendLeave ?? x.CreatedAt) >= startDate && (x.DateSendLeave ?? x.CreatedAt) <= endDate)) - .Where(x => x.LeaveStartDate.Date >= fiscalStart.Date && x.LeaveStartDate.Date <= fiscalEnd.Date) + .Where(x => (x.DateSendLeave ?? x.CreatedAt) < sendLeaveDate) + .Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") .ToListAsync(); @@ -1957,19 +1955,14 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests return 0; } - public async Task GetSumApproveLeaveTotalByTypeAndRangeForUserBefore(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate) + public async Task GetSumApproveLeaveTotalByTypeAndRangeForUserBefore(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate,DateTime sendLeaveDate) { - // คำนวณปีงบประมาณจาก startDate (ปีงบประมาณเริ่ม 1 ต.ค. และสิ้นสุด 30 ก.ย.) - var fiscalYear = startDate.Month >= 10 ? startDate.Year + 1 : startDate.Year; - var fiscalStart = new DateTime(fiscalYear - 1, 10, 1); - var fiscalEnd = new DateTime(fiscalYear, 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.DateSendLeave ?? x.CreatedAt) >= startDate && (x.DateSendLeave ?? x.CreatedAt) < endDate)) - .Where(x => x.LeaveStartDate.Date >= fiscalStart.Date && x.LeaveStartDate.Date <= fiscalEnd.Date) + .Where(x => (x.DateSendLeave ?? x.CreatedAt) < sendLeaveDate) + .Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") .ToListAsync(); @@ -1979,19 +1972,14 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests return 0; } - public async Task GetSumApproveLeaveTotalByTypeAndRangeForUserByProfile(Guid profileId, Guid leaveTypeId, DateTime startDate, DateTime endDate) + public async Task GetSumApproveLeaveTotalByTypeAndRangeForUserByProfile(Guid profileId, Guid leaveTypeId, DateTime startDate, DateTime endDate,DateTime sendLeaveDate) { - // คำนวณปีงบประมาณจาก startDate (ปีงบประมาณเริ่ม 1 ต.ค. และสิ้นสุด 30 ก.ย.) - var fiscalYear = startDate.Month >= 10 ? startDate.Year + 1 : startDate.Year; - var fiscalStart = new DateTime(fiscalYear - 1, 10, 1); - var fiscalEnd = new DateTime(fiscalYear, 9, 30); - var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.ProfileId == profileId) .Where(x => x.Type.Id == leaveTypeId) - .Where(x => ((x.DateSendLeave ?? x.CreatedAt) >= startDate && (x.DateSendLeave ??x.CreatedAt) <= endDate)) - .Where(x => x.LeaveStartDate.Date >= fiscalStart.Date && x.LeaveStartDate.Date <= fiscalEnd.Date) + .Where(x => (x.DateSendLeave ?? x.CreatedAt) < sendLeaveDate) + .Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") .ToListAsync(); @@ -2001,38 +1989,28 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests return 0; } - public async Task GetSumApproveLeaveCountByTypeAndRangeForUserByProfile(Guid profileId, Guid leaveTypeId, DateTime startDate, DateTime endDate) + public async Task GetSumApproveLeaveCountByTypeAndRangeForUserByProfile(Guid profileId, Guid leaveTypeId, DateTime startDate, DateTime endDate, DateTime sendLeaveDate) { - // คำนวณปีงบประมาณจาก startDate (ปีงบประมาณเริ่ม 1 ต.ค. และสิ้นสุด 30 ก.ย.) - var fiscalYear = startDate.Month >= 10 ? startDate.Year + 1 : startDate.Year; - var fiscalStart = new DateTime(fiscalYear - 1, 10, 1); - var fiscalEnd = new DateTime(fiscalYear, 9, 30); - var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.ProfileId == profileId) .Where(x => x.Type.Id == leaveTypeId) - .Where(x => ((x.DateSendLeave ?? x.CreatedAt) >= startDate && (x.DateSendLeave ??x.CreatedAt) <= endDate)) - .Where(x => x.LeaveStartDate.Date >= fiscalStart.Date && x.LeaveStartDate.Date <= fiscalEnd.Date) + .Where(x => (x.DateSendLeave ?? x.CreatedAt) < sendLeaveDate) + .Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") .ToListAsync(); return data.Count; } - public async Task GetSumApproveLeaveCountByTypeAndRangeForUser2(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate) + public async Task GetSumApproveLeaveCountByTypeAndRangeForUser2(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate, DateTime sendLeaveDate) { - // คำนวณปีงบประมาณจาก startDate (ปีงบประมาณเริ่ม 1 ต.ค. และสิ้นสุด 30 ก.ย.) - var fiscalYear = startDate.Month >= 10 ? startDate.Year + 1 : startDate.Year; - var fiscalStart = new DateTime(fiscalYear - 1, 10, 1); - var fiscalEnd = new DateTime(fiscalYear, 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.DateSendLeave ?? x.CreatedAt) >= startDate && (x.DateSendLeave ??x.CreatedAt) <= endDate)) - .Where(x => x.LeaveStartDate.Date >= fiscalStart.Date && x.LeaveStartDate.Date <= fiscalEnd.Date) + .Where(x => (x.DateSendLeave ?? x.CreatedAt) < sendLeaveDate) + .Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") .ToListAsync(); @@ -2046,21 +2024,16 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests /// /// /// + /// /// - public async Task GetSumDraftLeaveTotalByTypeAndRangeForUser2(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate) + public async Task GetSumDraftLeaveTotalByTypeAndRangeForUser2(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate, DateTime sendLeaveDate) { - // คำนวณปีงบประมาณจาก startDate (ปีงบประมาณเริ่ม 1 ต.ค. และสิ้นสุด 30 ก.ย.) - var fiscalYear = startDate.Month >= 10 ? startDate.Year + 1 : startDate.Year; - var fiscalStart = new DateTime(fiscalYear - 1, 10, 1); - var fiscalEnd = new DateTime(fiscalYear, 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.DateSendLeave ?? x.CreatedAt).Date >= startDate - && (x.DateSendLeave ?? x.CreatedAt).Date < endDate)) - .Where(x => x.LeaveStartDate.Date >= fiscalStart.Date && x.LeaveStartDate.Date <= fiscalEnd.Date) + .Where(x => (x.DateSendLeave ?? x.CreatedAt) < sendLeaveDate) + .Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) .Where(x => x.LeaveStatus == "DRAFT") .ToListAsync(); @@ -2078,19 +2051,14 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests /// /// /// - public async Task GetSumNewLeaveTotalByTypeAndRangeForUser2(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate) + public async Task GetSumNewLeaveTotalByTypeAndRangeForUser2(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate,DateTime sendLeaveDate) { - // คำนวณปีงบประมาณจาก startDate (ปีงบประมาณเริ่ม 1 ต.ค. และสิ้นสุด 30 ก.ย.) - var fiscalYear = startDate.Month >= 10 ? startDate.Year + 1 : startDate.Year; - var fiscalStart = new DateTime(fiscalYear - 1, 10, 1); - var fiscalEnd = new DateTime(fiscalYear, 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.DateSendLeave ?? x.CreatedAt) >= startDate && (x.DateSendLeave ??x.CreatedAt) < endDate)) - .Where(x => x.LeaveStartDate.Date >= fiscalStart.Date && x.LeaveStartDate.Date <= fiscalEnd.Date) + .Where(x => (x.DateSendLeave ?? x.CreatedAt) < sendLeaveDate) + .Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) .Where(x => (x.LeaveStatus == "NEW" || x.LeaveStatus == "PENDING")) .ToListAsync(); diff --git a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs index 2aa4988f..a3b0c124 100644 --- a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs @@ -410,8 +410,8 @@ namespace BMA.EHR.Leave.Service.Controllers if (req.LeaveDaysUsed is null || req.LeaveCount is null) { - var systemLeaveDays = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUserByProfile(req.ProfileId, req.LeaveTypeId, startFiscalDate, endFiscalDate); - var systemLeaveCount = await _leaveRequestRepository.GetSumApproveLeaveCountByTypeAndRangeForUserByProfile(req.ProfileId, req.LeaveTypeId, startFiscalDate, endFiscalDate); + var systemLeaveDays = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUserByProfile(req.ProfileId, req.LeaveTypeId, startFiscalDate, endFiscalDate,endFiscalDate.AddDays(1)); + var systemLeaveCount = await _leaveRequestRepository.GetSumApproveLeaveCountByTypeAndRangeForUserByProfile(req.ProfileId, req.LeaveTypeId, startFiscalDate, endFiscalDate,endFiscalDate.AddDays(1)); leaveBeginning.LeaveDaysUsed = req.BeginningLeaveDays + systemLeaveDays; leaveBeginning.LeaveCount = req.BeginningLeaveCount + systemLeaveCount; diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 764ba1a5..a2d32dda 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -162,13 +162,14 @@ namespace BMA.EHR.Leave.Service.Controllers var startFiscalYear = (new DateTime(data.LeaveStartDate.Year - 1, 10, 1)).Date; var endFiscalYear = (data.DateSendLeave ?? data.CreatedAt); + var sendLeaveDate = data.DateSendLeave ?? data.CreatedAt; var thisYear = data.LeaveStartDate.Year; var toDay = data.LeaveStartDate.Date; if (toDay >= new DateTime(toDay.Year, 10, 1) && toDay <= new DateTime(toDay.Year, 12, 31)) thisYear = thisYear + 1; var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUser2Async(thisYear, data.Type.Id, data.KeycloakUserId); - var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUserBefore(data.KeycloakUserId, data.Type.Id, fiscalStart, endFiscalYear); + var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUserBefore(data.KeycloakUserId, data.Type.Id, fiscalStart, endFiscalYear, sendLeaveDate); if (leaveData != null) { sumLeave += leaveData.BeginningLeaveDays; @@ -355,8 +356,9 @@ namespace BMA.EHR.Leave.Service.Controllers //var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser(data.KeycloakUserId, data.Type.Id, startFiscalYear, endFiscalYear); + var sendLeaveDate = data.DateSendLeave ?? data.CreatedAt; - var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUserBefore(data.KeycloakUserId, data.Type.Id, fiscalStart, endFiscalYear); + var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUserBefore(data.KeycloakUserId, data.Type.Id, fiscalStart, endFiscalYear, sendLeaveDate); if (leaveData != null) { sumLeave += leaveData.BeginningLeaveDays; diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index c6eac2ef..e5b0ecd1 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -927,9 +927,10 @@ namespace BMA.EHR.Leave.Service.Controllers // var lastSalary = profile.ProfileSalary; var leaveLast = await _leaveRequestRepository.GetLeaveLastByTypeForUserAsync(userId, req.Type); + - var leaveDraftSummary = await _leaveRequestRepository.GetSumDraftLeaveTotalByTypeAndRangeForUser2(userId, req.Type, startFiscalDate, endFiscalDate); - var leaveWaitingSummary = await _leaveRequestRepository.GetSumNewLeaveTotalByTypeAndRangeForUser2(userId, req.Type, startFiscalDate, endFiscalDate); + var leaveDraftSummary = await _leaveRequestRepository.GetSumDraftLeaveTotalByTypeAndRangeForUser2(userId, req.Type, startFiscalDate, endFiscalDate,endFiscalDate.AddDays(1)); + var leaveWaitingSummary = await _leaveRequestRepository.GetSumNewLeaveTotalByTypeAndRangeForUser2(userId, req.Type, startFiscalDate, endFiscalDate,endFiscalDate.AddDays(1)); var result = new GetUserLeaveProfileResultDto { @@ -1684,11 +1685,13 @@ namespace BMA.EHR.Leave.Service.Controllers var rawData = await _leaveRequestRepository.GetByIdAsync(id); var thisYear = DateTime.Now.Year; + if (rawData == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } + var fiscalYear = rawData.LeaveStartDate.Month >= 10 ? rawData.LeaveStartDate.Year + 1 : rawData.LeaveStartDate.Year; // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(rawData.KeycloakUserId, AccessToken); @@ -1734,10 +1737,15 @@ namespace BMA.EHR.Leave.Service.Controllers var restDayOld = 0.0; //restDayOld = govAge < 180 ? 0 : leaveData == null ? 0 : (leaveData.LeaveDays + leaveData.BeginningLeaveDays - 10); + + + restDayOld = govAge < 180 ? 0 : leaveData == null ? 0 : (leaveData.LeaveDays - 10); if (restDayOld < 0) restDayOld = 0; var restDayCurrent = govAge < 180 ? 0 : 10; + if (thisYear < fiscalYear) + restDayOld = 0; var result = new GetLeaveRequestByIdDto { @@ -2903,20 +2911,22 @@ namespace BMA.EHR.Leave.Service.Controllers orgName += $" {rawData.Root}"; var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUser2Async(thisYear, rawData.Type.Id, rawData.KeycloakUserId); + + var currentYear = DateTime.Now.Year; var fiscalYear = rawData.LeaveStartDate.Month >= 10 ? rawData.LeaveStartDate.Year + 1 : rawData.LeaveStartDate.Year; - var fiscalStart = new DateTime(fiscalYear - 1, 10, 1); + var fiscalStart = new DateTime((fiscalYear - 1), 10, 1); var fiscalEnd = new DateTime(fiscalYear, 9, 30); var startFiscalYear = new DateTime(rawData.LeaveStartDate.Year - 1, 10, 1); - var endFiscalYear = rawData.DateSendLeave ?? rawData.CreatedAt; + var sendLeaveDate = rawData.DateSendLeave ?? rawData.CreatedAt; var endFiscalYear2 = new DateTime(rawData.LeaveStartDate.Year, 9, 30); //var endFiscalYear3 = rawData.DateSendLeave ?? rawData.CreatedAt; - var leaveSummary = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, fiscalStart, endFiscalYear); + var leaveSummary = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, fiscalStart, fiscalEnd, sendLeaveDate); // วันลาแบบร่างและที่ยื่นลาไปแล้ว - var leaveDraftSummary = await _leaveRequestRepository.GetSumDraftLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, fiscalStart, fiscalEnd); - var leaveWaitingSummary = await _leaveRequestRepository.GetSumNewLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, fiscalStart, fiscalEnd); + var leaveDraftSummary = await _leaveRequestRepository.GetSumDraftLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, fiscalStart, fiscalEnd, sendLeaveDate); + var leaveWaitingSummary = await _leaveRequestRepository.GetSumNewLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, fiscalStart, fiscalEnd, sendLeaveDate); //var leaveSummary = leaveData == null ? 0.0 : leaveData.LeaveDaysUsed; if (leaveData != null) @@ -2929,6 +2939,8 @@ namespace BMA.EHR.Leave.Service.Controllers { leaveLimit = leaveData == null ? 0.0 : leaveData.LeaveDays; extendLeave = leaveLimit <= 0 ? 0 : leaveLimit - 10; + if (thisYear < fiscalYear) + extendLeave = 0; } var result = new GetLeaveRequestForAdminByIdDto From a2dc4b5b826dbf93f8ceffb97a248d85d44395bf Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 22 Jun 2026 22:41:21 +0700 Subject: [PATCH 11/18] fix param fiscalEndDate #2551 --- BMA.EHR.Leave/Controllers/LeaveReportController.cs | 4 ++-- BMA.EHR.Leave/Controllers/LeaveRequestController.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index a2d32dda..11458917 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -169,7 +169,7 @@ namespace BMA.EHR.Leave.Service.Controllers if (toDay >= new DateTime(toDay.Year, 10, 1) && toDay <= new DateTime(toDay.Year, 12, 31)) thisYear = thisYear + 1; var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUser2Async(thisYear, data.Type.Id, data.KeycloakUserId); - var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUserBefore(data.KeycloakUserId, data.Type.Id, fiscalStart, endFiscalYear, sendLeaveDate); + var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUserBefore(data.KeycloakUserId, data.Type.Id, fiscalStart, fiscalEnd, sendLeaveDate); if (leaveData != null) { sumLeave += leaveData.BeginningLeaveDays; @@ -358,7 +358,7 @@ namespace BMA.EHR.Leave.Service.Controllers var sendLeaveDate = data.DateSendLeave ?? data.CreatedAt; - var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUserBefore(data.KeycloakUserId, data.Type.Id, fiscalStart, endFiscalYear, sendLeaveDate); + var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUserBefore(data.KeycloakUserId, data.Type.Id, fiscalStart, fiscalEnd, sendLeaveDate); if (leaveData != null) { sumLeave += leaveData.BeginningLeaveDays; diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index e5b0ecd1..9b1140cd 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -2925,8 +2925,8 @@ namespace BMA.EHR.Leave.Service.Controllers var leaveSummary = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, fiscalStart, fiscalEnd, sendLeaveDate); // วันลาแบบร่างและที่ยื่นลาไปแล้ว - var leaveDraftSummary = await _leaveRequestRepository.GetSumDraftLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, fiscalStart, fiscalEnd, sendLeaveDate); - var leaveWaitingSummary = await _leaveRequestRepository.GetSumNewLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, fiscalStart, fiscalEnd, sendLeaveDate); + var leaveDraftSummary = await _leaveRequestRepository.GetSumDraftLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, fiscalStart, fiscalEnd, fiscalEnd.AddDays(1)); + var leaveWaitingSummary = await _leaveRequestRepository.GetSumNewLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, fiscalStart, fiscalEnd, fiscalEnd.AddDays(1)); //var leaveSummary = leaveData == null ? 0.0 : leaveData.LeaveDaysUsed; if (leaveData != null) From ae417e4777b14af640b4b740c07acf629f3d8402 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 22 Jun 2026 23:13:51 +0700 Subject: [PATCH 12/18] add API #1600 --- BMA.EHR.Leave/Controllers/LeaveController.cs | 142 ++++++++++++++++++ .../DTOs/AdditionalCheck/ApproveRequestDto.cs | 17 +++ 2 files changed, 159 insertions(+) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index f04c00ce..7e6bad99 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -3818,6 +3818,148 @@ namespace BMA.EHR.Leave.Service.Controllers return Success(); } + + [HttpPut("admin/edit/approve-list")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> ApproveRequestListAsync([FromBody] List reqs) + { + 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); + } + + foreach (var req in reqs) + { + if (req.Reason == null || req.Reason == string.Empty) + { + return Error("กรุณากรอกเหตุผล", StatusCodes.Status400BadRequest); + } + + var requestData = await _additionalCheckRequestRepository.GetByIdAsync(req.RecId); + 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.GetProfileByKeycloakIdNew2Async(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) /// diff --git a/BMA.EHR.Leave/DTOs/AdditionalCheck/ApproveRequestDto.cs b/BMA.EHR.Leave/DTOs/AdditionalCheck/ApproveRequestDto.cs index 7b50313b..f40ad39a 100644 --- a/BMA.EHR.Leave/DTOs/AdditionalCheck/ApproveRequestDto.cs +++ b/BMA.EHR.Leave/DTOs/AdditionalCheck/ApproveRequestDto.cs @@ -12,4 +12,21 @@ public string Reason { get; set; } } + + public class ApproveRequestListItemDto + { + /// + /// id ของ record รายการคำขอลงเวลาพิเศษนั้นๆ + /// + public Guid RecId { get; set; } + public string CheckInTime { get; set; } + + public string CheckOutTime { get; set; } + + public string CheckInStatus { get; set; } + + public string CheckOutStatus { get; set; } + + public string Reason { get; set; } + } } From 5f678b289809e23aaa4e23585bbc5115f87f60d7 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 23 Jun 2026 11:25:42 +0700 Subject: [PATCH 13/18] =?UTF-8?q?=E0=B9=81=E0=B8=81=E0=B9=89=E0=B8=9B?= =?UTF-8?q?=E0=B8=B1=E0=B8=8D=E0=B8=AB=E0=B8=B2=20RabbitMQ=20=E0=B8=9B?= =?UTF-8?q?=E0=B8=A3=E0=B8=B0=E0=B8=A1=E0=B8=A7=E0=B8=A5=E0=B8=9C=E0=B8=A5?= =?UTF-8?q?=E0=B9=83=E0=B8=99=E0=B8=84=E0=B8=B4=E0=B8=A7=E0=B8=8A=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=20=E0=B9=81=E0=B8=A5=E0=B8=B0=E0=B8=A3=E0=B8=AD?= =?UTF-8?q?=E0=B8=84=E0=B8=B4=E0=B8=A7=E0=B8=99=E0=B8=B2=E0=B8=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CHANGELOG-checkin-speedup.md | 83 +++++++++++++++++++ BMA.EHR.CheckInConsumer/Program.cs | 75 ++++++++++++----- BMA.EHR.CheckInConsumer/appsettings.json | 5 +- 3 files changed, 139 insertions(+), 24 deletions(-) create mode 100644 BMA.EHR.CheckInConsumer/CHANGELOG-checkin-speedup.md diff --git a/BMA.EHR.CheckInConsumer/CHANGELOG-checkin-speedup.md b/BMA.EHR.CheckInConsumer/CHANGELOG-checkin-speedup.md new file mode 100644 index 00000000..7b20dd1b --- /dev/null +++ b/BMA.EHR.CheckInConsumer/CHANGELOG-checkin-speedup.md @@ -0,0 +1,83 @@ +# สรุปการปรับปรุงระบบลงเวลา (CheckInConsumer) + +วันที่แก้ไข: 23 มิถุนายน 2026 + +--- + +## ปัญหาเดิม + +ตอนที่พนักงานลงเวลาพร้อมกันจำนวนมาก (ประมาณ 2,000 รายการ) ระบบประมวลผลทีละรายการ ทำให้ต้องรอคิวนานถึง **22 นาที** กว่าจะประมวลผลเสร็จทั้งหมด + +เปรียบเทียบเหมือน **โต๊ะบัญชี 1 คน รับคิวทีละคน** ทั้งที่มีคนรอ 2,000 คน → คิวยาวมาก + +--- + +## วิธีที่แก้ (เข้าใจง่าย ๆ) + +### 1. เพิ่มคนช่วยประมวลผลพร้อมกัน (Concurrency) +- **ก่อน:** ประมวลผลทีละรายการ (เหมือนมีโต๊ะบัญชี 1 โต๊ะ) +- **หลัง:** ประมวลผลพร้อมกันได้สูงสุด **5 รายการ** (เหมือนเปิดโต๊ะบัญชี 5 โต๊ะ) + +> ผลที่ได้: เวลารอคิวลดลงจาก **22 นาที → ประมาณ 4–5 นาที** + +### 2. จัดคิวล่วงหน้าให้ RabbitMQ (Prefetch) +- **ก่อน:** ระบบดึงข้อมูลมาทีละชิ้น ทำให้เสียเวลารอส่งต่อ +- **หลัง:** ระบบดึงข้อมูลมาเป็นชุด ๆ ละ 20 ชิ้นไว้เตรียมพร้อม → ลดเวลารอระหว่างรายการ + +### 3. ลดเวลารอเมื่อ API มีปัญหา (Timeout) +- **ก่อน:** ถ้า API ค้าง ระบบจะรอนานถึง **5 นาที** ต่อรายการ +- **หลัง:** ลดเหลือ **1 นาที** → รายการที่มีปัญหาจะถูกปฏิเสธเร็วขึ้น ไม่ทำให้คิวค้าง + +### 4. ปรับปรุงการเชื่อมต่อ HTTP +- เปลี่ยนระบบเชื่อมต่อให้รองรับการส่งคำขอหลายรายการพร้อมกันโดยไม่สะดุด + +--- + +## ตัวเลขเปรียบเทียบ + +| รายการ | ก่อนแก้ | หลังแก้ | +|---|---|---| +| จำนวนรายการที่ประมวลผลพร้อมกัน | 1 | 5 | +| เวลารอคิวสูงสุด (2,000 รายการ) | ~22 นาที | ~4–5 นาที | +| เวลารอเมื่อ API มีปัญหา | 5 นาที | 1 นาที | + +--- + +## ไฟล์ที่แก้ไข + +1. **`Program.cs`** — โค้ดหลักของตัวประมวลผลคิว +2. **`appsettings.json`** — ไฟล์ตั้งค่าระบบ + +--- + +## วิธีปรับความเร็วเพิ่มเติม (ไม่ต้องเขียนโค้ดใหม่) + +ถ้าหลังทดสอบแล้วเห็นว่าระบบรับได้ และอยากให้เร็วขึ้นอีก ให้แก้ไขไฟล์ `appsettings.json` แล้ว restart โปรแกรมได้เลย: + +```json +{ + "MaxConcurrency": 10, ← เพิ่มจาก 5 เป็น 10 (ประมวลผลพร้อมกัน 10 รายการ) + "PrefetchCount": 50, ← ควรตั้งเป็น ประมาณ MaxConcurrency × 2 ขึ้นไป + "HttpTimeoutSeconds": 60 ← เวลารอ API วินาที +} +``` + +**ค่าที่ใช้และผลที่คาดการณ์:** +- `MaxConcurrency = 5` → ใช้เวลา ~4–5 นาที (ค่าเริ่มต้นปลอดภัย) +- `MaxConcurrency = 10` → ใช้เวลา ~2–3 นาที +- `MaxConcurrency = 20` → ใช้เวลา ~1–2 นาที (ต้องตรวจสอบว่าระบบหลังบ้านรับไหวก่อน) + +--- + +## ข้อควรระวัง / คำแนะนำ + +1. **ควรทดสอบในระบบทดสอบก่อน** โดยดูว่า + - ไม่มี error ในระบบหลัก (API) + - ฐานข้อมูลไม่ช้าผิดปกติ + - ไม่พบปัญหาลงเวลาซ้ำซ้อน + +2. ถ้าพบปัญหา เช่น + - มี error ใน API → **ลด** `MaxConcurrency` เหลือ 2 หรือ 3 + - ลงเวลาซ้ำ → แจ้งทีมเทคนิคเพื่อแก้ฝั่ง API เพิ่มเติม + +3. **ค่า `MaxConcurrency = 5` เป็นค่าปลอดภัย** เพราะระบบ API ด้านหลังยังมีข้อจำกัดอยู่บางส่วน หากต้องการเพิ่มให้สูงกว่านี้ (เช่น 20–50) ควรปรึกษาทีมเทคนิคเพื่อปรับปรุงฝั่ง API ก่อน diff --git a/BMA.EHR.CheckInConsumer/Program.cs b/BMA.EHR.CheckInConsumer/Program.cs index 95dac001..a0ff686c 100644 --- a/BMA.EHR.CheckInConsumer/Program.cs +++ b/BMA.EHR.CheckInConsumer/Program.cs @@ -18,6 +18,13 @@ var user = configuration["Rabbit:User"] ?? ""; var pass = configuration["Rabbit:Password"] ?? ""; var queue = configuration["Rabbit:Queue"] ?? "basic-queue"; +// Concurrency & prefetch (configurable via appsettings.json) +var maxConcurrency = int.TryParse(configuration["MaxConcurrency"], out var c) && c > 0 ? c : 5; +var prefetchCount = ushort.TryParse(configuration["PrefetchCount"], out var p) && p > 0 ? p : (ushort)20; +var httpTimeoutSec = int.TryParse(configuration["HttpTimeoutSeconds"], out var t) && t > 0 ? t : 60; + +WriteToConsole($"Config -> MaxConcurrency: {maxConcurrency}, PrefetchCount: {prefetchCount}, HttpTimeout: {httpTimeoutSec}s"); + // create connection var factory = new ConnectionFactory() { @@ -32,39 +39,61 @@ using var channel = connection.CreateModel(); channel.QueueDeclare(queue: queue, durable: true, exclusive: false, autoDelete: false, arguments: null); -// Create a SINGLE static HttpClient instance to prevent socket exhaustion -using var httpClient = new HttpClient(); -httpClient.Timeout = TimeSpan.FromSeconds(300); // 5 นาที +// Prefetch: RabbitMQ จะส่ง message หลายตัวมาที่ consumer พร้อมกัน (ลด network round-trip) +channel.BasicQos(prefetchSize: 0, prefetchCount: prefetchCount, global: false); + +// HttpClient แบบ SocketsHttpHandler พร้อม connection pooling รองรับ concurrent requests +var socketsHandler = new SocketsHttpHandler +{ + MaxConnectionsPerServer = maxConcurrency * 2, + PooledConnectionLifetime = TimeSpan.FromMinutes(2), + PooledConnectionIdleTimeout = TimeSpan.FromSeconds(30) +}; +using var httpClient = new HttpClient(socketsHandler); +httpClient.Timeout = TimeSpan.FromSeconds(httpTimeoutSec); + +// SemaphoreSlim คุมจำนวน message ที่ประมวลผลพร้อมกัน (เนื่องจาก API มีข้อจำกัดเรื่อง concurrency) +using var semaphore = new SemaphoreSlim(maxConcurrency, maxConcurrency); var consumer = new AsyncEventingBasicConsumer(channel); -consumer.Received += async (model, ea) => +consumer.Received += (model, ea) => { - try + // รอ semaphore ก่อนเริ่มประมวลผล + semaphore.WaitAsync().ContinueWith(async _ => { - var body = ea.Body.ToArray(); - var message = Encoding.UTF8.GetString(body); - - WriteToConsole($"Received message: {message}"); - - var success = await CallRestApi(message, httpClient, configuration); - - if (success) + try { - channel.BasicAck(ea.DeliveryTag, multiple: false); - WriteToConsole("Message processed successfully"); + var body = ea.Body.ToArray(); + var message = Encoding.UTF8.GetString(body); + + WriteToConsole($"Received message: {message}"); + + var success = await CallRestApi(message, httpClient, configuration); + + if (success) + { + channel.BasicAck(ea.DeliveryTag, multiple: false); + WriteToConsole("Message processed successfully"); + } + else + { + channel.BasicNack(ea.DeliveryTag, multiple: false, requeue: false); + WriteToConsole("Message processing failed - message rejected"); + } } - else + catch (Exception ex) { + WriteToConsole($"Error processing message: {ex.Message}"); channel.BasicNack(ea.DeliveryTag, multiple: false, requeue: false); - WriteToConsole("Message processing failed - message rejected"); } - } - catch (Exception ex) - { - WriteToConsole($"Error processing message: {ex.Message}"); - channel.BasicNack(ea.DeliveryTag, multiple: false, requeue: false); - } + finally + { + semaphore.Release(); + } + }, TaskScheduler.Default).ConfigureAwait(false); + + return Task.CompletedTask; }; channel.BasicConsume(queue: queue, autoAck: false, consumer: consumer); diff --git a/BMA.EHR.CheckInConsumer/appsettings.json b/BMA.EHR.CheckInConsumer/appsettings.json index 76f86c86..1fbba85b 100644 --- a/BMA.EHR.CheckInConsumer/appsettings.json +++ b/BMA.EHR.CheckInConsumer/appsettings.json @@ -5,5 +5,8 @@ "Password": "12345678", "Queue": "hrms-checkin-queue-dev" }, - "API": "https://localhost:7283/api/v1" + "API": "https://localhost:7283/api/v1", + "MaxConcurrency": 5, + "PrefetchCount": 20, + "HttpTimeoutSeconds": 60 } From 65ca175f980820fa1d56f7a8fcf5e73a3875a195 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 23 Jun 2026 11:38:40 +0700 Subject: [PATCH 14/18] fix Dockerfile --- BMA.EHR.CheckInConsumer/.dockerignore | 24 ++++++++++++++++++++++++ BMA.EHR.CheckInConsumer/Dockerfile | 22 ++++++++-------------- 2 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 BMA.EHR.CheckInConsumer/.dockerignore diff --git a/BMA.EHR.CheckInConsumer/.dockerignore b/BMA.EHR.CheckInConsumer/.dockerignore new file mode 100644 index 00000000..741ce7d4 --- /dev/null +++ b/BMA.EHR.CheckInConsumer/.dockerignore @@ -0,0 +1,24 @@ +# Build artifacts +bin/ +obj/ + +# IDE / tooling +Properties/ +.vs/ +.vscode/ +.idea/ + +# Source control +.git/ +.gitignore + +# Documentation +*.md + +# Docker +Dockerfile +.dockerignore + +# OS files +.DS_Store +Thumbs.db diff --git a/BMA.EHR.CheckInConsumer/Dockerfile b/BMA.EHR.CheckInConsumer/Dockerfile index 1c90d6f2..314f1b71 100644 --- a/BMA.EHR.CheckInConsumer/Dockerfile +++ b/BMA.EHR.CheckInConsumer/Dockerfile @@ -1,4 +1,4 @@ -## See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. +## See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. # ## This stage is used when running from VS in fast mode (Default for Debug configuration) #FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base @@ -34,25 +34,19 @@ FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build # กำหนด working directory ภายใน container WORKDIR /src -# คัดลอกไฟล์ .csproj และ restore dependencies -# COPY *.csproj ./ -COPY . ./ -RUN dotnet restore +# copy เฉพาะ .csproj ก่อน เพื่อใช้ layer caching (restore เร็ว เก็บ cache นาน) +COPY BMA.EHR.CheckInConsumer.csproj ./ +RUN dotnet restore "BMA.EHR.CheckInConsumer.csproj" -# คัดลอกไฟล์ทั้งหมดและ build +# คัดลอก source ที่เหลือแล้ว publish COPY . ./ -RUN dotnet build -c Release -o /app/build -# WORKDIR "/src/BMA.EHR.CheckInConsumer" -# RUN dotnet build "BMA.EHR.CheckInConsumer.csproj" -c Release -o /app/build +RUN dotnet publish "BMA.EHR.CheckInConsumer.csproj" -c Release -o /app/publish /p:UseAppHost=false -# ใช้ stage ใหม่สำหรับการ runtime +# ใช้ stage ใหม่สำหรับ runtime (image เล็กลง) FROM mcr.microsoft.com/dotnet/runtime:8.0 AS runtime -# กำหนด working directory สำหรับ runtime WORKDIR /app -# คัดลอกไฟล์จาก build stage มายัง runtime stage -COPY --from=build /app/build . +COPY --from=build /app/publish . -# ระบุ entry point ของแอปพลิเคชัน ENTRYPOINT ["dotnet", "BMA.EHR.CheckInConsumer.dll"] From f6c8b4f754ac54e34ad7ce0cbbb5d6fe44dc1b9c Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 23 Jun 2026 11:45:28 +0700 Subject: [PATCH 15/18] Change Dockerfile --- BMA.EHR.CheckInConsumer/Dockerfile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/BMA.EHR.CheckInConsumer/Dockerfile b/BMA.EHR.CheckInConsumer/Dockerfile index 314f1b71..81c430d9 100644 --- a/BMA.EHR.CheckInConsumer/Dockerfile +++ b/BMA.EHR.CheckInConsumer/Dockerfile @@ -21,6 +21,7 @@ #ARG BUILD_CONFIGURATION=Release #RUN dotnet publish "BMA.EHR.CheckInConsumer.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false # + ## This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration) #FROM base AS final #WORKDIR /app @@ -29,17 +30,18 @@ # ใช้ official .NET SDK image สำหรับการ build +# Note: Build context = repository root (ตามที่ GitHub Actions ใช้) FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build -# กำหนด working directory ภายใน container WORKDIR /src # copy เฉพาะ .csproj ก่อน เพื่อใช้ layer caching (restore เร็ว เก็บ cache นาน) -COPY BMA.EHR.CheckInConsumer.csproj ./ +COPY BMA.EHR.CheckInConsumer/BMA.EHR.CheckInConsumer.csproj ./BMA.EHR.CheckInConsumer/ +WORKDIR /src/BMA.EHR.CheckInConsumer RUN dotnet restore "BMA.EHR.CheckInConsumer.csproj" # คัดลอก source ที่เหลือแล้ว publish -COPY . ./ +COPY BMA.EHR.CheckInConsumer/ ./ RUN dotnet publish "BMA.EHR.CheckInConsumer.csproj" -c Release -o /app/publish /p:UseAppHost=false # ใช้ stage ใหม่สำหรับ runtime (image เล็กลง) From bf5dc2cf190533b31556e47491022cc0c8820a49 Mon Sep 17 00:00:00 2001 From: harid Date: Tue, 23 Jun 2026 18:24:30 +0700 Subject: [PATCH 16/18] =?UTF-8?q?=E0=B9=80=E0=B8=81=E0=B9=87=E0=B8=9A=20la?= =?UTF-8?q?stUpdate=20=E0=B8=AB=E0=B8=B2=E0=B8=81=E0=B8=A1=E0=B8=B5?= =?UTF-8?q?=E0=B9=80=E0=B8=8A=E0=B9=87=E0=B8=84=E0=B8=AD=E0=B8=AD=E0=B8=81?= =?UTF-8?q?=E0=B8=84=E0=B8=B3=E0=B8=AA=E0=B8=B1=E0=B9=88=E0=B8=87=E0=B8=A2?= =?UTF-8?q?=E0=B9=89=E0=B8=AD=E0=B8=99=E0=B8=AB=E0=B8=A5=E0=B8=B1=E0=B8=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PlacementAppointmentController.cs | 48 ++++++++++++++++--- .../Controllers/PlacementController.cs | 30 ++++++++++-- .../Controllers/PlacementOfficerController.cs | 3 ++ .../Controllers/PlacementReceiveController.cs | 8 +++- .../PlacementRepatriationController.cs | 8 +++- .../PlacementTransferController.cs | 8 +++- 6 files changed, 93 insertions(+), 12 deletions(-) diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs index 50114759..a8d91874 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs @@ -1062,7 +1062,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); + data.ForEach(profile => + { + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = DateTime.Now; + }); await _context.SaveChangesAsync(); // Return resultData for Node to process directly (Linear Flow) @@ -1286,7 +1292,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); + data.ForEach(profile => + { + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = DateTime.Now; + }); await _context.SaveChangesAsync(); // Return resultData for Node to process directly (Linear Flow) @@ -1494,7 +1506,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); + data.ForEach(profile => + { + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = DateTime.Now; + }); await _context.SaveChangesAsync(); // Return resultData for Node to process directly (Linear Flow) @@ -1707,7 +1725,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); + data.ForEach(profile => + { + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = DateTime.Now; + }); await _context.SaveChangesAsync(); // Return resultData for Node to process directly (Linear Flow) @@ -1943,7 +1967,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); + data.ForEach(profile => + { + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = DateTime.Now; + }); await _context.SaveChangesAsync(); // Return resultData for Node to process directly (Linear Flow) @@ -2114,7 +2144,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); + data.ForEach(profile => + { + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = DateTime.Now; + }); await _context.SaveChangesAsync(); // Return resultData for Node to process directly (Linear Flow) diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs index 9557ae00..1e1d5417 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs @@ -2035,6 +2035,9 @@ namespace BMA.EHR.Placement.Service.Controllers placementProfile.ForEach(profile => { profile.PlacementStatus = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = DateTime.Now; if (req.refIds.Length > 0) { profile.commandId = req.refIds[0].commandId; @@ -2441,6 +2444,9 @@ namespace BMA.EHR.Placement.Service.Controllers placementProfile.ForEach(profile => { profile.PlacementStatus = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = DateTime.Now; if (req.refIds.Length > 0) { profile.commandId = req.refIds[0].commandId; @@ -2710,7 +2716,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.PlacementStatus = "DONE"); + data.ForEach(profile => + { + profile.PlacementStatus = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = DateTime.Now; + }); await _context.SaveChangesAsync(); // Return resultData for Node to process directly (Linear Flow) @@ -2960,7 +2972,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.PlacementStatus = "DONE"); + data.ForEach(profile => + { + profile.PlacementStatus = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = DateTime.Now; + }); await _context.SaveChangesAsync(); // Return resultData for Node to process directly (Linear Flow) @@ -3193,7 +3211,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.PlacementStatus = "DONE"); + data.ForEach(profile => + { + profile.PlacementStatus = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = DateTime.Now; + }); await _context.SaveChangesAsync(); // Return resultData for Node to process directly (Linear Flow) diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementOfficerController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementOfficerController.cs index 8fcb51cf..7c26a018 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementOfficerController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementOfficerController.cs @@ -832,6 +832,9 @@ namespace BMA.EHR.Placement.Service.Controllers { profile.Status = "DONE"; profile.commandNo = commandNoText; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = DateTime.Now; }); await _context.SaveChangesAsync(); diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs index 32f91eae..262d86bd 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs @@ -1306,7 +1306,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - data.ForEach(profile => profile.Status = "DONE"); + data.ForEach(profile => + { + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = DateTime.Now; + }); Console.WriteLine($"[ReceiveReportExcecute] Saving changes to database for {data.Count} profiles"); await _context.SaveChangesAsync(); Console.WriteLine($"[ReceiveReportExcecute] Process completed successfully at {DateTime.Now}"); diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementRepatriationController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementRepatriationController.cs index b56fc804..97e1e406 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementRepatriationController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementRepatriationController.cs @@ -686,7 +686,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); + data.ForEach(profile => + { + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = DateTime.Now; + }); await _context.SaveChangesAsync(); // Return resultData for Node to process directly (Linear Flow) diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs index 73741918..843fa25a 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs @@ -1212,7 +1212,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); + data.ForEach(profile => + { + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = DateTime.Now; + }); // Return resultData for Node to process directly (Linear Flow) return Success(resultData); From 5021c00dd3a75ef5304c85332aaf3cb80147629a Mon Sep 17 00:00:00 2001 From: harid Date: Wed, 24 Jun 2026 16:20:49 +0700 Subject: [PATCH 17/18] =?UTF-8?q?=E0=B9=80=E0=B8=81=E0=B9=87=E0=B8=9A=20la?= =?UTF-8?q?stUpdate=20=E0=B8=AB=E0=B8=B2=E0=B8=81=E0=B8=A1=E0=B8=B5?= =?UTF-8?q?=E0=B9=80=E0=B8=8A=E0=B9=87=E0=B8=84=E0=B8=AD=E0=B8=AD=E0=B8=81?= =?UTF-8?q?=E0=B8=84=E0=B8=B3=E0=B8=AA=E0=B8=B1=E0=B9=88=E0=B8=87=E0=B8=A2?= =?UTF-8?q?=E0=B9=89=E0=B8=AD=E0=B8=99=E0=B8=AB=E0=B8=A5=E0=B8=B1=E0=B8=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PlacementAppointmentController.cs | 18 ++++++++----- .../Controllers/PlacementController.cs | 15 +++++++---- .../Controllers/PlacementOfficerController.cs | 3 ++- .../Controllers/PlacementReceiveController.cs | 3 ++- .../PlacementRepatriationController.cs | 3 ++- .../PlacementTransferController.cs | 3 ++- .../Controllers/RetirementOtherController.cs | 18 +++++++++++-- .../Controllers/RetirementOutController.cs | 9 ++++++- .../Controllers/RetirementResignController.cs | 27 ++++++++++++++++--- .../RetirementResignEmployeeController.cs | 18 +++++++++++-- 10 files changed, 94 insertions(+), 23 deletions(-) diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs index a8d91874..c7cbf249 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs @@ -1062,12 +1062,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + var now = DateTime.Now; data.ForEach(profile => { profile.Status = "DONE"; profile.LastUpdateFullName = FullName ?? "System Administrator"; profile.LastUpdateUserId = UserId ?? ""; - profile.LastUpdatedAt = DateTime.Now; + profile.LastUpdatedAt = now; }); await _context.SaveChangesAsync(); @@ -1292,12 +1293,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + var now = DateTime.Now; data.ForEach(profile => { profile.Status = "DONE"; profile.LastUpdateFullName = FullName ?? "System Administrator"; profile.LastUpdateUserId = UserId ?? ""; - profile.LastUpdatedAt = DateTime.Now; + profile.LastUpdatedAt = now; }); await _context.SaveChangesAsync(); @@ -1506,12 +1508,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + var now = DateTime.Now; data.ForEach(profile => { profile.Status = "DONE"; profile.LastUpdateFullName = FullName ?? "System Administrator"; profile.LastUpdateUserId = UserId ?? ""; - profile.LastUpdatedAt = DateTime.Now; + profile.LastUpdatedAt = now; }); await _context.SaveChangesAsync(); @@ -1725,12 +1728,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + var now = DateTime.Now; data.ForEach(profile => { profile.Status = "DONE"; profile.LastUpdateFullName = FullName ?? "System Administrator"; profile.LastUpdateUserId = UserId ?? ""; - profile.LastUpdatedAt = DateTime.Now; + profile.LastUpdatedAt = now; }); await _context.SaveChangesAsync(); @@ -1967,12 +1971,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + var now = DateTime.Now; data.ForEach(profile => { profile.Status = "DONE"; profile.LastUpdateFullName = FullName ?? "System Administrator"; profile.LastUpdateUserId = UserId ?? ""; - profile.LastUpdatedAt = DateTime.Now; + profile.LastUpdatedAt = now; }); await _context.SaveChangesAsync(); @@ -2144,12 +2149,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + var now = DateTime.Now; data.ForEach(profile => { profile.Status = "DONE"; profile.LastUpdateFullName = FullName ?? "System Administrator"; profile.LastUpdateUserId = UserId ?? ""; - profile.LastUpdatedAt = DateTime.Now; + profile.LastUpdatedAt = now; }); await _context.SaveChangesAsync(); diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs index 1e1d5417..af1d954e 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs @@ -2032,12 +2032,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow + var now = DateTime.Now; placementProfile.ForEach(profile => { profile.PlacementStatus = "DONE"; profile.LastUpdateFullName = FullName ?? "System Administrator"; profile.LastUpdateUserId = UserId ?? ""; - profile.LastUpdatedAt = DateTime.Now; + profile.LastUpdatedAt = now; if (req.refIds.Length > 0) { profile.commandId = req.refIds[0].commandId; @@ -2441,12 +2442,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow + var now = DateTime.Now; placementProfile.ForEach(profile => { profile.PlacementStatus = "DONE"; profile.LastUpdateFullName = FullName ?? "System Administrator"; profile.LastUpdateUserId = UserId ?? ""; - profile.LastUpdatedAt = DateTime.Now; + profile.LastUpdatedAt = now; if (req.refIds.Length > 0) { profile.commandId = req.refIds[0].commandId; @@ -2716,12 +2718,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + var now = DateTime.Now; data.ForEach(profile => { profile.PlacementStatus = "DONE"; profile.LastUpdateFullName = FullName ?? "System Administrator"; profile.LastUpdateUserId = UserId ?? ""; - profile.LastUpdatedAt = DateTime.Now; + profile.LastUpdatedAt = now; }); await _context.SaveChangesAsync(); @@ -2972,12 +2975,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + var now = DateTime.Now; data.ForEach(profile => { profile.PlacementStatus = "DONE"; profile.LastUpdateFullName = FullName ?? "System Administrator"; profile.LastUpdateUserId = UserId ?? ""; - profile.LastUpdatedAt = DateTime.Now; + profile.LastUpdatedAt = now; }); await _context.SaveChangesAsync(); @@ -3211,12 +3215,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + var now = DateTime.Now; data.ForEach(profile => { profile.PlacementStatus = "DONE"; profile.LastUpdateFullName = FullName ?? "System Administrator"; profile.LastUpdateUserId = UserId ?? ""; - profile.LastUpdatedAt = DateTime.Now; + profile.LastUpdatedAt = now; }); await _context.SaveChangesAsync(); diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementOfficerController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementOfficerController.cs index 7c26a018..29d38df5 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementOfficerController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementOfficerController.cs @@ -828,13 +828,14 @@ namespace BMA.EHR.Placement.Service.Controllers // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ var firstRef = req.refIds.FirstOrDefault(); var commandNoText = firstRef != null ? $"{firstRef.commandNo}/{firstRef.commandYear.ToThaiYear()}" : null; + var now = DateTime.Now; data.ForEach(profile => { profile.Status = "DONE"; profile.commandNo = commandNoText; profile.LastUpdateFullName = FullName ?? "System Administrator"; profile.LastUpdateUserId = UserId ?? ""; - profile.LastUpdatedAt = DateTime.Now; + profile.LastUpdatedAt = now; }); await _context.SaveChangesAsync(); diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs index 262d86bd..31db814b 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs @@ -1306,12 +1306,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow + var now = DateTime.Now; data.ForEach(profile => { profile.Status = "DONE"; profile.LastUpdateFullName = FullName ?? "System Administrator"; profile.LastUpdateUserId = UserId ?? ""; - profile.LastUpdatedAt = DateTime.Now; + profile.LastUpdatedAt = now; }); Console.WriteLine($"[ReceiveReportExcecute] Saving changes to database for {data.Count} profiles"); await _context.SaveChangesAsync(); diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementRepatriationController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementRepatriationController.cs index 97e1e406..2cc8536d 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementRepatriationController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementRepatriationController.cs @@ -686,12 +686,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + var now = DateTime.Now; data.ForEach(profile => { profile.Status = "DONE"; profile.LastUpdateFullName = FullName ?? "System Administrator"; profile.LastUpdateUserId = UserId ?? ""; - profile.LastUpdatedAt = DateTime.Now; + profile.LastUpdatedAt = now; }); await _context.SaveChangesAsync(); diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs index 843fa25a..a165be68 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs @@ -1212,12 +1212,13 @@ namespace BMA.EHR.Placement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ + var now = DateTime.Now; data.ForEach(profile => { profile.Status = "DONE"; profile.LastUpdateFullName = FullName ?? "System Administrator"; profile.LastUpdateUserId = UserId ?? ""; - profile.LastUpdatedAt = DateTime.Now; + profile.LastUpdatedAt = now; }); // Return resultData for Node to process directly (Linear Flow) diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs index 868aac4f..b1580cf5 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs @@ -926,7 +926,14 @@ namespace BMA.EHR.Retirement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); + var now = DateTime.Now; + data.ForEach(profile => + { + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); await _context.SaveChangesAsync(); // Return resultData for Node to process directly (Linear Flow) @@ -1183,7 +1190,14 @@ namespace BMA.EHR.Retirement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); + var now = DateTime.Now; + data.ForEach(profile => + { + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); await _context.SaveChangesAsync(); // Return resultData for Node to process directly (Linear Flow) diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs index ae528316..003166f9 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs @@ -712,7 +712,14 @@ namespace BMA.EHR.Retirement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); + var now = DateTime.Now; + data.ForEach(profile => + { + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); await _context.SaveChangesAsync(); // Return resultData for Node to process directly (Linear Flow) diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs index 1b434380..019451c5 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs @@ -2972,7 +2972,14 @@ namespace BMA.EHR.Retirement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); + var now = DateTime.Now; + data.ForEach(profile => + { + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); await _context.SaveChangesAsync(); // Return resultData for Node to process directly (Linear Flow) @@ -3164,7 +3171,14 @@ namespace BMA.EHR.Retirement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); + var now = DateTime.Now; + data.ForEach(profile => + { + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); await _context.SaveChangesAsync(); // Return resultData for Node to process directly (Linear Flow) @@ -3399,7 +3413,14 @@ namespace BMA.EHR.Retirement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); + var now = DateTime.Now; + data.ForEach(profile => + { + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); data.ForEach(profile => profile.RetirementResign.Status = "CANCEL"); await _context.SaveChangesAsync(); diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs index 4449099b..dd0d850b 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs @@ -2468,7 +2468,14 @@ namespace BMA.EHR.Retirement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); + var now = DateTime.Now; + data.ForEach(profile => + { + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); await _context.SaveChangesAsync(); // Return resultData for Node to process directly (Linear Flow) @@ -2700,7 +2707,14 @@ namespace BMA.EHR.Retirement.Service.Controllers #endregion // New: Linear Flow - Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); + var now = DateTime.Now; + data.ForEach(profile => + { + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); data.ForEach(profile => profile.RetirementResignEmployee.Status = "CANCEL"); await _context.SaveChangesAsync(); From a2fe424b87e2c04ea399c73b41ca4c7585360a35 Mon Sep 17 00:00:00 2001 From: harid Date: Fri, 26 Jun 2026 16:38:41 +0700 Subject: [PATCH 18/18] Linear Flow (DisciplineService) #224 --- .../Controllers/DisciplineResultController.cs | 751 +++++++++++------- .../PlacementAppointmentController.cs | 2 +- .../Controllers/RetirementOutController.cs | 2 +- 3 files changed, 472 insertions(+), 283 deletions(-) diff --git a/BMA.EHR.Discipline.Service/Controllers/DisciplineResultController.cs b/BMA.EHR.Discipline.Service/Controllers/DisciplineResultController.cs index 35a9dfa2..f6697c2e 100644 --- a/BMA.EHR.Discipline.Service/Controllers/DisciplineResultController.cs +++ b/BMA.EHR.Discipline.Service/Controllers/DisciplineResultController.cs @@ -1028,10 +1028,6 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); - // Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => { profile.Status = "REPORTED"; profile.CommandTypeId = null; }); - await _context.SaveChangesAsync(); - var resultData = (from p in data join r in req.refIds on p.Id.ToString() equals r.refId @@ -1067,52 +1063,95 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers posNoAbb = p.child4ShortName != null ? p.child4ShortName : (p.child3ShortName != null ? p.child3ShortName : (p.child2ShortName != null ? p.child2ShortName : (p.child1ShortName != null ? p.child1ShortName : (p.rootShortName != null ? p.rootShortName : "")))), }).ToList(); - var baseAPIOrg = _configuration["API"]; - var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; - using (var client = new HttpClient()) + #region Old: Circular Flow + // var baseAPIOrg = _configuration["API"]; + // var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData, + // }); + // var _result = await _res.Content.ReadAsStringAsync(); + // if (_res.IsSuccessStatusCode) + // { + // // คำสั่งไล่ออก หรือ ปลดออก Status หลังออกคำสั่งใช้ "REPORTED" เพื่อไม่ให้ส่งรายชื่อไปออกคำสั่งซ้ำได้ + // data.ForEach(profile => { profile.Status = "REPORTED"; profile.CommandTypeId = null; }); + // var _profile = new List(); + // DateTime _date = DateTime.Now; + // foreach (var item in data) + // { + // _profile.Add(new ProfileComplaintInvestigate + // { + // PersonId = item.PersonId, + // Prefix = item.Prefix, + // FirstName = item.FirstName, + // LastName = item.LastName, + // CitizenId = item.CitizenId, + // rootDnaId = item.rootDnaId, + // child1DnaId = item.child1DnaId, + // child2DnaId = item.child2DnaId, + // child3DnaId = item.child3DnaId, + // child4DnaId = item.child4DnaId, + // profileType = item.profileType, + // commandType = "C-PM-19", + // CreatedAt = _date, + // CreatedUserId = UserId, + // CreatedFullName = FullName, + // LastUpdatedAt = _date, + // LastUpdateUserId = UserId, + // LastUpdateFullName = FullName, + // }); + // } + // _context.ProfileComplaintInvestigate.AddRange(_profile); + // await _context.SaveChangesAsync(); + // } + // } + #endregion + + // New: Linear Flow + var now = DateTime.Now; + data.ForEach(profile => { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new + profile.Status = "REPORTED"; + profile.CommandTypeId = null; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); + + var _profile = new List(); + foreach (var item in data) + { + _profile.Add(new ProfileComplaintInvestigate { - data = resultData, + PersonId = item.PersonId, + Prefix = item.Prefix, + FirstName = item.FirstName, + LastName = item.LastName, + CitizenId = item.CitizenId, + rootDnaId = item.rootDnaId, + child1DnaId = item.child1DnaId, + child2DnaId = item.child2DnaId, + child3DnaId = item.child3DnaId, + child4DnaId = item.child4DnaId, + profileType = item.profileType, + commandType = "C-PM-19", + CreatedAt = now, + CreatedUserId = UserId ?? "", + CreatedFullName = FullName ?? "System Administrator", + LastUpdatedAt = now, + LastUpdateUserId = UserId ?? "", + LastUpdateFullName = FullName ?? "System Administrator", }); - var _result = await _res.Content.ReadAsStringAsync(); - if (_res.IsSuccessStatusCode) - { - //// คำสั่งไล่ออก หรือ ปลดออก Status หลังออกคำสั่งใช้ "REPORTED" เพื่อไม่ให้ส่งรายชื่อไปออกคำสั่งซ้ำได้ - // data.ForEach(profile => { profile.Status = "REPORTED"; profile.CommandTypeId = null; }); - var _profile = new List(); - DateTime _date = DateTime.Now; - foreach (var item in data) - { - _profile.Add(new ProfileComplaintInvestigate - { - PersonId = item.PersonId, - Prefix = item.Prefix, - FirstName = item.FirstName, - LastName = item.LastName, - CitizenId = item.CitizenId, - rootDnaId = item.rootDnaId, - child1DnaId = item.child1DnaId, - child2DnaId = item.child2DnaId, - child3DnaId = item.child3DnaId, - child4DnaId = item.child4DnaId, - profileType = item.profileType, - commandType = "C-PM-19", - CreatedAt = _date, - CreatedUserId = UserId, - CreatedFullName = FullName, - LastUpdatedAt = _date, - LastUpdateUserId = UserId, - LastUpdateFullName = FullName, - }); - } - _context.ProfileComplaintInvestigate.AddRange(_profile); - await _context.SaveChangesAsync(); - } } - return Success(); + _context.ProfileComplaintInvestigate.AddRange(_profile); + await _context.SaveChangesAsync(); + + // Return resultData for Node to process directly (Linear Flow) + return Success(resultData); } /// @@ -1184,10 +1223,6 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); - // Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => { profile.Status = "REPORTED"; profile.CommandTypeId = null; }); - await _context.SaveChangesAsync(); - var resultData = (from p in data join r in req.refIds on p.Id.ToString() equals r.refId @@ -1223,52 +1258,95 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers posNoAbb = p.child4ShortName != null ? p.child4ShortName : (p.child3ShortName != null ? p.child3ShortName : (p.child2ShortName != null ? p.child2ShortName : (p.child1ShortName != null ? p.child1ShortName : (p.rootShortName != null ? p.rootShortName : "")))), }).ToList(); - var baseAPIOrg = _configuration["API"]; - var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; - using (var client = new HttpClient()) + #region Old: Circular Flow + // var baseAPIOrg = _configuration["API"]; + // var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData, + // }); + // var _result = await _res.Content.ReadAsStringAsync(); + // if (_res.IsSuccessStatusCode) + // { + // // คำสั่งไล่ออก หรือ ปลดออก Status หลังออกคำสั่งใช้ "REPORTED" เพื่อไม่ให้ส่งรายชื่อไปออกคำสั่งซ้ำได้ + // data.ForEach(profile => { profile.Status = "REPORTED"; profile.CommandTypeId = null; }); + // var _profile = new List(); + // DateTime _date = DateTime.Now; + // foreach (var item in data) + // { + // _profile.Add(new ProfileComplaintInvestigate + // { + // PersonId = item.PersonId, + // Prefix = item.Prefix, + // FirstName = item.FirstName, + // LastName = item.LastName, + // CitizenId = item.CitizenId, + // rootDnaId = item.rootDnaId, + // child1DnaId = item.child1DnaId, + // child2DnaId = item.child2DnaId, + // child3DnaId = item.child3DnaId, + // child4DnaId = item.child4DnaId, + // profileType = item.profileType, + // commandType = "C-PM-20", + // CreatedAt = _date, + // CreatedUserId = UserId, + // CreatedFullName = FullName, + // LastUpdatedAt = _date, + // LastUpdateUserId = UserId, + // LastUpdateFullName = FullName, + // }); + // } + // _context.ProfileComplaintInvestigate.AddRange(_profile); + // await _context.SaveChangesAsync(); + // } + // } + #endregion + + // New: Linear Flow + var now = DateTime.Now; + data.ForEach(profile => { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new + profile.Status = "REPORTED"; + profile.CommandTypeId = null; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); + + var _profile = new List(); + foreach (var item in data) + { + _profile.Add(new ProfileComplaintInvestigate { - data = resultData, + PersonId = item.PersonId, + Prefix = item.Prefix, + FirstName = item.FirstName, + LastName = item.LastName, + CitizenId = item.CitizenId, + rootDnaId = item.rootDnaId, + child1DnaId = item.child1DnaId, + child2DnaId = item.child2DnaId, + child3DnaId = item.child3DnaId, + child4DnaId = item.child4DnaId, + profileType = item.profileType, + commandType = "C-PM-20", + CreatedAt = now, + CreatedUserId = UserId ?? "", + CreatedFullName = FullName ?? "System Administrator", + LastUpdatedAt = now, + LastUpdateUserId = UserId ?? "", + LastUpdateFullName = FullName ?? "System Administrator", }); - var _result = await _res.Content.ReadAsStringAsync(); - if (_res.IsSuccessStatusCode) - { - //// คำสั่งไล่ออก หรือ ปลดออก Status หลังออกคำสั่งใช้ "REPORTED" เพื่อไม่ให้ส่งรายชื่อไปออกคำสั่งซ้ำได้ - // data.ForEach(profile => { profile.Status = "REPORTED"; profile.CommandTypeId = null; }); - var _profile = new List(); - DateTime _date = DateTime.Now; - foreach (var item in data) - { - _profile.Add(new ProfileComplaintInvestigate - { - PersonId = item.PersonId, - Prefix = item.Prefix, - FirstName = item.FirstName, - LastName = item.LastName, - CitizenId = item.CitizenId, - rootDnaId = item.rootDnaId, - child1DnaId = item.child1DnaId, - child2DnaId = item.child2DnaId, - child3DnaId = item.child3DnaId, - child4DnaId = item.child4DnaId, - profileType = item.profileType, - commandType = "C-PM-20", - CreatedAt = _date, - CreatedUserId = UserId, - CreatedFullName = FullName, - LastUpdatedAt = _date, - LastUpdateUserId = UserId, - LastUpdateFullName = FullName, - }); - } - _context.ProfileComplaintInvestigate.AddRange(_profile); - await _context.SaveChangesAsync(); - } } - return Success(); + _context.ProfileComplaintInvestigate.AddRange(_profile); + await _context.SaveChangesAsync(); + + // Return resultData for Node to process directly (Linear Flow) + return Success(resultData); } /// @@ -1418,10 +1496,6 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); - // Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); - await _context.SaveChangesAsync(); - var resultData = (from p in data join r in req.refIds on p.Id.ToString() equals r.refId @@ -1457,24 +1531,39 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers posNoAbb = p.child4ShortName != null ? p.child4ShortName : (p.child3ShortName != null ? p.child3ShortName : (p.child2ShortName != null ? p.child2ShortName : (p.child1ShortName != null ? p.child1ShortName : (p.rootShortName != null ? p.rootShortName : "")))), }).ToList(); - var baseAPIOrg = _configuration["API"]; - var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; - using (var client = new HttpClient()) + #region Old: Circular Flow + // var baseAPIOrg = _configuration["API"]; + // var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData, + // }); + // var _result = await _res.Content.ReadAsStringAsync(); + // if (_res.IsSuccessStatusCode) + // { + // data.ForEach(profile => profile.Status = "DONE"); + // await _context.SaveChangesAsync(); + // } + // } + #endregion + + // New: Linear Flow + var now = DateTime.Now; + data.ForEach(profile => { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - data = resultData, - }); - //// var _result = await _res.Content.ReadAsStringAsync(); - //// if (_res.IsSuccessStatusCode) - //// { - //// data.ForEach(profile => profile.Status = "DONE"); - //// await _context.SaveChangesAsync(); - //// } - } - return Success(); + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); + await _context.SaveChangesAsync(); + + // Return resultData for Node to process directly (Linear Flow) + return Success(resultData); } /// @@ -1562,10 +1651,6 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); - // Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.Status = "DONE"); - await _context.SaveChangesAsync(); - var resultData = (from p in data join r in req.refIds on p.Id.ToString() equals r.refId @@ -1601,24 +1686,39 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers posNoAbb = p.child4ShortName != null ? p.child4ShortName : (p.child3ShortName != null ? p.child3ShortName : (p.child2ShortName != null ? p.child2ShortName : (p.child1ShortName != null ? p.child1ShortName : (p.rootShortName != null ? p.rootShortName : "")))), }).ToList(); - var baseAPIOrg = _configuration["API"]; - var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; - using (var client = new HttpClient()) + #region Old: Circular Flow + // var baseAPIOrg = _configuration["API"]; + // var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData, + // }); + // var _result = await _res.Content.ReadAsStringAsync(); + // if (_res.IsSuccessStatusCode) + // { + // data.ForEach(profile => profile.Status = "DONE"); + // await _context.SaveChangesAsync(); + // } + // } + #endregion + + // New: Linear Flow + var now = DateTime.Now; + data.ForEach(profile => { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - data = resultData, - }); - //// var _result = await _res.Content.ReadAsStringAsync(); - //// if (_res.IsSuccessStatusCode) - //// { - //// data.ForEach(profile => profile.Status = "DONE"); - //// await _context.SaveChangesAsync(); - //// } - } - return Success(); + profile.Status = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); + await _context.SaveChangesAsync(); + + // Return resultData for Node to process directly (Linear Flow) + return Success(resultData); } /// @@ -1704,10 +1804,6 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); - // Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); - await _context.SaveChangesAsync(); - string? _null = null; var resultData = (from p in data join r in req.refIds @@ -1743,24 +1839,40 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers posNoAbb = p.child4ShortName != null ? p.child4ShortName : (p.child3ShortName != null ? p.child3ShortName : (p.child2ShortName != null ? p.child2ShortName : (p.child1ShortName != null ? p.child1ShortName : (p.rootShortName != null ? p.rootShortName : "")))), }).ToList(); - var baseAPIOrg = _configuration["API"]; - var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; - using (var client = new HttpClient()) + #region Old: Circular Flow + // var baseAPIOrg = _configuration["API"]; + // var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData, + // }); + // var _result = await _res.Content.ReadAsStringAsync(); + // if (_res.IsSuccessStatusCode) + // { + // data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); + // await _context.SaveChangesAsync(); + // } + // } + #endregion + + // New: Linear Flow + var now = DateTime.Now; + data.ForEach(profile => { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - data = resultData, - }); - //// var _result = await _res.Content.ReadAsStringAsync(); - //// if (_res.IsSuccessStatusCode) - //// { - //// data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); - //// await _context.SaveChangesAsync(); - //// } - } - return Success(); + profile.Status = "NEW"; + profile.CommandTypeId = null; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); + await _context.SaveChangesAsync(); + + // Return resultData for Node to process directly (Linear Flow) + return Success(resultData); } /// @@ -1846,10 +1958,6 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); - // Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); - await _context.SaveChangesAsync(); - string? _null = null; var resultData = (from p in data join r in req.refIds @@ -1885,24 +1993,40 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers posNoAbb = p.child4ShortName != null ? p.child4ShortName : (p.child3ShortName != null ? p.child3ShortName : (p.child2ShortName != null ? p.child2ShortName : (p.child1ShortName != null ? p.child1ShortName : (p.rootShortName != null ? p.rootShortName : "")))), }).ToList(); - var baseAPIOrg = _configuration["API"]; - var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; - using (var client = new HttpClient()) + #region Old: Circular Flow + // var baseAPIOrg = _configuration["API"]; + // var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData, + // }); + // var _result = await _res.Content.ReadAsStringAsync(); + // if (_res.IsSuccessStatusCode) + // { + // data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); + // await _context.SaveChangesAsync(); + // } + // } + #endregion + + // New: Linear Flow + var now = DateTime.Now; + data.ForEach(profile => { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - data = resultData, - }); - //// var _result = await _res.Content.ReadAsStringAsync(); - //// if (_res.IsSuccessStatusCode) - //// { - //// data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); - //// await _context.SaveChangesAsync(); - //// } - } - return Success(); + profile.Status = "NEW"; + profile.CommandTypeId = null; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); + await _context.SaveChangesAsync(); + + // Return resultData for Node to process directly (Linear Flow) + return Success(resultData); } /// @@ -1988,10 +2112,6 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); - // Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); - await _context.SaveChangesAsync(); - string? _null = null; var resultData = (from p in data join r in req.refIds @@ -2027,24 +2147,40 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers posNoAbb = p.child4ShortName != null ? p.child4ShortName : (p.child3ShortName != null ? p.child3ShortName : (p.child2ShortName != null ? p.child2ShortName : (p.child1ShortName != null ? p.child1ShortName : (p.rootShortName != null ? p.rootShortName : "")))), }).ToList(); - var baseAPIOrg = _configuration["API"]; - var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; - using (var client = new HttpClient()) + #region Old: Circular Flow + // var baseAPIOrg = _configuration["API"]; + // var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData, + // }); + // var _result = await _res.Content.ReadAsStringAsync(); + // if (_res.IsSuccessStatusCode) + // { + // data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); + // await _context.SaveChangesAsync(); + // } + // } + #endregion + + // New: Linear Flow + var now = DateTime.Now; + data.ForEach(profile => { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - data = resultData, - }); - //// var _result = await _res.Content.ReadAsStringAsync(); - //// if (_res.IsSuccessStatusCode) - //// { - //// data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); - //// await _context.SaveChangesAsync(); - //// } - } - return Success(); + profile.Status = "NEW"; + profile.CommandTypeId = null; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); + await _context.SaveChangesAsync(); + + // Return resultData for Node to process directly (Linear Flow) + return Success(resultData); } /// @@ -2130,10 +2266,6 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); - // Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); - await _context.SaveChangesAsync(); - string? _null = null; var resultData = (from p in data join r in req.refIds @@ -2169,24 +2301,40 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers posNoAbb = p.child4ShortName != null ? p.child4ShortName : (p.child3ShortName != null ? p.child3ShortName : (p.child2ShortName != null ? p.child2ShortName : (p.child1ShortName != null ? p.child1ShortName : (p.rootShortName != null ? p.rootShortName : "")))), }).ToList(); - var baseAPIOrg = _configuration["API"]; - var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; - using (var client = new HttpClient()) + #region Old: Circular Flow + // var baseAPIOrg = _configuration["API"]; + // var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData, + // }); + // var _result = await _res.Content.ReadAsStringAsync(); + // if (_res.IsSuccessStatusCode) + // { + // data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); + // await _context.SaveChangesAsync(); + // } + // } + #endregion + + // New: Linear Flow + var now = DateTime.Now; + data.ForEach(profile => { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - data = resultData, - }); - //// var _result = await _res.Content.ReadAsStringAsync(); - //// if (_res.IsSuccessStatusCode) - //// { - //// data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); - //// await _context.SaveChangesAsync(); - //// } - } - return Success(); + profile.Status = "NEW"; + profile.CommandTypeId = null; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); + await _context.SaveChangesAsync(); + + // Return resultData for Node to process directly (Linear Flow) + return Success(resultData); } /// @@ -2272,10 +2420,6 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); - // Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); - await _context.SaveChangesAsync(); - string? _null = null; var resultData = (from p in data join r in req.refIds @@ -2311,24 +2455,40 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers posNoAbb = p.child4ShortName != null ? p.child4ShortName : (p.child3ShortName != null ? p.child3ShortName : (p.child2ShortName != null ? p.child2ShortName : (p.child1ShortName != null ? p.child1ShortName : (p.rootShortName != null ? p.rootShortName : "")))), }).ToList(); - var baseAPIOrg = _configuration["API"]; - var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; - using (var client = new HttpClient()) + #region Old: Circular Flow + // var baseAPIOrg = _configuration["API"]; + // var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData, + // }); + // var _result = await _res.Content.ReadAsStringAsync(); + // if (_res.IsSuccessStatusCode) + // { + // data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); + // await _context.SaveChangesAsync(); + // } + // } + #endregion + + // New: Linear Flow + var now = DateTime.Now; + data.ForEach(profile => { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - data = resultData, - }); - //// var _result = await _res.Content.ReadAsStringAsync(); - //// if (_res.IsSuccessStatusCode) - //// { - //// data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); - //// await _context.SaveChangesAsync(); - //// } - } - return Success(); + profile.Status = "NEW"; + profile.CommandTypeId = null; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); + await _context.SaveChangesAsync(); + + // Return resultData for Node to process directly (Linear Flow) + return Success(resultData); } /// @@ -2424,15 +2584,21 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers [HttpPost("command32/report/excecute")] public async Task> PostReportCommand32Execute([FromBody] ReportExecuteRequest req) { + // C-PM-32 (คำสั่งยุติเรื่อง) ต้องยุติงานใน 2 track ที่เก็บอยู่คนละตาราง: + // - data / resultData = ฝั่ง "การสอบสวน" (DisciplineInvestigate_ProfileComplaint) + // - data1 / resultData1 = ฝั่ง "การพิจารณาลงโทษ" (DisciplineDisciplinary_ProfileComplaintInvestigate) + // บุคคลเดียวกัน (profileId เดียวกัน) อาจอยู่ในทั้ง 2 track จึงต้องส่งให้ org แยก 2 ครั้ง (ห้าม merge รวมครั้งเดียว) var data = await _context.DisciplineInvestigate_ProfileComplaints .Include(x => x.DisciplineInvestigate) // .Where(x => x.IsReport == "REPORT") .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); - // Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ - data.ForEach(profile => profile.IsReport = "DONE"); - await _context.SaveChangesAsync(); + var data1 = await _context.DisciplineDisciplinary_ProfileComplaintInvestigates + .Include(x => x.DisciplineDisciplinary) + // .Where(x => x.IsReport == "REPORT") + .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) + .ToListAsync(); string? _null = null; var resultData = (from p in data @@ -2469,29 +2635,6 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers posNoAbb = p.child4ShortName != null ? p.child4ShortName : (p.child3ShortName != null ? p.child3ShortName : (p.child2ShortName != null ? p.child2ShortName : (p.child1ShortName != null ? p.child1ShortName : (p.rootShortName != null ? p.rootShortName : "")))), }).ToList(); - var baseAPIOrg = _configuration["API"]; - var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; - using (var client = new HttpClient()) - { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - data = resultData, - }); - //// var _result = await _res.Content.ReadAsStringAsync(); - //// if (_res.IsSuccessStatusCode) - //// { - //// data.ForEach(profile => profile.IsReport = "DONE"); - //// await _context.SaveChangesAsync(); - //// } - } - - var data1 = await _context.DisciplineDisciplinary_ProfileComplaintInvestigates - .Include(x => x.DisciplineDisciplinary) - // .Where(x => x.IsReport == "REPORT") - .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) - .ToListAsync(); var resultData1 = (from p in data1 join r in req.refIds on p.Id.ToString() equals r.refId @@ -2525,23 +2668,69 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers posNo = p.posMasterNo != null ? p.posMasterNo.ToString() : null, posNoAbb = p.child4ShortName != null ? p.child4ShortName : (p.child3ShortName != null ? p.child3ShortName : (p.child2ShortName != null ? p.child2ShortName : (p.child1ShortName != null ? p.child1ShortName : (p.rootShortName != null ? p.rootShortName : "")))), }).ToList(); - using (var client = new HttpClient()) - { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - data = resultData1, - }); - var _result = await _res.Content.ReadAsStringAsync(); - if (_res.IsSuccessStatusCode) - { - data1.ForEach(profile => profile.IsReport = "DONE"); - await _context.SaveChangesAsync(); - } - } - return Success(); + #region Old: Circular Flow + // var baseAPIOrg = _configuration["API"]; + // var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave-discipline"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData, + // }); + // var _result = await _res.Content.ReadAsStringAsync(); + // if (_res.IsSuccessStatusCode) + // { + // data.ForEach(profile => profile.IsReport = "DONE"); + // await _context.SaveChangesAsync(); + // } + // } + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // data = resultData1, + // }); + // var _result = await _res.Content.ReadAsStringAsync(); + // if (_res.IsSuccessStatusCode) + // { + // data1.ForEach(profile => profile.IsReport = "DONE"); + // await _context.SaveChangesAsync(); + // } + // } + #endregion + + // New: Linear Flow + var now = DateTime.Now; + data.ForEach(profile => + { + profile.IsReport = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); + data1.ForEach(profile => + { + profile.IsReport = "DONE"; + profile.LastUpdateFullName = FullName ?? "System Administrator"; + profile.LastUpdateUserId = UserId ?? ""; + profile.LastUpdatedAt = now; + }); + await _context.SaveChangesAsync(); + + // Return resultData for Node to process directly (Linear Flow) + // + // NOTE สำหรับฝั่ง Node (C-PM-32 เท่านั้น): + // - response.result เป็น object { data, data1 } (ไม่ใช่ array เหมือนคำสั่งอื่น) + // data = รายการฝั่ง "การสอบสวน" → ยิง POST {API}/org/command/excexute/salary-leave-discipline ครั้งที่ 1 + // data1 = รายการฝั่ง "การพิจารณาลงโทษ" → ยิง POST {API}/org/command/excexute/salary-leave-discipline ครั้งที่ 2 + // - ต้องยิง 2 ครั้งตามลำดับ data ก่อน → data1 (ห้าม merge รวมในครั้งเดียว เพราะ profileId อาจซ้ำข้าม 2 track) + // - คำสั่งอื่น (C-PM-19/20/25/26/27/28/29/30/31) response.result เป็น array → ยิง org 1 ครั้ง + return Success(new { data = resultData, data1 = resultData1 }); } /// diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs index c7cbf249..fe2b519a 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs @@ -2074,7 +2074,7 @@ namespace BMA.EHR.Placement.Service.Controllers } /// - /// ออกคำสั่ง C-PM-47 + /// ออกคำสั่ง C-PM-47 โปรดเกล้าฯ แต่งตั้งให้ดำรงตำแหน่ง /// /// /// diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs index 003166f9..4ef0e395 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs @@ -629,7 +629,7 @@ namespace BMA.EHR.Retirement.Service.Controllers } /// - /// ออกคำสั่ง C-PM-18 คำสั่งให้ออกจากราชการ + /// ออกคำสั่ง C-PM-18 ให้ออกจากราชการ && C-PM-43 ให้ลูกจ้างออกจากราชการ /// /// ///