diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs index 2d14db9c..a62aa291 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs @@ -80,7 +80,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task UpdateLeaveUsageAsync(int year, Guid typeId, Guid userId, double day) { // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (pf == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -102,7 +102,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task UpdateLeaveCountAsync(int year, Guid typeId, Guid userId, int count) { // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (pf == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -124,7 +124,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task GetByYearAndTypeIdForUserAsync(int year, Guid typeId, Guid userId) { // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (pf == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -263,7 +263,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task GetByYearAndTypeIdForUser2Async(int year, Guid typeId, Guid userId) { // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (pf == null) { return null; diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs index 8416af4e..54f1488d 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs @@ -254,7 +254,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task> GetLeaveRequestByYearAsync(int year, Guid userId) { // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (profile == null) { @@ -354,7 +354,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests var rawData = _dbContext.Set().AsNoTracking() .Include(x => x.Type) .Where(x => x.LeaveStatus != "DRAFT") - .OrderByDescending(x => x.CreatedAt) + .OrderByDescending(x => (x.DateSendLeave ?? x.CreatedAt)) .AsQueryable(); if (year != 0) @@ -380,7 +380,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests var rawData = _dbContext.Set().AsNoTracking() .Include(x => x.Type) .Where(x => x.LeaveStatus != "DRAFT") - .OrderByDescending(x => x.CreatedAt) + .OrderByDescending(x => (x.DateSendLeave ?? x.CreatedAt)) .AsQueryable(); // fix issue : 1830 if (year != 0) @@ -447,7 +447,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests .Include(x => x.Type) .Where(x => keycloakIdList.Contains(x.KeycloakUserId)) .Where(x => x.LeaveStatus != "DRAFT") - .OrderByDescending(x => x.CreatedAt) + .OrderByDescending(x =>(x.DateSendLeave ?? x.CreatedAt)) .AsQueryable(); if (year != 0) @@ -497,7 +497,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task GetSumLeaveByTypeForUserAsync(Guid keycloakUserId, Guid leaveTypeId, int year) { // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(keycloakUserId, AccessToken); - var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(keycloakUserId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(keycloakUserId, AccessToken); if (pf == null) throw new Exception(GlobalMessages.DataNotFound); @@ -574,12 +574,12 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) //.Where(x => x.LeaveStartDate.Date < beforeDate.Date) - .Where(x => x.CreatedAt < beforeDate) + .Where(x => (x.DateSendLeave ?? x.CreatedAt) < beforeDate) .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.Type.Id == leaveTypeId) .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") //.Where(x => x.LeaveStatus != "REJECT" && x.LeaveStatus != "DELETE") - .OrderByDescending(x => x.CreatedAt) + .OrderByDescending(x => (x.DateSendLeave ?? x.CreatedAt)) .FirstOrDefaultAsync(); return data; @@ -651,7 +651,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests try { // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken ?? ""); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(data.KeycloakUserId, AccessToken ?? ""); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(data.KeycloakUserId, AccessToken ?? ""); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -728,7 +728,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests } // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken ?? ""); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(rawData.KeycloakUserId, AccessToken ?? ""); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(rawData.KeycloakUserId, AccessToken ?? ""); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -817,7 +817,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests } // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken ?? ""); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(rawData.KeycloakUserId, AccessToken ?? ""); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(rawData.KeycloakUserId, AccessToken ?? ""); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -904,6 +904,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests rawData.LeaveStatus = "NEW"; + rawData.DateSendLeave = DateTime.Now; // Update วันที่ยื่นลาเป็นวันที่ปัจจุบัน //rawData.ApproveStep = "st2"; await UpdateAsync(rawData); @@ -1242,7 +1243,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests else { // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(rawData.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(rawData.KeycloakUserId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -1412,7 +1413,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests else { // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(rawData.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(rawData.KeycloakUserId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -1876,7 +1877,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests .Include(x => x.Type) .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.Type.Id == leaveTypeId) - .Where(x => x.CreatedAt.Date >= startDate && x.CreatedAt < endDate) + .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.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") .ToListAsync(); @@ -1887,6 +1888,57 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests return 0; } + /// + /// วันลาที่สร้างแบบร่างยังไม่ได้ยื่น + /// + /// + /// + /// + /// + /// + public async Task GetSumDraftLeaveTotalByTypeAndRangeForUser2(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate) + { + var data = await _dbContext.Set().AsQueryable().AsNoTracking() + .Include(x => x.Type) + .Where(x => x.KeycloakUserId == keycloakUserId) + .Where(x => x.Type.Id == leaveTypeId) + .Where(x => (x.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.LeaveStatus == "DRAFT") + .ToListAsync(); + + if (data.Count > 0) + return data.Sum(x => x.LeaveTotal); + else + return 0; + } + + /// + /// วันลาที่ยื่นแล้วรอพิจารณา + /// + /// + /// + /// + /// + /// + public async Task GetSumNewLeaveTotalByTypeAndRangeForUser2(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate) + { + var data = await _dbContext.Set().AsQueryable().AsNoTracking() + .Include(x => x.Type) + .Where(x => x.KeycloakUserId == keycloakUserId) + .Where(x => x.Type.Id == leaveTypeId) + .Where(x => (x.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.LeaveStatus == "NEW") + .ToListAsync(); + + if (data.Count > 0) + return data.Sum(x => x.LeaveTotal); + else + return 0; + } + public async Task GetCountApproveLeaveByTypeAndRangeForUser(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate) { var data = await _dbContext.Set().AsQueryable().AsNoTracking() diff --git a/BMA.EHR.Application/Repositories/PermissionRepository.cs b/BMA.EHR.Application/Repositories/PermissionRepository.cs index 84191f91..a63207ec 100644 --- a/BMA.EHR.Application/Repositories/PermissionRepository.cs +++ b/BMA.EHR.Application/Repositories/PermissionRepository.cs @@ -10,6 +10,7 @@ using System.Net.Http.Headers; using Microsoft.Extensions.Configuration; using System.Security.Claims; using System.Net.Http.Json; +using BMA.EHR.Application.Responses.Leaves; namespace BMA.EHR.Application.Repositories { @@ -76,6 +77,39 @@ namespace BMA.EHR.Application.Repositories } } + public async Task GetPermissionWithActingAPIAsync(string action, string system) + { + try + { + var apiPath = $"{_configuration["API"]}/org/permission/dotnet-acting/{action}/{system}"; + + using (var client = new HttpClient()) + { + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", AccessToken.Replace("Bearer ", "")); + client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + var req = await client.GetAsync(apiPath); + if (!req.IsSuccessStatusCode) + { + throw new Exception("Error calling permission API"); + } + var apiResult = await req.Content.ReadAsStringAsync(); + //return res; + + if (apiResult != null) + { + var raw = JsonConvert.DeserializeObject(apiResult); + return raw; + } + return null; + } + } + catch + { + throw; + } + } + public async Task GetPermissionOrgAPIAsync(string action, string system, string profileId) { try diff --git a/BMA.EHR.Application/Repositories/Reports/RetireReportRepository.cs b/BMA.EHR.Application/Repositories/Reports/RetireReportRepository.cs index ecb65e2f..6ad5a61a 100644 --- a/BMA.EHR.Application/Repositories/Reports/RetireReportRepository.cs +++ b/BMA.EHR.Application/Repositories/Reports/RetireReportRepository.cs @@ -192,7 +192,7 @@ namespace BMA.EHR.Application.Repositories.Reports }).ToList(); } string SignDate = retireHistorys.SignDate != null ? DateTime.Parse(retireHistorys.SignDate.ToString()).ToThaiFullDate().ToString().ToThaiNumber() : "-"; - return new { SignDate, retireHistorys.Detail, retireHistorys.Id, retireHistorys.CreatedAt, Year = retireHistorys.Year.ToThaiYear().ToString().ToThaiNumber(), retireHistorys.Round, retireHistorys.Type, retireHistorys.TypeReport, Total = retireHistorys.Total.ToString().ToThaiNumber(), profiles = mapProfiles }; + return new { SignDate, Detail = retireHistorys.Detail.ToThaiNumber(), retireHistorys.Id, retireHistorys.CreatedAt, Year = retireHistorys.Year.ToThaiYear().ToString().ToThaiNumber(), retireHistorys.Round, retireHistorys.Type, retireHistorys.TypeReport, Total = retireHistorys.Total.ToString().ToThaiNumber(), profiles = mapProfiles }; } } else @@ -312,7 +312,7 @@ namespace BMA.EHR.Application.Repositories.Reports root = (isDuplicateRoot ? "" : profile.root + "\n") + (isDuplicateHospital || !hospital.ToObject>().Contains(profile.child1) ? "" : profile.child1 + "\n") + (isDuplicatePosType ? "" : $"ตำแหน่งประเภท{profile.posTypeName}" + "\n") + - (isDuplicatePosLevel ? "" : $"ระดับ{profile.posLevelName}"), + (isDuplicatePosLevel ? "" : $"ระดับ{profile.posLevelName}").ToThaiNumber(), child = (profile.posExecutiveName == null ? "" : profile.posExecutiveName + "\n") + (profile.child4 == null ? "" : profile.child4 + "\n") + (profile.child3 == null ? "" : profile.child3 + "\n") + @@ -326,7 +326,7 @@ namespace BMA.EHR.Application.Repositories.Reports }).ToList(); } string SignDate = retire.SignDate != null ? DateTime.Parse(retire.SignDate.ToString()).ToThaiFullDate().ToString().ToThaiNumber() : "-"; - return new { SignDate, retire.Detail, retire.Id, retire.CreatedAt, Year = retire.Year.ToThaiYear().ToString().ToThaiNumber(), retire.Round, retire.Type, retire.TypeReport, Total = profile_retire.Count.ToString().ToThaiNumber(), profiles = mapProfiles }; + return new { SignDate, Detail = retire.Detail.ToThaiNumber(), retire.Id, retire.CreatedAt, Year = retire.Year.ToThaiYear().ToString().ToThaiNumber(), retire.Round, retire.Type, retire.TypeReport, Total = profile_retire.Count.ToString().ToThaiNumber(), profiles = mapProfiles }; } } #endregion diff --git a/BMA.EHR.Application/Responses/Leaves/GetPermissionWithActingDto.cs b/BMA.EHR.Application/Responses/Leaves/GetPermissionWithActingDto.cs new file mode 100644 index 00000000..083c4b20 --- /dev/null +++ b/BMA.EHR.Application/Responses/Leaves/GetPermissionWithActingDto.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using BMA.EHR.Domain.Shared; +using Newtonsoft.Json; + +namespace BMA.EHR.Application.Responses.Leaves +{ + public class GetPermissionWithActingDto + { + public string privilege {get; set;} = string.Empty; + public bool isAct {get; set;} = false; + public List posMasterActs {get; set;} = new(); + } + + public class ActingPermission + { + public string posNo {get; set;} = string.Empty; + //public string? privilege {get; set;} = "PARENT"; + [JsonConverter(typeof(PrivilegeConverter))] + public string privilege {get; set;} = "CHILD"; + + public Guid? rootDnaId {get; set;} + public Guid? child1DnaId {get; set;} + public Guid? child2DnaId {get; set;} + public Guid? child3DnaId {get; set;} + public Guid? child4DnaId {get; set;} + } + + public class GetPermissionWithActingResultDto + { + public int status {get; set;} = 0; + public string message {get; set;} = string.Empty; + public GetPermissionWithActingDto result {get; set;} = new(); + } +} \ No newline at end of file diff --git a/BMA.EHR.Domain/Models/Leave/Requests/LeaveRequest.cs b/BMA.EHR.Domain/Models/Leave/Requests/LeaveRequest.cs index 69088cbb..f6c9ce2d 100644 --- a/BMA.EHR.Domain/Models/Leave/Requests/LeaveRequest.cs +++ b/BMA.EHR.Domain/Models/Leave/Requests/LeaveRequest.cs @@ -210,5 +210,7 @@ namespace BMA.EHR.Domain.Models.Leave.Requests public Guid? Child4DnaId { get; set; } = Guid.Empty; + public DateTime? DateSendLeave { get; set; } + } } diff --git a/BMA.EHR.Domain/Shared/GlobalMessages.cs b/BMA.EHR.Domain/Shared/GlobalMessages.cs index 8746de95..a99dc3c9 100644 --- a/BMA.EHR.Domain/Shared/GlobalMessages.cs +++ b/BMA.EHR.Domain/Shared/GlobalMessages.cs @@ -8,6 +8,8 @@ public static readonly string DataNotFound = "ไม่พบข้อมูลในระบบ"; + public static readonly string ProfileNotFound = "ไม่พบข้อมูลในระบบทะเบียนประวัติ"; + public static readonly string NotAuthorized = "กรุณาเข้าสู่ระบบก่อนใช้งาน!"; public static readonly string ForbiddenAccess = "คุณไม่ได้รับอนุญาติให้เข้าใช้งาน!"; diff --git a/BMA.EHR.Domain/Shared/PrivilegeConverter.cs b/BMA.EHR.Domain/Shared/PrivilegeConverter.cs new file mode 100644 index 00000000..59f8168c --- /dev/null +++ b/BMA.EHR.Domain/Shared/PrivilegeConverter.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace BMA.EHR.Domain.Shared +{ + public class PrivilegeConverter : JsonConverter +{ + public override bool CanConvert(Type objectType) + { + return objectType == typeof(string); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + { + return "EMPTY"; + } + return reader.Value; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(value); + } +} +} \ No newline at end of file diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260423083625_Add DateSendLeave.Designer.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260423083625_Add DateSendLeave.Designer.cs new file mode 100644 index 00000000..8e79c20b --- /dev/null +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260423083625_Add DateSendLeave.Designer.cs @@ -0,0 +1,1805 @@ +// +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("20260423083625_Add DateSendLeave")] + partial class AddDateSendLeave + { + /// + 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.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("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/20260423083625_Add DateSendLeave.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260423083625_Add DateSendLeave.cs new file mode 100644 index 00000000..02d58933 --- /dev/null +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260423083625_Add DateSendLeave.cs @@ -0,0 +1,29 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BMA.EHR.Infrastructure.Migrations.LeaveDb +{ + /// + public partial class AddDateSendLeave : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DateSendLeave", + table: "LeaveRequests", + type: "datetime(6)", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "DateSendLeave", + table: "LeaveRequests"); + } + } +} diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs index 4e384f55..887d4f97 100644 --- a/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs @@ -428,6 +428,9 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.Property("DateAppoint") .HasColumnType("datetime(6)"); + b.Property("DateSendLeave") + .HasColumnType("datetime(6)"); + b.Property("Dear") .HasColumnType("longtext") .HasComment("เรียนใคร"); diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 0a423d60..c77bad95 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -3,8 +3,10 @@ using BMA.EHR.Application.Repositories.Commands; using BMA.EHR.Application.Repositories.Leaves.LeaveRequests; using BMA.EHR.Application.Repositories.Leaves.TimeAttendants; using BMA.EHR.Application.Repositories.MessageQueue; +using BMA.EHR.Application.Responses.Leaves; using BMA.EHR.Application.Responses.Profiles; using BMA.EHR.Domain.Common; +using BMA.EHR.Domain.Extensions; using BMA.EHR.Domain.Models.Leave.TimeAttendants; using BMA.EHR.Domain.Models.Notifications; using BMA.EHR.Domain.Shared; @@ -447,7 +449,7 @@ namespace BMA.EHR.Leave.Service.Controllers // Get user's last check-in record and profile in parallel var dataTask = _userTimeStampRepository.GetLastRecord(userId); - var profileTask = _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profileTask = _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); var defaultRoundTask = _dutyTimeRepository.GetDefaultAsync(); await Task.WhenAll(dataTask, profileTask, defaultRoundTask); @@ -936,7 +938,7 @@ namespace BMA.EHR.Leave.Service.Controllers await _checkInJobStatusRepository.UpdateToProcessingAsync(taskId); } - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, data.Token); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, data.Token); if (profile == null) { @@ -1589,7 +1591,7 @@ namespace BMA.EHR.Leave.Service.Controllers public async Task> CheckInOldAsync([FromForm] CheckTimeDto data) { var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (profile == null) return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -1754,7 +1756,7 @@ namespace BMA.EHR.Leave.Service.Controllers { var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -2043,7 +2045,7 @@ namespace BMA.EHR.Leave.Service.Controllers } else { - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(d.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(d.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -2993,7 +2995,7 @@ namespace BMA.EHR.Leave.Service.Controllers var time = DateTime.Now; var userId = UserId != null ? Guid.Parse(UserId) : Guid.Empty; - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -3113,7 +3115,7 @@ namespace BMA.EHR.Leave.Service.Controllers } var userId = UserId != null ? Guid.Parse(UserId) : Guid.Empty; - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -3162,13 +3164,14 @@ namespace BMA.EHR.Leave.Service.Controllers [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> GetAdditionalCheckRequestAsync([Required] int year, [Required] int month, [Required] int page = 1, [Required] int pageSize = 10, string keyword = "", string? sortBy = "", bool? descending = false) { - var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_CHECKIN_SPECIAL"); - var jsonData = JsonConvert.DeserializeObject(getPermission); - if (jsonData["status"]?.ToString() != "200") + var jsonData = await _permission.GetPermissionWithActingAPIAsync("LIST", "SYS_CHECKIN_SPECIAL"); + //var jsonData = JsonConvert.DeserializeObject(getPermission); + if (jsonData!.status != 200) { - return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); + return Error(jsonData.message, StatusCodes.Status403Forbidden); } - string role = jsonData["result"]?.ToString(); + //string role = jsonData["result"]?.ToString(); + string role = jsonData.result.privilege; var nodeId = string.Empty; var profileAdmin = new GetUserOCAllDto(); profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken); @@ -3206,6 +3209,82 @@ namespace BMA.EHR.Leave.Service.Controllers //var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequests(year, month); var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequestsByAdminRole(year, month, role, nodeId, profileAdmin?.Node, keyword); + // ถ้ามีการรักษาการ + if (jsonData.result.isAct) + { + var posActs = jsonData.result.posMasterActs.Where(x => x.privilege != "EMPTY"); + foreach(var act in posActs) + { + var actRole = act.privilege; + string actNodeId = string.Empty; + int? actNode; + + if (actRole == "NORMAL" || actRole == "CHILD") + { + actNodeId = act.child4DnaId != null ? + act.child4DnaId.Value.ToString("D") : + act.child3DnaId != null ? + act.child3DnaId.Value.ToString("D") : + act.child2DnaId != null ? + act.child2DnaId.Value.ToString("D") : + act.child1DnaId != null ? + act.child1DnaId.Value.ToString("D") : + act.rootDnaId != null ? + act.rootDnaId.Value.ToString("D") : + ""; + actNode = act.child4DnaId != null ? + 4 : + act.child3DnaId != null ? + 3 : + act.child2DnaId != null ? + 2 : + act.child1DnaId != null ? + 1 : + act.rootDnaId != null ? + 0 : + null; + } + else if (actRole == "BROTHER") + { + actNodeId = act.child4DnaId != null ? + act.child3DnaId.Value.ToString("D") : + act.child3DnaId != null ? + act.child2DnaId.Value.ToString("D") : + act.child2DnaId != null ? + act.child1DnaId!.Value.ToString("D") : + act.child1DnaId != null ? + act.rootDnaId.Value.ToString("D") : + act.rootDnaId != null ? + act.rootDnaId.Value.ToString("D") : + ""; + actNode = act.child4DnaId != null ? + 4 : + act.child3DnaId != null ? + 3 : + act.child2DnaId != null ? + 2 : + act.child1DnaId != null ? + 1 : + act.rootDnaId != null ? + 0 : + null; + } + else if (actRole == "ROOT" /*|| role == "PARENT"*/) + { + actNodeId = act.rootDnaId!.Value.ToString("D"); + actNode = 0; + } + + var rawDataAct = await _additionalCheckRequestRepository.GetAdditionalCheckRequestsByAdminRole(year, month, actRole, actNodeId, profileAdmin?.Node, keyword); + if (rawDataAct != null) + { + if (rawData != null) + rawData = rawData.Union(rawDataAct).DistinctBy(x => x.Id).ToList(); + else + rawData = rawDataAct; + } + } + } var total = rawData.Count; var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync(); @@ -3276,7 +3355,7 @@ namespace BMA.EHR.Leave.Service.Controllers foreach (var data in rawDataPaged) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(data.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(data.KeycloakUserId, AccessToken); UserDutyTime? effectiveDate = null; if (profile != null) { @@ -3455,7 +3534,7 @@ namespace BMA.EHR.Leave.Service.Controllers // change user timestamp var processTimeStamp = await _processUserTimeStampRepository.GetTimestampByDateAsync(requestData.KeycloakUserId, requestData.CheckDate.Date); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(requestData.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(requestData.KeycloakUserId, AccessToken); if (processTimeStamp == null) { @@ -3609,7 +3688,7 @@ namespace BMA.EHR.Leave.Service.Controllers requestData.Comment = req.Reason; await _additionalCheckRequestRepository.UpdateAsync(requestData); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(requestData.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(requestData.KeycloakUserId, AccessToken); var recvId = new List { profile.Id }; await _notificationRepository.PushNotificationsAsync(recvId.ToArray(), "ลงเวลากรณีพิเศษ", "การขอลงเวลากรณีพิเศษของคุณไม่ได้รับการอนุมัติ", "", "", true, false); @@ -3653,7 +3732,7 @@ namespace BMA.EHR.Leave.Service.Controllers } else { - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(d.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(d.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -3747,7 +3826,7 @@ namespace BMA.EHR.Leave.Service.Controllers foreach (var data in rawData) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(data.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(data.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -4037,7 +4116,7 @@ namespace BMA.EHR.Leave.Service.Controllers //var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); // แก้เป็นมาใช้งาน KeycloakUserId แทน - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(data.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(data.KeycloakUserId, AccessToken); var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { @@ -4107,7 +4186,7 @@ namespace BMA.EHR.Leave.Service.Controllers [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> GetLeaveSummaryByProfileAsync(Guid id, [FromBody] GetLeaveSummaryDto req) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(id, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(id, AccessToken); var thisYear = DateTime.Now.Year; var startDate = req.StartDate; @@ -4177,7 +4256,7 @@ namespace BMA.EHR.Leave.Service.Controllers { var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 7e4b7b56..e726aabd 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -182,7 +182,7 @@ namespace BMA.EHR.Leave.Service.Controllers data = new { leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", - dateSendLeave = data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), + dateSendLeave = data.DateSendLeave != null ? data.DateSendLeave.Value.Date.ToThaiShortDate().ToThaiNumber() : data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), leaveTypeName = data.Type.Name, leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), @@ -272,7 +272,7 @@ namespace BMA.EHR.Leave.Service.Controllers data = new { leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", - dateSendLeave = data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), + dateSendLeave = data.DateSendLeave != null ? data.DateSendLeave.Value.Date.ToThaiShortDate().ToThaiNumber() : data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), leaveTypeName = data.Type.Name, leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), @@ -366,7 +366,7 @@ namespace BMA.EHR.Leave.Service.Controllers data = new { leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", - dateSendLeave = data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), + dateSendLeave = data.DateSendLeave != null ? data.DateSendLeave.Value.Date.ToThaiShortDate().ToThaiNumber() : data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), leaveTypeName = data.Type.Name, leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), @@ -461,7 +461,7 @@ namespace BMA.EHR.Leave.Service.Controllers data = new { leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", - dateSendLeave = data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), + dateSendLeave = data.DateSendLeave != null ? data.DateSendLeave.Value.Date.ToThaiShortDate().ToThaiNumber() : data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), leaveTypeName = data.Type.Name, leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), @@ -515,7 +515,7 @@ namespace BMA.EHR.Leave.Service.Controllers data = new { leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", - dateSendLeave = data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), + dateSendLeave = data.DateSendLeave != null ? data.DateSendLeave.Value.Date.ToThaiShortDate().ToThaiNumber() : data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), leaveTypeName = data.Type.Name, leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), @@ -602,7 +602,7 @@ namespace BMA.EHR.Leave.Service.Controllers data = new { leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", - dateSendLeave = data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), + dateSendLeave = data.DateSendLeave != null ? data.DateSendLeave.Value.Date.ToThaiShortDate().ToThaiNumber() : data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), leaveTypeName = data.Type.Name, leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), @@ -683,7 +683,7 @@ namespace BMA.EHR.Leave.Service.Controllers data = new { leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", - dateSendLeave = data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), + dateSendLeave = data.DateSendLeave != null ? data.DateSendLeave.Value.Date.ToThaiShortDate().ToThaiNumber() : data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), leaveTypeName = data.Type.Name, leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), @@ -786,7 +786,7 @@ namespace BMA.EHR.Leave.Service.Controllers data = new { leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", - dateSendLeave = data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), + dateSendLeave = data.DateSendLeave != null ? data.DateSendLeave.Value.Date.ToThaiShortDate().ToThaiNumber() : data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), leaveTypeName = data.Type.Name, leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), @@ -905,7 +905,7 @@ namespace BMA.EHR.Leave.Service.Controllers data = new { leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", - dateSendLeave = data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), + dateSendLeave = data.DateSendLeave != null ? data.DateSendLeave.Value.Date.ToThaiShortDate().ToThaiNumber() : data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), leaveTypeName = data.Type.Name, leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), @@ -1001,7 +1001,7 @@ namespace BMA.EHR.Leave.Service.Controllers data = new { leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", - dateSendLeave = data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), + dateSendLeave = data.DateSendLeave != null ? data.DateSendLeave.Value.Date.ToThaiShortDate().ToThaiNumber() : data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), leaveTypeName = data.Type.Name, leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), @@ -1227,7 +1227,7 @@ namespace BMA.EHR.Leave.Service.Controllers data = new { leaveWrote = data.CancelLeaveWrote!.ToThaiNumber() ?? "", - dateSendLeave = data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), + dateSendLeave = data.DateSendLeave != null ? data.DateSendLeave.Value.Date.ToThaiShortDate().ToThaiNumber() : data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), leaveTypeName = data.Type.Name, fullname = fullName, position = string.IsNullOrEmpty(profile.Position) ? "-" : profile.Position, diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 7bc3b151..daa2c07c 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -214,7 +214,7 @@ namespace BMA.EHR.Leave.Service.Controllers var thisYear = DateTime.Now.Year; // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (profile == null) { @@ -502,7 +502,7 @@ namespace BMA.EHR.Leave.Service.Controllers foreach (var leave in leaves) { // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(leave.KeycloakUserId, AccessToken); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(leave.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(leave.KeycloakUserId, AccessToken); if (profile != null) { leave.Prefix = profile.Prefix; @@ -563,7 +563,7 @@ namespace BMA.EHR.Leave.Service.Controllers // } // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (profile == null) { @@ -855,10 +855,10 @@ namespace BMA.EHR.Leave.Service.Controllers { var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); - var thisYear = DateTime.Now.Year; + var thisYear = DateTime.Now.Year - 1; var toDay = DateTime.Now.Date; - var startFiscalDate = new DateTime(DateTime.Now.Year, 10, 1); - var endFiscalDate = new DateTime(DateTime.Now.Year + 1, 9, 30); + var startFiscalDate = new DateTime(DateTime.Now.Year - 1, 10, 1); + var endFiscalDate = new DateTime(DateTime.Now.Year, 9, 30); if (toDay >= startFiscalDate && toDay <= endFiscalDate) thisYear = thisYear + 1; @@ -925,6 +925,9 @@ namespace BMA.EHR.Leave.Service.Controllers 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 result = new GetUserLeaveProfileResultDto { DateSendLeave = DateTime.Now.Date, @@ -960,7 +963,10 @@ namespace BMA.EHR.Leave.Service.Controllers CurrentDistrict = profile.CurrentDistrict ?? "", CurrentProvince = profile.CurrentProvince ?? "", CurrentZipCode = profile.CurrentZipCode ?? "", - GovAge = govAge + GovAge = govAge, + + LeaveDraftSummary = leaveDraftSummary, + LeaveWaitingSummary = leaveWaitingSummary }; return Success(result); @@ -1026,7 +1032,7 @@ namespace BMA.EHR.Leave.Service.Controllers var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); var govAge = (profile?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); var startDate = profile?.DateStart?.Date ?? DateTime.Now.Date; // var date1Raw = profile?.DateStart?.Date ?? DateTime.Now.Date; @@ -1324,7 +1330,7 @@ namespace BMA.EHR.Leave.Service.Controllers Id = d.Id, LeaveTypeId = d.Type.Id, LeaveTypeName = d.Type.Name, - DateSendLeave = d.CreatedAt.Date, + DateSendLeave = d.DateSendLeave != null ? d.DateSendLeave.Value.Date : d.CreatedAt.Date, Status = d.LeaveStatus, FullName = $"{d.Prefix}{d.FirstName} {d.LastName}", LeaveEndDate = d.LeaveEndDate, @@ -1352,14 +1358,14 @@ namespace BMA.EHR.Leave.Service.Controllers public async Task> GetLeaveRequestCalendarAdminAsync( [FromBody] GetLeaveRequestCalendarDto req) { - var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_LEAVE_LIST"); - var jsonData = JsonConvert.DeserializeObject(getPermission); - if (jsonData["status"]?.ToString() != "200") + var jsonData = await _permission.GetPermissionWithActingAPIAsync("LIST", "SYS_LEAVE_LIST"); + //var jsonData = JsonConvert.DeserializeObject(getPermission); + if (jsonData!.status != 200) { - return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); + return Error(jsonData.message, StatusCodes.Status403Forbidden); } - - string role = jsonData["result"]?.ToString(); + //string role = jsonData["result"]?.ToString(); + string role = jsonData.result.privilege; var nodeId = string.Empty; var profileAdmin = new GetUserOCAllDto(); profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken); @@ -1395,6 +1401,87 @@ namespace BMA.EHR.Leave.Service.Controllers } var data = await _leaveRequestRepository.GetLeaveRequestByYearForAdminAsync(req.Year, role, nodeId, profileAdmin.Node); + + // ถ้ามีการรักษาการ + if (jsonData.result.isAct) + { + var posActs = jsonData.result.posMasterActs.Where(x => x.privilege != "EMPTY"); + foreach(var act in posActs) + { + var actRole = act.privilege; + string actNodeId = string.Empty; + int? actNode = null; + + if (actRole == "NORMAL" || actRole == "CHILD") + { + actNodeId = act.child4DnaId != null ? + act.child4DnaId.Value.ToString("D") : + act.child3DnaId != null ? + act.child3DnaId.Value.ToString("D") : + act.child2DnaId != null ? + act.child2DnaId.Value.ToString("D") : + act.child1DnaId != null ? + act.child1DnaId.Value.ToString("D") : + act.rootDnaId != null ? + act.rootDnaId.Value.ToString("D") : + ""; + actNode = act.child4DnaId != null ? + 4 : + act.child3DnaId != null ? + 3 : + act.child2DnaId != null ? + 2 : + act.child1DnaId != null ? + 1 : + act.rootDnaId != null ? + 0 : + null; + } + else if (actRole == "BROTHER") + { + actNodeId = act.child4DnaId != null ? + act.child3DnaId.Value.ToString("D") : + act.child3DnaId != null ? + act.child2DnaId.Value.ToString("D") : + act.child2DnaId != null ? + act.child1DnaId!.Value.ToString("D") : + act.child1DnaId != null ? + act.rootDnaId.Value.ToString("D") : + act.rootDnaId != null ? + act.rootDnaId.Value.ToString("D") : + ""; + actNode = act.child4DnaId != null ? + 4 : + act.child3DnaId != null ? + 3 : + act.child2DnaId != null ? + 2 : + act.child1DnaId != null ? + 1 : + act.rootDnaId != null ? + 0 : + null; + } + else if (actRole == "ROOT" /*|| role == "PARENT"*/) + { + actNodeId = act.rootDnaId!.Value.ToString("D"); + actNode = 0; + } + + var rawDataAct = await _leaveRequestRepository.GetLeaveRequestByYearForAdminAsync(req.Year, actRole, actNodeId, actNode); + if (rawDataAct != null) + { + if (data != null) + data = data.Union(rawDataAct).DistinctBy(x => x.Id).ToList(); + else + data = rawDataAct; + } + } + } + + + + var resultData = (from d in data //join p in profileList on d.KeycloakUserId equals p.Keycloak select new GetLeaveRequestCalendarResultDto @@ -1402,7 +1489,7 @@ namespace BMA.EHR.Leave.Service.Controllers Id = d.Id, LeaveTypeId = d.Type.Id, LeaveTypeName = d.Type.Name, - DateSendLeave = d.CreatedAt.Date, + DateSendLeave = d.DateSendLeave != null ? d.DateSendLeave.Value.Date : d.CreatedAt.Date, Status = d.LeaveStatus, FullName = $"{d.Prefix}{d.FirstName} {d.LastName}", LeaveEndDate = d.LeaveEndDate, @@ -1455,7 +1542,7 @@ namespace BMA.EHR.Leave.Service.Controllers LeaveTypeName = item.Type.Name, LeaveSubTypeName = item.LeaveSubTypeName ?? "", FullName = $"{item.Prefix}{item.FirstName} {item.LastName}", - DateSendLeave = item.CreatedAt, + DateSendLeave = item.DateSendLeave ?? item.CreatedAt, IsDelete = item.LeaveStatus == "DELETE", Status = item.LeaveStatus, LeaveStartDate = item.LeaveStartDate, @@ -1577,7 +1664,7 @@ namespace BMA.EHR.Leave.Service.Controllers } // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(rawData.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(rawData.KeycloakUserId, AccessToken); if (profile == null) { @@ -1630,7 +1717,7 @@ namespace BMA.EHR.Leave.Service.Controllers LeaveSubTypeName = rawData.LeaveSubTypeName, LeaveTypeId = rawData.Type.Id, FullName = $"{rawData.Prefix}{rawData.FirstName} {rawData.LastName}", - DateSendLeave = rawData.CreatedAt, + DateSendLeave = rawData.DateSendLeave ?? rawData.CreatedAt, Status = rawData.LeaveStatus, LeaveStartDate = rawData.LeaveStartDate, LeaveEndDate = rawData.LeaveEndDate, @@ -1750,14 +1837,14 @@ namespace BMA.EHR.Leave.Service.Controllers public async Task> GetLeaveRequestForAdminAsync( [FromBody] GetLeaveRequestForAdminDto req) { - var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_LEAVE_LIST"); - var jsonData = JsonConvert.DeserializeObject(getPermission); - if (jsonData["status"]?.ToString() != "200") + var jsonData = await _permission.GetPermissionWithActingAPIAsync("LIST", "SYS_LEAVE_LIST"); + //var jsonData = JsonConvert.DeserializeObject(getPermission); + if (jsonData!.status != 200) { - return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); + return Error(jsonData.message, StatusCodes.Status403Forbidden); } - - string role = jsonData["result"]?.ToString(); + //string role = jsonData["result"]?.ToString(); + string role = jsonData.result.privilege; var nodeId = string.Empty; var profileAdmin = new GetUserOCAllDto(); profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken); @@ -1794,6 +1881,85 @@ namespace BMA.EHR.Leave.Service.Controllers var rawData = await _leaveRequestRepository.GetListLeaveRequestForAdminAsync(req.Year, req.Type, req.Status, req.StartDate, req.EndDate, role, nodeId, profileAdmin?.Node); + + // ถ้ามีการรักษาการ + if (jsonData.result.isAct) + { + var posActs = jsonData.result.posMasterActs.Where(x => x.privilege != "EMPTY"); + foreach(var act in posActs) + { + var actRole = act.privilege; + string actNodeId = string.Empty; + int? actNode = null; + + if (actRole == "NORMAL" || actRole == "CHILD") + { + actNodeId = act.child4DnaId != null ? + act.child4DnaId.Value.ToString("D") : + act.child3DnaId != null ? + act.child3DnaId.Value.ToString("D") : + act.child2DnaId != null ? + act.child2DnaId.Value.ToString("D") : + act.child1DnaId != null ? + act.child1DnaId.Value.ToString("D") : + act.rootDnaId != null ? + act.rootDnaId.Value.ToString("D") : + ""; + actNode = act.child4DnaId != null ? + 4 : + act.child3DnaId != null ? + 3 : + act.child2DnaId != null ? + 2 : + act.child1DnaId != null ? + 1 : + act.rootDnaId != null ? + 0 : + null; + } + else if (actRole == "BROTHER") + { + actNodeId = act.child4DnaId != null ? + act.child3DnaId.Value.ToString("D") : + act.child3DnaId != null ? + act.child2DnaId.Value.ToString("D") : + act.child2DnaId != null ? + act.child1DnaId!.Value.ToString("D") : + act.child1DnaId != null ? + act.rootDnaId.Value.ToString("D") : + act.rootDnaId != null ? + act.rootDnaId.Value.ToString("D") : + ""; + actNode = act.child4DnaId != null ? + 4 : + act.child3DnaId != null ? + 3 : + act.child2DnaId != null ? + 2 : + act.child1DnaId != null ? + 1 : + act.rootDnaId != null ? + 0 : + null; + } + else if (actRole == "ROOT" /*|| role == "PARENT"*/) + { + actNodeId = act.rootDnaId!.Value.ToString("D"); + actNode = 0; + } + + var rawDataAct = await _leaveRequestRepository.GetListLeaveRequestForAdminAsync(req.Year, req.Type, req.Status, req.StartDate, req.EndDate, actRole, actNodeId, actNode); + if (rawDataAct != null) + { + if (rawData != null) + rawData = rawData.Union(rawDataAct).DistinctBy(x => x.Id).ToList(); + else + rawData = rawDataAct; + } + } + } + + var result = new List(); foreach (var item in rawData) @@ -1826,7 +1992,7 @@ namespace BMA.EHR.Leave.Service.Controllers LeaveSubTypeName = item.LeaveSubTypeName, FullName = $"{item.Prefix}{item.FirstName} {item.LastName}", ProfileType = item.ProfileType ?? "-", - DateSendLeave = item.CreatedAt, + DateSendLeave = item.DateSendLeave ?? item.CreatedAt, Status = item.LeaveStatus, CitizenId = item.CitizenId ?? "", LeaveStartDate = item.LeaveStartDate, @@ -1876,6 +2042,12 @@ namespace BMA.EHR.Leave.Service.Controllers return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(data.KeycloakUserId, AccessToken); + if (profile == null) + { + return Error(GlobalMessages.ProfileNotFound, StatusCodes.Status404NotFound); + } + // change status to delete // แก้จาก DELETE เป็น DELETING ไว้ก่อน รอ approve ค่อยเปลี่ยนเป็น DELETE // data.LeaveStatus = "DELETE"; @@ -1912,7 +2084,7 @@ namespace BMA.EHR.Leave.Service.Controllers // TODO: Send notification to all users who need to approve the cancel leave request var approvers = data.Approvers - .Where(x => x.ApproveStatus!.ToUpper() == "PENDING") + //.Where(x => x.ApproveStatus!.ToUpper() == "PENDING") .OrderBy(x => x.Seq) .ToList(); @@ -1924,11 +2096,33 @@ namespace BMA.EHR.Leave.Service.Controllers Body = $"คำร้องขอยกเลิกการลาของคุณ {data.FirstName} {data.LastName} รอรับการอนุมัติจากคุณ", ReceiverUserId = approver!.ProfileId, Type = "", - Payload = $"{URL}/leave/detail/{id}", + Payload = $"{URL}/leave-reject/detail/{id}", }; _appDbContext.Set().Add(noti1); } + // Get Officer List + var officers = await _userProfileRepository.GetOCStaffAsync(profile.Id, AccessToken); + var approverProfileIdList = approvers.Select(x => x.ProfileId).ToList(); + + if(officers != null && officers.Count > 0) + { + officers = officers.Where(x => !approverProfileIdList.Contains(x.ProfileId)).ToList(); + foreach (var officer in officers) + { + // Send Notification + var noti = new Notification + { + Body = $"คำร้องขอยกเลิกการลาของคุณ {data.FirstName} {data.LastName} รอรับการอนุมัติจากคุณ", + ReceiverUserId = officer.ProfileId, + Type = "", + Payload = $"{URL}/leave-reject/detail/{id}", + }; + _appDbContext.Set().Add(noti); + } + await _appDbContext.SaveChangesAsync(); + } + return Success(); } @@ -1948,14 +2142,14 @@ namespace BMA.EHR.Leave.Service.Controllers public async Task> GetCancelLeaveRequestForAdminAsync( [FromBody] GetLeaveRequestForAdminDto req) { - var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_LEAVE_LIST"); - var jsonData = JsonConvert.DeserializeObject(getPermission); - if (jsonData["status"]?.ToString() != "200") + var jsonData = await _permission.GetPermissionWithActingAPIAsync("LIST", "SYS_LEAVE_LIST"); + //var jsonData = JsonConvert.DeserializeObject(getPermission); + if (jsonData!.status != 200) { - return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); + return Error(jsonData.message, StatusCodes.Status403Forbidden); } - - string role = jsonData["result"]?.ToString(); + //string role = jsonData["result"]?.ToString(); + string role = jsonData.result.privilege; var nodeId = string.Empty; var profileAdmin = new GetUserOCAllDto(); profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken); @@ -1993,6 +2187,84 @@ namespace BMA.EHR.Leave.Service.Controllers var rawData = await _leaveRequestRepository.GetCancelLeaveRequestForAdminAsync(req.Year, req.Type, req.Status, role, nodeId, profileAdmin?.Node); + // ถ้ามีการรักษาการ + if (jsonData.result.isAct) + { + var posActs = jsonData.result.posMasterActs.Where(x => x.privilege != "EMPTY"); + foreach(var act in posActs) + { + var actRole = act.privilege; + string actNodeId = string.Empty; + int? actNode = null; + + if (actRole == "NORMAL" || actRole == "CHILD") + { + actNodeId = act.child4DnaId != null ? + act.child4DnaId.Value.ToString("D") : + act.child3DnaId != null ? + act.child3DnaId.Value.ToString("D") : + act.child2DnaId != null ? + act.child2DnaId.Value.ToString("D") : + act.child1DnaId != null ? + act.child1DnaId.Value.ToString("D") : + act.rootDnaId != null ? + act.rootDnaId.Value.ToString("D") : + ""; + actNode = act.child4DnaId != null ? + 4 : + act.child3DnaId != null ? + 3 : + act.child2DnaId != null ? + 2 : + act.child1DnaId != null ? + 1 : + act.rootDnaId != null ? + 0 : + null; + } + else if (actRole == "BROTHER") + { + actNodeId = act.child4DnaId != null ? + act.child3DnaId.Value.ToString("D") : + act.child3DnaId != null ? + act.child2DnaId.Value.ToString("D") : + act.child2DnaId != null ? + act.child1DnaId!.Value.ToString("D") : + act.child1DnaId != null ? + act.rootDnaId.Value.ToString("D") : + act.rootDnaId != null ? + act.rootDnaId.Value.ToString("D") : + ""; + actNode = act.child4DnaId != null ? + 4 : + act.child3DnaId != null ? + 3 : + act.child2DnaId != null ? + 2 : + act.child1DnaId != null ? + 1 : + act.rootDnaId != null ? + 0 : + null; + } + else if (actRole == "ROOT" /*|| role == "PARENT"*/) + { + actNodeId = act.rootDnaId!.Value.ToString("D"); + actNode = 0; + } + + var rawDataAct = await _leaveRequestRepository.GetCancelLeaveRequestForAdminAsync(req.Year, req.Type, req.Status, actRole, actNodeId, actNode); + if (rawDataAct != null) + { + if (rawData != null) + rawData = rawData.Union(rawDataAct).DistinctBy(x => x.Id).ToList(); + else + rawData = rawDataAct; + } + } + } + + var recCount = rawData.Count; if (req.Keyword != "") @@ -2015,7 +2287,7 @@ namespace BMA.EHR.Leave.Service.Controllers LeaveSubTypeName = item.LeaveSubTypeName, ProfileType = item.ProfileType ?? "-", FullName = $"{item.Prefix}{item.FirstName} {item.LastName}", - DateSendLeave = item.CreatedAt.Date, + DateSendLeave = item.DateSendLeave != null ? item.DateSendLeave.Value.Date : item.CreatedAt.Date, Status = item.LeaveCancelStatus ?? "" }; result.Add(res); @@ -2112,7 +2384,7 @@ namespace BMA.EHR.Leave.Service.Controllers } // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(Guid.Parse(UserId!), AccessToken); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(Guid.Parse(UserId!), AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(Guid.Parse(UserId!), AccessToken); if (profile == null) { @@ -2564,7 +2836,13 @@ namespace BMA.EHR.Leave.Service.Controllers var startFiscalYear = new DateTime(rawData.LeaveStartDate.Year - 1, 10, 1); var endFiscalYear = rawData.CreatedAt; + var endFiscalYear2 = new DateTime(rawData.LeaveStartDate.Year, 9, 30); var leaveSummary = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, startFiscalYear, 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 leaveSummary = leaveData == null ? 0.0 : leaveData.LeaveDaysUsed; if (leaveData != null) leaveSummary += leaveData.BeginningLeaveDays; @@ -2588,7 +2866,7 @@ namespace BMA.EHR.Leave.Service.Controllers LeaveSubTypeName = rawData.LeaveSubTypeName, LeaveTypeId = rawData.Type.Id, FullName = $"{rawData.Prefix}{rawData.FirstName} {rawData.LastName}", - DateSendLeave = rawData.CreatedAt, + DateSendLeave = rawData.DateSendLeave ?? rawData.CreatedAt, Status = rawData.LeaveStatus, LeaveStartDate = rawData.LeaveStartDate, LeaveEndDate = rawData.LeaveEndDate, @@ -2678,7 +2956,10 @@ namespace BMA.EHR.Leave.Service.Controllers LeaveLimit = rawData.Type.Limit + extendLeave, LeaveSummary = leaveSummary, - LeaveRemain = (rawData.Type.Limit + extendLeave) - leaveSummary + LeaveRemain = (rawData.Type.Limit + extendLeave) - leaveSummary, + + LeaveDraftSummary = leaveDraftSummary, + LeaveWaitingSummary = leaveWaitingSummary }; if (rawData.LeaveDocument != null && rawData.LeaveDocument.Count > 0) @@ -2757,7 +3038,7 @@ namespace BMA.EHR.Leave.Service.Controllers var rejectList = await _leaveRequestRepository.GetSumRejectLeaveAsync(thisYear); var deleteList = await _leaveRequestRepository.GetSumDeleteLeaveAsync(thisYear); // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (pf == null) { diff --git a/BMA.EHR.Leave/DTOs/LeaveRequest/GetLeaveRequestForAdminByIdDto.cs b/BMA.EHR.Leave/DTOs/LeaveRequest/GetLeaveRequestForAdminByIdDto.cs index 88dfe7da..e9bae1bd 100644 --- a/BMA.EHR.Leave/DTOs/LeaveRequest/GetLeaveRequestForAdminByIdDto.cs +++ b/BMA.EHR.Leave/DTOs/LeaveRequest/GetLeaveRequestForAdminByIdDto.cs @@ -147,6 +147,10 @@ namespace BMA.EHR.Leave.Service.DTOs.LeaveRequest public List Approvers { get; set; } = new(); public Guid? KeycloakUserId { get; set; } = Guid.Empty; + + + public double LeaveDraftSummary { get; set; } = 0; + public double LeaveWaitingSummary { get; set; } = 0; } public class GetLeaveApproverDto diff --git a/BMA.EHR.Leave/DTOs/LeaveRequest/GetUserLeaveProfileResultDto.cs b/BMA.EHR.Leave/DTOs/LeaveRequest/GetUserLeaveProfileResultDto.cs index e64b5ac3..a59eab85 100644 --- a/BMA.EHR.Leave/DTOs/LeaveRequest/GetUserLeaveProfileResultDto.cs +++ b/BMA.EHR.Leave/DTOs/LeaveRequest/GetUserLeaveProfileResultDto.cs @@ -53,5 +53,8 @@ public string? CurrentZipCode { get; set; } public int GovAge { get; set; } = 0; + + public double LeaveDraftSummary { get; set; } = 0; + public double LeaveWaitingSummary { get; set; } = 0; } } diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs index 2a1e5399..fbb6e2de 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs @@ -903,6 +903,49 @@ namespace BMA.EHR.Placement.Service.Controllers return Success(); } + /// + /// API สำหรับยกเลิกการส่งตัว + /// + /// + /// + /// ค่าตัวแปรที่ส่งมาไม่ถูกต้อง + /// ไม่ได้ Login เข้าระบบ + /// เมื่อเกิดข้อผิดพลาดในการทำงาน + [HttpPost("update/draft-status")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> PersonUpdateDraftStatus([FromBody] PersonUpdateStatusRequest req) + { + var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_PLACEMENT_PASS"); + var jsonData = JsonConvert.DeserializeObject(getPermission); + if (jsonData["status"]?.ToString() != "200") + return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); + + string role = jsonData["result"]?.ToString(); + if (role != "OWNER") + return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); + + var person = await _context.PlacementProfiles + .FirstOrDefaultAsync(x => x.Id == req.PersonalId); + if (person == null) + return Error(GlobalMessages.DataNotFound, 404); + + if (person.PlacementStatus == "REPORT") + return Error("ไม่สามารถยกเลิกการส่งตัวได้ เนื่องจากส่งไปออกคำสั่งแล้ว"); + + if (person.PlacementStatus == "DONE") + return Error("ไม่สามารถยกเลิกการส่งตัวได้ เนื่องจากบรรจุไปแล้ว"); + + person.Draft = false; + person.LastUpdateFullName = FullName ?? "System Administrator"; + person.LastUpdateUserId = UserId ?? ""; + person.LastUpdatedAt = DateTime.Now; + await _context.SaveChangesAsync(); + return Success(); + } + [HttpGet("pass/deferment/{personalId:length(36)}")] public async Task> GetPersonDeferment(Guid personalId) { @@ -2002,7 +2045,7 @@ namespace BMA.EHR.Placement.Service.Controllers .Where(x => req.refIds.Contains(x.Id.ToString())) // .Where(x => x.PlacementStatus.ToUpper() == "REPORT") .ToListAsync(); - placementProfiles.ForEach(profile => profile.PlacementStatus = "PREPARE-CONTAI"); + placementProfiles.ForEach(profile => profile.PlacementStatus = "PREPARE-CONTAIN"); await _context.SaveChangesAsync(); return Success(); } diff --git a/BMA.EHR.Retirement.Service/BMA.EHR.Retirement.Service.csproj b/BMA.EHR.Retirement.Service/BMA.EHR.Retirement.Service.csproj index 255f2a75..6a820e15 100644 --- a/BMA.EHR.Retirement.Service/BMA.EHR.Retirement.Service.csproj +++ b/BMA.EHR.Retirement.Service/BMA.EHR.Retirement.Service.csproj @@ -40,10 +40,17 @@ + + + + PreserveNewest + + + diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementController.cs index ad3105ba..b7a8b8df 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementController.cs @@ -7,6 +7,7 @@ using BMA.EHR.Domain.Models.Retirement; using BMA.EHR.Domain.Shared; using BMA.EHR.Infrastructure.Persistence; using BMA.EHR.Retirement.Service.Requests; +using BMA.EHR.Retirement.Service.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -37,6 +38,7 @@ namespace BMA.EHR.Retirement.Service.Controllers private readonly PermissionRepository _permission; private readonly DisciplineDbContext _contextDiscipline; private readonly RetireReportRepository _service; + private readonly RetirementReportService _reportService; public RetirementController(RetirementRepository repository, NotificationRepository repositoryNoti, ApplicationDBContext context, @@ -46,7 +48,8 @@ namespace BMA.EHR.Retirement.Service.Controllers IHttpContextAccessor httpContextAccessor, PermissionRepository permission, DisciplineDbContext contextDiscipline, - RetireReportRepository service) + RetireReportRepository service, + RetirementReportService reportService) { _repository = repository; _repositoryNoti = repositoryNoti; @@ -58,6 +61,7 @@ namespace BMA.EHR.Retirement.Service.Controllers _permission = permission; _contextDiscipline = contextDiscipline; _service = service; + _reportService = reportService; } #region " Properties " @@ -2213,5 +2217,83 @@ namespace BMA.EHR.Retirement.Service.Controllers } } #endregion + + #region รายงานรายชื่อผู้เกษียณอายุราชการ ข้าราชการ & ลูกจ้างประจำ + /// + /// รายงานรายชื่อผู้เกษียณอายุราชการ ข้าราชการ & ลูกจ้างประจำ + /// + /// Id ของรอบเกษียณ + /// pdf, docx + /// + /// เมื่อทำการอ่านข้อมูลจาก Relational Database สำเร็จ + /// ไม่ได้ Login เข้าระบบ + /// เมื่อเกิดข้อผิดพลาดในการทำงาน + [HttpGet("report/{exportType}/{Id}")] + public async Task> GetReportProfileRetirement([FromRoute] Guid Id, string exportType = "pdf") + { + var retire = await _service.GetProfileRetirementdAsync(Id, token); + if (retire != null) + { + var reportfile = string.Empty; + exportType = exportType.Trim(); + + switch (retire.GetType().GetProperty("Type").GetValue(retire)) + { + case "OFFICER": + if (string.IsNullOrEmpty(retire.GetType().GetProperty("TypeReport").GetValue(retire))) + { + reportfile = $"retire-1"; + } + else if (retire.GetType().GetProperty("TypeReport").GetValue(retire) == "ADD" || retire.GetType().GetProperty("TypeReport").GetValue(retire) == "EDIT") + { + reportfile = $"retire-2"; + } + else if (retire.GetType().GetProperty("TypeReport").GetValue(retire) == "REMOVE") + { + reportfile = $"retire-3"; + } + else + { + return Error(retire.GetType().GetProperty("TypeReport").GetValue(retire)); + } + break; + case "EMPLOYEE": + if (string.IsNullOrEmpty(retire.GetType().GetProperty("TypeReport").GetValue(retire))) + { + reportfile = $"retire-emp-1"; + } + else if (retire.GetType().GetProperty("TypeReport").GetValue(retire) == "ADD" || retire.GetType().GetProperty("TypeReport").GetValue(retire) == "EDIT") + { + reportfile = $"retire-emp-2"; + } + else if (retire.GetType().GetProperty("TypeReport").GetValue(retire) == "REMOVE") + { + reportfile = $"retire-emp-3"; + } + else + { + return Error(retire.GetType().GetProperty("TypeReport").GetValue(retire)); + } + break; + default: + return Error(retire.GetType().GetProperty("Type").GetValue(retire)); + } + + var reportBytes = await _reportService.GenerateReportAsync(reportfile, retire, exportType); + + var fileName = $"reportRetirement-{DateTime.Now:yyyyMMdd-HHmmss}.{exportType}"; + var contentType = exportType.Trim().ToLower() == "pdf" + ? "application/pdf" + : "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; + + return File(reportBytes, contentType, fileName); + + } + else + { + return NotFound(); + } + } + #endregion } } diff --git a/BMA.EHR.Retirement.Service/Program.cs b/BMA.EHR.Retirement.Service/Program.cs index ee807e0a..a55f9674 100644 --- a/BMA.EHR.Retirement.Service/Program.cs +++ b/BMA.EHR.Retirement.Service/Program.cs @@ -3,6 +3,7 @@ using BMA.EHR.Domain.Middlewares; using BMA.EHR.Infrastructure; using BMA.EHR.Infrastructure.Persistence; using BMA.EHR.Retirement.Service; +using BMA.EHR.Retirement.Service.Services; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApiExplorer; @@ -86,6 +87,7 @@ var builder = WebApplication.CreateBuilder(args); builder.Services.AddApplication(); builder.Services.AddLeaveApplication(); builder.Services.AddPersistence(builder.Configuration); + builder.Services.AddScoped(); builder.Services.AddLeavePersistence(builder.Configuration); builder.Services.AddHttpClient(); diff --git a/BMA.EHR.Retirement.Service/Services/RetirementReportService.cs b/BMA.EHR.Retirement.Service/Services/RetirementReportService.cs new file mode 100644 index 00000000..82bf2b27 --- /dev/null +++ b/BMA.EHR.Retirement.Service/Services/RetirementReportService.cs @@ -0,0 +1,638 @@ +using BMA.EHR.Application.Responses; +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Wordprocessing; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace BMA.EHR.Retirement.Service.Services +{ + public class RetirementReportService + { + private readonly IWebHostEnvironment _environment; + private readonly ILogger _logger; + private readonly IConfiguration _configuration; + + /// + /// Initializes a new instance of the RetirementReportService class. + /// + public RetirementReportService( + IWebHostEnvironment environment, + ILogger logger, + IConfiguration configuration) + { + _environment = environment; + _logger = logger; + _configuration = configuration; + } + + #region Public Methods + + /// + /// สร้างรายงานจาก Template (.docx) + /// + public async Task GenerateReportAsync(string templateName, dynamic data, string exportType) + { + try + { + var templatePath = GetTemplatePath(templateName); + var docxBytes = await ProcessTemplateAsync(templatePath, data); + + return exportType.ToLower() == "pdf" + ? await ConvertToPdfAsync(docxBytes) + : docxBytes; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error generating report"); + throw; + } + } + + #endregion + + #region Template Processing + + private string GetTemplatePath(string templateName) + { + var path = Path.Combine(_environment.ContentRootPath, "Templates", $"{templateName}.docx"); + if (!File.Exists(path)) + throw new FileNotFoundException($"Template not found: {templateName}"); + return path; + } + + private async Task ProcessTemplateAsync(string templatePath, dynamic data) + { + using var templateStream = File.OpenRead(templatePath); + using var outputStream = new MemoryStream(); + await templateStream.CopyToAsync(outputStream); + outputStream.Position = 0; + + using (var wordDoc = WordprocessingDocument.Open(outputStream, true)) + { + var mainPart = wordDoc.MainDocumentPart; + if (mainPart == null) return Array.Empty(); + + ReplacePlaceholders(mainPart, data); + wordDoc.Save(); + } + + return outputStream.ToArray(); + } + + private void ReplacePlaceholders(MainDocumentPart mainPart, dynamic data) + { + var document = mainPart.Document; + if (document == null) return; + + var processor = CreateDataProcessor(data); + processor.Process(document, new Action(FillTableRows)); + } + + #endregion + + #region Data Processing Strategy + + private IDataProcessor CreateDataProcessor(dynamic data) + { + var dataType = data.GetType(); + var isDictionary = dataType.IsGenericType && + dataType.GetGenericTypeDefinition() == typeof(Dictionary<,>); + + return isDictionary + ? new DictionaryDataProcessor(data) + : new ObjectDataProcessor(data); + } + + #endregion + + #region Table Processing + + private void FillTableRows(Document document, System.Collections.IEnumerable profiles) + { + var table = document.Descendants().FirstOrDefault(); + if (table == null) return; + + var rows = table.Elements().ToList(); + if (rows.Count == 0) return; + + var strategy = CreateTableStrategy(rows); + strategy.Process(table, rows, profiles); + } + + private static ITableStrategy CreateTableStrategy(List rows) + { + // retire-1 format: 1 row, 1 cell, 1 paragraph + if (IsSingleParagraphFormat(rows)) + return new SingleParagraphTableStrategy(); + + // retire-3 format: 2+ rows (header + template) + return new MultiRowTableStrategy(); + } + + private static bool IsSingleParagraphFormat(List rows) => + rows.Count == 1 && + rows[0].Elements().Count() == 1 && + rows[0].Elements().First().Elements().Count() == 1; + + #endregion + + #region PDF Conversion + + private async Task ConvertToPdfAsync(byte[] docxBytes) + { + var tempDocx = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.docx"); + var tempPdf = Path.ChangeExtension(tempDocx, ".pdf"); + + try + { + await File.WriteAllBytesAsync(tempDocx, docxBytes); + await ConvertToPdfInternalAsync(tempDocx, tempPdf); + return await File.ReadAllBytesAsync(tempPdf); + } + finally + { + if (File.Exists(tempDocx)) File.Delete(tempDocx); + if (File.Exists(tempPdf)) File.Delete(tempPdf); + } + } + + private async Task ConvertToPdfInternalAsync(string docxPath, string pdfPath) + { + try + { + var useDocker = _configuration.GetValue("LibreOffice:UseDocker", false); + var timeout = _configuration.GetValue("LibreOffice:Timeout", 180000); + + if (useDocker) + { + await ConvertToPdfViaDockerAsync(docxPath, pdfPath, timeout); + } + else + { + // // PROD: Disabled local LibreOffice conversion + // await ConvertToPdfLocallyAsync(docxPath, pdfPath, timeout); + throw new NotSupportedException("LibreOffice conversion is disabled."); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error converting to PDF"); + throw; + } + } + + private async Task ConvertToPdfViaDockerAsync(string docxPath, string pdfPath, int timeout) + { + var inputDir = _configuration["LibreOffice:InputDirectory"] ?? "/app/libreoffice/input"; + var outputDir = _configuration["LibreOffice:OutputDirectory"] ?? "/app/libreoffice/output"; + var fileName = Path.GetFileName(docxPath); + var pdfName = Path.ChangeExtension(fileName, ".pdf"); + + // Ensure directories exist + Directory.CreateDirectory(inputDir); + Directory.CreateDirectory(outputDir); + + // Copy file to input folder (LibreOffice watcher will pick it up) + var inputPath = Path.Combine(inputDir, fileName).Replace('\\', '/'); + var outputPath = Path.Combine(outputDir, pdfName).Replace('\\', '/'); + + _logger.LogInformation("📤 Sending file to LibreOffice: {FileName}", fileName); + await File.WriteAllBytesAsync(inputPath, await File.ReadAllBytesAsync(docxPath)); + + // Wait for LibreOffice to convert (file watcher handles it) + var stopwatch = System.Diagnostics.Stopwatch.StartNew(); + var pollInterval = TimeSpan.FromMilliseconds(500); + + while (stopwatch.ElapsedMilliseconds < timeout) + { + if (File.Exists(outputPath)) + { + _logger.LogInformation("✅ PDF received: {PdfName} (took {ElapsedMs}ms)", pdfName, stopwatch.ElapsedMilliseconds); + + await File.WriteAllBytesAsync(pdfPath, await File.ReadAllBytesAsync(outputPath)); + + // Cleanup + try + { + if (File.Exists(outputPath)) File.Delete(outputPath); + _logger.LogDebug("🗑️ Cleaned up output file: {PdfName}", pdfName); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Failed to cleanup output file"); + } + + return; + } + + await Task.Delay(pollInterval); + } + + throw new TimeoutException($"LibreOffice conversion timed out after {timeout}ms. File not found: {outputPath}"); + } + + // // PROD: Disabled local LibreOffice conversion + // private async Task ConvertToPdfLocallyAsync(string docxPath, string pdfPath, int timeout) + // { + // var libreOfficePath = _configuration["LibreOffice:Path"] ?? GetDefaultLibreOfficePath(); + // var arguments = _configuration["LibreOffice:Arguments"] ?? "--headless --convert-to pdf --nologo --norestore"; + // var outputDir = Path.GetDirectoryName(pdfPath); + + // if (string.IsNullOrEmpty(outputDir)) + // { + // throw new DirectoryNotFoundException("Output directory cannot be determined"); + // } + + // var psi = new ProcessStartInfo + // { + // FileName = libreOfficePath, + // Arguments = $"{arguments} --outdir \"{outputDir}\" \"{docxPath}\"", + // UseShellExecute = false, + // RedirectStandardOutput = true, + // RedirectStandardError = true, + // CreateNoWindow = true + // }; + + // using var process = Process.Start(psi); + // var exited = process.WaitForExit(timeout); + + // if (!exited) + // { + // process.Kill(entireProcessTree: true); + // throw new TimeoutException($"LibreOffice conversion timed out after {timeout}ms"); + // } + + // if (process.ExitCode != 0) + // { + // var error = await process.StandardError.ReadToEndAsync(); + // throw new Exception($"LibreOffice conversion failed: {error}"); + // } + // } + + // // PROD: Disabled local LibreOffice path detection + // private static string GetDefaultLibreOfficePath() + // { + // if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + // { + // var possiblePaths = new[] + // { + // @"C:\Program Files\LibreOffice\program\soffice.exe", + // @"C:\Program Files (x86)\LibreOffice\program\soffice.exe", + // @"C:\Program Files\LibreOffice\program\soffice.com" + // }; + + // return possiblePaths.FirstOrDefault(File.Exists) + // ?? throw new FileNotFoundException("LibreOffice not found. Please install LibreOffice or configure the path in appsettings.json"); + // } + + // // Linux/Docker: use default path + // return "libreoffice"; + // } + + #endregion + } + + #region Data Processor Interfaces & Implementations + + internal interface IDataProcessor + { + void Process(Document document, Action tableFiller); + } + + internal class DictionaryDataProcessor : IDataProcessor + { + private readonly dynamic _data; + + public DictionaryDataProcessor(dynamic data) + { + _data = data; + } + + public void Process(Document document, Action tableFiller) + { + var keys = _data.Keys as System.Collections.ICollection; + if (keys == null) return; + + System.Collections.IEnumerable? profiles = null; + + foreach (string key in keys) + { + if (key.Equals("profiles", StringComparison.OrdinalIgnoreCase)) + { + profiles = _data[key] as System.Collections.IEnumerable; + continue; + } + + var valueObj = _data[key]; + if (valueObj != null && typeof(System.Collections.IEnumerable).IsAssignableFrom(valueObj.GetType()) && + valueObj.GetType() != typeof(string)) + { + continue; + } + + var value = valueObj?.ToString() ?? string.Empty; + var placeholder = $"{{{{{key}}}}}"; + TextReplacer.ReplaceAll(document, placeholder, value); + } + + if (profiles != null) + { + tableFiller(document, profiles); + } + } + } + + internal class ObjectDataProcessor : IDataProcessor + { + private readonly dynamic _data; + + public ObjectDataProcessor(dynamic data) + { + _data = data; + } + + public void Process(Document document, Action tableFiller) + { + var dataType = _data.GetType(); + var allProps = dataType.GetProperties(); + var validProps = new List(); + + foreach (var p in allProps) + { + if (p.GetIndexParameters().Length == 0) + { + validProps.Add(p); + } + } + + System.Collections.IEnumerable? profiles = null; + + foreach (var prop in validProps) + { + var propType = prop.PropertyType; + bool isEnumerable = typeof(System.Collections.IEnumerable).IsAssignableFrom(propType); + bool isString = propType == typeof(string); + + if (isEnumerable && !isString) + { + if (prop.Name.Equals("profiles", StringComparison.OrdinalIgnoreCase)) + { + profiles = prop.GetValue(_data) as System.Collections.IEnumerable; + } + continue; + } + + var value = prop.GetValue(_data)?.ToString() ?? string.Empty; + var placeholder = $"{{{{{prop.Name}}}}}"; + TextReplacer.ReplaceAll(document, placeholder, value); + } + + if (profiles != null) + { + tableFiller(document, profiles); + } + } + } + + #endregion + + #region Text Replacer + + internal static class TextReplacer + { + public static void ReplaceAll(Document document, string oldValue, string newValue) + { + bool found = false; + + // Method 1: Check within single Run + foreach (var run in document.Descendants()) + { + var textElements = run.Elements().ToList(); + if (textElements.Count == 0) continue; + + var combinedText = string.Concat(textElements.Select(t => t.Text)); + if (combinedText.Contains(oldValue)) + { + found = true; + var replacedText = combinedText.Replace(oldValue, newValue); + textElements[0].Text = replacedText; + for (int i = 1; i < textElements.Count; i++) + { + textElements[i].Text = string.Empty; + } + } + } + + // Method 2: Check across all Runs in Paragraph + foreach (var para in document.Descendants()) + { + var allRuns = para.Elements().ToList(); + if (allRuns.Count == 0) continue; + + var combinedParaText = string.Concat(allRuns.SelectMany(r => r.Elements().Select(t => t.Text))); + if (combinedParaText.Contains(oldValue)) + { + found = true; + var replacedText = combinedParaText.Replace(oldValue, newValue); + + var firstRunTexts = allRuns[0].Elements().ToList(); + if (firstRunTexts.Count > 0) + { + firstRunTexts[0].Text = replacedText; + for (int i = 1; i < firstRunTexts.Count; i++) + { + firstRunTexts[i].Text = string.Empty; + } + } + + for (int i = 1; i < allRuns.Count; i++) + { + foreach (var t in allRuns[i].Elements()) + { + t.Text = string.Empty; + } + } + } + } + + // Fallback: Check individual Text elements + if (!found) + { + foreach (var text in document.Descendants()) + { + if (!string.IsNullOrEmpty(text.Text) && text.Text.Contains(oldValue)) + { + found = true; + text.Text = text.Text.Replace(oldValue, newValue); + } + } + } + } + + public static void ReplaceInRow(TableRow row, string oldValue, string newValue) + { + bool found = false; + + foreach (var cell in row.Descendants()) + { + foreach (var para in cell.Elements()) + { + found = ReplaceInParagraph(para, oldValue, newValue) || found; + } + } + + // Fallback: Check individual Text elements + if (!found) + { + foreach (var text in row.Descendants()) + { + if (!string.IsNullOrEmpty(text.Text) && text.Text.Contains(oldValue)) + { + found = true; + text.Text = text.Text.Replace(oldValue, newValue); + } + } + } + } + + public static bool ReplaceInParagraph(Paragraph paragraph, string oldValue, string newValue) + { + bool found = false; + + var allTexts = paragraph.Descendants().ToList(); + if (allTexts.Count == 0) return false; + + var combinedParaText = string.Concat(allTexts.Select(t => t.Text)); + + if (combinedParaText.Contains(oldValue)) + { + found = true; + var replacedText = combinedParaText.Replace(oldValue, newValue); + allTexts[0].Text = replacedText; + + for (int i = 1; i < allTexts.Count; i++) + { + allTexts[i].Text = string.Empty; + } + } + + // Fallback: Check individual Text elements + if (!found) + { + foreach (var text in allTexts) + { + if (!string.IsNullOrEmpty(text.Text) && text.Text.Contains(oldValue)) + { + found = true; + text.Text = text.Text.Replace(oldValue, newValue); + } + } + } + + return found; + } + } + + #endregion + + #region Table Strategy Interfaces & Implementations + + internal interface ITableStrategy + { + void Process(Table table, List rows, System.Collections.IEnumerable profiles); + } + + internal class SingleParagraphTableStrategy : ITableStrategy + { + public void Process(Table table, List rows, System.Collections.IEnumerable profiles) + { + var cell = rows[0].Elements().First(); + var templatePara = cell.Elements().First(); + + var profileList = profiles.Cast().ToList(); + + foreach (var profile in profileList) + { + var props = profile.GetType() + .GetProperties() + .Where(p => p.GetIndexParameters().Length == 0) + .ToList(); + + var newPara = (Paragraph)templatePara.CloneNode(true); + + foreach (var prop in props) + { + var value = prop.GetValue(profile)?.ToString() ?? string.Empty; + var placeholder = $"{{{{{prop.Name}}}}}"; + TextReplacer.ReplaceInParagraph(newPara, placeholder, value); + } + + cell.Append(newPara); + } + + templatePara.Remove(); + } + } + + internal class MultiRowTableStrategy : ITableStrategy + { + public void Process(Table table, List rows, System.Collections.IEnumerable profiles) + { + var templateRowIndex = rows.Count >= 2 ? 1 : 0; + var templateRow = rows[templateRowIndex]; + templateRow.Remove(); + + var profileList = profiles.Cast().ToList(); + + // Process header row if exists + if (rows.Count >= 2) + { + ProcessHeaderRow(rows[0], profileList); + } + + // Process template rows + foreach (var profile in profileList) + { + var newRow = (TableRow)templateRow.CloneNode(true); + var props = profile.GetType() + .GetProperties() + .Where(p => p.GetIndexParameters().Length == 0) + .ToList(); + + foreach (var prop in props) + { + var value = prop.GetValue(profile)?.ToString() ?? string.Empty; + var placeholder = $"{{{{{prop.Name}}}}}"; + TextReplacer.ReplaceInRow(newRow, placeholder, value); + } + + table.AppendChild(newRow); + } + } + + private static void ProcessHeaderRow(TableRow headerRow, List profileList) + { + var firstProfile = profileList.FirstOrDefault(); + if (firstProfile == null) return; + + var props = firstProfile.GetType() + .GetProperties() + .Where(p => p.GetIndexParameters().Length == 0) + .ToList(); + + foreach (var prop in props) + { + var value = prop.GetValue(firstProfile)?.ToString() ?? string.Empty; + var placeholder = $"{{{{{prop.Name}}}}}"; + + if (!string.IsNullOrWhiteSpace(value)) + { + TextReplacer.ReplaceInRow(headerRow, placeholder, value); + } + } + } + } + + #endregion +} diff --git a/BMA.EHR.Retirement.Service/Templates/retire-1.docx b/BMA.EHR.Retirement.Service/Templates/retire-1.docx new file mode 100644 index 00000000..aae58587 Binary files /dev/null and b/BMA.EHR.Retirement.Service/Templates/retire-1.docx differ diff --git a/BMA.EHR.Retirement.Service/Templates/retire-2.docx b/BMA.EHR.Retirement.Service/Templates/retire-2.docx new file mode 100644 index 00000000..4c9db823 Binary files /dev/null and b/BMA.EHR.Retirement.Service/Templates/retire-2.docx differ diff --git a/BMA.EHR.Retirement.Service/Templates/retire-3.docx b/BMA.EHR.Retirement.Service/Templates/retire-3.docx new file mode 100644 index 00000000..4257c5f6 Binary files /dev/null and b/BMA.EHR.Retirement.Service/Templates/retire-3.docx differ diff --git a/BMA.EHR.Retirement.Service/Templates/retire-emp-1.docx b/BMA.EHR.Retirement.Service/Templates/retire-emp-1.docx new file mode 100644 index 00000000..28e5c9ea Binary files /dev/null and b/BMA.EHR.Retirement.Service/Templates/retire-emp-1.docx differ diff --git a/BMA.EHR.Retirement.Service/Templates/retire-emp-2.docx b/BMA.EHR.Retirement.Service/Templates/retire-emp-2.docx new file mode 100644 index 00000000..3fd290de Binary files /dev/null and b/BMA.EHR.Retirement.Service/Templates/retire-emp-2.docx differ diff --git a/BMA.EHR.Retirement.Service/Templates/retire-emp-3.docx b/BMA.EHR.Retirement.Service/Templates/retire-emp-3.docx new file mode 100644 index 00000000..622f5b8e Binary files /dev/null and b/BMA.EHR.Retirement.Service/Templates/retire-emp-3.docx differ