Compare commits

..

81 commits

Author SHA1 Message Date
Suphonchai Phoonsawat
4827906d1d Merge branch 'dev' into working
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m48s
2026-05-29 15:46:56 +07:00
Suphonchai Phoonsawat
8ae822d05b leave report #2524 2026-05-29 15:46:29 +07:00
harid
71a4748d39 Job: อัพเดทสถานะผู้สอบผ่านที่ลาออกไปแล้วแต่ยังไม่ส่งไปออกคำสั่ง ทำงานทุกวันเวลา 05:00 น. #2518
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m55s
2026-05-29 15:32:26 +07:00
harid
ad70043264 กรองสถานะขอลาออกฝั่ง user #1586
All checks were successful
Build & Deploy Retirement Service / build (push) Successful in 1m52s
2026-05-29 14:56:16 +07:00
Suphonchai Phoonsawat
513956c861 Merge branch 'dev' into working
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m56s
2026-05-29 10:26:01 +07:00
Suphonchai Phoonsawat
a48f3fa804 fix issue #2534 2026-05-29 10:25:11 +07:00
harid
dc5ac329e2 API ลบรายการเฉพาะสิทธิ์ OWNER #1586
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 2m5s
Build & Deploy Placement Service / build (push) Successful in 1m43s
Build & Deploy Retirement Service / build (push) Successful in 1m47s
2026-05-28 14:53:32 +07:00
harid
3f98e07419 บันทึกข้อมูล CommandCode ใน DisciplineDisciplinary_ProfileComplaintInvestigates #2377
All checks were successful
Build & Deploy Discipline Service / build (push) Successful in 2m23s
2026-05-27 17:40:30 +07:00
ce4558c240 Merge branch 'develop' into adiDev
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m42s
2026-05-25 18:17:47 +07:00
6a38f000ba #2505 2026-05-25 18:17:07 +07:00
harid
f50ad38503 เพิ่ม log api candidate/report/excecute
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m59s
2026-05-25 18:08:13 +07:00
aeb2ceea6f Merge branch 'develop' into adiDev 2026-05-25 17:20:46 +07:00
e96f606c85 แก้ format ฟิว posMasterNo (5) 2026-05-25 17:20:07 +07:00
Suphonchai Phoonsawat
81dc7f4544 ปรับ logic การส่งไฟล์ภาพ #2521
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m53s
2026-05-25 16:28:15 +07:00
Suphonchai Phoonsawat
a16ef7ac55 Merge branch 'develop' into working
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 2m9s
2026-05-25 14:20:53 +07:00
Suphonchai Phoonsawat
f6bf1ab026 ปรับให้เก็บข้อมูลเพิ่มเติมใน CheckinJobStatus 2026-05-25 14:19:44 +07:00
harid
caa01088c6 เพิ่ม log api recruit/report/excecute
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m52s
2026-05-22 18:28:28 +07:00
harid
782d1526b5 fix คำสั่ง C-PM-08, C-PM-09 ให้ส่ง positionId เพิ่ม
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m50s
2026-05-22 13:58:55 +07:00
harid
04ac35c10b Merge branch 'develop' into develop-Bright
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m51s
Build & Deploy Retirement Service / build (push) Successful in 1m55s
2026-05-21 16:47:57 +07:00
harid
02abedc973 fix เลือกตำแหน่งในสายงานหาย เมื่อมีการเปลี่ยนแปลงข้อมูลตำแหน่งในโครงสร้าง #2505 2026-05-21 16:47:19 +07:00
8415ffc92d Merge branch 'develop' into adiDev
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 2m2s
2026-05-21 16:38:49 +07:00
64c75cd9d5 แก้ format ฟิว posMasterNo (4) สำหรับบันทึกตอนทำรายกาขอโอน และรับโอน 2026-05-21 16:38:08 +07:00
harid
22e04d90fe Migrate เพิ่มฟิลด์ posExecutiveId ตาราง PlacementProfiles, PlacementReceives, PlacementAppointments, RetirementOthers 2026-05-21 16:00:07 +07:00
Suphonchai Phoonsawat
2146e0e0ca fix issue #2515
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m53s
2026-05-21 15:49:51 +07:00
Suphonchai Phoonsawat
3f8ae1ede9 fix #2503
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 2m0s
2026-05-20 16:24:24 +07:00
harid
c2795d6891 fix ส่งขอลาออกจากทะเบียนประวัติ ต้องสามารถแก้ไขข้อมูลเงื่อนไขต่าง ๆ ได้ #2501
All checks were successful
Build & Deploy Retirement Service / build (push) Successful in 3m47s
2026-05-19 16:20:58 +07:00
harid
6e5284b3c7 fix ยศไม่แสดงในระบบทะเบียนประวัติหลังออกคำสั่งรับโอนเสร็จสิ้น #2469
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m52s
2026-05-15 17:14:50 +07:00
Suphonchai Phoonsawat
76697c4f63 แก้ไขข้อความ Notofication
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m55s
2026-05-15 16:14:50 +07:00
Suphonchai Phoonsawat
e5f6a44f5f เพิ่ม Call API GetProfileByCheckInAsync ใช้แทนของเดิม 2026-05-15 16:04:39 +07:00
Suphonchai Phoonsawat
583569682d register notification service
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m52s
2026-05-15 13:13:29 +07:00
Suphonchai Phoonsawat
9f756771cd เพิ่ม keycloak_id + token ลงใน Job AdditionalData เพื่อเอาไว้ตรวจสอบ
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m54s
2026-05-15 11:54:04 +07:00
Suphonchai Phoonsawat
219b172073 Merge branch 'develop' into working
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m53s
2026-05-15 11:42:39 +07:00
Suphonchai Phoonsawat
ecf660e4cf send noti process-checkin 2026-05-15 11:41:59 +07:00
harid
0365fad723 Migrate add field PlacementReceives.rank #2469
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m44s
2026-05-12 14:59:22 +07:00
Suphonchai Phoonsawat
20e8dfddd6 นับวันหยุด ต้องตัดเสาร์ อาทิตย์ออก
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m39s
2026-05-12 12:02:45 +07:00
Suphonchai Phoonsawat
91c479ef9e fix addition checkin
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m53s
2026-05-12 11:37:32 +07:00
Suphonchai Phoonsawat
80fcda61cf issue #1580
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m45s
2026-05-11 15:12:09 +07:00
Suphonchai Phoonsawat
f02413f2b2 หลังอนุมัติลา แก้สถานะการลงเวลาเป็น ปกติ
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m47s
2026-05-11 10:11:40 +07:00
Suphonchai Phoonsawat
1739aa8057 fix issue #1575
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m52s
2026-05-07 16:14:01 +07:00
Suphonchai Phoonsawat
bc3bba547f fix api
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m49s
2026-05-06 19:02:57 +07:00
Suphonchai Phoonsawat
4161fcc1cf fix
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m50s
2026-05-06 18:53:53 +07:00
Suphonchai Phoonsawat
6d0921a76a #2466
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m50s
2026-05-06 16:25:54 +07:00
Suphonchai Phoonsawat
df7bebe0ba api check-time แกะ profileId จาก token เพื่อไม่ต้องไป call เอาจาก org
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m42s
2026-05-05 21:23:46 +07:00
Suphonchai Phoonsawat
82a45b6811 remove default value
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m46s
2026-05-05 12:56:31 +07:00
Suphonchai Phoonsawat
63d983f831 fix issue #1572
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m49s
2026-05-05 12:37:38 +07:00
Suphonchai Phoonsawat
e326e43ae6 fix Issue #2458 2026-05-05 10:23:40 +07:00
Suphonchai Phoonsawat
4dab3c5cd9 fix report leaave
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m52s
2026-05-01 16:47:34 +07:00
Suphonchai Phoonsawat
4bd46d13e5 fix issue
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m52s
2026-05-01 16:38:47 +07:00
Suphonchai Phoonsawat
132a59b946 Merge branch 'develop' into working
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m52s
2026-05-01 11:39:11 +07:00
Suphonchai Phoonsawat
740a9984c9 fix #2456 2026-05-01 11:20:44 +07:00
harid
5b0fcd0680 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ #224
All checks were successful
Build & Deploy Retirement Service / build (push) Successful in 1m46s
Build & Deploy Placement Service / build (push) Successful in 1m56s
Build & Deploy Discipline Service / build (push) Successful in 1m55s
2026-05-01 11:20:15 +07:00
Suphonchai Phoonsawat
2bdb8bb733 กทม. ต้องการพิ่มรายการลงเวลานอกสถานะที่ "ปฏิบัติงานในจุดบริการด่วนมหานคร" กำหนดเวลา 10.30-18.30 น. #2458
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m41s
2026-05-01 10:23:27 +07:00
Suphonchai Phoonsawat
aed1e8a58d Merge branch 'develop' into working
All checks were successful
Build & Deploy Checkin Service / build (push) Successful in 20s
2026-04-30 22:03:33 +07:00
Suphonchai Phoonsawat
fb3cb2aa94 สิ่งที่แก้ไข:
autoAck: false - ไม่ ack อัตโนมัติ ต้องรอ process เสร็จก่อน
Manual Ack/Nack:
BasicAck เมื่อ API return success
BasicNack (reject) เมื่อ API return error (401, 500, etc.) หรือ exception
CallRestApi return bool - บอกว่าสำเร็จหรือล้มเหลว
Better logging - แสดง status code และ error message ชัดเจนขึ้น
ตอนนี้เมื่อ API return 401 หรือ error อื่น:

จะ log error พร้อม status code
Message จะถูก reject (ไม่ requeue)
Consumer จะทำงานต่อรับ message ถัดไปเลย (ไม่ค้าง)
2026-04-30 22:02:59 +07:00
harid
b5025c382f fix รายงานเกษียณแก้ให้แสดงสำนัก #2261
All checks were successful
Build & Deploy Retirement Service / build (push) Successful in 2m6s
2026-04-30 14:24:42 +07:00
Suphonchai Phoonsawat
4e2113eef2 fix consumer from Event to AsyncEvent
All checks were successful
Build & Deploy Checkin Service / build (push) Successful in 1m42s
2026-04-30 11:53:53 +07:00
Suphonchai Phoonsawat
ffcf95c210 change docker file
All checks were successful
Build & Deploy Checkin Service / build (push) Successful in 1m59s
2026-04-30 11:20:52 +07:00
Suphonchai Phoonsawat
e4bcfee80c fix issue #2448
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m47s
2026-04-29 11:45:46 +07:00
Suphonchai Phoonsawat
a173a7dc3c fix CheckInConsumer Memery Falult
All checks were successful
Build & Deploy Checkin Service / build (push) Successful in 1m27s
2026-04-29 09:57:26 +07:00
Suphonchai Phoonsawat
361ded2078 fix issue #2444
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 2m20s
2026-04-28 15:56:19 +07:00
Suphonchai Phoonsawat
1379dab556 fix issue #2441 2026-04-28 15:42:41 +07:00
Suphonchai Phoonsawat
d8039379ad change dc constr to use tunnel
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 2m33s
2026-04-28 11:32:04 +07:00
Suphonchai Phoonsawat
2d4116d79a BROHTER เปลี่ยนเป็น BROTHER 2026-04-28 11:14:40 +07:00
Suphonchai Phoonsawat
c65a4a04ac Merge branch 'develop' into working
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m44s
Build & Deploy Placement Service / build (push) Successful in 2m8s
2026-04-23 19:12:24 +07:00
Suphonchai Phoonsawat
d58c7dc07e Logic การคำนวนวันลา แบบร่าง และ นะหว่างพิจาณา #1567 2026-04-23 19:11:54 +07:00
Suphonchai Phoonsawat
bb329f86de เพิ่ม DateSendLeave และปรับ Logic การดึงค่าวันที่ยื่นลา #1567 2026-04-23 15:48:20 +07:00
harid
b3298e88c6 fix path separator
All checks were successful
Build & Deploy Retirement Service / build (push) Successful in 1m49s
2026-04-21 10:34:21 +07:00
harid
04d0067ee8 ถ้ามีรักษาการแก้สิทธิ์ BROTHER
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m58s
2026-04-20 16:38:58 +07:00
harid
c035c13405 check runtime
All checks were successful
Build & Deploy Retirement Service / build (push) Successful in 1m47s
2026-04-20 14:31:28 +07:00
harid
2e9db2d42c รายงานประกาศเกษียณ #2262, #2261
All checks were successful
Build & Deploy Retirement Service / build (push) Successful in 1m50s
2026-04-20 12:37:06 +07:00
Suphonchai Phoonsawat
1389df0225 แก้ไข
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m54s
ถ้าไม่ได้ Add สิทธิ์มา จะข้ามตำแหน่งนั้นไป
แก้บั้กการแสดงข้อมูล กรณี "ROOT"

#2431
2026-04-20 11:42:41 +07:00
Suphonchai Phoonsawat
058027ea29 เปลี่ยน DEFAULT สิทธิ์เป็น "CHILD" กรณีไม่ตั้งค่ามา #2431
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m53s
2026-04-17 20:11:18 +07:00
Suphonchai Phoonsawat
ee2d16925a แก้ไข รักษาการ ถ้าไม่ได้ใส่ privilage มาจะให้ default = "PARENT" #2431
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 2m7s
2026-04-17 19:21:30 +07:00
Suphonchai Phoonsawat
42f3813a7a Merge branch 'develop' into working
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 2m2s
2026-04-17 15:42:36 +07:00
Suphonchai Phoonsawat
7bafbf5001 เพิ่มรายการรักษาการ #2431 2026-04-17 15:35:29 +07:00
harid
db99630e0d fix #2430
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m59s
2026-04-17 15:18:30 +07:00
Suphonchai Phoonsawat
34ec9bb77c Remove Check Pending Status For Cancel Leave #2432
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m54s
2026-04-17 10:31:09 +07:00
Suphonchai Phoonsawat
5606e8b50a Change GetProfileByKeycloakIdNewAsync To GetProfileByKeycloakIdNew2Async 2026-04-17 09:41:52 +07:00
Suphonchai Phoonsawat
ee4e9c3699 แก้ leave/detail เป็น leave-reject/detail สำหรับรายการยกเลิก #2432
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m51s
2026-04-16 21:30:17 +07:00
Suphonchai Phoonsawat
6efeec3f1f เพิ่ม List ของการเจ้หน้าที่ ส่ง noti ขอยกเลิกการลา #2432
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m52s
2026-04-16 19:05:26 +07:00
harid
c34fe35506 API ยกเลิกการส่งตัวบรรจุผู้สอบผ่าน
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m57s
2026-04-16 11:57:47 +07:00
77 changed files with 51668 additions and 498 deletions

View file

@ -80,7 +80,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
public async Task UpdateLeaveUsageAsync(int year, Guid typeId, Guid userId, double day) public async Task UpdateLeaveUsageAsync(int year, Guid typeId, Guid userId, double day)
{ {
// var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken);
var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
if (pf == null) if (pf == null)
{ {
throw new Exception(GlobalMessages.DataNotFound); 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) public async Task UpdateLeaveCountAsync(int year, Guid typeId, Guid userId, int count)
{ {
// var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken);
var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
if (pf == null) if (pf == null)
{ {
throw new Exception(GlobalMessages.DataNotFound); throw new Exception(GlobalMessages.DataNotFound);
@ -124,7 +124,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
public async Task<LeaveBeginning?> GetByYearAndTypeIdForUserAsync(int year, Guid typeId, Guid userId) public async Task<LeaveBeginning?> GetByYearAndTypeIdForUserAsync(int year, Guid typeId, Guid userId)
{ {
// var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken);
var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
if (pf == null) if (pf == null)
{ {
throw new Exception(GlobalMessages.DataNotFound); throw new Exception(GlobalMessages.DataNotFound);
@ -149,7 +149,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
var prevRemain = 0.0; var prevRemain = 0.0;
if (prev != null) if (prev != null)
{ {
prevRemain = prev.LeaveDays - prev.LeaveDaysUsed; prevRemain = prev.LeaveDays - (prev.LeaveDaysUsed ?? 0.0);
} }
if (govAge >= 180) if (govAge >= 180)
@ -215,7 +215,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
var prevRemain = 0.0; var prevRemain = 0.0;
if (prev != null) if (prev != null)
{ {
prevRemain = prev.LeaveDays - prev.LeaveDaysUsed; prevRemain = prev.LeaveDays - (prev.LeaveDaysUsed ?? 0.0);
} }
if (govAge >= 180) if (govAge >= 180)
@ -263,7 +263,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
public async Task<LeaveBeginning?> GetByYearAndTypeIdForUser2Async(int year, Guid typeId, Guid userId) public async Task<LeaveBeginning?> GetByYearAndTypeIdForUser2Async(int year, Guid typeId, Guid userId)
{ {
// var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken);
var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
if (pf == null) if (pf == null)
{ {
return null; return null;
@ -288,7 +288,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
var prevRemain = 0.0; var prevRemain = 0.0;
if (prev != null) if (prev != null)
{ {
prevRemain = prev.LeaveDays - prev.LeaveDaysUsed; prevRemain = prev.LeaveDays - (prev.LeaveDaysUsed ?? 0.0);
} }
if (govAge >= 180) if (govAge >= 180)
@ -376,7 +376,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
var prevRemain = 0.0; var prevRemain = 0.0;
if (prev != null) if (prev != null)
{ {
prevRemain = prev.LeaveDays - prev.LeaveDaysUsed; prevRemain = prev.LeaveDays - (prev.LeaveDaysUsed ?? 0.0);
} }
if (govAge >= 180) if (govAge >= 180)

View file

@ -11,6 +11,8 @@ using Microsoft.Extensions.Configuration;
using System.IO.Compression; using System.IO.Compression;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Net.Http.Json; using System.Net.Http.Json;
using BMA.EHR.Application.Repositories.Leaves.TimeAttendants;
using BMA.EHR.Domain.Models.Leave.TimeAttendants;
namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
{ {
@ -29,6 +31,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
private readonly MinIOLeaveService _minIOService; private readonly MinIOLeaveService _minIOService;
private readonly LeaveBeginningRepository _leaveBeginningRepository; private readonly LeaveBeginningRepository _leaveBeginningRepository;
private readonly ProcessUserTimeStampRepository _processUserTimeStampRepository;
private readonly string URL = string.Empty; private readonly string URL = string.Empty;
@ -44,7 +47,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
EmailSenderService emailSenderService, EmailSenderService emailSenderService,
IApplicationDBContext appDbContext, IApplicationDBContext appDbContext,
MinIOLeaveService minIOService, MinIOLeaveService minIOService,
LeaveBeginningRepository leaveBeginningRepository) : base(dbContext, httpContextAccessor) LeaveBeginningRepository leaveBeginningRepository,
ProcessUserTimeStampRepository processUserTimeStampRepository) : base(dbContext, httpContextAccessor)
{ {
_dbContext = dbContext; _dbContext = dbContext;
_httpContextAccessor = httpContextAccessor; _httpContextAccessor = httpContextAccessor;
@ -58,6 +62,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
Console.WriteLine($"URL : {URL}"); Console.WriteLine($"URL : {URL}");
_minIOService = minIOService; _minIOService = minIOService;
_leaveBeginningRepository = leaveBeginningRepository; _leaveBeginningRepository = leaveBeginningRepository;
_processUserTimeStampRepository = processUserTimeStampRepository;
} }
#endregion #endregion
@ -254,7 +259,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
public async Task<List<LeaveRequest>> GetLeaveRequestByYearAsync(int year, Guid userId) public async Task<List<LeaveRequest>> GetLeaveRequestByYearAsync(int year, Guid userId)
{ {
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken);
var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
if (profile == null) if (profile == null)
{ {
@ -354,7 +359,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
var rawData = _dbContext.Set<LeaveRequest>().AsNoTracking() var rawData = _dbContext.Set<LeaveRequest>().AsNoTracking()
.Include(x => x.Type) .Include(x => x.Type)
.Where(x => x.LeaveStatus != "DRAFT") .Where(x => x.LeaveStatus != "DRAFT")
.OrderByDescending(x => x.CreatedAt) .OrderByDescending(x => (x.DateSendLeave ?? x.CreatedAt))
.AsQueryable(); .AsQueryable();
if (year != 0) if (year != 0)
@ -380,7 +385,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
var rawData = _dbContext.Set<LeaveRequest>().AsNoTracking() var rawData = _dbContext.Set<LeaveRequest>().AsNoTracking()
.Include(x => x.Type) .Include(x => x.Type)
.Where(x => x.LeaveStatus != "DRAFT") .Where(x => x.LeaveStatus != "DRAFT")
.OrderByDescending(x => x.CreatedAt) .OrderByDescending(x => (x.DateSendLeave ?? x.CreatedAt))
.AsQueryable(); .AsQueryable();
// fix issue : 1830 // fix issue : 1830
if (year != 0) if (year != 0)
@ -447,7 +452,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
.Include(x => x.Type) .Include(x => x.Type)
.Where(x => keycloakIdList.Contains(x.KeycloakUserId)) .Where(x => keycloakIdList.Contains(x.KeycloakUserId))
.Where(x => x.LeaveStatus != "DRAFT") .Where(x => x.LeaveStatus != "DRAFT")
.OrderByDescending(x => x.CreatedAt) .OrderByDescending(x =>(x.DateSendLeave ?? x.CreatedAt))
.AsQueryable(); .AsQueryable();
if (year != 0) if (year != 0)
@ -497,7 +502,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
public async Task<double> GetSumLeaveByTypeForUserAsync(Guid keycloakUserId, Guid leaveTypeId, int year) public async Task<double> GetSumLeaveByTypeForUserAsync(Guid keycloakUserId, Guid leaveTypeId, int year)
{ {
// var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(keycloakUserId, AccessToken); // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(keycloakUserId, AccessToken);
var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(keycloakUserId, AccessToken); var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(keycloakUserId, AccessToken);
if (pf == null) if (pf == null)
throw new Exception(GlobalMessages.DataNotFound); throw new Exception(GlobalMessages.DataNotFound);
@ -522,7 +527,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
//.Where(x => x.LeaveStatus != "REJECT" && x.LeaveStatus != "DELETE") //.Where(x => x.LeaveStatus != "REJECT" && x.LeaveStatus != "DELETE")
.ToListAsync(); .ToListAsync();
return data.Sum(x => x.LeaveTotal) + (beginningLeave == null ? 0 : beginningLeave.LeaveDaysUsed); return data.Sum(x => x.LeaveTotal) + (beginningLeave == null ? 0 : (beginningLeave.LeaveDaysUsed ?? 0.0));
} }
//public async Task<double> GetSumApproveLeaveByTypeForUserAsync(Guid keycloakUserId, Guid leaveTypeId, int year) //public async Task<double> GetSumApproveLeaveByTypeForUserAsync(Guid keycloakUserId, Guid leaveTypeId, int year)
@ -574,12 +579,12 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
var data = await _dbContext.Set<LeaveRequest>().AsQueryable().AsNoTracking() var data = await _dbContext.Set<LeaveRequest>().AsQueryable().AsNoTracking()
.Include(x => x.Type) .Include(x => x.Type)
//.Where(x => x.LeaveStartDate.Date < beforeDate.Date) //.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.KeycloakUserId == keycloakUserId)
.Where(x => x.Type.Id == leaveTypeId) .Where(x => x.Type.Id == leaveTypeId)
.Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING")
//.Where(x => x.LeaveStatus != "REJECT" && x.LeaveStatus != "DELETE") //.Where(x => x.LeaveStatus != "REJECT" && x.LeaveStatus != "DELETE")
.OrderByDescending(x => x.CreatedAt) .OrderByDescending(x => (x.DateSendLeave ?? x.CreatedAt))
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
return data; return data;
@ -651,7 +656,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
try try
{ {
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken ?? ""); // 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) if (profile == null)
{ {
throw new Exception(GlobalMessages.DataNotFound); throw new Exception(GlobalMessages.DataNotFound);
@ -728,7 +733,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
} }
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken ?? ""); // 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) if (profile == null)
{ {
throw new Exception(GlobalMessages.DataNotFound); throw new Exception(GlobalMessages.DataNotFound);
@ -817,7 +822,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
} }
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken ?? ""); // 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) if (profile == null)
{ {
throw new Exception(GlobalMessages.DataNotFound); throw new Exception(GlobalMessages.DataNotFound);
@ -904,6 +909,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
rawData.LeaveStatus = "NEW"; rawData.LeaveStatus = "NEW";
rawData.DateSendLeave = DateTime.Now; // Update วันที่ยื่นลาเป็นวันที่ปัจจุบัน
//rawData.ApproveStep = "st2"; //rawData.ApproveStep = "st2";
await UpdateAsync(rawData); await UpdateAsync(rawData);
@ -1242,7 +1248,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
else else
{ {
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); // 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) if (profile == null)
{ {
throw new Exception(GlobalMessages.DataNotFound); throw new Exception(GlobalMessages.DataNotFound);
@ -1324,9 +1330,68 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
} }
await _appDbContext.SaveChangesAsync(); await _appDbContext.SaveChangesAsync();
// insert to process timestamp // ปรับสถานะการลงเวลา
if (rawData.LeaveStartDate.Date == rawData.LeaveEndDate.Date)
{
var processCheckIn = await _dbContext.Set<ProcessUserTimeStamp>()
.Where(x => x.KeycloakUserId == rawData.KeycloakUserId)
.Where(x => x.CheckIn.Date == rawData.LeaveStartDate.Date)
.FirstOrDefaultAsync();
if (processCheckIn is not null)
{
switch (rawData.LeaveRange.Trim().ToUpper())
{
case "MORNING":
processCheckIn.CheckInStatus = "NORMAL";
break;
case "AFTERNOON":
processCheckIn.CheckOutStatus = "NORMAL";
break;
case "ALL":
processCheckIn.CheckInStatus = "NORMAL";
processCheckIn.CheckOutStatus = "NORMAL";
break;
default:
break;
}
}
await _dbContext.SaveChangesAsync();
}
else
{
var from = rawData.LeaveStartDate.Date;
var to = rawData.LeaveEndDate.Date;
for (var day = from.Date; day <= to.Date; day = day.AddDays(1))
{
var processCheckIn = await _dbContext.Set<ProcessUserTimeStamp>()
.Where(x => x.KeycloakUserId == rawData.KeycloakUserId)
.Where(x => x.CheckIn.Date == day.Date)
.FirstOrDefaultAsync();
if (processCheckIn is not null)
{
switch (rawData.LeaveRange.Trim().ToUpper())
{
case "MORNING":
processCheckIn.CheckInStatus = "NORMAL";
break;
case "AFTERNOON":
processCheckIn.CheckOutStatus = "NORMAL";
break;
case "ALL":
processCheckIn.CheckInStatus = "NORMAL";
processCheckIn.CheckOutStatus = "NORMAL";
break;
default:
break;
}
}
}
await _dbContext.SaveChangesAsync();
}
// Send Noti // Send Noti
var noti = new Notification var noti = new Notification
{ {
@ -1412,7 +1477,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
else else
{ {
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); // 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) if (profile == null)
{ {
throw new Exception(GlobalMessages.DataNotFound); throw new Exception(GlobalMessages.DataNotFound);
@ -1485,7 +1550,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
KeycloakUserId = pf.Keycloak == null ? Guid.Empty : pf.Keycloak.Value, KeycloakUserId = pf.Keycloak == null ? Guid.Empty : pf.Keycloak.Value,
LeaveTypeId = b.LeaveTypeId, LeaveTypeId = b.LeaveTypeId,
LeaveTypeCode = b.LeaveType!.Code, LeaveTypeCode = b.LeaveType!.Code,
SumLeaveDay = b.LeaveDaysUsed SumLeaveDay = b.LeaveDaysUsed ?? 0.0
}); });
} }
} }
@ -1876,7 +1941,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
.Include(x => x.Type) .Include(x => x.Type)
.Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.KeycloakUserId == keycloakUserId)
.Where(x => x.Type.Id == leaveTypeId) .Where(x => x.Type.Id == leaveTypeId)
.Where(x => x.CreatedAt.Date >= startDate && x.CreatedAt < endDate) .Where(x => ((x.DateSendLeave ?? x.CreatedAt) >= startDate && (x.DateSendLeave ??x.CreatedAt) <= endDate))
//.Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) //.Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date)
.Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING")
.ToListAsync(); .ToListAsync();
@ -1887,6 +1952,102 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
return 0; return 0;
} }
public async Task<double> GetSumApproveLeaveTotalByTypeAndRangeForUserByProfile(Guid profileId, Guid leaveTypeId, DateTime startDate, DateTime endDate)
{
var data = await _dbContext.Set<LeaveRequest>().AsQueryable().AsNoTracking()
.Include(x => x.Type)
.Where(x => x.ProfileId == profileId)
.Where(x => x.Type.Id == leaveTypeId)
.Where(x => ((x.DateSendLeave ?? x.CreatedAt) >= startDate && (x.DateSendLeave ??x.CreatedAt) <= endDate))
//.Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date)
.Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING")
.ToListAsync();
if (data.Count > 0)
return data.Sum(x => x.LeaveTotal);
else
return 0;
}
public async Task<int> GetSumApproveLeaveCountByTypeAndRangeForUserByProfile(Guid profileId, Guid leaveTypeId, DateTime startDate, DateTime endDate)
{
var data = await _dbContext.Set<LeaveRequest>().AsQueryable().AsNoTracking()
.Include(x => x.Type)
.Where(x => x.ProfileId == profileId)
.Where(x => x.Type.Id == leaveTypeId)
.Where(x => ((x.DateSendLeave ?? x.CreatedAt) >= startDate && (x.DateSendLeave ??x.CreatedAt) <= endDate))
//.Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date)
.Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING")
.ToListAsync();
return data.Count;
}
public async Task<int> GetSumApproveLeaveCountByTypeAndRangeForUser2(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate)
{
var data = await _dbContext.Set<LeaveRequest>().AsQueryable().AsNoTracking()
.Include(x => x.Type)
.Where(x => x.KeycloakUserId == keycloakUserId)
.Where(x => x.Type.Id == leaveTypeId)
.Where(x => ((x.DateSendLeave ?? x.CreatedAt) >= startDate && (x.DateSendLeave ??x.CreatedAt) <= endDate))
//.Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date)
.Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING")
.ToListAsync();
return data.Count;
}
/// <summary>
/// วันลาที่สร้างแบบร่างยังไม่ได้ยื่น
/// </summary>
/// <param name="keycloakUserId"></param>
/// <param name="leaveTypeId"></param>
/// <param name="startDate"></param>
/// <param name="endDate"></param>
/// <returns></returns>
public async Task<double> GetSumDraftLeaveTotalByTypeAndRangeForUser2(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate)
{
var data = await _dbContext.Set<LeaveRequest>().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;
}
/// <summary>
/// วันลาที่ยื่นแล้วรอพิจารณา
/// </summary>
/// <param name="keycloakUserId"></param>
/// <param name="leaveTypeId"></param>
/// <param name="startDate"></param>
/// <param name="endDate"></param>
/// <returns></returns>
public async Task<double> GetSumNewLeaveTotalByTypeAndRangeForUser2(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate)
{
var data = await _dbContext.Set<LeaveRequest>().AsQueryable().AsNoTracking()
.Include(x => x.Type)
.Where(x => x.KeycloakUserId == keycloakUserId)
.Where(x => x.Type.Id == leaveTypeId)
.Where(x => ((x.DateSendLeave ?? x.CreatedAt) >= startDate && (x.DateSendLeave ??x.CreatedAt) < endDate))
//.Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date)
.Where(x => (x.LeaveStatus == "NEW" || x.LeaveStatus == "PENDING"))
.ToListAsync();
if (data.Count > 0)
return data.Sum(x => x.LeaveTotal);
else
return 0;
}
public async Task<int> GetCountApproveLeaveByTypeAndRangeForUser(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate) public async Task<int> GetCountApproveLeaveByTypeAndRangeForUser(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate)
{ {
var data = await _dbContext.Set<LeaveRequest>().AsQueryable().AsNoTracking() var data = await _dbContext.Set<LeaveRequest>().AsQueryable().AsNoTracking()

View file

@ -213,6 +213,79 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants
} }
} }
public async Task<List<AdditionalCheckRequest>> GetAdditionalCheckRequestsByAdminRole2(DateTime startDate, DateTime endDate, string role, string nodeId, int? node, string? keyword, string? status)
{
try
{
var data = await _dbContext.Set<AdditionalCheckRequest>().AsQueryable()
.Where(x => (x.CheckDate.Date >= startDate.Date && x.CheckDate.Date <= endDate.Date))
.OrderByDescending(x => x.CreatedAt.Date)
.ToListAsync();
if(!string.IsNullOrEmpty(status))
data = data.Where(x => x.Status == status).ToList();
if (!string.IsNullOrEmpty(keyword))
{
data = data.Where(x =>
(
(x.Prefix ?? "") + (x.FirstName ?? "") + " " + (x.LastName ?? "")).Contains(keyword)
|| x.Description.Contains(keyword)
).ToList();
}
if (role == "OWNER")
{
node = null;
}
if (role == "OWNER" || role == "CHILD")
{
data = data
.Where(x => node == 4 ? x.Child4DnaId == Guid.Parse(nodeId!) : (node == 3 ? x.Child3DnaId == Guid.Parse(nodeId!) : (node == 2 ? x.Child2DnaId == Guid.Parse(nodeId!) : (node == 1 ? x.Child1DnaId == Guid.Parse(nodeId!) : (node == 0 ? x.RootDnaId == Guid.Parse(nodeId!) : (node == null ? true : true))))))
.ToList();
}
else if (role == "BROTHER")
{
data = data
.Where(x => node == 4 ? x.Child3DnaId == Guid.Parse(nodeId!) : (node == 3 ? x.Child2DnaId == Guid.Parse(nodeId!) : (node == 2 ? x.Child1DnaId == Guid.Parse(nodeId!) : (node == 1 || node == 0 ? x.RootDnaId == Guid.Parse(nodeId!) : (node == null ? true : true)))))
.ToList();
}
else if (role == "ROOT")
{
data = data
.Where(x => x.RootDnaId == Guid.Parse(nodeId!)).ToList();
}
// else if (role == "PARENT")
// {
// data = data
// .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null && x.Child1DnaId != Guid.Empty).ToList();
// }
else if (role == "NORMAL")
{
data = data.Where(x =>
node == 0 ? x.RootDnaId == Guid.Parse(nodeId!) &&
(x.Child1DnaId == Guid.Empty || x.Child1DnaId == null) :
node == 1 ? x.Child1DnaId == Guid.Parse(nodeId!) &&
(x.Child2DnaId == Guid.Empty || x.Child2DnaId == null) :
node == 2 ? x.Child2DnaId == Guid.Parse(nodeId!) &&
(x.Child3DnaId == Guid.Empty || x.Child3DnaId == null) :
node == 3 ? x.Child3DnaId == Guid.Parse(nodeId!) &&
(x.Child4DnaId == Guid.Empty || x.Child4DnaId == null) :
node == 4 ? x.Child4DnaId == Guid.Parse(nodeId!) :
true
).ToList();
}
return data;
}
catch
{
throw;
}
}
#endregion #endregion
} }
} }

View file

@ -114,6 +114,62 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants
return job!; return job!;
} }
/// <summary>
/// ดึงข้อมูลงานที่ค้างอยู่ในสถานะ PENDING หรือ PROCESSING เกินเวลาที่กำหนด (นาที)
/// </summary>
public async Task<List<CheckInJobStatus>> GetStalePendingOrProcessingJobsAsync(int timeoutMinutes = 30)
{
//var cutoffDate = DateTime.Now.AddMinutes(-timeoutMinutes);
var cutoffDate = DateTime.Now.AddMinutes(-timeoutMinutes);
var staleJobs = await _dbContext.Set<CheckInJobStatus>()
.Where(x => (x.Status == "PENDING" || x.Status == "PROCESSING")
&& x.CreatedDate <= cutoffDate)
.OrderBy(x => x.CreatedDate)
.ToListAsync();
return staleJobs;
}
/// <summary>
/// ดึงข้อมูลงานที่ค้างอยู่ในสถานะ PENDING หรือ PROCESSING เกินเวลาที่กำหนด (นาที) ของ user คนใดคนหนึ่ง
/// </summary>
public async Task<List<CheckInJobStatus>> GetStalePendingOrProcessingJobsByUserAsync(Guid userId, int timeoutMinutes = 30)
{
var cutoffDate = DateTime.Now.AddMinutes(-timeoutMinutes);
//var cutoffDate = new DateTime(2026, 5, 28, 23, 59, 59);
var staleJobs = await _dbContext.Set<CheckInJobStatus>()
.Where(x => x.KeycloakUserId == userId
&& (x.Status == "PENDING" || x.Status == "PROCESSING")
&& x.CreatedDate < cutoffDate)
.OrderBy(x => x.CreatedDate)
.ToListAsync();
return staleJobs;
}
/// <summary>
/// Mark งานที่ค้างเกินเวลาที่กำหนดเป็น FAILED
/// </summary>
public async Task<int> MarkStaleJobsAsFailedAsync(int timeoutMinutes = 30)
{
var staleJobs = await GetStalePendingOrProcessingJobsAsync(timeoutMinutes);
foreach (var job in staleJobs)
{
job.Status = "FAILED";
job.CompletedDate = DateTime.Now;
job.ErrorMessage = $"งานค้างในสถานะ {job.Status} เกิน {timeoutMinutes} นาที ระบบทำเครื่องหมายเป็น FAILED อัตโนมัติ";
}
if (staleJobs.Any())
{
_dbContext.Set<CheckInJobStatus>().UpdateRange(staleJobs);
await _dbContext.SaveChangesAsync();
}
return staleJobs.Count;
}
/// <summary> /// <summary>
/// ล้างข้อมูล Job Status ที่เก่าเกิน X วัน /// ล้างข้อมูล Job Status ที่เก่าเกิน X วัน
/// </summary> /// </summary>

View file

@ -49,12 +49,16 @@ namespace BMA.EHR.Application.Repositories.MetaData
public async Task<int> GetHolidayCountAsync(DateTime startDate, DateTime endDate, string category = "NORMAL") public async Task<int> GetHolidayCountAsync(DateTime startDate, DateTime endDate, string category = "NORMAL")
{ {
var data = await _dbContext.Set<Holiday>().AsQueryable() var query = _dbContext.Set<Holiday>().AsQueryable()
.Where(x => x.Category == category) .Where(x => x.Category == category)
.Where(x => x.HolidayDate.Date >= startDate && x.HolidayDate.Date <= endDate) .Where(x => x.HolidayDate.Date >= startDate && x.HolidayDate.Date <= endDate);
.CountAsync();
return data; if (category == "NORMAL")
query = query.Where(x => x.HolidayDate.DayOfWeek != DayOfWeek.Saturday && x.HolidayDate.DayOfWeek != DayOfWeek.Sunday);
else
query = query.Where(x => x.HolidayDate.DayOfWeek != DayOfWeek.Sunday);
return await query.CountAsync();
} }
public List<DateTime> GetWeekEnd(DateTime startDate, DateTime endDate, string category = "NORMAL") public List<DateTime> GetWeekEnd(DateTime startDate, DateTime endDate, string category = "NORMAL")

View file

@ -10,6 +10,7 @@ using System.Net.Http.Headers;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using System.Security.Claims; using System.Security.Claims;
using System.Net.Http.Json; using System.Net.Http.Json;
using BMA.EHR.Application.Responses.Leaves;
namespace BMA.EHR.Application.Repositories namespace BMA.EHR.Application.Repositories
{ {
@ -76,6 +77,39 @@ namespace BMA.EHR.Application.Repositories
} }
} }
public async Task<GetPermissionWithActingResultDto?> 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<GetPermissionWithActingResultDto>(apiResult);
return raw;
}
return null;
}
}
catch
{
throw;
}
}
public async Task<dynamic> GetPermissionOrgAPIAsync(string action, string system, string profileId) public async Task<dynamic> GetPermissionOrgAPIAsync(string action, string system, string profileId)
{ {
try try

View file

@ -2,6 +2,9 @@
using BMA.EHR.Domain.Models.Placement; using BMA.EHR.Domain.Models.Placement;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System.Net.Http.Headers;
using Newtonsoft.Json;
namespace BMA.EHR.Application.Repositories namespace BMA.EHR.Application.Repositories
{ {
@ -11,15 +14,17 @@ namespace BMA.EHR.Application.Repositories
private readonly IApplicationDBContext _dbContext; private readonly IApplicationDBContext _dbContext;
private readonly IHttpContextAccessor _httpContextAccessor; private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IConfiguration _configuration;
#endregion #endregion
#region " Constructor and Destructor " #region " Constructor and Destructor "
public PlacementRepository(IApplicationDBContext dbContext, IHttpContextAccessor httpContextAccessor) : base(dbContext, httpContextAccessor) public PlacementRepository(IApplicationDBContext dbContext, IHttpContextAccessor httpContextAccessor, IConfiguration configuration) : base(dbContext, httpContextAccessor)
{ {
_dbContext = dbContext; _dbContext = dbContext;
_httpContextAccessor = httpContextAccessor; _httpContextAccessor = httpContextAccessor;
_configuration = configuration;
} }
#endregion #endregion
@ -76,6 +81,94 @@ namespace BMA.EHR.Application.Repositories
return data; return data;
} }
/// <summary>
/// Job อัพเดทสถานะผู้สอบผ่านที่ลาออกไปแล้วแต่ยังไม่ส่งไปออกคำสั่ง
/// ทำงานทุกวันเวลา 05:00 น.
/// </summary>
public async Task UpdateStatusPlacementProfiles()
{
Console.WriteLine("[Job:UpdateStatusPlacementProfiles] === STARTED ===");
var officerProfileIds = await _dbContext.Set<PlacementProfile>()
.Where(p => !string.IsNullOrEmpty(p.profileId)
&& p.IsOfficer == true
&& p.PlacementStatus != "DONE"
// && p.Id == Guid.Parse("08deb7de-3030-4d1b-8519-8148584949fc")
)
.Select(p => p.profileId)
.ToListAsync();
if (!officerProfileIds.Any())
{
Console.WriteLine("[Job:UpdateStatusPlacementProfiles] No profiles to process");
return;
}
Console.WriteLine($"[Job:UpdateStatusPlacementProfiles] พบข้าราชการที่สอบผ่านทั้งหมด {officerProfileIds.Count} คน ที่ยังไม่ส่งไปออกคำสั่ง");
var apiUrl = $"{_configuration["API"]}/org/dotnet/check-isLeave";
List<string> leaveProfileIds = new();
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]);
var payload = new
{
profileIds = officerProfileIds.Distinct().ToList()
};
var jsonPayload = JsonConvert.SerializeObject(payload);
var content = new StringContent(jsonPayload, System.Text.Encoding.UTF8, "application/json");
try
{
var response = await client.PostAsync(apiUrl, content);
var result = await response.Content.ReadAsStringAsync();
var responseObj = JsonConvert.DeserializeAnonymousType(result, new
{
status = 0,
message = "",
result = new List<string>()
});
leaveProfileIds = responseObj.result ?? new();
Console.WriteLine($"[Job:UpdateStatusPlacementProfiles] พบ {leaveProfileIds.Count} รายการที่ลาออก");
}
catch (Exception ex)
{
Console.WriteLine($"[Job:UpdateStatusPlacementProfiles] Call API failed: {ex.Message}");
return;
}
}
if (leaveProfileIds.Any())
{
var batchSize = 500;
var totalUpdated = 0;
var totalBatches = (int)Math.Ceiling((double)leaveProfileIds.Count / batchSize);
for (int i = 0; i < totalBatches; i++)
{
var batch = leaveProfileIds.Skip(i * batchSize).Take(batchSize).ToList();
var profilesToUpdate = await _dbContext.Set<PlacementProfile>()
.Where(p => !string.IsNullOrEmpty(p.profileId) && batch.Contains(p.profileId))
.ToListAsync();
foreach (var profile in profilesToUpdate)
{
profile.IsOfficer = false;
}
await _dbContext.SaveChangesAsync();
totalUpdated += profilesToUpdate.Count;
Console.WriteLine($"[Job:UpdateStatusPlacementProfiles] Batch {i + 1}/{totalBatches} → อัปเดต {profilesToUpdate.Count} รายการ");
}
Console.WriteLine($"[Job:UpdateStatusPlacementProfiles] อัปเดตรวมทั้งหมด {totalUpdated} รายการ → IsOfficer = false");
}
Console.WriteLine("[Job:UpdateStatusPlacementProfiles] === COMPLETED ===");
}
#endregion #endregion
} }
} }

View file

@ -192,7 +192,7 @@ namespace BMA.EHR.Application.Repositories.Reports
}).ToList(); }).ToList();
} }
string SignDate = retireHistorys.SignDate != null ? DateTime.Parse(retireHistorys.SignDate.ToString()).ToThaiFullDate().ToString().ToThaiNumber() : "-"; 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 else
@ -312,7 +312,7 @@ namespace BMA.EHR.Application.Repositories.Reports
root = (isDuplicateRoot ? "" : profile.root + "\n") + root = (isDuplicateRoot ? "" : profile.root + "\n") +
(isDuplicateHospital || !hospital.ToObject<List<string>>().Contains(profile.child1) ? "" : profile.child1 + "\n") + (isDuplicateHospital || !hospital.ToObject<List<string>>().Contains(profile.child1) ? "" : profile.child1 + "\n") +
(isDuplicatePosType ? "" : $"ตำแหน่งประเภท{profile.posTypeName}" + "\n") + (isDuplicatePosType ? "" : $"ตำแหน่งประเภท{profile.posTypeName}" + "\n") +
(isDuplicatePosLevel ? "" : $"ระดับ{profile.posLevelName}"), (isDuplicatePosLevel ? "" : $"ระดับ{profile.posLevelName}").ToThaiNumber(),
child = (profile.posExecutiveName == null ? "" : profile.posExecutiveName + "\n") + child = (profile.posExecutiveName == null ? "" : profile.posExecutiveName + "\n") +
(profile.child4 == null ? "" : profile.child4 + "\n") + (profile.child4 == null ? "" : profile.child4 + "\n") +
(profile.child3 == null ? "" : profile.child3 + "\n") + (profile.child3 == null ? "" : profile.child3 + "\n") +
@ -326,7 +326,7 @@ namespace BMA.EHR.Application.Repositories.Reports
}).ToList(); }).ToList();
} }
string SignDate = retire.SignDate != null ? DateTime.Parse(retire.SignDate.ToString()).ToThaiFullDate().ToString().ToThaiNumber() : "-"; 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 #endregion

View file

@ -234,6 +234,29 @@ namespace BMA.EHR.Application.Repositories
} }
} }
public async Task<GetProfileByKeycloakIdDto?> GetProfileByCheckInAsync(Guid keycloakId, string? accessToken)
{
try
{
var apiPath = $"{_configuration["API"]}/org/dotnet/check-keycloak/{keycloakId}";
var apiKey = _configuration["API_KEY"];
var apiResult = await GetExternalAPIAsync(apiPath, accessToken ?? "", apiKey);
if (apiResult != null)
{
var raw = JsonConvert.DeserializeObject<GetProfileByKeycloakIdResultDto>(apiResult);
if (raw != null)
return raw.Result;
}
return null;
}
catch
{
throw;
}
}
public async Task<GetProfileLeaveByKeycloakDto?> GetProfileLeaveByKeycloakIdAsync(Guid keycloakId, string? accessToken) public async Task<GetProfileLeaveByKeycloakDto?> GetProfileLeaveByKeycloakIdAsync(Guid keycloakId, string? accessToken)
{ {
@ -1039,6 +1062,42 @@ namespace BMA.EHR.Application.Repositories
} }
} }
public async Task<List<GetProfileByKeycloakIdRootDto>> GetEmployeeByAdminRolev2(string? accessToken, int? node, string? nodeId, string role, string? revisionId, int? reqNode, string? reqNodeId, DateTime? startDate, DateTime? endDate)
{
try
{
var apiPath = $"{_configuration["API"]}/org/dotnet/employee-by-admin-rolev2";
var apiKey = _configuration["API_KEY"];
var body = new
{
node = node,
nodeId = nodeId,
role = role,
// isRetirement
reqNode = reqNode,
reqNodeId = reqNodeId,
date = endDate
};
Console.WriteLine(body);
var profiles = new List<SearchProfileDto>();
var apiResult = await PostExternalAPIAsync(apiPath, accessToken, body, apiKey);
if (apiResult != null)
{
var raw = JsonConvert.DeserializeObject<GetListProfileByKeycloakIdRootResultDto>(apiResult);
if (raw != null)
return raw.Result;
}
return new List<GetProfileByKeycloakIdRootDto>();
}
catch
{
throw;
}
}
public async Task<GetProfileByKeycloakIdRootAddTotalDto> SearchProfile(string? citizenId, string? firstName, string? lastName, string accessToken, int page, int pageSize, string? role, string? nodeId, int? node,string? selectedNodeId,int? selectedNode ) public async Task<GetProfileByKeycloakIdRootAddTotalDto> SearchProfile(string? citizenId, string? firstName, string? lastName, string accessToken, int page, int pageSize, string? role, string? nodeId, int? node,string? selectedNodeId,int? selectedNode )
{ {
try try

View file

@ -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<ActingPermission> 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();
}
}

View file

@ -1,4 +1,4 @@
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using RabbitMQ.Client; using RabbitMQ.Client;
using RabbitMQ.Client.Events; using RabbitMQ.Client.Events;
using System.Text; using System.Text;
@ -21,63 +21,69 @@ var queue = configuration["Rabbit:Queue"] ?? "basic-queue";
// create connection // create connection
var factory = new ConnectionFactory() var factory = new ConnectionFactory()
{ {
//Uri = new Uri("amqp://admin:P@ssw0rd@192.168.4.11:5672") HostName = host,
HostName = host,// หรือ hostname ของ RabbitMQ Server ที่คุณใช้ UserName = user,
UserName = user, // ใส่ชื่อผู้ใช้ของคุณ Password = pass,
Password = pass // ใส่รหัสผ่านของคุณ DispatchConsumersAsync = true
}; };
using var connection = factory.CreateConnection(); using var connection = factory.CreateConnection();
using var channel = connection.CreateModel(); using var channel = connection.CreateModel();
//channel.QueueDeclare(queue: "bma-checkin-queue", durable: true, exclusive: false, autoDelete: false, arguments: null);
channel.QueueDeclare(queue: queue, durable: true, exclusive: false, autoDelete: false, arguments: null); channel.QueueDeclare(queue: queue, durable: true, exclusive: false, autoDelete: false, arguments: null);
var consumer = new EventingBasicConsumer(channel); // Create a SINGLE static HttpClient instance to prevent socket exhaustion
using var httpClient = new HttpClient();
httpClient.Timeout = TimeSpan.FromSeconds(300); // 5 นาที
var consumer = new AsyncEventingBasicConsumer(channel);
consumer.Received += async (model, ea) => consumer.Received += async (model, ea) =>
{ {
try
{
var body = ea.Body.ToArray(); var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body); var message = Encoding.UTF8.GetString(body);
await CallRestApi(message);
// convert string into object WriteToConsole($"Received message: {message}");
//var request = JsonConvert.DeserializeObject<CheckInRequest>(message);
//using (var db = new ApplicationDbContext())
//{
// var item = new AttendantItem
// {
// Name = request.Name,
// CheckInDateTime = request.CheckInDateTime,
// };
// db.AttendantItems.Add(item);
// db.SaveChanges();
// WriteToConsole($"ได้รับคำขอจาก Queue: {message}"); var success = await CallRestApi(message, httpClient, configuration);
// WriteToConsole($"ตอบกลับจาก REST API: {JsonConvert.SerializeObject(item)}");
//}
WriteToConsole($"ได้รับคำขอจาก Queue: {message}"); if (success)
//WriteToConsole($"ตอบกลับจาก REST API: {JsonConvert.SerializeObject(item)}"); {
channel.BasicAck(ea.DeliveryTag, multiple: false);
WriteToConsole("Message processed successfully");
}
else
{
channel.BasicNack(ea.DeliveryTag, multiple: false, requeue: false);
WriteToConsole("Message processing failed - message rejected");
}
}
catch (Exception ex)
{
WriteToConsole($"Error processing message: {ex.Message}");
channel.BasicNack(ea.DeliveryTag, multiple: false, requeue: false);
}
}; };
//channel.BasicConsume(queue: "bma-checkin-queue", autoAck: true, consumer: consumer); channel.BasicConsume(queue: queue, autoAck: false, consumer: consumer);
channel.BasicConsume(queue: queue, autoAck: true, consumer: consumer);
//Console.WriteLine("\nPress 'Enter' to exit the process..."); WriteToConsole("Consumer started. Waiting for messages...");
// Keep the application running
await Task.Delay(-1); await Task.Delay(-1);
static void WriteToConsole(string message) static void WriteToConsole(string message)
{ {
Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} : {message}"); Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} : {message}");
} }
async Task CallRestApi(string requestData) static async Task<bool> CallRestApi(string requestData, HttpClient client, IConfiguration configuration)
{ {
using var client = new HttpClient(); try
{
var apiPath = $"{configuration["API"]}/leave/process-check-in"; var apiPath = $"{configuration["API"]}/leave/process-check-in";
var content = new StringContent(requestData, Encoding.UTF8, "application/json"); var content = new StringContent(requestData, Encoding.UTF8, "application/json");
var response = await client.PostAsync(apiPath, content); var response = await client.PostAsync(apiPath, content);
@ -85,13 +91,31 @@ async Task CallRestApi(string requestData)
if (response.IsSuccessStatusCode) if (response.IsSuccessStatusCode)
{ {
var responseContent = await response.Content.ReadAsStringAsync(); var responseContent = await response.Content.ReadAsStringAsync();
WriteToConsole(responseContent); WriteToConsole($"API Success: {responseContent}");
return true;
} }
else else
{ {
var errorMessage = await response.Content.ReadAsStringAsync(); var errorMessage = await response.Content.ReadAsStringAsync();
var res = JsonSerializer.Deserialize<ResponseObject>(errorMessage); var res = JsonSerializer.Deserialize<ResponseObject>(errorMessage);
WriteToConsole($"Error: {res.Message}"); WriteToConsole($"API Error ({response.StatusCode}): {res?.Message ?? errorMessage}");
return false;
}
}
catch (HttpRequestException ex)
{
WriteToConsole($"HTTP Error: {ex.Message}");
return false;
}
catch (TaskCanceledException ex)
{
WriteToConsole($"Timeout: {ex.Message}");
return false;
}
catch (Exception ex)
{
WriteToConsole($"Unexpected Error: {ex.Message}");
return false;
} }
} }
@ -110,28 +134,14 @@ public class ResponseObject
public class CheckTimeDtoRB public class CheckTimeDtoRB
{ {
public Guid? CheckInId { get; set; } public Guid? CheckInId { get; set; }
public double Lat { get; set; } = 0; public double Lat { get; set; } = 0;
public double Lon { get; set; } = 0; public double Lon { get; set; } = 0;
public string POI { get; set; } = string.Empty; public string POI { get; set; } = string.Empty;
public bool IsLocation { get; set; } = true; public bool IsLocation { get; set; } = true;
public string? LocationName { get; set; } = string.Empty; public string? LocationName { get; set; } = string.Empty;
public string? Remark { get; set; } = string.Empty; public string? Remark { get; set; } = string.Empty;
public Guid? UserId { get; set; } public Guid? UserId { get; set; }
public DateTime? CurrentDate { get; set; } public DateTime? CurrentDate { get; set; }
public string? CheckInFileName { get; set; } public string? CheckInFileName { get; set; }
public byte[]? CheckInFileBytes { get; set; } public byte[]? CheckInFileBytes { get; set; }
} }

View file

@ -968,12 +968,20 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
/// <response code="401">ไม่ได้ Login เข้าระบบ</response> /// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response> /// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("command19/report")] [HttpPost("command19/report")]
public async Task<ActionResult<ResponseObject>> PostReportCommand19([FromBody] ReportPersonRequest req) public async Task<ActionResult<ResponseObject>> PostReportCommand19([FromBody] ReportPersonAndCommandRequest req)
{ {
var data = await _context.DisciplineDisciplinary_ProfileComplaintInvestigates var data = await _context.DisciplineDisciplinary_ProfileComplaintInvestigates
.Where(x => req.refIds.Contains(x.Id.ToString())) .Where(x => req.refIds.Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
data.ForEach(profile => profile.Status = req.status.Trim().ToUpper()); // data.ForEach(profile => profile.Status = req.status.Trim().ToUpper());
data.ForEach(profile =>
{
profile.Status = !string.IsNullOrEmpty(req.status)
? req.status.Trim().ToUpper() : null;
profile.CommandTypeId = !string.IsNullOrEmpty(req.commandTypeId) && Guid.TryParse(req.commandTypeId, out var cmdTypeId)
? cmdTypeId : null;
profile.CommandCode = req.commandCode ?? null;
});
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Success(); return Success();
} }
@ -993,7 +1001,13 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
.Where(x => req.refIds.Contains(x.Id.ToString())) .Where(x => req.refIds.Contains(x.Id.ToString()))
// .Where(x => x.Status.ToUpper() == "REPORT") // .Where(x => x.Status.ToUpper() == "REPORT")
.ToListAsync(); .ToListAsync();
data.ForEach(profile => profile.Status = "NEW"); // data.ForEach(profile => profile.Status = "NEW");
data.ForEach(profile =>
{
profile.Status = "NEW";
profile.CommandTypeId = null;
profile.CommandCode = null;
});
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Success(); return Success();
} }
@ -1013,6 +1027,11 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
.Include(x => x.DisciplineDisciplinary) .Include(x => x.DisciplineDisciplinary)
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => { profile.Status = "REPORTED"; profile.CommandTypeId = null; });
await _context.SaveChangesAsync();
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
on p.Id.ToString() equals r.refId on p.Id.ToString() equals r.refId
@ -1061,8 +1080,8 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
var _result = await _res.Content.ReadAsStringAsync(); var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) if (_res.IsSuccessStatusCode)
{ {
// คำสั่งไล่ออก หรือ ปลดออก Status หลังออกคำสั่งใช้ "REPORTED" เพื่อไม่ให้ส่งรายชื่อไปออกคำสั่งซ้ำได้ //// คำสั่งไล่ออก หรือ ปลดออก Status หลังออกคำสั่งใช้ "REPORTED" เพื่อไม่ให้ส่งรายชื่อไปออกคำสั่งซ้ำได้
data.ForEach(profile => { profile.Status = "REPORTED"; profile.CommandTypeId = null; }); // data.ForEach(profile => { profile.Status = "REPORTED"; profile.CommandTypeId = null; });
var _profile = new List<ProfileComplaintInvestigate>(); var _profile = new List<ProfileComplaintInvestigate>();
DateTime _date = DateTime.Now; DateTime _date = DateTime.Now;
foreach (var item in data) foreach (var item in data)
@ -1105,12 +1124,20 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
/// <response code="401">ไม่ได้ Login เข้าระบบ</response> /// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response> /// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("command20/report")] [HttpPost("command20/report")]
public async Task<ActionResult<ResponseObject>> PostReportcommand20([FromBody] ReportPersonRequest req) public async Task<ActionResult<ResponseObject>> PostReportcommand20([FromBody] ReportPersonAndCommandRequest req)
{ {
var data = await _context.DisciplineDisciplinary_ProfileComplaintInvestigates var data = await _context.DisciplineDisciplinary_ProfileComplaintInvestigates
.Where(x => req.refIds.Contains(x.Id.ToString())) .Where(x => req.refIds.Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
data.ForEach(profile => profile.Status = req.status.Trim().ToUpper()); // data.ForEach(profile => profile.Status = req.status.Trim().ToUpper());
data.ForEach(profile =>
{
profile.Status = !string.IsNullOrEmpty(req.status)
? req.status.Trim().ToUpper() : null;
profile.CommandTypeId = !string.IsNullOrEmpty(req.commandTypeId) && Guid.TryParse(req.commandTypeId, out var cmdTypeId)
? cmdTypeId : null;
profile.CommandCode = req.commandCode ?? null;
});
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Success(); return Success();
} }
@ -1130,7 +1157,13 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
.Where(x => req.refIds.Contains(x.Id.ToString())) .Where(x => req.refIds.Contains(x.Id.ToString()))
// .Where(x => x.Status.ToUpper() == "REPORT") // .Where(x => x.Status.ToUpper() == "REPORT")
.ToListAsync(); .ToListAsync();
data.ForEach(profile => profile.Status = "NEW"); // data.ForEach(profile => profile.Status = "NEW");
data.ForEach(profile =>
{
profile.Status = "NEW";
profile.CommandTypeId = null;
profile.CommandCode = null;
});
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Success(); return Success();
} }
@ -1150,6 +1183,11 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
.Include(x => x.DisciplineDisciplinary) .Include(x => x.DisciplineDisciplinary)
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => { profile.Status = "REPORTED"; profile.CommandTypeId = null; });
await _context.SaveChangesAsync();
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
on p.Id.ToString() equals r.refId on p.Id.ToString() equals r.refId
@ -1198,8 +1236,8 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
var _result = await _res.Content.ReadAsStringAsync(); var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) if (_res.IsSuccessStatusCode)
{ {
// คำสั่งไล่ออก หรือ ปลดออก Status หลังออกคำสั่งใช้ "REPORTED" เพื่อไม่ให้ส่งรายชื่อไปออกคำสั่งซ้ำได้ //// คำสั่งไล่ออก หรือ ปลดออก Status หลังออกคำสั่งใช้ "REPORTED" เพื่อไม่ให้ส่งรายชื่อไปออกคำสั่งซ้ำได้
data.ForEach(profile => { profile.Status = "REPORTED"; profile.CommandTypeId = null; }); // data.ForEach(profile => { profile.Status = "REPORTED"; profile.CommandTypeId = null; });
var _profile = new List<ProfileComplaintInvestigate>(); var _profile = new List<ProfileComplaintInvestigate>();
DateTime _date = DateTime.Now; DateTime _date = DateTime.Now;
foreach (var item in data) foreach (var item in data)
@ -1379,6 +1417,11 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
// .Where(x => x.Status == "REPORT") // .Where(x => x.Status == "REPORT")
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync();
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
on p.Id.ToString() equals r.refId on p.Id.ToString() equals r.refId
@ -1424,12 +1467,12 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); //// var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) //// if (_res.IsSuccessStatusCode)
{ //// {
data.ForEach(profile => profile.Status = "DONE"); //// data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync(); //// await _context.SaveChangesAsync();
} //// }
} }
return Success(); return Success();
} }
@ -1518,6 +1561,11 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
// .Where(x => x.Status == "REPORT") // .Where(x => x.Status == "REPORT")
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync();
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
on p.Id.ToString() equals r.refId on p.Id.ToString() equals r.refId
@ -1563,12 +1611,12 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); //// var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) //// if (_res.IsSuccessStatusCode)
{ //// {
data.ForEach(profile => profile.Status = "DONE"); //// data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync(); //// await _context.SaveChangesAsync();
} //// }
} }
return Success(); return Success();
} }
@ -1582,14 +1630,22 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
/// <response code="401">ไม่ได้ Login เข้าระบบ</response> /// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response> /// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("command27/report")] [HttpPost("command27/report")]
public async Task<ActionResult<ResponseObject>> PostReportCommand27([FromBody] ReportPersonRequest req) public async Task<ActionResult<ResponseObject>> PostReportCommand27([FromBody] ReportPersonAndCommandRequest req)
{ {
try try
{ {
var data = await _context.DisciplineDisciplinary_ProfileComplaintInvestigates var data = await _context.DisciplineDisciplinary_ProfileComplaintInvestigates
.Where(x => req.refIds.Contains(x.Id.ToString())) .Where(x => req.refIds.Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
data.ForEach(profile => profile.Status = req.status.Trim().ToUpper()); // data.ForEach(profile => profile.Status = req.status.Trim().ToUpper());
data.ForEach(profile =>
{
profile.Status = !string.IsNullOrEmpty(req.status)
? req.status.Trim().ToUpper() : null;
profile.CommandTypeId = !string.IsNullOrEmpty(req.commandTypeId) && Guid.TryParse(req.commandTypeId, out var cmdTypeId)
? cmdTypeId : null;
profile.CommandCode = req.commandCode ?? null;
});
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Success(); return Success();
} }
@ -1616,7 +1672,13 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
.Where(x => req.refIds.Contains(x.Id.ToString())) .Where(x => req.refIds.Contains(x.Id.ToString()))
// .Where(x => x.Status.ToUpper() == "REPORT") // .Where(x => x.Status.ToUpper() == "REPORT")
.ToListAsync(); .ToListAsync();
data.ForEach(profile => profile.Status = "NEW"); // data.ForEach(profile => profile.Status = "NEW");
data.ForEach(profile =>
{
profile.Status = "NEW";
profile.CommandTypeId = null;
profile.CommandCode = null;
});
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Success(); return Success();
} }
@ -1641,6 +1703,11 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
.Include(x => x.DisciplineDisciplinary) .Include(x => x.DisciplineDisciplinary)
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; });
await _context.SaveChangesAsync();
string? _null = null; string? _null = null;
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
@ -1686,12 +1753,12 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); //// var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) //// if (_res.IsSuccessStatusCode)
{ //// {
data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); //// data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; });
await _context.SaveChangesAsync(); //// await _context.SaveChangesAsync();
} //// }
} }
return Success(); return Success();
} }
@ -1705,14 +1772,22 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
/// <response code="401">ไม่ได้ Login เข้าระบบ</response> /// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response> /// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("command28/report")] [HttpPost("command28/report")]
public async Task<ActionResult<ResponseObject>> PostReportCommand28([FromBody] ReportPersonRequest req) public async Task<ActionResult<ResponseObject>> PostReportCommand28([FromBody] ReportPersonAndCommandRequest req)
{ {
try try
{ {
var data = await _context.DisciplineDisciplinary_ProfileComplaintInvestigates var data = await _context.DisciplineDisciplinary_ProfileComplaintInvestigates
.Where(x => req.refIds.Contains(x.Id.ToString())) .Where(x => req.refIds.Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
data.ForEach(profile => profile.Status = req.status.Trim().ToUpper()); // data.ForEach(profile => profile.Status = req.status.Trim().ToUpper());
data.ForEach(profile =>
{
profile.Status = !string.IsNullOrEmpty(req.status)
? req.status.Trim().ToUpper() : null;
profile.CommandTypeId = !string.IsNullOrEmpty(req.commandTypeId) && Guid.TryParse(req.commandTypeId, out var cmdTypeId)
? cmdTypeId : null;
profile.CommandCode = req.commandCode ?? null;
});
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Success(); return Success();
} }
@ -1739,7 +1814,13 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
.Where(x => req.refIds.Contains(x.Id.ToString())) .Where(x => req.refIds.Contains(x.Id.ToString()))
// .Where(x => x.Status.ToUpper() == "REPORT") // .Where(x => x.Status.ToUpper() == "REPORT")
.ToListAsync(); .ToListAsync();
data.ForEach(profile => profile.Status = "NEW"); // data.ForEach(profile => profile.Status = "NEW");
data.ForEach(profile =>
{
profile.Status = "NEW";
profile.CommandTypeId = null;
profile.CommandCode = null;
});
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Success(); return Success();
} }
@ -1764,6 +1845,11 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
.Include(x => x.DisciplineDisciplinary) .Include(x => x.DisciplineDisciplinary)
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; });
await _context.SaveChangesAsync();
string? _null = null; string? _null = null;
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
@ -1809,12 +1895,12 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); //// var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) //// if (_res.IsSuccessStatusCode)
{ //// {
data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); //// data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; });
await _context.SaveChangesAsync(); //// await _context.SaveChangesAsync();
} //// }
} }
return Success(); return Success();
} }
@ -1828,14 +1914,22 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
/// <response code="401">ไม่ได้ Login เข้าระบบ</response> /// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response> /// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("command29/report")] [HttpPost("command29/report")]
public async Task<ActionResult<ResponseObject>> PostReportCommand29([FromBody] ReportPersonRequest req) public async Task<ActionResult<ResponseObject>> PostReportCommand29([FromBody] ReportPersonAndCommandRequest req)
{ {
try try
{ {
var data = await _context.DisciplineDisciplinary_ProfileComplaintInvestigates var data = await _context.DisciplineDisciplinary_ProfileComplaintInvestigates
.Where(x => req.refIds.Contains(x.Id.ToString())) .Where(x => req.refIds.Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
data.ForEach(profile => profile.Status = req.status.Trim().ToUpper()); // data.ForEach(profile => profile.Status = req.status.Trim().ToUpper());
data.ForEach(profile =>
{
profile.Status = !string.IsNullOrEmpty(req.status)
? req.status.Trim().ToUpper() : null;
profile.CommandTypeId = !string.IsNullOrEmpty(req.commandTypeId) && Guid.TryParse(req.commandTypeId, out var cmdTypeId)
? cmdTypeId : null;
profile.CommandCode = req.commandCode ?? null;
});
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Success(); return Success();
} }
@ -1862,7 +1956,13 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
.Where(x => req.refIds.Contains(x.Id.ToString())) .Where(x => req.refIds.Contains(x.Id.ToString()))
// .Where(x => x.Status.ToUpper() == "REPORT") // .Where(x => x.Status.ToUpper() == "REPORT")
.ToListAsync(); .ToListAsync();
data.ForEach(profile => profile.Status = "NEW"); // data.ForEach(profile => profile.Status = "NEW");
data.ForEach(profile =>
{
profile.Status = "NEW";
profile.CommandTypeId = null;
profile.CommandCode = null;
});
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Success(); return Success();
} }
@ -1887,6 +1987,11 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
.Include(x => x.DisciplineDisciplinary) .Include(x => x.DisciplineDisciplinary)
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; });
await _context.SaveChangesAsync();
string? _null = null; string? _null = null;
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
@ -1932,12 +2037,12 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); //// var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) //// if (_res.IsSuccessStatusCode)
{ //// {
data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); //// data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; });
await _context.SaveChangesAsync(); //// await _context.SaveChangesAsync();
} //// }
} }
return Success(); return Success();
} }
@ -1951,14 +2056,22 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
/// <response code="401">ไม่ได้ Login เข้าระบบ</response> /// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response> /// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("command30/report")] [HttpPost("command30/report")]
public async Task<ActionResult<ResponseObject>> PostReportCommand30([FromBody] ReportPersonRequest req) public async Task<ActionResult<ResponseObject>> PostReportCommand30([FromBody] ReportPersonAndCommandRequest req)
{ {
try try
{ {
var data = await _context.DisciplineDisciplinary_ProfileComplaintInvestigates var data = await _context.DisciplineDisciplinary_ProfileComplaintInvestigates
.Where(x => req.refIds.Contains(x.Id.ToString())) .Where(x => req.refIds.Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
data.ForEach(profile => profile.Status = req.status.Trim().ToUpper()); // data.ForEach(profile => profile.Status = req.status.Trim().ToUpper());
data.ForEach(profile =>
{
profile.Status = !string.IsNullOrEmpty(req.status)
? req.status.Trim().ToUpper() : null;
profile.CommandTypeId = !string.IsNullOrEmpty(req.commandTypeId) && Guid.TryParse(req.commandTypeId, out var cmdTypeId)
? cmdTypeId : null;
profile.CommandCode = req.commandCode ?? null;
});
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Success(); return Success();
} }
@ -1985,7 +2098,13 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
.Where(x => req.refIds.Contains(x.Id.ToString())) .Where(x => req.refIds.Contains(x.Id.ToString()))
// .Where(x => x.Status.ToUpper() == "REPORT") // .Where(x => x.Status.ToUpper() == "REPORT")
.ToListAsync(); .ToListAsync();
data.ForEach(profile => profile.Status = "NEW"); // data.ForEach(profile => profile.Status = "NEW");
data.ForEach(profile =>
{
profile.Status = "NEW";
profile.CommandTypeId = null;
profile.CommandCode = null;
});
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Success(); return Success();
} }
@ -2010,6 +2129,11 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
.Include(x => x.DisciplineDisciplinary) .Include(x => x.DisciplineDisciplinary)
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; });
await _context.SaveChangesAsync();
string? _null = null; string? _null = null;
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
@ -2055,12 +2179,12 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); //// var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) //// if (_res.IsSuccessStatusCode)
{ //// {
data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); //// data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; });
await _context.SaveChangesAsync(); //// await _context.SaveChangesAsync();
} //// }
} }
return Success(); return Success();
} }
@ -2074,14 +2198,22 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
/// <response code="401">ไม่ได้ Login เข้าระบบ</response> /// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response> /// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("command31/report")] [HttpPost("command31/report")]
public async Task<ActionResult<ResponseObject>> PostReportCommand31([FromBody] ReportPersonRequest req) public async Task<ActionResult<ResponseObject>> PostReportCommand31([FromBody] ReportPersonAndCommandRequest req)
{ {
try try
{ {
var data = await _context.DisciplineDisciplinary_ProfileComplaintInvestigates var data = await _context.DisciplineDisciplinary_ProfileComplaintInvestigates
.Where(x => req.refIds.Contains(x.Id.ToString())) .Where(x => req.refIds.Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
data.ForEach(profile => profile.Status = req.status.Trim().ToUpper()); // data.ForEach(profile => profile.Status = req.status.Trim().ToUpper());
data.ForEach(profile =>
{
profile.Status = !string.IsNullOrEmpty(req.status)
? req.status.Trim().ToUpper() : null;
profile.CommandTypeId = !string.IsNullOrEmpty(req.commandTypeId) && Guid.TryParse(req.commandTypeId, out var cmdTypeId)
? cmdTypeId : null;
profile.CommandCode = req.commandCode ?? null;
});
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Success(); return Success();
} }
@ -2108,7 +2240,13 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
.Where(x => req.refIds.Contains(x.Id.ToString())) .Where(x => req.refIds.Contains(x.Id.ToString()))
// .Where(x => x.Status.ToUpper() == "REPORT") // .Where(x => x.Status.ToUpper() == "REPORT")
.ToListAsync(); .ToListAsync();
data.ForEach(profile => profile.Status = "NEW"); // data.ForEach(profile => profile.Status = "NEW");
data.ForEach(profile =>
{
profile.Status = "NEW";
profile.CommandTypeId = null;
profile.CommandCode = null;
});
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Success(); return Success();
} }
@ -2133,6 +2271,11 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
.Include(x => x.DisciplineDisciplinary) .Include(x => x.DisciplineDisciplinary)
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; });
await _context.SaveChangesAsync();
string? _null = null; string? _null = null;
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
@ -2178,12 +2321,12 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); //// var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) //// if (_res.IsSuccessStatusCode)
{ //// {
data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; }); //// data.ForEach(profile => { profile.Status = "NEW"; profile.CommandTypeId = null; });
await _context.SaveChangesAsync(); //// await _context.SaveChangesAsync();
} //// }
} }
return Success(); return Success();
} }
@ -2197,7 +2340,7 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
/// <response code="401">ไม่ได้ Login เข้าระบบ</response> /// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response> /// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("command32/report")] [HttpPost("command32/report")]
public async Task<ActionResult<ResponseObject>> PostReportCommand32([FromBody] ReportPersonRequest req) public async Task<ActionResult<ResponseObject>> PostReportCommand32([FromBody] ReportPersonAndCommandRequest req)
{ {
try try
{ {
@ -2210,7 +2353,15 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
.Where(x => req.refIds.Contains(x.Id.ToString())) .Where(x => req.refIds.Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
data2.ForEach(profile => profile.IsReport = req.status.Trim().ToUpper()); // data2.ForEach(profile => profile.IsReport = req.status.Trim().ToUpper());
data2.ForEach(profile =>
{
profile.Status = !string.IsNullOrEmpty(req.status)
? req.status.Trim().ToUpper() : null;
profile.CommandTypeId = !string.IsNullOrEmpty(req.commandTypeId) && Guid.TryParse(req.commandTypeId, out var cmdTypeId)
? cmdTypeId : null;
profile.CommandCode = req.commandCode ?? null;
});
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Success(); return Success();
@ -2245,7 +2396,13 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
// .Where(x => x.IsReport == "REPORT") // .Where(x => x.IsReport == "REPORT")
.ToListAsync(); .ToListAsync();
data2.ForEach(profile => profile.IsReport = "NEW"); // data2.ForEach(profile => profile.IsReport = "NEW");
data2.ForEach(profile =>
{
profile.Status = "NEW";
profile.CommandTypeId = null;
profile.CommandCode = null;
});
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Success(); return Success();
@ -2272,6 +2429,11 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
// .Where(x => x.IsReport == "REPORT") // .Where(x => x.IsReport == "REPORT")
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => profile.IsReport = "DONE");
await _context.SaveChangesAsync();
string? _null = null; string? _null = null;
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
@ -2317,12 +2479,12 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); //// var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) //// if (_res.IsSuccessStatusCode)
{ //// {
data.ForEach(profile => profile.IsReport = "DONE"); //// data.ForEach(profile => profile.IsReport = "DONE");
await _context.SaveChangesAsync(); //// await _context.SaveChangesAsync();
} //// }
} }
var data1 = await _context.DisciplineDisciplinary_ProfileComplaintInvestigates var data1 = await _context.DisciplineDisciplinary_ProfileComplaintInvestigates

View file

@ -96,6 +96,9 @@ namespace BMA.EHR.Domain.Common
protected string? Prefix => User.GetPrefix(); protected string? Prefix => User.GetPrefix();
protected string? FullNameFromClaim => User.GetName(); protected string? FullNameFromClaim => User.GetName();
protected string? FirstName => User.GetFirstName();
protected string? LastName => User.GetLastName();
#endregion #endregion
#endregion #endregion

View file

@ -26,5 +26,7 @@ namespace BMA.EHR.Domain.Extensions
public static Guid? GetProfileId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.ProfileId); public static Guid? GetProfileId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.ProfileId);
public static string? GetPrefix(this ClaimsPrincipal user) => user.GetClaimValue(BmaClaimTypes.Prefix); public static string? GetPrefix(this ClaimsPrincipal user) => user.GetClaimValue(BmaClaimTypes.Prefix);
public static string? GetName(this ClaimsPrincipal user) => user.GetClaimValue(BmaClaimTypes.Name); public static string? GetName(this ClaimsPrincipal user) => user.GetClaimValue(BmaClaimTypes.Name);
public static string? GetFirstName(this ClaimsPrincipal user) => user.GetClaimValue(BmaClaimTypes.GivenName);
public static string? GetLastName(this ClaimsPrincipal user) => user.GetClaimValue(BmaClaimTypes.FamilyName);
} }
} }

View file

@ -27,11 +27,11 @@ namespace BMA.EHR.Domain.Models.Leave.Requests
[Required, Comment("จำนวนวันลาทั้งหมด")] [Required, Comment("จำนวนวันลาทั้งหมด")]
public double LeaveDays { get; set; } = 0.0; public double LeaveDays { get; set; } = 0.0;
[Required, Comment("จำนวนวันลาที่ใช้ไป")] [Comment("จำนวนวันลาที่ใช้ไป")]
public double LeaveDaysUsed { get; set; } = 0.0; public double? LeaveDaysUsed { get; set; } = 0.0;
[Comment("จำนวนครั้งที่ลาสะสม")] [Comment("จำนวนครั้งที่ลาสะสม")]
public int LeaveCount { get; set; } = 0; public int? LeaveCount { get; set; } = 0;
public Guid? RootDnaId { get; set; } public Guid? RootDnaId { get; set; }

View file

@ -210,5 +210,7 @@ namespace BMA.EHR.Domain.Models.Leave.Requests
public Guid? Child4DnaId { get; set; } = Guid.Empty; public Guid? Child4DnaId { get; set; } = Guid.Empty;
public DateTime? DateSendLeave { get; set; }
} }
} }

View file

@ -38,5 +38,10 @@ namespace BMA.EHR.Domain.Models.Leave.Requests
public string Comment { get; set; } = string.Empty; public string Comment { get; set; } = string.Empty;
public string? ApproveType { get; set; } = string.Empty; // ผู้บังคับบัญชา = commander, ผู้มีอำนาจอนุมัติ = Approver public string? ApproveType { get; set; } = string.Empty; // ผู้บังคับบัญชา = commander, ผู้มีอำนาจอนุมัติ = Approver
public bool IsAct { get; set; } = false;
public string KeyId { get; set; } = string.Empty;
} }
} }

View file

@ -119,6 +119,10 @@ namespace BMA.EHR.Domain.Models.Placement
public string? position { get; set; } public string? position { get; set; }
[Comment("ตำแหน่งทางการบริหาร")] [Comment("ตำแหน่งทางการบริหาร")]
public string? PositionExecutive { get; set; } public string? PositionExecutive { get; set; }
[Comment("id ตำแหน่งทางการบริหาร")]
public string? posExecutiveId { get; set; }
[Comment("id ประเภทตำแหน่ง")] [Comment("id ประเภทตำแหน่ง")]
public string? posTypeId { get; set; } public string? posTypeId { get; set; }
[Comment("ชื่อประเภทตำแหน่ง")] [Comment("ชื่อประเภทตำแหน่ง")]

View file

@ -321,6 +321,10 @@ namespace BMA.EHR.Domain.Models.Placement
public string? positionName { get; set; } public string? positionName { get; set; }
[Comment("ตำแหน่งทางการบริหาร")] [Comment("ตำแหน่งทางการบริหาร")]
public string? PositionExecutive { get; set; } public string? PositionExecutive { get; set; }
[Comment("id ตำแหน่งทางการบริหาร")]
public string? posExecutiveId { get; set; }
[Comment("สายงาน")] [Comment("สายงาน")]
public string? positionField { get; set; } public string? positionField { get; set; }
[Comment("id ประเภทตำแหน่ง")] [Comment("id ประเภทตำแหน่ง")]

View file

@ -64,6 +64,10 @@ namespace BMA.EHR.Domain.Models.Placement
public string? profileId { get; set; } public string? profileId { get; set; }
[Comment("คำนำหน้า")] [Comment("คำนำหน้า")]
public string? prefix { get; set; } public string? prefix { get; set; }
[Comment("ยศ")]
public string? rank { get; set; }
[Comment("ชื่อ")] [Comment("ชื่อ")]
public string? firstName { get; set; } public string? firstName { get; set; }
[Comment("นามสกุล")] [Comment("นามสกุล")]
@ -128,6 +132,10 @@ namespace BMA.EHR.Domain.Models.Placement
public string? position { get; set; } public string? position { get; set; }
[Comment("ตำแหน่งทางการบริหาร")] [Comment("ตำแหน่งทางการบริหาร")]
public string? PositionExecutive { get; set; } public string? PositionExecutive { get; set; }
[Comment("id ตำแหน่งทางการบริหาร")]
public string? posExecutiveId { get; set; }
[Comment("id ประเภทตำแหน่ง")] [Comment("id ประเภทตำแหน่ง")]
public string? posTypeId { get; set; } public string? posTypeId { get; set; }
[Comment("ชื่อประเภทตำแหน่ง")] [Comment("ชื่อประเภทตำแหน่ง")]

View file

@ -173,6 +173,10 @@ namespace BMA.EHR.Domain.Models.Retirement
public string? position { get; set; } public string? position { get; set; }
[Comment("ตำแหน่งทางการบริหาร")] [Comment("ตำแหน่งทางการบริหาร")]
public string? PositionExecutive { get; set; } public string? PositionExecutive { get; set; }
[Comment("id ตำแหน่งทางการบริหาร")]
public string? posExecutiveId { get; set; }
[Comment("id ประเภทตำแหน่ง")] [Comment("id ประเภทตำแหน่ง")]
public string? posTypeId { get; set; } public string? posTypeId { get; set; }
[Comment("ชื่อประเภทตำแหน่ง")] [Comment("ชื่อประเภทตำแหน่ง")]

View file

@ -8,6 +8,8 @@
public static readonly string DataNotFound = "ไม่พบข้อมูลในระบบ"; public static readonly string DataNotFound = "ไม่พบข้อมูลในระบบ";
public static readonly string ProfileNotFound = "ไม่พบข้อมูลในระบบทะเบียนประวัติ";
public static readonly string NotAuthorized = "กรุณาเข้าสู่ระบบก่อนใช้งาน!"; public static readonly string NotAuthorized = "กรุณาเข้าสู่ระบบก่อนใช้งาน!";
public static readonly string ForbiddenAccess = "คุณไม่ได้รับอนุญาติให้เข้าใช้งาน!"; public static readonly string ForbiddenAccess = "คุณไม่ได้รับอนุญาติให้เข้าใช้งาน!";

View file

@ -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);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,30 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BMA.EHR.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class update_PlacementReceives_add_rank : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "rank",
table: "PlacementReceives",
type: "longtext",
nullable: true,
comment: "ยศ")
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "rank",
table: "PlacementReceives");
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,66 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BMA.EHR.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class update_Tables_add_posExecutiveId : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "posExecutiveId",
table: "RetirementOthers",
type: "longtext",
nullable: true,
comment: "id ตำแหน่งทางการบริหาร")
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<string>(
name: "posExecutiveId",
table: "PlacementReceives",
type: "longtext",
nullable: true,
comment: "id ตำแหน่งทางการบริหาร")
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<string>(
name: "posExecutiveId",
table: "PlacementProfiles",
type: "longtext",
nullable: true,
comment: "id ตำแหน่งทางการบริหาร")
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<string>(
name: "posExecutiveId",
table: "PlacementAppointments",
type: "longtext",
nullable: true,
comment: "id ตำแหน่งทางการบริหาร")
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "posExecutiveId",
table: "RetirementOthers");
migrationBuilder.DropColumn(
name: "posExecutiveId",
table: "PlacementReceives");
migrationBuilder.DropColumn(
name: "posExecutiveId",
table: "PlacementProfiles");
migrationBuilder.DropColumn(
name: "posExecutiveId",
table: "PlacementAppointments");
}
}
}

View file

@ -11721,6 +11721,10 @@ namespace BMA.EHR.Infrastructure.Migrations
.HasColumnType("longtext") .HasColumnType("longtext")
.HasComment("id revision"); .HasComment("id revision");
b.Property<string>("posExecutiveId")
.HasColumnType("longtext")
.HasComment("id ตำแหน่งทางการบริหาร");
b.Property<string>("posLevelId") b.Property<string>("posLevelId")
.HasColumnType("longtext") .HasColumnType("longtext")
.HasComment("id ระดับตำแหน่ง"); .HasComment("id ระดับตำแหน่ง");
@ -13108,6 +13112,10 @@ namespace BMA.EHR.Infrastructure.Migrations
.HasColumnType("longtext") .HasColumnType("longtext")
.HasComment("ชื่อหน่วยงาน"); .HasComment("ชื่อหน่วยงาน");
b.Property<string>("posExecutiveId")
.HasColumnType("longtext")
.HasComment("id ตำแหน่งทางการบริหาร");
b.Property<string>("posLevelId") b.Property<string>("posLevelId")
.HasColumnType("longtext") .HasColumnType("longtext")
.HasComment("id ระดับตำแหน่ง"); .HasComment("id ระดับตำแหน่ง");
@ -13613,6 +13621,10 @@ namespace BMA.EHR.Infrastructure.Migrations
.HasColumnType("longtext") .HasColumnType("longtext")
.HasComment("id revision"); .HasComment("id revision");
b.Property<string>("posExecutiveId")
.HasColumnType("longtext")
.HasComment("id ตำแหน่งทางการบริหาร");
b.Property<string>("posLevelId") b.Property<string>("posLevelId")
.HasColumnType("longtext") .HasColumnType("longtext")
.HasComment("id ระดับตำแหน่ง"); .HasComment("id ระดับตำแหน่ง");
@ -13693,6 +13705,10 @@ namespace BMA.EHR.Infrastructure.Migrations
.HasColumnType("longtext") .HasColumnType("longtext")
.HasComment("profile Id"); .HasComment("profile Id");
b.Property<string>("rank")
.HasColumnType("longtext")
.HasComment("ยศ");
b.Property<string>("root") b.Property<string>("root")
.HasColumnType("longtext") .HasColumnType("longtext")
.HasComment("ชื่อหน่วยงาน root"); .HasComment("ชื่อหน่วยงาน root");
@ -15799,6 +15815,10 @@ namespace BMA.EHR.Infrastructure.Migrations
.HasColumnType("longtext") .HasColumnType("longtext")
.HasComment("id revision"); .HasComment("id revision");
b.Property<string>("posExecutiveId")
.HasColumnType("longtext")
.HasComment("id ตำแหน่งทางการบริหาร");
b.Property<string>("posLevelId") b.Property<string>("posLevelId")
.HasColumnType("longtext") .HasColumnType("longtext")
.HasComment("id ระดับตำแหน่ง"); .HasComment("id ระดับตำแหน่ง");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
{
/// <inheritdoc />
public partial class AddDateSendLeave : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "DateSendLeave",
table: "LeaveRequests",
type: "datetime(6)",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "DateSendLeave",
table: "LeaveRequests");
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,62 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
{
/// <inheritdoc />
public partial class ChangeField : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<double>(
name: "LeaveDaysUsed",
table: "LeaveBeginnings",
type: "double",
nullable: true,
comment: "จำนวนวันลาที่ใช้ไป",
oldClrType: typeof(double),
oldType: "double",
oldComment: "จำนวนวันลาที่ใช้ไป");
migrationBuilder.AlterColumn<int>(
name: "LeaveCount",
table: "LeaveBeginnings",
type: "int",
nullable: true,
comment: "จำนวนครั้งที่ลาสะสม",
oldClrType: typeof(int),
oldType: "int",
oldComment: "จำนวนครั้งที่ลาสะสม");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<double>(
name: "LeaveDaysUsed",
table: "LeaveBeginnings",
type: "double",
nullable: false,
defaultValue: 0.0,
comment: "จำนวนวันลาที่ใช้ไป",
oldClrType: typeof(double),
oldType: "double",
oldNullable: true,
oldComment: "จำนวนวันลาที่ใช้ไป");
migrationBuilder.AlterColumn<int>(
name: "LeaveCount",
table: "LeaveBeginnings",
type: "int",
nullable: false,
defaultValue: 0,
comment: "จำนวนครั้งที่ลาสะสม",
oldClrType: typeof(int),
oldType: "int",
oldNullable: true,
oldComment: "จำนวนครั้งที่ลาสะสม");
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
{
/// <inheritdoc />
public partial class AddApproverField : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsAct",
table: "LeaveRequestApprovers",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<string>(
name: "KeyId",
table: "LeaveRequestApprovers",
type: "longtext",
nullable: false)
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsAct",
table: "LeaveRequestApprovers");
migrationBuilder.DropColumn(
name: "KeyId",
table: "LeaveRequestApprovers");
}
}
}

View file

@ -192,7 +192,7 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
.HasColumnOrder(102) .HasColumnOrder(102)
.HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ");
b.Property<int>("LeaveCount") b.Property<int?>("LeaveCount")
.HasColumnType("int") .HasColumnType("int")
.HasComment("จำนวนครั้งที่ลาสะสม"); .HasComment("จำนวนครั้งที่ลาสะสม");
@ -200,7 +200,7 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
.HasColumnType("double") .HasColumnType("double")
.HasComment("จำนวนวันลาทั้งหมด"); .HasComment("จำนวนวันลาทั้งหมด");
b.Property<double>("LeaveDaysUsed") b.Property<double?>("LeaveDaysUsed")
.HasColumnType("double") .HasColumnType("double")
.HasComment("จำนวนวันลาที่ใช้ไป"); .HasComment("จำนวนวันลาที่ใช้ไป");
@ -428,6 +428,9 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
b.Property<DateTime?>("DateAppoint") b.Property<DateTime?>("DateAppoint")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
b.Property<DateTime?>("DateSendLeave")
.HasColumnType("datetime(6)");
b.Property<string>("Dear") b.Property<string>("Dear")
.HasColumnType("longtext") .HasColumnType("longtext")
.HasComment("เรียนใคร"); .HasComment("เรียนใคร");
@ -710,6 +713,13 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
.IsRequired() .IsRequired()
.HasColumnType("longtext"); .HasColumnType("longtext");
b.Property<bool>("IsAct")
.HasColumnType("tinyint(1)");
b.Property<string>("KeyId")
.IsRequired()
.HasColumnType("longtext");
b.Property<Guid>("KeycloakId") b.Property<Guid>("KeycloakId")
.HasColumnType("char(36)"); .HasColumnType("char(36)");

View file

@ -74,6 +74,9 @@
<Content Update="wwwroot\keycloak.json"> <Content Update="wwwroot\keycloak.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Update="wwwroot\blank.jpeg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -33,6 +33,7 @@ namespace BMA.EHR.Leave.Service.Controllers
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
private readonly UserProfileRepository _userProfileRepository; private readonly UserProfileRepository _userProfileRepository;
private readonly PermissionRepository _permission; private readonly PermissionRepository _permission;
private readonly LeaveRequestRepository _leaveRequestRepository;
#endregion #endregion
@ -44,7 +45,8 @@ namespace BMA.EHR.Leave.Service.Controllers
IWebHostEnvironment hostingEnvironment, IWebHostEnvironment hostingEnvironment,
IConfiguration configuration, IConfiguration configuration,
UserProfileRepository userProfileRepository, UserProfileRepository userProfileRepository,
PermissionRepository permission) PermissionRepository permission,
LeaveRequestRepository leaveRequestRepository)
{ {
_leaveBeginningRepository = leaveBeginningRepository; _leaveBeginningRepository = leaveBeginningRepository;
_context = context; _context = context;
@ -53,6 +55,7 @@ namespace BMA.EHR.Leave.Service.Controllers
_configuration = configuration; _configuration = configuration;
_userProfileRepository = userProfileRepository; _userProfileRepository = userProfileRepository;
_permission = permission; _permission = permission;
_leaveRequestRepository = leaveRequestRepository;
} }
#endregion #endregion
@ -375,6 +378,15 @@ namespace BMA.EHR.Leave.Service.Controllers
try try
{ {
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
// var profileId = ProfileId ?? Guid.Empty;
// var prefix = Prefix ?? "";
// var firstName = FirstName ?? "";
// var lastName = LastName ?? "";
// var rootDnaId = OrgRootDnaId ?? Guid.Empty;
// var child1DnaId = OrgChild1DnaId ?? Guid.Empty;
// var child2DnaId = OrgChild2DnaId ?? Guid.Empty;
// var child3DnaId = OrgChild3DnaId ?? Guid.Empty;
// var child4DnaId = OrgChild4DnaId ?? Guid.Empty;
var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_LEAVE_HISTORY"); var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_LEAVE_HISTORY");
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission); var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
@ -386,20 +398,41 @@ namespace BMA.EHR.Leave.Service.Controllers
if (leaveBeginning == null) if (leaveBeginning == null)
return Error("ไม่พบข้อมูลที่ต้องการแก้ไข", StatusCodes.Status404NotFound); return Error("ไม่พบข้อมูลที่ต้องการแก้ไข", StatusCodes.Status404NotFound);
var profile = await _userProfileRepository.GetProfileByProfileIdAsync(req.ProfileId, AccessToken); var profile = await _userProfileRepository.GetProfileByProfileIdAsync(req.ProfileId, AccessToken);
if (profile == null) if (profile == null)
{ {
return Error("ไม่พบข้อมูลข้าราชการหรือลูกจ้าง", StatusCodes.Status404NotFound); return Error("ไม่พบข้อมูลข้าราชการหรือลูกจ้าง", StatusCodes.Status404NotFound);
} }
leaveBeginning.LeaveTypeId = req.LeaveTypeId; var startFiscalDate = new DateTime(DateTime.Now.Year - 1, 10, 1);
leaveBeginning.LeaveYear = req.LeaveYear; var endFiscalDate = new DateTime(DateTime.Now.Year, 9, 30);
leaveBeginning.LeaveDays = req.LeaveDays;
if (req.LeaveDaysUsed is null || req.LeaveCount is null)
{
var systemLeaveDays = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUserByProfile(req.ProfileId, req.LeaveTypeId, startFiscalDate, endFiscalDate);
var systemLeaveCount = await _leaveRequestRepository.GetSumApproveLeaveCountByTypeAndRangeForUserByProfile(req.ProfileId, req.LeaveTypeId, startFiscalDate, endFiscalDate);
leaveBeginning.LeaveDaysUsed = req.BeginningLeaveDays + systemLeaveDays;
leaveBeginning.LeaveCount = req.BeginningLeaveCount + systemLeaveCount;
leaveBeginning.BeginningLeaveDays = req.BeginningLeaveDays;
leaveBeginning.BeginningLeaveCount = req.BeginningLeaveCount;
}
else
{
leaveBeginning.LeaveDaysUsed = req.LeaveDaysUsed; leaveBeginning.LeaveDaysUsed = req.LeaveDaysUsed;
leaveBeginning.LeaveCount = req.LeaveCount; leaveBeginning.LeaveCount = req.LeaveCount;
leaveBeginning.BeginningLeaveDays = req.BeginningLeaveDays; leaveBeginning.BeginningLeaveDays = req.BeginningLeaveDays;
leaveBeginning.BeginningLeaveCount = req.BeginningLeaveCount; leaveBeginning.BeginningLeaveCount = req.BeginningLeaveCount;
//var systemLeaveDays = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser2(profile.Keycloak ?? Guid.Empty, req.LeaveTypeId, startFiscalDate, endFiscalDate);
//var systemLeaveCount = await _leaveRequestRepository.GetSumApproveLeaveCountByTypeAndRangeForUser2(profile.Keycloak ?? Guid.Empty, req.LeaveTypeId, startFiscalDate, endFiscalDate);
}
leaveBeginning.LeaveTypeId = req.LeaveTypeId;
leaveBeginning.LeaveYear = req.LeaveYear;
leaveBeginning.LeaveDays = req.LeaveDays;
leaveBeginning.ProfileId = req.ProfileId; leaveBeginning.ProfileId = req.ProfileId;
leaveBeginning.Prefix = profile.Prefix; leaveBeginning.Prefix = profile.Prefix;
@ -440,6 +473,17 @@ namespace BMA.EHR.Leave.Service.Controllers
try try
{ {
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
// var profileId = ProfileId ?? Guid.Empty;
// var prefix = Prefix ?? "";
// var firstName = FirstName ?? "";
// var lastName = LastName ?? "";
// var rootDnaId = OrgRootDnaId ?? Guid.Empty;
// var child1DnaId = OrgChild1DnaId ?? Guid.Empty;
// var child2DnaId = OrgChild2DnaId ?? Guid.Empty;
// var child3DnaId = OrgChild3DnaId ?? Guid.Empty;
// var child4DnaId = OrgChild4DnaId ?? Guid.Empty;
var getPermission = await _permission.GetPermissionAPIAsync("CREATE", "SYS_LEAVE_HISTORY"); var getPermission = await _permission.GetPermissionAPIAsync("CREATE", "SYS_LEAVE_HISTORY");
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission); var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
if (jsonData["status"]?.ToString() != "200") if (jsonData["status"]?.ToString() != "200")
@ -464,13 +508,26 @@ namespace BMA.EHR.Leave.Service.Controllers
} }
var leaveBeginning = new LeaveBeginning(); var leaveBeginning = new LeaveBeginning();
leaveBeginning.LeaveTypeId = req.LeaveTypeId;
leaveBeginning.LeaveYear = req.LeaveYear; if (req.LeaveDaysUsed is null || req.LeaveCount is null)
leaveBeginning.LeaveDays = req.LeaveDays; {
leaveBeginning.LeaveDaysUsed = req.BeginningLeaveDays;
leaveBeginning.LeaveCount = req.BeginningLeaveCount;
leaveBeginning.BeginningLeaveDays = req.BeginningLeaveDays;
leaveBeginning.BeginningLeaveCount = req.BeginningLeaveCount;
}
else
{
leaveBeginning.LeaveDaysUsed = req.LeaveDaysUsed; leaveBeginning.LeaveDaysUsed = req.LeaveDaysUsed;
leaveBeginning.LeaveCount = req.LeaveCount; leaveBeginning.LeaveCount = req.LeaveCount;
leaveBeginning.BeginningLeaveDays = req.BeginningLeaveDays; leaveBeginning.BeginningLeaveDays = req.BeginningLeaveDays;
leaveBeginning.BeginningLeaveCount = req.BeginningLeaveCount; leaveBeginning.BeginningLeaveCount = req.BeginningLeaveCount;
}
leaveBeginning.LeaveTypeId = req.LeaveTypeId;
leaveBeginning.LeaveYear = req.LeaveYear;
leaveBeginning.LeaveDays = req.LeaveDays;
leaveBeginning.ProfileId = req.ProfileId; leaveBeginning.ProfileId = req.ProfileId;
leaveBeginning.Prefix = profile.Prefix; leaveBeginning.Prefix = profile.Prefix;

View file

@ -3,8 +3,10 @@ using BMA.EHR.Application.Repositories.Commands;
using BMA.EHR.Application.Repositories.Leaves.LeaveRequests; using BMA.EHR.Application.Repositories.Leaves.LeaveRequests;
using BMA.EHR.Application.Repositories.Leaves.TimeAttendants; using BMA.EHR.Application.Repositories.Leaves.TimeAttendants;
using BMA.EHR.Application.Repositories.MessageQueue; using BMA.EHR.Application.Repositories.MessageQueue;
using BMA.EHR.Application.Responses.Leaves;
using BMA.EHR.Application.Responses.Profiles; using BMA.EHR.Application.Responses.Profiles;
using BMA.EHR.Domain.Common; using BMA.EHR.Domain.Common;
using BMA.EHR.Domain.Extensions;
using BMA.EHR.Domain.Models.Leave.TimeAttendants; using BMA.EHR.Domain.Models.Leave.TimeAttendants;
using BMA.EHR.Domain.Models.Notifications; using BMA.EHR.Domain.Models.Notifications;
using BMA.EHR.Domain.Shared; using BMA.EHR.Domain.Shared;
@ -31,6 +33,7 @@ using System.Diagnostics;
using System.Security.Claims; using System.Security.Claims;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using BMA.EHR.Leave.Services;
using SearchProfileResultDto = BMA.EHR.Leave.Service.DTOs.ChangeRound.SearchProfileResultDto; using SearchProfileResultDto = BMA.EHR.Leave.Service.DTOs.ChangeRound.SearchProfileResultDto;
namespace BMA.EHR.Leave.Service.Controllers namespace BMA.EHR.Leave.Service.Controllers
@ -61,6 +64,7 @@ namespace BMA.EHR.Leave.Service.Controllers
private readonly UserCalendarRepository _userCalendarRepository; private readonly UserCalendarRepository _userCalendarRepository;
private readonly PermissionRepository _permission; private readonly PermissionRepository _permission;
private readonly CheckInJobStatusRepository _checkInJobStatusRepository; private readonly CheckInJobStatusRepository _checkInJobStatusRepository;
private readonly NotificationService _notificationService;
private readonly CommandRepository _commandRepository; private readonly CommandRepository _commandRepository;
@ -100,7 +104,8 @@ namespace BMA.EHR.Leave.Service.Controllers
CheckInJobStatusRepository checkInJobStatusRepository, CheckInJobStatusRepository checkInJobStatusRepository,
HttpClient httpClient, HttpClient httpClient,
ApplicationDBContext appDbContext, ApplicationDBContext appDbContext,
LeaveProcessJobStatusRepository leaveProcessJobStatusRepository) LeaveProcessJobStatusRepository leaveProcessJobStatusRepository,
NotificationService notificationService)
{ {
_dutyTimeRepository = dutyTimeRepository; _dutyTimeRepository = dutyTimeRepository;
_context = context; _context = context;
@ -120,6 +125,7 @@ namespace BMA.EHR.Leave.Service.Controllers
_notificationRepository = notificationRepository; _notificationRepository = notificationRepository;
_checkInJobStatusRepository = checkInJobStatusRepository; _checkInJobStatusRepository = checkInJobStatusRepository;
_leaveProcessJobStatusRepository = leaveProcessJobStatusRepository; _leaveProcessJobStatusRepository = leaveProcessJobStatusRepository;
_notificationService = notificationService;
_objectPool = objectPool; _objectPool = objectPool;
_permission = permission; _permission = permission;
@ -444,29 +450,30 @@ namespace BMA.EHR.Leave.Service.Controllers
public async Task<ActionResult<ResponseObject>> CheckTimeAsync(CancellationToken cancellationToken = default) public async Task<ActionResult<ResponseObject>> CheckTimeAsync(CancellationToken cancellationToken = default)
{ {
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
var profileId = ProfileId ?? Guid.Empty;
// Get user's last check-in record and profile in parallel // Get user's last check-in record and profile in parallel
var dataTask = _userTimeStampRepository.GetLastRecord(userId); var dataTask = _userTimeStampRepository.GetLastRecord(userId);
var profileTask = _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); //var profileTask = _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
var defaultRoundTask = _dutyTimeRepository.GetDefaultAsync(); var defaultRoundTask = _dutyTimeRepository.GetDefaultAsync();
await Task.WhenAll(dataTask, profileTask, defaultRoundTask); await Task.WhenAll(dataTask, defaultRoundTask);
var data = await dataTask; var data = await dataTask;
var profile = await profileTask; //var profile = await profileTask;
var getDefaultRound = await defaultRoundTask; var getDefaultRound = await defaultRoundTask;
if (profile == null) // if (profile == null)
{ // {
throw new Exception(GlobalMessages.DataNotFound); // throw new Exception(GlobalMessages.DataNotFound);
} // }
if (getDefaultRound == null) if (getDefaultRound == null)
{ {
return Error("ไม่พบรอบลงเวลา Default", StatusCodes.Status404NotFound); return Error("ไม่พบรอบลงเวลา Default", StatusCodes.Status404NotFound);
} }
var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profileId);
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
@ -530,6 +537,17 @@ namespace BMA.EHR.Leave.Service.Controllers
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
var currentDate = DateTime.Now; var currentDate = DateTime.Now;
// ตรวจสอบและ mark งานเก่าที่ค้างเกิน 30 นาทีเป็น FAILED อัตโนมัติ
var staleJobs = await _checkInJobStatusRepository.GetStalePendingOrProcessingJobsByUserAsync(userId, 30);
if (staleJobs != null && staleJobs.Count > 0)
{
foreach (var staleJob in staleJobs)
{
await _checkInJobStatusRepository.UpdateToFailedAsync(staleJob.TaskId,
$"งานค้างในสถานะ {staleJob.Status} เกิน 30 นาที ระบบทำเครื่องหมายเป็น FAILED อัตโนมัติ");
}
}
// ตรวจสอบว่ามีงานที่กำลัง pending หรือ processing อยู่หรือไม่ // ตรวจสอบว่ามีงานที่กำลัง pending หรือ processing อยู่หรือไม่
var existingJobs = await _checkInJobStatusRepository.GetPendingOrProcessingJobsAsync(userId); var existingJobs = await _checkInJobStatusRepository.GetPendingOrProcessingJobsAsync(userId);
if (existingJobs != null && existingJobs.Count > 0) if (existingJobs != null && existingJobs.Count > 0)
@ -597,7 +615,16 @@ namespace BMA.EHR.Leave.Service.Controllers
properties.Persistent = true; properties.Persistent = true;
properties.MessageId = taskId; properties.MessageId = taskId;
// บันทึกสถานะงานก่อนส่งไป RabbitMQ // ส่งไป RabbitMQ
channel.BasicPublish(exchange: "",
routingKey: queue,
basicProperties: properties,
body: body);
// Clear Byte data Before Save to DB
checkData.CheckInFileBytes = new byte[0];
// บันทึกสถานะงานหลังส่งไป RabbitMQ
jobStatus = new CheckInJobStatus jobStatus = new CheckInJobStatus
{ {
TaskId = Guid.Parse(taskId), TaskId = Guid.Parse(taskId),
@ -606,21 +633,10 @@ namespace BMA.EHR.Leave.Service.Controllers
Status = "PENDING", Status = "PENDING",
CheckType = data.CheckInId == null ? "CHECK_IN" : "CHECK_OUT", CheckType = data.CheckInId == null ? "CHECK_IN" : "CHECK_OUT",
CheckInId = data.CheckInId, CheckInId = data.CheckInId,
AdditionalData = JsonConvert.SerializeObject(new AdditionalData = JsonConvert.SerializeObject(checkData)
{
IsLocation = data.IsLocation,
LocationName = data.LocationName,
POI = data.POI
})
}; };
await _checkInJobStatusRepository.AddAsync(jobStatus); await _checkInJobStatusRepository.AddAsync(jobStatus);
// ส่งไป RabbitMQ
channel.BasicPublish(exchange: "",
routingKey: queue,
basicProperties: properties,
body: body);
return Success(new { date = currentDate, taskId = taskId, keycloakId = userId }); return Success(new { date = currentDate, taskId = taskId, keycloakId = userId });
} }
catch (Exception ex) catch (Exception ex)
@ -718,6 +734,117 @@ namespace BMA.EHR.Leave.Service.Controllers
return Success(new { count = result.Count, jobs = result }); return Success(new { count = result.Count, jobs = result });
} }
/// <summary>
/// ประมวลผลงาน CheckIn ที่ค้างอยู่ในสถานะ PENDING/PROCESSING เกินเวลาที่กำหนดใหม่อีกครั้ง
/// </summary>
/// <returns></returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("reprocess-stale-checkin-jobs")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> ReprocessStaleCheckInJobsAsync([FromQuery] int timeoutMinutes = 30)
{
try
{
var staleJobs = await _checkInJobStatusRepository.GetStalePendingOrProcessingJobsAsync(timeoutMinutes);
if (staleJobs == null || staleJobs.Count == 0)
{
return Success(new { message = "ไม่พบงานที่ค้างอยู่", count = 0 });
}
var results = new List<object>();
foreach (var job in staleJobs)
{
try
{
// อ่านข้อมูลเดิมจาก AdditionalData
if (string.IsNullOrEmpty(job.AdditionalData))
{
await _checkInJobStatusRepository.UpdateToFailedAsync(job.TaskId,
"ไม่พบข้อมูลสำหรับประมวลผลซ้ำ (AdditionalData is null)");
results.Add(new
{
taskId = job.TaskId,
keycloakUserId = job.KeycloakUserId,
checkType = job.CheckType,
createdDate = job.CreatedDate,
previousStatus = job.Status,
newStatus = "FAILED",
errorMessage = "ไม่พบข้อมูลสำหรับประมวลผลซ้ำ"
});
continue;
}
var checkData = JsonConvert.DeserializeObject<CheckTimeDtoRB>(job.AdditionalData);
checkData.UserId = job.KeycloakUserId;
checkData.CurrentDate = job.CreatedDate;
if (checkData == null)
{
await _checkInJobStatusRepository.UpdateToFailedAsync(job.TaskId,
"ไม่สามารถอ่านข้อมูลสำหรับประมวลผลซ้ำได้");
results.Add(new
{
taskId = job.TaskId,
keycloakUserId = job.KeycloakUserId,
checkType = job.CheckType,
createdDate = job.CreatedDate,
previousStatus = job.Status,
newStatus = "FAILED",
errorMessage = "ไม่สามารถอ่านข้อมูลสำหรับประมวลผลซ้ำได้"
});
continue;
}
// ตั้ง TaskId ให้ตรงกับ job เดิม
checkData.TaskId = job.TaskId;
// เรียก ProcessCheckInAsync ด้วยข้อมูลเดิม
var processResult = await ProcessCheckInAsync(checkData);
results.Add(new
{
taskId = job.TaskId,
keycloakUserId = job.KeycloakUserId,
checkType = job.CheckType,
createdDate = job.CreatedDate,
previousStatus = job.Status,
result = processResult
});
}
catch (Exception ex)
{
await _checkInJobStatusRepository.UpdateToFailedAsync(job.TaskId,
$"เกิดข้อผิดพลาดในการประมวลผลซ้ำ: {ex.Message}");
results.Add(new
{
taskId = job.TaskId,
keycloakUserId = job.KeycloakUserId,
checkType = job.CheckType,
createdDate = job.CreatedDate,
previousStatus = job.Status,
newStatus = "FAILED",
errorMessage = ex.Message
});
}
}
return Success(new
{
message = $"ประมวลผลซ้ำงาน {staleJobs.Count} รายการเสร็จสิ้น",
count = staleJobs.Count,
jobs = results
});
}
catch (Exception ex)
{
return Error(ex);
}
}
[HttpGet("check-status")] [HttpGet("check-status")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status401Unauthorized)]
@ -936,11 +1063,13 @@ namespace BMA.EHR.Leave.Service.Controllers
await _checkInJobStatusRepository.UpdateToProcessingAsync(taskId); await _checkInJobStatusRepository.UpdateToProcessingAsync(taskId);
} }
var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, data.Token); var profile = await _userProfileRepository.GetProfileByCheckInAsync(userId, data.Token);
if (profile == null) if (profile == null)
{ {
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลผู้ใช้"); await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "เกิดข้อผิดพลาดจากการเรียก API [GetProfileByCheckInAsync] : ไม่พบข้อมูลผู้ใช้");
await _notificationService.SendNotificationAsync(data.Token, true,
$"ลงเวลาไม่สำเร็จ \r\nเนื่องจาก ไม่พบข้อมูลผู้ใช้ \r\nกรุณาลองใหม่อีกครั้ง");
//var staffList = await _userProfileRepository.GetOCStaffAsync(profile) //var staffList = await _userProfileRepository.GetOCStaffAsync(profile)
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
} }
@ -951,11 +1080,14 @@ namespace BMA.EHR.Leave.Service.Controllers
{ {
//throw new Exception(GlobalMessages.NoFileToUpload); //throw new Exception(GlobalMessages.NoFileToUpload);
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, GlobalMessages.NoFileToUpload); await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, GlobalMessages.NoFileToUpload);
await _notificationService.SendNotificationAsync(data.Token, true,
$"ลงเวลาไม่สำเร็จ \r\nเนื่องจาก {GlobalMessages.NoFileToUpload}\r\nกรุณาลองใหม่อีกครั้ง");
// send notification to user // send notification to user
var noti1 = new Notification var noti1 = new Notification
{ {
Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจาก {GlobalMessages.NoFileToUpload}", Body =
$"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจาก {GlobalMessages.NoFileToUpload}",
ReceiverUserId = profile.Id, ReceiverUserId = profile.Id,
Type = "", Type = "",
Payload = "", Payload = "",
@ -972,38 +1104,47 @@ namespace BMA.EHR.Leave.Service.Controllers
var check_status = data.CheckInId == null ? "check-in-picture" : "check-out-picture"; var check_status = data.CheckInId == null ? "check-in-picture" : "check-out-picture";
var check_out_status = "check-out-picture"; var check_out_status = "check-out-picture";
var fileName = $"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{check_status}/{data.CheckInFileName}"; // ถ้าไม่มี CheckInFileBytes ให้ใช้ภาพ blank.jpeg แทน
var fileNameCheckOut = $"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{check_out_status}/{data.CheckInFileName}"; var fileBytes = data.CheckInFileBytes;
using (var ms = new MemoryStream(data.CheckInFileBytes ?? new byte[0])) if (fileBytes == null || fileBytes.Length == 0)
{
var blankPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot", "blank.jpeg");
fileBytes = await System.IO.File.ReadAllBytesAsync(blankPath);
data.CheckInFileName = "blank.jpeg";
}
var fileName =
$"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{check_status}/{data.CheckInFileName}";
var fileNameCheckOut =
$"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{check_out_status}/{data.CheckInFileName}";
using (var ms = new MemoryStream(fileBytes))
{ {
try try
{ {
await _minIOService.UploadFileAsync(fileName, ms); await _minIOService.UploadFileAsync(fileName, ms);
// if (lastCheckIn != null && lastCheckIn.CheckOut == null)
// {
// // ยังไม่เคย check-out มาก่อน หรือ check-out เป็น null ให้ใช้ชื่อไฟล์แบบ check-out
// await _minIOService.UploadFileAsync(fileNameCheckOut, ms);
// }
} }
catch (Exception ex) catch (Exception ex)
{ {
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, $"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}"); await _checkInJobStatusRepository.UpdateToFailedAsync(taskId,
$"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}");
await _notificationService.SendNotificationAsync(data.Token, true,
$"ลงเวลาไม่สำเร็จ \r\nเนื่องจากไม่สามารถอัปโหลดรูปภาพได้ {ex.Message}\r\nกรุณาลองใหม่อีกครั้ง");
// send notification to user // send notification to user
var noti1 = new Notification var noti2 = new Notification
{ {
Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่สามารถอัปโหลดรูปภาพได้ {ex.Message}", Body =
$"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่สามารถอัปโหลดรูปภาพได้ {ex.Message}",
ReceiverUserId = profile.Id, ReceiverUserId = profile.Id,
Type = "", Type = "",
Payload = "", Payload = "",
}; };
_appDbContext.Set<Notification>().Add(noti1); _appDbContext.Set<Notification>().Add(noti2);
await _appDbContext.SaveChangesAsync(); await _appDbContext.SaveChangesAsync();
return Error($"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}",
return Error($"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}", StatusCodes.Status500InternalServerError); StatusCodes.Status500InternalServerError);
} }
} }
if (lastCheckIn != null && lastCheckIn.CheckOut == null) if (lastCheckIn != null && lastCheckIn.CheckOut == null)
@ -1013,39 +1154,38 @@ namespace BMA.EHR.Leave.Service.Controllers
try try
{ {
await _minIOService.UploadFileAsync(fileNameCheckOut, ms2); await _minIOService.UploadFileAsync(fileNameCheckOut, ms2);
// if (lastCheckIn != null && lastCheckIn.CheckOut == null)
// {
// // ยังไม่เคย check-out มาก่อน หรือ check-out เป็น null ให้ใช้ชื่อไฟล์แบบ check-out
// await _minIOService.UploadFileAsync(fileNameCheckOut, ms);
// }
} }
catch (Exception ex) catch (Exception ex)
{ {
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, $"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}"); await _checkInJobStatusRepository.UpdateToFailedAsync(taskId,
$"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}");
await _notificationService.SendNotificationAsync(data.Token, true,
$"ลงเวลาไม่สำเร็จ \r\nเนื่องจากไม่สามารถอัปโหลดรูปภาพได้ {ex.Message}\r\nกรุณาลองใหม่อีกครั้ง");
// send notification to user // send notification to user
var noti1 = new Notification var noti3 = new Notification
{ {
Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่สามารถอัปโหลดรูปภาพได้ {ex.Message}", Body =
$"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่สามารถอัปโหลดรูปภาพได้ {ex.Message}",
ReceiverUserId = profile.Id, ReceiverUserId = profile.Id,
Type = "", Type = "",
Payload = "", Payload = "",
}; };
_appDbContext.Set<Notification>().Add(noti1); _appDbContext.Set<Notification>().Add(noti3);
await _appDbContext.SaveChangesAsync(); await _appDbContext.SaveChangesAsync();
return Error($"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}",
return Error($"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}", StatusCodes.Status500InternalServerError); StatusCodes.Status500InternalServerError);
} }
} }
} }
var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
if (defaultRound == null) if (defaultRound == null)
{ {
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบรอบการลงเวลาทำงาน Default"); await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบรอบการลงเวลาทำงาน Default");
await _notificationService.SendNotificationAsync(data.Token, true,
$"ลงเวลาไม่สำเร็จ \r\nเนื่องจากไม่พบรอบการลงเวลาทำงาน Default\r\nกรุณาลองใหม่อีกครั้ง");
// send notification to user // send notification to user
var noti1 = new Notification var noti1 = new Notification
{ {
@ -1085,6 +1225,12 @@ namespace BMA.EHR.Leave.Service.Controllers
endTime1 = "14:30"; endTime1 = "14:30";
endTimeMorning1 = "12:00"; endTimeMorning1 = "12:00";
} }
else if (!data.IsLocation && data.LocationName == "ปฏิบัติงานในจุดบริการด่วนมหานคร")
{
startTime1 = "13:00";
endTime1 = "18:30";
endTimeMorning1 = "12:00";
}
else else
{ {
endTime1 = duty.EndTimeAfternoon; endTime1 = duty.EndTimeAfternoon;
@ -1193,11 +1339,12 @@ namespace BMA.EHR.Leave.Service.Controllers
if (currentCheckIn != null) if (currentCheckIn != null)
{ {
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่สามารถลงเวลาได้ เนื่องจากมีการลงเวลาในวันนี้แล้ว"); await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่สามารถลงเวลาได้ เนื่องจากมีการลงเวลาในวันนี้แล้ว");
await _notificationService.SendNotificationAsync(data.Token, true,$"ลงเวลาไม่สำเร็จ \r\nเนื่องจากมีการลงเวลาในวันนี้แล้ว\r\nกรุณาลองใหม่อีกครั้ง");
// send notification to user // send notification to user
var noti1 = new Notification var noti1 = new Notification
{ {
Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากมีการลงเวลาในวันนี้แล้ว", Body = $"ลงเวลาไม่สำเร็จ \r\nเนื่องจากมีการลงเวลาในวันนี้แล้ว",
ReceiverUserId = profile.Id, ReceiverUserId = profile.Id,
Type = "", Type = "",
Payload = "", Payload = "",
@ -1255,6 +1402,12 @@ namespace BMA.EHR.Leave.Service.Controllers
startTime = "10:30"; startTime = "10:30";
endTime = "12:00"; endTime = "12:00";
} }
else if (!data.IsLocation && data.LocationName == "ปฏิบัติงานในจุดบริการด่วนมหานคร")
{
//startTime = "09:30";
startTime = "10:30";
endTime = "12:00";
}
else else
{ {
startTime = duty.StartTimeMorning; startTime = duty.StartTimeMorning;
@ -1344,6 +1497,7 @@ namespace BMA.EHR.Leave.Service.Controllers
if (checkout == null) if (checkout == null)
{ {
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลการลงเวลาทำงาน"); await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลการลงเวลาทำงาน");
await _notificationService.SendNotificationAsync(data.Token,true, $"ลงเวลาไม่สำเร็จ \r\nเนื่องจากไม่พบข้อมูลการลงเวลาทำงาน\r\nกรุณาลองใหม่อีกครั้ง");
// send notification to user // send notification to user
var noti1 = new Notification var noti1 = new Notification
@ -1364,6 +1518,7 @@ namespace BMA.EHR.Leave.Service.Controllers
if (currentCheckInProcess == null) if (currentCheckInProcess == null)
{ {
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลการประมวลผลเวลาทำงาน (CheckIn)"); await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลการประมวลผลเวลาทำงาน (CheckIn)");
await _notificationService.SendNotificationAsync(data.Token, true, $"ลงเวลาไม่สำเร็จ \r\nเนื่องจากไม่พบข้อมูลการประมวลผลเวลาทำงาน (CheckIn)\r\nกรุณาลองใหม่อีกครั้ง");
// send notification to user // send notification to user
var noti1 = new Notification var noti1 = new Notification
@ -1402,6 +1557,12 @@ namespace BMA.EHR.Leave.Service.Controllers
endTime = "14:30"; endTime = "14:30";
endTimeMorning = "12:00"; endTimeMorning = "12:00";
} }
else if (!data.IsLocation && data.LocationName == "ปฏิบัติงานในจุดบริการด่วนมหานคร")
{
startTime = "13:00";
endTime = "18:30";
endTimeMorning = "12:00";
}
else else
{ {
endTime = duty.EndTimeAfternoon; endTime = duty.EndTimeAfternoon;
@ -1535,6 +1696,7 @@ namespace BMA.EHR.Leave.Service.Controllers
else else
{ {
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลการประมวลผลเวลาทำงาน"); await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลการประมวลผลเวลาทำงาน");
await _notificationService.SendNotificationAsync(data.Token,true, $"ลงเวลาไม่สำเร็จ \r\nเนื่องจากไม่พบข้อมูลการประมวลผลเวลาทำงาน\r\nกรุณาลองใหม่อีกครั้ง");
// send notification to user // send notification to user
var noti1 = new Notification var noti1 = new Notification
{ {
@ -1558,6 +1720,8 @@ namespace BMA.EHR.Leave.Service.Controllers
ProcessedDate = currentDate ProcessedDate = currentDate
}); });
await _checkInJobStatusRepository.UpdateToCompletedAsync(taskId, additionalData); await _checkInJobStatusRepository.UpdateToCompletedAsync(taskId, additionalData);
await _notificationService.SendNotificationAsync(data.Token, false,
$"ลงเวลาสำเร็จ");
} }
var checkInType = data.CheckInId == null ? "check-in" : "check-out"; var checkInType = data.CheckInId == null ? "check-in" : "check-out";
@ -1569,6 +1733,7 @@ namespace BMA.EHR.Leave.Service.Controllers
if (taskId != Guid.Empty) if (taskId != Guid.Empty)
{ {
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, ex.Message); await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, ex.Message);
await _notificationService.SendNotificationAsync(data.Token, true, ex.Message);
} }
return Error(ex); return Error(ex);
} }
@ -1589,7 +1754,7 @@ namespace BMA.EHR.Leave.Service.Controllers
public async Task<ActionResult<ResponseObject>> CheckInOldAsync([FromForm] CheckTimeDto data) public async Task<ActionResult<ResponseObject>> CheckInOldAsync([FromForm] CheckTimeDto data)
{ {
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); 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) if (profile == null)
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
@ -1754,7 +1919,7 @@ namespace BMA.EHR.Leave.Service.Controllers
{ {
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); 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) if (profile == null)
{ {
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
@ -1884,7 +2049,7 @@ namespace BMA.EHR.Leave.Service.Controllers
? profileAdmin?.RootDnaId ? profileAdmin?.RootDnaId
: ""; : "";
} }
else if (role == "BROHTER") else if (role == "BROTHER")
{ {
nodeId = profileAdmin?.Node == 4 nodeId = profileAdmin?.Node == 4
? profileAdmin?.Child3DnaId ? profileAdmin?.Child3DnaId
@ -1914,7 +2079,7 @@ namespace BMA.EHR.Leave.Service.Controllers
ProfileType = d.ProfileType ?? "", ProfileType = d.ProfileType ?? "",
CheckInDate = d.CheckIn.Date, CheckInDate = d.CheckIn.Date,
CheckInTime = d.CheckIn.ToString("HH:mm:ss"), CheckInTime = d.CheckIn.ToString("HH:mm"),
CheckInLocation = d.CheckInPOI, CheckInLocation = d.CheckInPOI,
CheckInLat = d.CheckInLat, CheckInLat = d.CheckInLat,
CheckInLon = d.CheckInLon, CheckInLon = d.CheckInLon,
@ -1925,7 +2090,7 @@ namespace BMA.EHR.Leave.Service.Controllers
CheckInLocationName = d.CheckInLocationName ?? "", CheckInLocationName = d.CheckInLocationName ?? "",
CheckOutDate = d.CheckOut?.Date, CheckOutDate = d.CheckOut?.Date,
CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm:ss"), CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm"),
CheckOutLocation = d.CheckOut == null ? "" : d.CheckOutPOI, CheckOutLocation = d.CheckOut == null ? "" : d.CheckOutPOI,
CheckOutLat = d.CheckOut == null ? null : d.CheckOutLat, CheckOutLat = d.CheckOut == null ? null : d.CheckOutLat,
CheckOutLon = d.CheckOut == null ? null : d.CheckOutLon, CheckOutLon = d.CheckOut == null ? null : d.CheckOutLon,
@ -2043,7 +2208,7 @@ namespace BMA.EHR.Leave.Service.Controllers
} }
else else
{ {
var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(d.KeycloakUserId, AccessToken); var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(d.KeycloakUserId, AccessToken);
if (profile == null) if (profile == null)
{ {
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
@ -2990,10 +3155,12 @@ namespace BMA.EHR.Leave.Service.Controllers
[ProducesResponseType(StatusCodes.Status500InternalServerError)] [ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> CheckoutCheckAsync(string isSeminar = "N") public async Task<ActionResult<ResponseObject>> CheckoutCheckAsync(string isSeminar = "N")
{ {
// "S" = Seminar, "N" = Normal, "O" = One Stop Service
var time = DateTime.Now; var time = DateTime.Now;
var userId = UserId != null ? Guid.Parse(UserId) : Guid.Empty; 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) if (profile == null)
{ {
throw new Exception(GlobalMessages.DataNotFound); throw new Exception(GlobalMessages.DataNotFound);
@ -3021,9 +3188,11 @@ namespace BMA.EHR.Leave.Service.Controllers
//var endTime = DateTimeOffset.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")}T{duty.EndTimeAfternoon}:00.0000000+07:00").ToLocalTime().DateTime; //var endTime = DateTimeOffset.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")}T{duty.EndTimeAfternoon}:00.0000000+07:00").ToLocalTime().DateTime;
 //var endTime = DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")}T{duty.EndTimeAfternoon}:00.0000000+07:00"); //var endTime = DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")}T{duty.EndTimeAfternoon}:00.0000000+07:00");
var endTime = isSeminar.Trim().ToUpper() == "Y" var endTime = isSeminar.Trim().ToUpper() == "S"
? DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} 14:30") ? DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} 14:30")
: isSeminar.Trim().ToUpper() == "O"
? DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} 18:30")
: DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); : DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}");
var endTimeMorning = DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}"); var endTimeMorning = DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}");
@ -3112,8 +3281,17 @@ namespace BMA.EHR.Leave.Service.Controllers
return Error("ไม่สามารถขอลงเวลากรณีพิเศษในวันที่มากกว่าวันที่ปัจจุบันได้", StatusCodes.Status400BadRequest); return Error("ไม่สามารถขอลงเวลากรณีพิเศษในวันที่มากกว่าวันที่ปัจจุบันได้", StatusCodes.Status400BadRequest);
} }
var userId = UserId != null ? Guid.Parse(UserId) : Guid.Empty; var userId = UserId != null ? Guid.Parse(UserId) : Guid.Empty;
var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken);
var checkin = await _userTimeStampRepository.GetTimestampByDateAsync(userId, req.CheckDate.Date);
if (checkin != null && checkin.CheckOut == null)
{
return Error("ระบบพบรายการลงเวลาของวันที่ต้องการแก้ไข แต่ยังไม่มีข้อมูลการลงเวลาออก กรุณาลงเวลาออกให้เรียบร้อยก่อนดำเนินการ");
}
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
if (profile == null) if (profile == null)
{ {
throw new Exception(GlobalMessages.DataNotFound); throw new Exception(GlobalMessages.DataNotFound);
@ -3160,15 +3338,16 @@ namespace BMA.EHR.Leave.Service.Controllers
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)] [ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> GetAdditionalCheckRequestAsync([Required] int year, [Required] int month, [Required] int page = 1, [Required] int pageSize = 10, string keyword = "", string? sortBy = "", bool? descending = false) public async Task<ActionResult<ResponseObject>> GetAdditionalCheckRequestAsync([Required] DateTime startDate, [Required] DateTime endDate, [Required] int page = 1, [Required] int pageSize = 10, string keyword = "", string? sortBy = "", bool? descending = false,string? status = "")
{ {
var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_CHECKIN_SPECIAL"); var jsonData = await _permission.GetPermissionWithActingAPIAsync("LIST", "SYS_CHECKIN_SPECIAL");
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission); //var jsonData = JsonConvert.DeserializeObject<GetPermissionWithActingResultDto>(getPermission);
if (jsonData["status"]?.ToString() != "200") 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 nodeId = string.Empty;
var profileAdmin = new GetUserOCAllDto(); var profileAdmin = new GetUserOCAllDto();
profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken); profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken);
@ -3204,8 +3383,84 @@ namespace BMA.EHR.Leave.Service.Controllers
} }
//var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequests(year, month); //var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequests(year, month);
var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequestsByAdminRole(year, month, role, nodeId, profileAdmin?.Node, keyword); var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequestsByAdminRole2(startDate, endDate, role, nodeId, profileAdmin?.Node, keyword,status);
// ถ้ามีการรักษาการ
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.GetAdditionalCheckRequestsByAdminRole2(startDate, endDate, actRole, actNodeId, profileAdmin?.Node, keyword,status);
if (rawDataAct != null)
{
if (rawData != null)
rawData = rawData.Union(rawDataAct).DistinctBy(x => x.Id).ToList();
else
rawData = rawDataAct;
}
}
}
var total = rawData.Count; var total = rawData.Count;
var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync(); var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync();
@ -3276,7 +3531,7 @@ namespace BMA.EHR.Leave.Service.Controllers
foreach (var data in rawDataPaged) foreach (var data in rawDataPaged)
{ {
var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(data.KeycloakUserId, AccessToken); var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(data.KeycloakUserId, AccessToken);
UserDutyTime? effectiveDate = null; UserDutyTime? effectiveDate = null;
if (profile != null) if (profile != null)
{ {
@ -3455,7 +3710,7 @@ namespace BMA.EHR.Leave.Service.Controllers
// change user timestamp // change user timestamp
var processTimeStamp = await _processUserTimeStampRepository.GetTimestampByDateAsync(requestData.KeycloakUserId, requestData.CheckDate.Date); 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) if (processTimeStamp == null)
{ {
@ -3609,7 +3864,7 @@ namespace BMA.EHR.Leave.Service.Controllers
requestData.Comment = req.Reason; requestData.Comment = req.Reason;
await _additionalCheckRequestRepository.UpdateAsync(requestData); await _additionalCheckRequestRepository.UpdateAsync(requestData);
var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(requestData.KeycloakUserId, AccessToken); var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(requestData.KeycloakUserId, AccessToken);
var recvId = new List<Guid> { profile.Id }; var recvId = new List<Guid> { profile.Id };
await _notificationRepository.PushNotificationsAsync(recvId.ToArray(), "ลงเวลากรณีพิเศษ", "การขอลงเวลากรณีพิเศษของคุณไม่ได้รับการอนุมัติ", "", "", true, false); await _notificationRepository.PushNotificationsAsync(recvId.ToArray(), "ลงเวลากรณีพิเศษ", "การขอลงเวลากรณีพิเศษของคุณไม่ได้รับการอนุมัติ", "", "", true, false);
@ -3653,7 +3908,7 @@ namespace BMA.EHR.Leave.Service.Controllers
} }
else else
{ {
var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(d.KeycloakUserId, AccessToken); var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(d.KeycloakUserId, AccessToken);
if (profile == null) if (profile == null)
{ {
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
@ -3712,6 +3967,10 @@ namespace BMA.EHR.Leave.Service.Controllers
"ABSENT" : "ABSENT" :
"NORMAL", "NORMAL",
CheckOutDescription = d.CheckOutRemark ?? "", CheckOutDescription = d.CheckOutRemark ?? "",
IsLocationCheckIn = d.IsLocationCheckIn,
IsLocationCheckOut = d.IsLocationCheckOut,
CheckInLocationName = d.CheckInLocationName ?? "",
CheckOutLocationName = d.CheckOutLocationName ?? ""
}; };
return Success(result); return Success(result);
@ -3747,7 +4006,7 @@ namespace BMA.EHR.Leave.Service.Controllers
foreach (var data in rawData) foreach (var data in rawData)
{ {
var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(data.KeycloakUserId, AccessToken); var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(data.KeycloakUserId, AccessToken);
if (profile == null) if (profile == null)
{ {
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
@ -3776,6 +4035,7 @@ namespace BMA.EHR.Leave.Service.Controllers
{ {
resultCheckInDate = checkInData == null ? null : checkInData.CheckIn; resultCheckInDate = checkInData == null ? null : checkInData.CheckIn;
resultCheckInTime = checkInData == null ? "00:00" : checkInData.CheckIn.ToString("HH:mm"); resultCheckInTime = checkInData == null ? "00:00" : checkInData.CheckIn.ToString("HH:mm");
resultCheckInLocation = checkInData == null ? "" : checkInData!.CheckInPOI;
} }
if (data.CheckOutEdit) if (data.CheckOutEdit)
@ -3793,6 +4053,7 @@ namespace BMA.EHR.Leave.Service.Controllers
resultCheckOutTime = checkInData == null ? "00:00" : resultCheckOutTime = checkInData == null ? "00:00" :
checkInData.CheckOut == null ? "00:00" : checkInData.CheckOut == null ? "00:00" :
checkInData.CheckOut.Value.ToString("HH:mm"); checkInData.CheckOut.Value.ToString("HH:mm");
resultCheckOutLocation = checkInData == null ? "" : checkInData!.CheckOutPOI;
} }
@ -4037,7 +4298,7 @@ namespace BMA.EHR.Leave.Service.Controllers
//var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); //var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
// แก้เป็นมาใช้งาน KeycloakUserId แทน // แก้เป็นมาใช้งาน KeycloakUserId แทน
var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(data.KeycloakUserId, AccessToken); var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(data.KeycloakUserId, AccessToken);
var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
if (defaultRound == null) if (defaultRound == null)
{ {
@ -4054,6 +4315,10 @@ namespace BMA.EHR.Leave.Service.Controllers
{ {
data.CheckIn = DateTime.Parse($"{data.CheckIn.Date.ToString("yyyy-MM-dd")} 10:30"); data.CheckIn = DateTime.Parse($"{data.CheckIn.Date.ToString("yyyy-MM-dd")} 10:30");
} }
else if (data.CheckInLocationName == "ปฏิบัติงานในจุดบริการด่วนมหานคร")
{
data.CheckIn = DateTime.Parse($"{data.CheckIn.Date.ToString("yyyy-MM-dd")} 10:30");
}
else else
{ {
data.CheckIn = DateTime.Parse($"{data.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}"); data.CheckIn = DateTime.Parse($"{data.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}");
@ -4069,6 +4334,10 @@ namespace BMA.EHR.Leave.Service.Controllers
{ {
roundCheckOutTime = DateTime.Parse($"{checkOutTime.Date.ToString("yyyy-MM-dd")} 14:30"); roundCheckOutTime = DateTime.Parse($"{checkOutTime.Date.ToString("yyyy-MM-dd")} 14:30");
} }
else if (data.CheckInLocationName == "ปฏิบัติงานในจุดบริการด่วนมหานคร")
{
roundCheckOutTime = DateTime.Parse($"{checkOutTime.Date.ToString("yyyy-MM-dd")} 18:30");
}
else else
{ {
roundCheckOutTime = DateTime.Parse($"{checkOutTime.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); roundCheckOutTime = DateTime.Parse($"{checkOutTime.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}");
@ -4107,7 +4376,7 @@ namespace BMA.EHR.Leave.Service.Controllers
[ProducesResponseType(StatusCodes.Status500InternalServerError)] [ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> GetLeaveSummaryByProfileAsync(Guid id, [FromBody] GetLeaveSummaryDto req) public async Task<ActionResult<ResponseObject>> 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 thisYear = DateTime.Now.Year;
var startDate = req.StartDate; var startDate = req.StartDate;
@ -4177,7 +4446,7 @@ namespace BMA.EHR.Leave.Service.Controllers
{ {
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); 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) if (profile == null)
{ {
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);

View file

@ -156,8 +156,8 @@ namespace BMA.EHR.Leave.Service.Controllers
await _leaveRequestRepository.GetLastLeaveRequestByTypeForUserAsync2(data.KeycloakUserId, await _leaveRequestRepository.GetLastLeaveRequestByTypeForUserAsync2(data.KeycloakUserId,
data.Type.Id, data.CreatedAt); data.Type.Id, data.CreatedAt);
var startFiscalYear = new DateTime(data.LeaveStartDate.Year - 1, 10, 1); var startFiscalYear = (new DateTime(data.LeaveStartDate.Year - 1, 10, 1)).Date;
var endFiscalYear = data.CreatedAt; var endFiscalYear = (data.DateSendLeave ?? data.CreatedAt);
var thisYear = data.LeaveStartDate.Year; var thisYear = data.LeaveStartDate.Year;
var toDay = data.LeaveStartDate.Date; var toDay = data.LeaveStartDate.Date;
@ -182,7 +182,7 @@ namespace BMA.EHR.Leave.Service.Controllers
data = new data = new
{ {
leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", 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, leaveTypeName = data.Type.Name,
leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "",
dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(),
@ -272,7 +272,7 @@ namespace BMA.EHR.Leave.Service.Controllers
data = new data = new
{ {
leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", 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, leaveTypeName = data.Type.Name,
leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "",
dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(),
@ -366,7 +366,7 @@ namespace BMA.EHR.Leave.Service.Controllers
data = new data = new
{ {
leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", 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, leaveTypeName = data.Type.Name,
leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "",
dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(),
@ -461,7 +461,7 @@ namespace BMA.EHR.Leave.Service.Controllers
data = new data = new
{ {
leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", 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, leaveTypeName = data.Type.Name,
leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "",
dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(),
@ -515,7 +515,7 @@ namespace BMA.EHR.Leave.Service.Controllers
data = new data = new
{ {
leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", 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, leaveTypeName = data.Type.Name,
leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "",
dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(),
@ -602,7 +602,7 @@ namespace BMA.EHR.Leave.Service.Controllers
data = new data = new
{ {
leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", 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, leaveTypeName = data.Type.Name,
leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "",
dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(),
@ -683,7 +683,7 @@ namespace BMA.EHR.Leave.Service.Controllers
data = new data = new
{ {
leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", 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, leaveTypeName = data.Type.Name,
leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "",
dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(),
@ -786,7 +786,7 @@ namespace BMA.EHR.Leave.Service.Controllers
data = new data = new
{ {
leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", 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, leaveTypeName = data.Type.Name,
leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "",
dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(),
@ -905,7 +905,7 @@ namespace BMA.EHR.Leave.Service.Controllers
data = new data = new
{ {
leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", 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, leaveTypeName = data.Type.Name,
leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "",
dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(),
@ -1001,7 +1001,7 @@ namespace BMA.EHR.Leave.Service.Controllers
data = new data = new
{ {
leaveWrote = data.LeaveWrote.ToThaiNumber() ?? "", 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, leaveTypeName = data.Type.Name,
leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "",
dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(),
@ -1227,7 +1227,7 @@ namespace BMA.EHR.Leave.Service.Controllers
data = new data = new
{ {
leaveWrote = data.CancelLeaveWrote!.ToThaiNumber() ?? "", 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, leaveTypeName = data.Type.Name,
fullname = fullName, fullname = fullName,
position = string.IsNullOrEmpty(profile.Position) ? "-" : profile.Position, position = string.IsNullOrEmpty(profile.Position) ? "-" : profile.Position,
@ -1352,7 +1352,7 @@ namespace BMA.EHR.Leave.Service.Controllers
} }
else else
{ {
profile = await _userProfileRepository.GetEmployeeByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); profile = await _userProfileRepository.GetEmployeeByAdminRolev2(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date);
} }
// get leave day // get leave day
var leaveDays = await _leaveRequestRepository.GetSumApproveLeaveByTypeAndRange(req.StartDate, req.EndDate); var leaveDays = await _leaveRequestRepository.GetSumApproveLeaveByTypeAndRange(req.StartDate, req.EndDate);
@ -1533,17 +1533,21 @@ namespace BMA.EHR.Leave.Service.Controllers
coupleCount = coupleCount, coupleCount = coupleCount,
therapyCount = therapyCount, therapyCount = therapyCount,
// ระบบนับจำนวนครั้ง วันลาพักผ่อนด้วย ซึ่งตามระเบียบไม่ให้นับจำนวนครั้งวันลาพักผ่อนครับ จำนวนครั้งนับเฉพาะ ป่วย กับ กิจ
leaveTotal = sickCount + leaveTotal = sickCount +
maternityCount + personalCount
wifeCount +
personalCount + // leaveTotal = sickCount +
restCount + // maternityCount +
ordainCount + // wifeCount +
absentCount + // personalCount +
studyCount + // restCount +
agencyCount + // ordainCount +
coupleCount + // absentCount +
therapyCount // studyCount +
// agencyCount +
// coupleCount +
// therapyCount
}; };
@ -1672,17 +1676,21 @@ namespace BMA.EHR.Leave.Service.Controllers
coupleCount = coupleCount, coupleCount = coupleCount,
therapyCount = therapyCount, therapyCount = therapyCount,
// ระบบนับจำนวนครั้ง วันลาพักผ่อนด้วย ซึ่งตามระเบียบไม่ให้นับจำนวนครั้งวันลาพักผ่อนครับ จำนวนครั้งนับเฉพาะ ป่วย กับ กิจ
leaveTotal = sickCount + leaveTotal = sickCount +
maternityCount + personalCount
wifeCount +
personalCount + // leaveTotal = sickCount +
restCount + // maternityCount +
ordainCount + // wifeCount +
absentCount + // personalCount +
studyCount + // restCount +
agencyCount + // ordainCount +
coupleCount + // absentCount +
therapyCount // studyCount +
// agencyCount +
// coupleCount +
// therapyCount
}; };
@ -2372,7 +2380,7 @@ namespace BMA.EHR.Leave.Service.Controllers
} }
else else
{ {
profile = await _userProfileRepository.GetEmployeeByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); profile = await _userProfileRepository.GetEmployeeByAdminRolev2(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date);
} }
// Child กรองตามที่ fe ส่งมาอีกชั้น // Child กรองตามที่ fe ส่งมาอีกชั้น
if ((role == "ROOT" || role == "OWNER" || role == "CHILD" || role == "PARENT" || role == "BROTHER") /*&& req.node > profileAdmin?.Node*/) if ((role == "ROOT" || role == "OWNER" || role == "CHILD" || role == "PARENT" || role == "BROTHER") /*&& req.node > profileAdmin?.Node*/)
@ -2436,6 +2444,15 @@ namespace BMA.EHR.Leave.Service.Controllers
var workTotal = 0; var workTotal = 0;
var seminarTotal = 0; var seminarTotal = 0;
var wfaTotal = 0; //ปฏิบัติงานนอกสถานที่
var outOfficeTotal = 0; //ขออนุญาติิิออกนอกสถานที่
var oneStopSrvrTotal = 0; //จุดบริการด่วนมหานคร
var otherTotal = 0; //อื่นๆ
var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
if (defaultRound == null) if (defaultRound == null)
{ {
@ -2619,10 +2636,18 @@ namespace BMA.EHR.Leave.Service.Controllers
workTotal += 1; workTotal += 1;
if (!timeStamps.IsLocationCheckIn) if (!timeStamps.IsLocationCheckIn)
{ {
if (timeStamps.CheckInLocationName == "ปฏิบัติงานที่บ้าน") if (timeStamps.CheckInLocationName!.Contains("ปฏิบัติงานที่บ้าน"))
wfhTotal += 1; wfhTotal += 1;
else if (timeStamps.CheckInLocationName == "ไปประชุม / อบรม / สัมมนา") else if (timeStamps.CheckInLocationName == "ไปประชุม / อบรม / สัมมนา")
seminarTotal += 1; seminarTotal += 1;
else if (timeStamps.CheckInLocationName.Contains("ปฏิบัติงานนอกสถานที่"))
wfaTotal += 1;
else if (timeStamps.CheckInLocationName.Contains("ขออนุญาตออกนอกสถานที่"))
outOfficeTotal += 1;
else if (timeStamps.CheckInLocationName.Contains("ปฏิบัติงานในจุดบริการด่วนมหานคร"))
oneStopSrvrTotal += 1;
else if (timeStamps.CheckInLocationName.Contains("อื่นๆ"))
otherTotal += 1;
} }
} }
@ -2773,19 +2798,36 @@ namespace BMA.EHR.Leave.Service.Controllers
worksheet.Cells[lastRow + 2, 8].Value = "อบรม ประชุม สัมมนาฯ"; worksheet.Cells[lastRow + 2, 8].Value = "อบรม ประชุม สัมมนาฯ";
worksheet.Cells[lastRow + 2, 9].Value = seminarTotal; worksheet.Cells[lastRow + 2, 9].Value = seminarTotal;
worksheet.Cells[lastRow + 2, 10].Value = "คน"; worksheet.Cells[lastRow + 2, 10].Value = "คน";
worksheet.Cells[lastRow + 3, 8].Value = "ปฎิบัติงานนอกสถานที่";
worksheet.Cells[lastRow + 3, 9].Value = wfaTotal;
worksheet.Cells[lastRow + 3, 10].Value = "คน";
worksheet.Cells[lastRow + 4, 8].Value = "ขออนุญาตออกนอกสถานที่";
worksheet.Cells[lastRow + 4, 9].Value = outOfficeTotal;
worksheet.Cells[lastRow + 4, 10].Value = "คน";
worksheet.Cells[lastRow + 5, 8].Value = "ปฎิบัติงานในจุดบริการด่วนมหานคร";
worksheet.Cells[lastRow + 5, 9].Value = oneStopSrvrTotal;
worksheet.Cells[lastRow + 5, 10].Value = "คน";
worksheet.Cells[lastRow + 6, 8].Value = "อื่นๆ";
worksheet.Cells[lastRow + 6, 9].Value = otherTotal;
worksheet.Cells[lastRow + 6, 10].Value = "คน";
worksheet.Cells[lastRow + 3, 2].Value = "ลาป่วย/ลากิจ"; worksheet.Cells[lastRow + 3, 2].Value = "ลาป่วย/ลากิจ";
worksheet.Cells[lastRow + 3, 5].Value = sickTotal; worksheet.Cells[lastRow + 3, 5].Value = sickTotal;
worksheet.Cells[lastRow + 3, 6].Value = "คน"; worksheet.Cells[lastRow + 3, 6].Value = "คน";
worksheet.Cells[lastRow + 4, 2].Value = "มาสาย"; worksheet.Cells[lastRow + 4, 2].Value = "มาสาย";
worksheet.Cells[lastRow + 4, 5].Value = lateTotal; worksheet.Cells[lastRow + 4, 5].Value = lateTotal;
worksheet.Cells[lastRow + 4, 6].Value = "คน"; worksheet.Cells[lastRow + 4, 6].Value = "คน";
worksheet.Cells[lastRow + 6, 2].Value = "เรียน"; worksheet.Cells[lastRow + 8, 2].Value = "เรียน";
worksheet.Cells[lastRow + 7, 2].Value = "เพื่อโปรดทราบ"; worksheet.Cells[lastRow + 9, 2].Value = "เพื่อโปรดทราบ";
worksheet.Cells[lastRow + 7, 9].Value = "ทราบ"; worksheet.Cells[lastRow + 9, 9].Value = "ทราบ";
worksheet.Cells[lastRow + 7, 9].Style.Font.Bold = true; worksheet.Cells[lastRow + 9, 9].Style.Font.Bold = true;
worksheet.Cells[lastRow + 7, 9].Style.Font.Size = 22; worksheet.Cells[lastRow + 9, 9].Style.Font.Size = 22;
worksheet.Cells[lastRow + 8, 2].Value = "................................"; worksheet.Cells[lastRow + 10, 2].Value = "................................";
worksheet.Cells[lastRow + 8, 9].Value = "................................"; worksheet.Cells[lastRow + 10, 9].Value = "................................";
worksheet.Cells[worksheet.Dimension.Address].AutoFitColumns(); worksheet.Cells[worksheet.Dimension.Address].AutoFitColumns();
var fileBytes = package.GetAsByteArray(); var fileBytes = package.GetAsByteArray();
return File(fileBytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "TimeStampRecords.xlsx"); return File(fileBytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "TimeStampRecords.xlsx");

View file

@ -175,6 +175,9 @@ namespace BMA.EHR.Leave.Service.Controllers
LastUpdateFullName = FullName ?? "", LastUpdateFullName = FullName ?? "",
LastUpdateUserId = UserId!, LastUpdateUserId = UserId!,
LastUpdatedAt = DateTime.Now, LastUpdatedAt = DateTime.Now,
IsAct = r.isAct,
KeyId = r.keyId
}); });
} }
@ -214,7 +217,7 @@ namespace BMA.EHR.Leave.Service.Controllers
var thisYear = DateTime.Now.Year; var thisYear = DateTime.Now.Year;
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken);
var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
if (profile == null) if (profile == null)
{ {
@ -502,7 +505,7 @@ namespace BMA.EHR.Leave.Service.Controllers
foreach (var leave in leaves) foreach (var leave in leaves)
{ {
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(leave.KeycloakUserId, AccessToken); // 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) if (profile != null)
{ {
leave.Prefix = profile.Prefix; leave.Prefix = profile.Prefix;
@ -563,7 +566,7 @@ namespace BMA.EHR.Leave.Service.Controllers
// } // }
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken);
var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
if (profile == null) if (profile == null)
{ {
@ -855,10 +858,10 @@ namespace BMA.EHR.Leave.Service.Controllers
{ {
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); 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 toDay = DateTime.Now.Date;
var startFiscalDate = new DateTime(DateTime.Now.Year, 10, 1); var startFiscalDate = new DateTime(DateTime.Now.Year - 1, 10, 1);
var endFiscalDate = new DateTime(DateTime.Now.Year + 1, 9, 30); var endFiscalDate = new DateTime(DateTime.Now.Year, 9, 30);
if (toDay >= startFiscalDate && toDay <= endFiscalDate) if (toDay >= startFiscalDate && toDay <= endFiscalDate)
thisYear = thisYear + 1; thisYear = thisYear + 1;
@ -925,6 +928,9 @@ namespace BMA.EHR.Leave.Service.Controllers
var leaveLast = await _leaveRequestRepository.GetLeaveLastByTypeForUserAsync(userId, req.Type); 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 var result = new GetUserLeaveProfileResultDto
{ {
DateSendLeave = DateTime.Now.Date, DateSendLeave = DateTime.Now.Date,
@ -940,8 +946,8 @@ namespace BMA.EHR.Leave.Service.Controllers
OrganizationName = orgName, //profile.Oc ?? "", OrganizationName = orgName, //profile.Oc ?? "",
LeaveLimit = leaveLimit, // จำนวนวันลาทั้งหมดในปีนั้นๆที่ลาได้ โดยรวมยอดที่เหลือจากปีก่อนมา (เอาค่ามาจากตาราง Beginning เลย) LeaveLimit = leaveLimit, // จำนวนวันลาทั้งหมดในปีนั้นๆที่ลาได้ โดยรวมยอดที่เหลือจากปีก่อนมา (เอาค่ามาจากตาราง Beginning เลย)
LeaveTotal = sumLeave, // จำนวนวันลาที่ลาไปแล้วในปีนั้นๆ โดยเมื่อมีการอนุมัติลา จะมาบวกค่านี้ไปเรื่อยๆ (เอาค่ามาจากตาราง Beginning เลย) LeaveTotal = sumLeave ?? 0, // จำนวนวันลาที่ลาไปแล้วในปีนั้นๆ โดยเมื่อมีการอนุมัติลา จะมาบวกค่านี้ไปเรื่อยๆ (เอาค่ามาจากตาราง Beginning เลย)
LeaveRemain = leaveLimit - sumLeave, LeaveRemain = leaveLimit - (sumLeave ?? 0),
RestDayTotalOld = restOldDay, // เอา leaveLimit มาลบ 10 (LV-005) RestDayTotalOld = restOldDay, // เอา leaveLimit มาลบ 10 (LV-005)
RestDayTotalCurrent = restCurrentDay,// 10 วันเสมอ (LV-005) RestDayTotalCurrent = restCurrentDay,// 10 วันเสมอ (LV-005)
BirthDate = profile.BirthDate.Date, BirthDate = profile.BirthDate.Date,
@ -960,7 +966,10 @@ namespace BMA.EHR.Leave.Service.Controllers
CurrentDistrict = profile.CurrentDistrict ?? "", CurrentDistrict = profile.CurrentDistrict ?? "",
CurrentProvince = profile.CurrentProvince ?? "", CurrentProvince = profile.CurrentProvince ?? "",
CurrentZipCode = profile.CurrentZipCode ?? "", CurrentZipCode = profile.CurrentZipCode ?? "",
GovAge = govAge GovAge = govAge,
LeaveDraftSummary = leaveDraftSummary,
LeaveWaitingSummary = leaveWaitingSummary
}; };
return Success(result); return Success(result);
@ -1026,7 +1035,7 @@ namespace BMA.EHR.Leave.Service.Controllers
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); // 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 govAge = (profile?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date);
var startDate = profile?.DateStart?.Date ?? DateTime.Now.Date; var startDate = profile?.DateStart?.Date ?? DateTime.Now.Date;
// var date1Raw = profile?.DateStart?.Date ?? DateTime.Now.Date; // var date1Raw = profile?.DateStart?.Date ?? DateTime.Now.Date;
@ -1324,7 +1333,7 @@ namespace BMA.EHR.Leave.Service.Controllers
Id = d.Id, Id = d.Id,
LeaveTypeId = d.Type.Id, LeaveTypeId = d.Type.Id,
LeaveTypeName = d.Type.Name, LeaveTypeName = d.Type.Name,
DateSendLeave = d.CreatedAt.Date, DateSendLeave = d.DateSendLeave != null ? d.DateSendLeave.Value.Date : d.CreatedAt.Date,
Status = d.LeaveStatus, Status = d.LeaveStatus,
FullName = $"{d.Prefix}{d.FirstName} {d.LastName}", FullName = $"{d.Prefix}{d.FirstName} {d.LastName}",
LeaveEndDate = d.LeaveEndDate, LeaveEndDate = d.LeaveEndDate,
@ -1352,14 +1361,14 @@ namespace BMA.EHR.Leave.Service.Controllers
public async Task<ActionResult<ResponseObject>> GetLeaveRequestCalendarAdminAsync( public async Task<ActionResult<ResponseObject>> GetLeaveRequestCalendarAdminAsync(
[FromBody] GetLeaveRequestCalendarDto req) [FromBody] GetLeaveRequestCalendarDto req)
{ {
var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_LEAVE_LIST"); var jsonData = await _permission.GetPermissionWithActingAPIAsync("LIST", "SYS_LEAVE_LIST");
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission); //var jsonData = JsonConvert.DeserializeObject<GetPermissionWithActingResultDto>(getPermission);
if (jsonData["status"]?.ToString() != "200") 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 nodeId = string.Empty;
var profileAdmin = new GetUserOCAllDto(); var profileAdmin = new GetUserOCAllDto();
profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken); profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken);
@ -1395,6 +1404,87 @@ namespace BMA.EHR.Leave.Service.Controllers
} }
var data = await _leaveRequestRepository.GetLeaveRequestByYearForAdminAsync(req.Year, role, nodeId, profileAdmin.Node); 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 var resultData = (from d in data
//join p in profileList on d.KeycloakUserId equals p.Keycloak //join p in profileList on d.KeycloakUserId equals p.Keycloak
select new GetLeaveRequestCalendarResultDto select new GetLeaveRequestCalendarResultDto
@ -1402,7 +1492,7 @@ namespace BMA.EHR.Leave.Service.Controllers
Id = d.Id, Id = d.Id,
LeaveTypeId = d.Type.Id, LeaveTypeId = d.Type.Id,
LeaveTypeName = d.Type.Name, LeaveTypeName = d.Type.Name,
DateSendLeave = d.CreatedAt.Date, DateSendLeave = d.DateSendLeave != null ? d.DateSendLeave.Value.Date : d.CreatedAt.Date,
Status = d.LeaveStatus, Status = d.LeaveStatus,
FullName = $"{d.Prefix}{d.FirstName} {d.LastName}", FullName = $"{d.Prefix}{d.FirstName} {d.LastName}",
LeaveEndDate = d.LeaveEndDate, LeaveEndDate = d.LeaveEndDate,
@ -1455,7 +1545,7 @@ namespace BMA.EHR.Leave.Service.Controllers
LeaveTypeName = item.Type.Name, LeaveTypeName = item.Type.Name,
LeaveSubTypeName = item.LeaveSubTypeName ?? "", LeaveSubTypeName = item.LeaveSubTypeName ?? "",
FullName = $"{item.Prefix}{item.FirstName} {item.LastName}", FullName = $"{item.Prefix}{item.FirstName} {item.LastName}",
DateSendLeave = item.CreatedAt, DateSendLeave = item.DateSendLeave ?? item.CreatedAt,
IsDelete = item.LeaveStatus == "DELETE", IsDelete = item.LeaveStatus == "DELETE",
Status = item.LeaveStatus, Status = item.LeaveStatus,
LeaveStartDate = item.LeaveStartDate, LeaveStartDate = item.LeaveStartDate,
@ -1577,7 +1667,7 @@ namespace BMA.EHR.Leave.Service.Controllers
} }
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); // 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) if (profile == null)
{ {
@ -1616,7 +1706,11 @@ namespace BMA.EHR.Leave.Service.Controllers
var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUserAsync(thisYear, rawData.Type.Id, rawData.KeycloakUserId); var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUserAsync(thisYear, rawData.Type.Id, rawData.KeycloakUserId);
var restDayOld = govAge < 180 ? 0 : leaveData == null ? 0 : (leaveData.LeaveDays + leaveData.BeginningLeaveDays - 10);
var restDayOld = 0.0;
//restDayOld = govAge < 180 ? 0 : leaveData == null ? 0 : (leaveData.LeaveDays + leaveData.BeginningLeaveDays - 10);
restDayOld = govAge < 180 ? 0 : leaveData == null ? 0 : (leaveData.LeaveDays - 10);
if (restDayOld < 0) restDayOld = 0; if (restDayOld < 0) restDayOld = 0;
var restDayCurrent = govAge < 180 ? 0 : 10; var restDayCurrent = govAge < 180 ? 0 : 10;
@ -1630,7 +1724,7 @@ namespace BMA.EHR.Leave.Service.Controllers
LeaveSubTypeName = rawData.LeaveSubTypeName, LeaveSubTypeName = rawData.LeaveSubTypeName,
LeaveTypeId = rawData.Type.Id, LeaveTypeId = rawData.Type.Id,
FullName = $"{rawData.Prefix}{rawData.FirstName} {rawData.LastName}", FullName = $"{rawData.Prefix}{rawData.FirstName} {rawData.LastName}",
DateSendLeave = rawData.CreatedAt, DateSendLeave = rawData.DateSendLeave ?? rawData.CreatedAt,
Status = rawData.LeaveStatus, Status = rawData.LeaveStatus,
LeaveStartDate = rawData.LeaveStartDate, LeaveStartDate = rawData.LeaveStartDate,
LeaveEndDate = rawData.LeaveEndDate, LeaveEndDate = rawData.LeaveEndDate,
@ -1750,14 +1844,14 @@ namespace BMA.EHR.Leave.Service.Controllers
public async Task<ActionResult<ResponseObject>> GetLeaveRequestForAdminAsync( public async Task<ActionResult<ResponseObject>> GetLeaveRequestForAdminAsync(
[FromBody] GetLeaveRequestForAdminDto req) [FromBody] GetLeaveRequestForAdminDto req)
{ {
var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_LEAVE_LIST"); var jsonData = await _permission.GetPermissionWithActingAPIAsync("LIST", "SYS_LEAVE_LIST");
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission); //var jsonData = JsonConvert.DeserializeObject<GetPermissionWithActingResultDto>(getPermission);
if (jsonData["status"]?.ToString() != "200") 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 nodeId = string.Empty;
var profileAdmin = new GetUserOCAllDto(); var profileAdmin = new GetUserOCAllDto();
profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken); profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken);
@ -1794,6 +1888,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); 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<GetLeaveRequestForAdminResultDto>(); var result = new List<GetLeaveRequestForAdminResultDto>();
foreach (var item in rawData) foreach (var item in rawData)
@ -1826,7 +1999,7 @@ namespace BMA.EHR.Leave.Service.Controllers
LeaveSubTypeName = item.LeaveSubTypeName, LeaveSubTypeName = item.LeaveSubTypeName,
FullName = $"{item.Prefix}{item.FirstName} {item.LastName}", FullName = $"{item.Prefix}{item.FirstName} {item.LastName}",
ProfileType = item.ProfileType ?? "-", ProfileType = item.ProfileType ?? "-",
DateSendLeave = item.CreatedAt, DateSendLeave = item.DateSendLeave ?? item.CreatedAt,
Status = item.LeaveStatus, Status = item.LeaveStatus,
CitizenId = item.CitizenId ?? "", CitizenId = item.CitizenId ?? "",
LeaveStartDate = item.LeaveStartDate, LeaveStartDate = item.LeaveStartDate,
@ -1876,6 +2049,12 @@ namespace BMA.EHR.Leave.Service.Controllers
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); 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 // change status to delete
// แก้จาก DELETE เป็น DELETING ไว้ก่อน รอ approve ค่อยเปลี่ยนเป็น DELETE // แก้จาก DELETE เป็น DELETING ไว้ก่อน รอ approve ค่อยเปลี่ยนเป็น DELETE
// data.LeaveStatus = "DELETE"; // data.LeaveStatus = "DELETE";
@ -1912,7 +2091,7 @@ namespace BMA.EHR.Leave.Service.Controllers
// TODO: Send notification to all users who need to approve the cancel leave request // TODO: Send notification to all users who need to approve the cancel leave request
var approvers = data.Approvers var approvers = data.Approvers
.Where(x => x.ApproveStatus!.ToUpper() == "PENDING") //.Where(x => x.ApproveStatus!.ToUpper() == "PENDING")
.OrderBy(x => x.Seq) .OrderBy(x => x.Seq)
.ToList(); .ToList();
@ -1924,12 +2103,73 @@ namespace BMA.EHR.Leave.Service.Controllers
Body = $"คำร้องขอยกเลิกการลาของคุณ {data.FirstName} {data.LastName} รอรับการอนุมัติจากคุณ", Body = $"คำร้องขอยกเลิกการลาของคุณ {data.FirstName} {data.LastName} รอรับการอนุมัติจากคุณ",
ReceiverUserId = approver!.ProfileId, ReceiverUserId = approver!.ProfileId,
Type = "", Type = "",
Payload = $"{URL}/leave/detail/{id}", Payload = $"{URL}/leave-reject/detail/{id}",
}; };
_appDbContext.Set<Notification>().Add(noti1); _appDbContext.Set<Notification>().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<Notification>().Add(noti);
}
await _appDbContext.SaveChangesAsync();
}
return Success();
}
/// <summary>
/// API ลบรายการการลา (ADMIN)
/// </summary>
/// <returns>
/// </returns>
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpDelete("admin/{id:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> DeleteLeaveRequestForAdminAsync(Guid id)
{
var jsonData = await _permission.GetPermissionWithActingAPIAsync("DELETE", "SYS_LEAVE_LIST");
if (jsonData!.status != 200)
{
return Error(jsonData.message, StatusCodes.Status403Forbidden);
}
// ตรวจสอบว่า role ต้องเป็น OWNER เท่านั้น
if (jsonData.result.privilege != "OWNER")
{
return Error("ไม่มีสิทธิ์ในการลบรายการขอลา", StatusCodes.Status403Forbidden);
}
var deleted = await _leaveRequestRepository.GetByIdAsync(id);
if (deleted == null)
return Error(GlobalMessages.DataNotFound);
// ห้ามลบเฉพาะสถานะ APPROVE, DELETING, DELETE
if (new[] { "APPROVE", "DELETING", "DELETE" }.Contains(deleted.LeaveStatus))
{
return Error("ไม่สามารถลบรายการขอลาสถานะนี้ได้");
}
await _leaveRequestRepository.DeleteAsync(deleted);
return Success(); return Success();
} }
@ -1948,14 +2188,14 @@ namespace BMA.EHR.Leave.Service.Controllers
public async Task<ActionResult<ResponseObject>> GetCancelLeaveRequestForAdminAsync( public async Task<ActionResult<ResponseObject>> GetCancelLeaveRequestForAdminAsync(
[FromBody] GetLeaveRequestForAdminDto req) [FromBody] GetLeaveRequestForAdminDto req)
{ {
var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_LEAVE_LIST"); var jsonData = await _permission.GetPermissionWithActingAPIAsync("LIST", "SYS_LEAVE_LIST");
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission); //var jsonData = JsonConvert.DeserializeObject<GetPermissionWithActingResultDto>(getPermission);
if (jsonData["status"]?.ToString() != "200") 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 nodeId = string.Empty;
var profileAdmin = new GetUserOCAllDto(); var profileAdmin = new GetUserOCAllDto();
profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken); profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken);
@ -1993,6 +2233,84 @@ namespace BMA.EHR.Leave.Service.Controllers
var rawData = var rawData =
await _leaveRequestRepository.GetCancelLeaveRequestForAdminAsync(req.Year, req.Type, req.Status, role, nodeId, profileAdmin?.Node); 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; var recCount = rawData.Count;
if (req.Keyword != "") if (req.Keyword != "")
@ -2015,7 +2333,7 @@ namespace BMA.EHR.Leave.Service.Controllers
LeaveSubTypeName = item.LeaveSubTypeName, LeaveSubTypeName = item.LeaveSubTypeName,
ProfileType = item.ProfileType ?? "-", ProfileType = item.ProfileType ?? "-",
FullName = $"{item.Prefix}{item.FirstName} {item.LastName}", 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 ?? "" Status = item.LeaveCancelStatus ?? ""
}; };
result.Add(res); result.Add(res);
@ -2112,7 +2430,7 @@ namespace BMA.EHR.Leave.Service.Controllers
} }
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(Guid.Parse(UserId!), AccessToken); // 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) if (profile == null)
{ {
@ -2563,8 +2881,15 @@ namespace BMA.EHR.Leave.Service.Controllers
var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUser2Async(thisYear, rawData.Type.Id, rawData.KeycloakUserId); var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUser2Async(thisYear, rawData.Type.Id, rawData.KeycloakUserId);
var startFiscalYear = new DateTime(rawData.LeaveStartDate.Year - 1, 10, 1); var startFiscalYear = new DateTime(rawData.LeaveStartDate.Year - 1, 10, 1);
var endFiscalYear = rawData.CreatedAt; var endFiscalYear = rawData.DateSendLeave ?? rawData.CreatedAt;
var endFiscalYear2 = new DateTime(rawData.LeaveStartDate.Year, 9, 30);
//var endFiscalYear3 = rawData.DateSendLeave ?? rawData.CreatedAt;
var leaveSummary = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, startFiscalYear, endFiscalYear); var leaveSummary = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, 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; //var leaveSummary = leaveData == null ? 0.0 : leaveData.LeaveDaysUsed;
if (leaveData != null) if (leaveData != null)
leaveSummary += leaveData.BeginningLeaveDays; leaveSummary += leaveData.BeginningLeaveDays;
@ -2588,7 +2913,7 @@ namespace BMA.EHR.Leave.Service.Controllers
LeaveSubTypeName = rawData.LeaveSubTypeName, LeaveSubTypeName = rawData.LeaveSubTypeName,
LeaveTypeId = rawData.Type.Id, LeaveTypeId = rawData.Type.Id,
FullName = $"{rawData.Prefix}{rawData.FirstName} {rawData.LastName}", FullName = $"{rawData.Prefix}{rawData.FirstName} {rawData.LastName}",
DateSendLeave = rawData.CreatedAt, DateSendLeave = rawData.DateSendLeave ?? rawData.CreatedAt,
Status = rawData.LeaveStatus, Status = rawData.LeaveStatus,
LeaveStartDate = rawData.LeaveStartDate, LeaveStartDate = rawData.LeaveStartDate,
LeaveEndDate = rawData.LeaveEndDate, LeaveEndDate = rawData.LeaveEndDate,
@ -2678,7 +3003,10 @@ namespace BMA.EHR.Leave.Service.Controllers
LeaveLimit = rawData.Type.Limit + extendLeave, LeaveLimit = rawData.Type.Limit + extendLeave,
LeaveSummary = leaveSummary, 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) if (rawData.LeaveDocument != null && rawData.LeaveDocument.Count > 0)
@ -2706,7 +3034,10 @@ namespace BMA.EHR.Leave.Service.Controllers
ApproveStatus = x.ApproveStatus, ApproveStatus = x.ApproveStatus,
Comment = x.Comment, Comment = x.Comment,
ProfileId = x.ProfileId, ProfileId = x.ProfileId,
KeycloakId = x.KeycloakId KeycloakId = x.KeycloakId,
isAct = x.IsAct,
keyId = x.KeyId
}).ToList(); }).ToList();
var approvers = rawData.Approvers.Where(x => x.ApproveType.ToUpper() == "APPROVER") var approvers = rawData.Approvers.Where(x => x.ApproveType.ToUpper() == "APPROVER")
@ -2721,7 +3052,10 @@ namespace BMA.EHR.Leave.Service.Controllers
ApproveStatus = x.ApproveStatus, ApproveStatus = x.ApproveStatus,
Comment = x.Comment, Comment = x.Comment,
ProfileId = x.ProfileId, ProfileId = x.ProfileId,
KeycloakId = x.KeycloakId KeycloakId = x.KeycloakId,
isAct = x.IsAct,
keyId = x.KeyId
}).ToList(); }).ToList();
result.Approvers.AddRange(approvers); result.Approvers.AddRange(approvers);
@ -2757,7 +3091,7 @@ namespace BMA.EHR.Leave.Service.Controllers
var rejectList = await _leaveRequestRepository.GetSumRejectLeaveAsync(thisYear); var rejectList = await _leaveRequestRepository.GetSumRejectLeaveAsync(thisYear);
var deleteList = await _leaveRequestRepository.GetSumDeleteLeaveAsync(thisYear); var deleteList = await _leaveRequestRepository.GetSumDeleteLeaveAsync(thisYear);
// var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken);
var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
if (pf == null) if (pf == null)
{ {
@ -2791,6 +3125,7 @@ namespace BMA.EHR.Leave.Service.Controllers
leaveBeginningDict.TryGetValue(leaveType.Id, out var leaveData); leaveBeginningDict.TryGetValue(leaveType.Id, out var leaveData);
var approve = leaveData?.LeaveDaysUsed ?? 0; var approve = leaveData?.LeaveDaysUsed ?? 0;
var approveCount = leaveData?.LeaveCount ?? 0;
// fix issue : SIT ระบบบันทึกการลา>> สิทธิ์การลา(โอนสิทธิ์การลา) #974 // fix issue : SIT ระบบบันทึกการลา>> สิทธิ์การลา(โอนสิทธิ์การลา) #974
var extendLeave = 0.0; var extendLeave = 0.0;
@ -2813,6 +3148,7 @@ namespace BMA.EHR.Leave.Service.Controllers
LeaveCountApprove = approve, LeaveCountApprove = approve,
LeaveCountReject = reject, LeaveCountReject = reject,
LeaveCountDelete = delete, LeaveCountDelete = delete,
LeaveCountApproveCount = approveCount,
}; };
result.Add(data); result.Add(data);
} }

View file

@ -17,16 +17,16 @@ namespace BMA.EHR.Leave.Service.DTOs.LeaveBeginnings
[Required, Comment("จำนวนวันลายกมา")] [Required, Comment("จำนวนวันลายกมา")]
public double LeaveDays { get; set; } = 0.0; public double LeaveDays { get; set; } = 0.0;
[Required, Comment("จำนวนวันลาที่ใช้ไป")] [Comment("จำนวนวันลาที่ใช้ไป")]
public double LeaveDaysUsed { get; set; } = 0.0; public double? LeaveDaysUsed { get; set; }
[Required, Comment("จำนวนครั้งที่ลาสะสม")] [Comment("จำนวนครั้งที่ลาสะสม")]
public int LeaveCount { get; set; } = 0; public int? LeaveCount { get; set; }
[Required, Comment("จำนวนวันลายกมา")] [Required, Comment("จำนวนวันลายกมาก่อนใช้ระบบ")]
public double BeginningLeaveDays { get; set; } = 0.0; public double BeginningLeaveDays { get; set; } = 0.0;
[Comment("จำนวนครั้งที่ลายกมา")] [Comment("จำนวนครั้งที่ลายกมาก่อนใช้ระบบ")]
public int BeginningLeaveCount { get; set; } = 0; public int BeginningLeaveCount { get; set; } = 0;
} }

View file

@ -147,6 +147,10 @@ namespace BMA.EHR.Leave.Service.DTOs.LeaveRequest
public List<GetLeaveApproverDto> Approvers { get; set; } = new(); public List<GetLeaveApproverDto> Approvers { get; set; } = new();
public Guid? KeycloakUserId { get; set; } = Guid.Empty; public Guid? KeycloakUserId { get; set; } = Guid.Empty;
public double LeaveDraftSummary { get; set; } = 0;
public double LeaveWaitingSummary { get; set; } = 0;
} }
public class GetLeaveApproverDto public class GetLeaveApproverDto
@ -170,5 +174,8 @@ namespace BMA.EHR.Leave.Service.DTOs.LeaveRequest
public string ApproveStatus { get; set; } = string.Empty; public string ApproveStatus { get; set; } = string.Empty;
public string Comment { get; set; } = string.Empty; public string Comment { get; set; } = string.Empty;
public bool isAct { get; set; } = false;
public string keyId { get; set; } = string.Empty;
} }
} }

View file

@ -53,5 +53,8 @@
public string? CurrentZipCode { get; set; } public string? CurrentZipCode { get; set; }
public int GovAge { get; set; } = 0; public int GovAge { get; set; } = 0;
public double LeaveDraftSummary { get; set; } = 0;
public double LeaveWaitingSummary { get; set; } = 0;
} }
} }

View file

@ -37,5 +37,11 @@ namespace BMA.EHR.Leave.Service.DTOs.LeaveRequest
[JsonProperty("organizationName")] [JsonProperty("organizationName")]
public string OrganizationName { get; set; } = string.Empty; public string OrganizationName { get; set; } = string.Empty;
[JsonProperty("isAct")]
public bool isAct { get; set; } = false;
[JsonProperty("keyId")]
public string keyId { get; set; } = string.Empty;
} }
} }

View file

@ -23,6 +23,7 @@ using Hangfire.Common;
using BMA.EHR.Application.Repositories.Leaves.TimeAttendants; using BMA.EHR.Application.Repositories.Leaves.TimeAttendants;
using BMA.EHR.Leave.Service.Extensions; using BMA.EHR.Leave.Service.Extensions;
using BMA.EHR.Leave.Service.Services; using BMA.EHR.Leave.Service.Services;
using BMA.EHR.Leave.Services;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
// ตั้ง TimeZone เป็น Asia/Bangkok ในโค้ด // ตั้ง TimeZone เป็น Asia/Bangkok ในโค้ด
@ -95,6 +96,7 @@ builder.Services.AddLeaveApplication();
builder.Services.AddPersistence(builder.Configuration); builder.Services.AddPersistence(builder.Configuration);
builder.Services.AddLeavePersistence(builder.Configuration); builder.Services.AddLeavePersistence(builder.Configuration);
builder.Services.AddTransient<HolidayService>(); builder.Services.AddTransient<HolidayService>();
builder.Services.AddTransient<NotificationService>();
// Configure HttpClient with increased timeout for long-running operations (e.g., RabbitMQ Management API) // Configure HttpClient with increased timeout for long-running operations (e.g., RabbitMQ Management API)
builder.Services.AddHttpClient(); builder.Services.AddHttpClient();
@ -196,12 +198,20 @@ if (manager != null)
// ทำความสะอาดข้อมูล CheckIn Job Status ที่เก่ากว่า 30 วัน - รันทุกวันเวลา 02:00 น. // ทำความสะอาดข้อมูล CheckIn Job Status ที่เก่ากว่า 30 วัน - รันทุกวันเวลา 02:00 น.
manager.AddOrUpdate("ทำความสะอาดข้อมูล CheckIn Job Status", Job.FromExpression<CheckInJobStatusRepository>(x => x.CleanupOldJobsAsync(30)), "0 2 * * *", bangkokTimeZone); manager.AddOrUpdate("ทำความสะอาดข้อมูล CheckIn Job Status", Job.FromExpression<CheckInJobStatusRepository>(x => x.CleanupOldJobsAsync(30)), "0 2 * * *", bangkokTimeZone);
manager.AddOrUpdate("ประมวลผลงานที่ค้างอยู่ในสถานะ Pending หรือ Processing", Job.FromExpression<LeaveProcessJobStatusRepository>(x => x.ProcessPendingJobsAsync()), "0 3 * * *", // ตรวจสอบและ mark งาน CheckIn ที่ค้างเกิน 30 นาทีเป็น FAILED - รันทุก 15 นาที
new RecurringJobOptions // manager.AddOrUpdate("ตรวจสอบงาน CheckIn ที่ค้างเกินเวลา", Job.FromExpression<CheckInJobStatusRepository>(x => x.MarkStaleJobsAsFailedAsync(30)), "*/15 * * * *",
{ // new RecurringJobOptions
TimeZone = bangkokTimeZone, // {
QueueName = "leave" // ← กำหนด queue // TimeZone = bangkokTimeZone,
}); // QueueName = "leave"
// });
//
// manager.AddOrUpdate("ประมวลผลงานที่ค้างอยู่ในสถานะ Pending หรือ Processing", Job.FromExpression<LeaveProcessJobStatusRepository>(x => x.ProcessPendingJobsAsync()), "0 3 * * *",
// new RecurringJobOptions
// {
// TimeZone = bangkokTimeZone,
// QueueName = "leave" // ← กำหนด queue
// });
} }
// apply migrations // apply migrations

View file

@ -0,0 +1,58 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Net.Http.Headers;
using System.Text;
namespace BMA.EHR.Leave.Services;
public class NotificationService
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<NotificationService> _logger;
private readonly IConfiguration _configuration;
private const string NotifyEndpoint = "https://hrmsbkk.case-collection.com/api/v1/org/through-socket/notify-from-token";
public NotificationService(IHttpClientFactory httpClientFactory, ILogger<NotificationService> logger, IConfiguration configuration)
{
_httpClientFactory = httpClientFactory;
_logger = logger;
_configuration = configuration;
}
public async Task SendNotificationAsync(string? token, bool error, string message)
{
if (string.IsNullOrEmpty(token))
{
_logger.LogWarning("Cannot send import notification: token is null or empty.");
return;
}
try
{
var client = _httpClientFactory.CreateClient("default");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", ""));
var payload = new
{
error,
message
};
var json = JsonConvert.SerializeObject(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync(NotifyEndpoint, content);
if (!response.IsSuccessStatusCode)
{
var responseBody = await response.Content.ReadAsStringAsync();
_logger.LogWarning("Import notification failed with status {StatusCode}: {Body}", response.StatusCode, responseBody);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to send import notification: {Message}", ex.Message);
}
}
}

View file

@ -23,15 +23,15 @@
"ExamConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;Database=hrms_exam;Allow User Variables=True;Convert Zero Datetime=True;Pooling=True;", "ExamConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;Database=hrms_exam;Allow User Variables=True;Convert Zero Datetime=True;Pooling=True;",
"LeaveConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;Database=hrms_leave;Allow User Variables=True;Convert Zero Datetime=True;Pooling=True;" "LeaveConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;Database=hrms_leave;Allow User Variables=True;Convert Zero Datetime=True;Pooling=True;"
// "DefaultConnection": "server=172.27.17.68;user=root;password=ey2qVVyyqGYw8CyA7h8X72559r2Ad84K;port=3306;database=hrms;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;Connection Timeout=180;", // "DefaultConnection": "server=127.0.0.1;user=root;password=ey2qVVyyqGYw8CyA7h8X72559r2Ad84K;port=13306;database=hrms;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;Connection Timeout=180;",
// "ExamConnection": "server=172.27.17.68;user=root;password=ey2qVVyyqGYw8CyA7h8X72559r2Ad84K;port=3306;database=hrms_exam;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;Connection Timeout=180;", // "ExamConnection": "server=127.0.0.1;user=root;password=ey2qVVyyqGYw8CyA7h8X72559r2Ad84K;port=13306;database=hrms_exam;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;Connection Timeout=180;",
// "LeaveConnection": "server=172.27.17.68;user=root;password=ey2qVVyyqGYw8CyA7h8X72559r2Ad84K;port=3306;database=hrms_leave;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;Connection Timeout=180;" // "LeaveConnection": "server=127.0.0.1;user=root;password=ey2qVVyyqGYw8CyA7h8X72559r2Ad84K;port=13306;database=hrms_leave;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;Connection Timeout=180;"
}, },
"Jwt": { "Jwt": {
"Key": "j7C9RO_p4nRtuwCH4z9Db_A_6We42tkD_p4lZtDrezc", "Key": "j7C9RO_p4nRtuwCH4z9Db_A_6We42tkD_p4lZtDrezc",
"Issuer": "https://hrmsbkk-id.case-collection.com/realms/hrms" "Issuer": "https://hrmsbkk-id.case-collection.com/realms/hrms"
// "Key": "xY2VR-EFvvNPsMs39u8ooVBWQL6mPwrNJOh3koJFTgU", // "Key": "xY2VR-EFvvNPsMs39u8ooVBWQL6mPwrNJOh3koJFTgU",
// "Issuer": "https://hrms-id.bangkok.go.th/realms/hrms" // "Issuer": "https://hrms-id.bangkok.go.th/realms/hrms"
}, },
"EPPlus": { "EPPlus": {
"ExcelPackage": { "ExcelPackage": {
@ -43,6 +43,10 @@
"AccessKey": "iwvzjyjgz0BKtLPmMpPu", "AccessKey": "iwvzjyjgz0BKtLPmMpPu",
"SecretKey": "Yv56vwctYdIspDknRJ46xztcBDzteGF3elZiDcAr", "SecretKey": "Yv56vwctYdIspDknRJ46xztcBDzteGF3elZiDcAr",
"BucketName": "hrms-fpt" "BucketName": "hrms-fpt"
// "Endpoint": "https://hrms-s3.bangkok.go.th/",
// "AccessKey": "frappet",
// "SecretKey": "FPTadmin2357",
// "BucketName": "bma-ehr-fpt"
}, },
"Protocol": "HTTPS", "Protocol": "HTTPS",
"Node": { "Node": {
@ -54,11 +58,11 @@
"Password": "12345678", "Password": "12345678",
"Queue": "hrms-checkin-queue-dev", "Queue": "hrms-checkin-queue-dev",
"URL": "http://192.168.1.63:9122/api/queues/%2F/" "URL": "http://192.168.1.63:9122/api/queues/%2F/"
// "Host": "172.27.17.68", // "Host": "172.27.17.68",
// "User": "admin", // "User": "admin",
// "Password": "admin123456", // "Password": "admin123456",
// "Queue": "hrms-checkin-queue", // "Queue": "hrms-checkin-queue",
// "URL": "http://172.27.17.68:9122/api/queues/%2F/" // "URL": "http://172.27.17.68:9122/api/queues/%2F/"
}, },
"Mail": { "Mail": {
"Server": "mail.bangkok.go.th", "Server": "mail.bangkok.go.th",
@ -72,10 +76,10 @@
"API": "https://hrmsbkk.case-collection.com/api/v1", "API": "https://hrmsbkk.case-collection.com/api/v1",
"APIV2": "https://hrmsbkk.case-collection.com/api/v2", "APIV2": "https://hrmsbkk.case-collection.com/api/v2",
"VITE_URL_MGT": "https://hrmsbkk-mgt.case-collection.com", "VITE_URL_MGT": "https://hrmsbkk-mgt.case-collection.com",
// "Domain": "https://hrms-exam.bangkok.go.th", // "Domain": "https://hrms.bangkok.go.th",
// "APIPROBATION": "https://hrms.bangkok.go.th/api/v1/probation", // "APIPROBATION": "https://hrms.bangkok.go.th/api/v1/probation",
// "API": "https://hrms.bangkok.go.th/api/v1", // "API": "https://hrms.bangkok.go.th/api/v1",
// "APIV2": "https://hrms.bangkok.go.th/api/v2", // "APIV2": "https://hrms.bangkok.go.th/api/v2",
// "VITE_URL_MGT": "https://hrms-mgt.bangkok.go.th", // "VITE_URL_MGT": "https://hrms-mgt.bangkok.go.th",
"API_KEY": "fKRL16yyEgbyTEJdsMw2h64tGSCmkW685PRtM3CygzX1JOSdptT9UJtpgWwKM8FybRTJups3GTFwj27ZRvlPdIkv3XgCoVJaD5LmR06ozuEPvCCRSdp2WFthg08V5xHc56fTPfZLpr1VmXrhd6dvYhHIqKkQUJR02Rlkss11cLRWEQOssEFVA4xdu2J5DIRO1EM5m7wRRvEwcDB4mYRXD9HH52SMq6iYqUWEWsMwLdbk7QW9yYESUEuzMW5gWrb6vIeWZxJV5bTz1PcWUyR7eO9Fyw1F5DiQYc9JgzTC1mW7cv31fEtTtrfbJYKIb5EbWilqIEUKC6A0UKBDDek35ML0006cqRVm0pvdOH6jeq7VQyYrhdXe59dBEyhYGUIfozoVBvW7Up4QBuOMjyPjSqJPlMBKwaseptfrblxQV1AOOivSBpf1ZcQyOZ8JktRtKUDSuXsmG0lsXwFlI3JCeSHdpVdgZWFYcJPegqfrB6KotR02t9AVkpLs1ZWrixwz" "API_KEY": "fKRL16yyEgbyTEJdsMw2h64tGSCmkW685PRtM3CygzX1JOSdptT9UJtpgWwKM8FybRTJups3GTFwj27ZRvlPdIkv3XgCoVJaD5LmR06ozuEPvCCRSdp2WFthg08V5xHc56fTPfZLpr1VmXrhd6dvYhHIqKkQUJR02Rlkss11cLRWEQOssEFVA4xdu2J5DIRO1EM5m7wRRvEwcDB4mYRXD9HH52SMq6iYqUWEWsMwLdbk7QW9yYESUEuzMW5gWrb6vIeWZxJV5bTz1PcWUyR7eO9Fyw1F5DiQYc9JgzTC1mW7cv31fEtTtrfbJYKIb5EbWilqIEUKC6A0UKBDDek35ML0006cqRVm0pvdOH6jeq7VQyYrhdXe59dBEyhYGUIfozoVBvW7Up4QBuOMjyPjSqJPlMBKwaseptfrblxQV1AOOivSBpf1ZcQyOZ8JktRtKUDSuXsmG0lsXwFlI3JCeSHdpVdgZWFYcJPegqfrB6KotR02t9AVkpLs1ZWrixwz"
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -554,7 +554,7 @@ namespace BMA.EHR.Placement.Service.Controllers
placementAppointment.positionAreaOld = org.result.positionArea; placementAppointment.positionAreaOld = org.result.positionArea;
placementAppointment.PositionLevelOld = org.result.posLevelName; placementAppointment.PositionLevelOld = org.result.posLevelName;
placementAppointment.PositionTypeOld = org.result.posTypeName; placementAppointment.PositionTypeOld = org.result.posTypeName;
placementAppointment.PositionNumberOld = org.result.nodeShortName + " " + org.result.posMasterNo; placementAppointment.PositionNumberOld = org.result.posNo;
placementAppointment.OrganizationOld = (org.result.child4 == null ? "" : org.result.child4 + "\n") + placementAppointment.OrganizationOld = (org.result.child4 == null ? "" : org.result.child4 + "\n") +
(org.result.child3 == null ? "" : org.result.child3 + "\n") + (org.result.child3 == null ? "" : org.result.child3 + "\n") +
(org.result.child2 == null ? "" : org.result.child2 + "\n") + (org.result.child2 == null ? "" : org.result.child2 + "\n") +
@ -676,6 +676,7 @@ namespace BMA.EHR.Placement.Service.Controllers
uppdated.posMasterNo = req.posMasterNo; uppdated.posMasterNo = req.posMasterNo;
uppdated.position = req.positionName; uppdated.position = req.positionName;
uppdated.PositionExecutive = req.posExecutiveName; uppdated.PositionExecutive = req.posExecutiveName;
uppdated.posExecutiveId = req.posExecutiveId;
uppdated.positionExecutiveField = req.positionExecutiveField; uppdated.positionExecutiveField = req.positionExecutiveField;
uppdated.positionArea = req.positionArea; uppdated.positionArea = req.positionArea;
uppdated.positionField = req.positionField; uppdated.positionField = req.positionField;
@ -1010,10 +1011,14 @@ namespace BMA.EHR.Placement.Service.Controllers
positionExecutive = p.PositionExecutive, positionExecutive = p.PositionExecutive,
positionExecutiveField = p.positionExecutiveField, positionExecutiveField = p.positionExecutiveField,
positionArea = p.positionArea, positionArea = p.positionArea,
positionTypeId = p.posTypeId,
positionType = p.posTypeName, positionType = p.posTypeName,
positionLevelId = p.posLevelId,
positionLevel = p.posLevelName, positionLevel = p.posLevelName,
posmasterId = p.posmasterId, posmasterId = p.posmasterId,
positionId = p.positionId, positionId = p.positionId,
posExecutiveId = p.posExecutiveId,
positionField = p.positionField,
commandId = r.commandId, commandId = r.commandId,
orgRoot = p.root, orgRoot = p.root,
orgChild1 = p.child1, orgChild1 = p.child1,
@ -1222,10 +1227,14 @@ namespace BMA.EHR.Placement.Service.Controllers
positionExecutive = p.PositionExecutive, positionExecutive = p.PositionExecutive,
positionExecutiveField = p.positionExecutiveField, positionExecutiveField = p.positionExecutiveField,
positionArea = p.positionArea, positionArea = p.positionArea,
positionTypeId = p.posTypeId,
positionType = p.posTypeName, positionType = p.posTypeName,
positionLevelId = p.posLevelId,
positionLevel = p.posLevelName, positionLevel = p.posLevelName,
posmasterId = p.posmasterId, posmasterId = p.posmasterId,
positionId = p.positionId, positionId = p.positionId,
posExecutiveId = p.posExecutiveId,
positionField = p.positionField,
commandId = r.commandId, commandId = r.commandId,
orgRoot = p.root, orgRoot = p.root,
orgChild1 = p.child1, orgChild1 = p.child1,
@ -1851,10 +1860,14 @@ namespace BMA.EHR.Placement.Service.Controllers
positionExecutive = p.PositionExecutive, positionExecutive = p.PositionExecutive,
positionExecutiveField = p.positionExecutiveField, positionExecutiveField = p.positionExecutiveField,
positionArea = p.positionArea, positionArea = p.positionArea,
positionTypeId = p.posTypeId,
positionType = p.posTypeName, positionType = p.posTypeName,
positionLevelId = p.posLevelId,
positionLevel = p.posLevelName, positionLevel = p.posLevelName,
posmasterId = p.posmasterId, posmasterId = p.posmasterId,
positionId = p.positionId, positionId = p.positionId,
posExecutiveId = p.posExecutiveId,
positionField = p.positionField,
commandId = r.commandId, commandId = r.commandId,
orgRoot = p.root, orgRoot = p.root,
orgChild1 = p.child1, orgChild1 = p.child1,
@ -2016,6 +2029,8 @@ namespace BMA.EHR.Placement.Service.Controllers
positionLevel = p.posLevelName, positionLevel = p.posLevelName,
posmasterId = p.posmasterId, posmasterId = p.posmasterId,
positionId = p.positionId, positionId = p.positionId,
posExecutiveId = p.posExecutiveId,
positionField = p.positionField,
commandId = r.commandId, commandId = r.commandId,
orgRoot = p.root, orgRoot = p.root,
orgChild1 = p.child1, orgChild1 = p.child1,

View file

@ -903,6 +903,49 @@ namespace BMA.EHR.Placement.Service.Controllers
return Success(); return Success();
} }
/// <summary>
/// API สำหรับยกเลิกการส่งตัว
/// </summary>
/// <returns></returns>
/// <response code="200"></response>
/// <response code="400">ค่าตัวแปรที่ส่งมาไม่ถูกต้อง</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("update/draft-status")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> PersonUpdateDraftStatus([FromBody] PersonUpdateStatusRequest req)
{
var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_PLACEMENT_PASS");
var jsonData = JsonConvert.DeserializeObject<JObject>(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)}")] [HttpGet("pass/deferment/{personalId:length(36)}")]
public async Task<ActionResult<ResponseObject>> GetPersonDeferment(Guid personalId) public async Task<ActionResult<ResponseObject>> GetPersonDeferment(Guid personalId)
{ {
@ -1022,6 +1065,7 @@ namespace BMA.EHR.Placement.Service.Controllers
person.positionId = req.positionId; person.positionId = req.positionId;
person.posMasterNo = req.posMasterNo; person.posMasterNo = req.posMasterNo;
person.positionName = req.positionName; person.positionName = req.positionName;
person.posExecutiveId = req.posExecutiveId;
person.PositionExecutive = req.posExecutiveName; person.PositionExecutive = req.posExecutiveName;
person.positionExecutiveField = req.positionExecutiveField; person.positionExecutiveField = req.positionExecutiveField;
person.positionArea = req.positionArea; person.positionArea = req.positionArea;
@ -1410,6 +1454,10 @@ namespace BMA.EHR.Placement.Service.Controllers
profile.posTypeName = null; profile.posTypeName = null;
profile.posLevelId = null; profile.posLevelId = null;
profile.posLevelName = null; profile.posLevelName = null;
profile.PositionExecutive = null;
profile.posExecutiveId = null;
profile.positionArea = null;
profile.positionExecutiveField = null;
// profile.PositionLevel = null; // profile.PositionLevel = null;
// profile.PositionType = null; // profile.PositionType = null;
@ -1767,16 +1815,27 @@ namespace BMA.EHR.Placement.Service.Controllers
[HttpPost("recruit/report/excecute")] [HttpPost("recruit/report/excecute")]
public async Task<ActionResult<ResponseObject>> PostReportExecuteRecruit([FromBody] ReportExecuteRequest req) public async Task<ActionResult<ResponseObject>> PostReportExecuteRecruit([FromBody] ReportExecuteRequest req)
{ {
Console.WriteLine($"[RecruitReportExcecute] Starting execution at {DateTime.Now}");
try try
{ {
Console.WriteLine($"[RecruitReportExcecute] Request received with {req?.refIds?.Length ?? 0} refIds");
var placementProfile = await _context.PlacementProfiles var placementProfile = await _context.PlacementProfiles
.Include(x => x.PlacementCertificates) .Include(x => x.PlacementCertificates)
.Include(x => x.PlacementEducations) .Include(x => x.PlacementEducations)
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
Console.WriteLine($"[RecruitReportExcecute] Found {placementProfile?.Count ?? 0} placement profiles");
if (placementProfile == null) if (placementProfile == null)
{
Console.Error.WriteLine("[RecruitReportExcecute] PlacementProfile is null - returning NotFound");
return NotFound(); return NotFound();
}
Console.WriteLine("[RecruitReportExcecute] Building resultData from placement profiles and refIds");
var resultData = (from p in placementProfile var resultData = (from p in placementProfile
join r in req.refIds join r in req.refIds
@ -1893,7 +1952,14 @@ namespace BMA.EHR.Placement.Service.Controllers
bodyPosition = new bodyPosition = new
{ {
posmasterId = p.posmasterId, posmasterId = p.posmasterId,
positionId = p.positionId positionId = p.positionId,
positionName = p.positionName,
positionField = p.positionField,
posTypeId = p.posTypeId,
posLevelId = p.posLevelId,
posExecutiveId = p.posExecutiveId,
positionExecutiveField = p.positionExecutiveField,
positionArea = p.positionArea,
}, },
bodyMarry = new bodyMarry = new
{ {
@ -1922,6 +1988,9 @@ namespace BMA.EHR.Placement.Service.Controllers
}, },
}).ToList(); }).ToList();
Console.WriteLine($"[RecruitReportExcecute] resultData built successfully with {resultData?.Count ?? 0} records");
Console.WriteLine($"[RecruitReportExcecute] Calling external API: {_configuration["API"]}/org/command/excexute/create-officer-profile");
var apiUrl = $"{_configuration["API"]}/org/command/excexute/create-officer-profile"; var apiUrl = $"{_configuration["API"]}/org/command/excexute/create-officer-profile";
using (var client = new HttpClient()) using (var client = new HttpClient())
{ {
@ -1933,8 +2002,10 @@ namespace BMA.EHR.Placement.Service.Controllers
data = resultData data = resultData
}); });
var _result = await _res.Content.ReadAsStringAsync(); var _result = await _res.Content.ReadAsStringAsync();
Console.WriteLine($"[RecruitReportExcecute] External API response status: {_res.StatusCode}");
if (_res.IsSuccessStatusCode) if (_res.IsSuccessStatusCode)
{ {
Console.WriteLine("[RecruitReportExcecute] External API call successful - updating placement profiles");
placementProfile.ForEach(profile => placementProfile.ForEach(profile =>
{ {
profile.PlacementStatus = "DONE"; profile.PlacementStatus = "DONE";
@ -1948,14 +2019,24 @@ namespace BMA.EHR.Placement.Service.Controllers
profile.templateDoc = req.refIds[0].remark; profile.templateDoc = req.refIds[0].remark;
} }
}); });
Console.WriteLine($"[RecruitReportExcecute] Saving changes to database for {placementProfile.Count} profiles");
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
Console.WriteLine("[RecruitReportExcecute] Database save completed successfully");
}
else
{
Console.Error.WriteLine($"[RecruitReportExcecute] External API call failed with status: {_res.StatusCode}");
Console.Error.WriteLine($"[RecruitReportExcecute] Response content: {_result}");
} }
} }
Console.WriteLine($"[RecruitReportExcecute] Process completed successfully at {DateTime.Now}");
return Success(); return Success();
} }
catch catch (Exception ex)
{ {
Console.Error.WriteLine($"[RecruitReportExcecute] Error occurred: {ex.Message}");
Console.Error.WriteLine($"[RecruitReportExcecute] Stack trace: {ex.StackTrace}");
throw; throw;
} }
} }
@ -2002,7 +2083,7 @@ namespace BMA.EHR.Placement.Service.Controllers
.Where(x => req.refIds.Contains(x.Id.ToString())) .Where(x => req.refIds.Contains(x.Id.ToString()))
// .Where(x => x.PlacementStatus.ToUpper() == "REPORT") // .Where(x => x.PlacementStatus.ToUpper() == "REPORT")
.ToListAsync(); .ToListAsync();
placementProfiles.ForEach(profile => profile.PlacementStatus = "PREPARE-CONTAI"); placementProfiles.ForEach(profile => profile.PlacementStatus = "PREPARE-CONTAIN");
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Success(); return Success();
} }
@ -2117,8 +2198,11 @@ namespace BMA.EHR.Placement.Service.Controllers
[HttpPost("candidate/report/excecute")] [HttpPost("candidate/report/excecute")]
public async Task<ActionResult<ResponseObject>> PostReportExecuteCandidate([FromBody] ReportExecuteRequest req) public async Task<ActionResult<ResponseObject>> PostReportExecuteCandidate([FromBody] ReportExecuteRequest req)
{ {
Console.WriteLine($"[CandidateReportExcecute] Starting execution at {DateTime.Now}");
try try
{ {
Console.WriteLine($"[CandidateReportExcecute] Request received with {req?.refIds?.Length ?? 0} refIds");
var placementProfile = await _context.PlacementProfiles var placementProfile = await _context.PlacementProfiles
.Include(x => x.PlacementCertificates) .Include(x => x.PlacementCertificates)
.Include(x => x.PlacementEducations) .Include(x => x.PlacementEducations)
@ -2126,8 +2210,15 @@ namespace BMA.EHR.Placement.Service.Controllers
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
Console.WriteLine($"[CandidateReportExcecute] Found {placementProfile?.Count ?? 0} placement profiles");
if (placementProfile == null) if (placementProfile == null)
{
Console.Error.WriteLine("[CandidateReportExcecute] PlacementProfile is null - returning NotFound");
return NotFound(); return NotFound();
}
Console.WriteLine("[CandidateReportExcecute] Building resultData from placement profiles and refIds");
var resultData = (from p in placementProfile var resultData = (from p in placementProfile
join r in req.refIds join r in req.refIds
@ -2249,7 +2340,14 @@ namespace BMA.EHR.Placement.Service.Controllers
bodyPosition = new bodyPosition = new
{ {
posmasterId = p.posmasterId, posmasterId = p.posmasterId,
positionId = p.positionId positionId = p.positionId,
positionName = p.positionName,
positionField = p.positionField,
posTypeId = p.posTypeId,
posLevelId = p.posLevelId,
posExecutiveId = p.posExecutiveId,
positionExecutiveField = p.positionExecutiveField,
positionArea = p.positionArea,
}, },
bodyMarry = new bodyMarry = new
{ {
@ -2278,6 +2376,9 @@ namespace BMA.EHR.Placement.Service.Controllers
}, },
}).ToList(); }).ToList();
Console.WriteLine($"[CandidateReportExcecute] resultData built successfully with {resultData?.Count ?? 0} records");
Console.WriteLine($"[CandidateReportExcecute] Calling external API: {_configuration["API"]}/org/command/excexute/create-officer-profile");
var apiUrl = $"{_configuration["API"]}/org/command/excexute/create-officer-profile"; var apiUrl = $"{_configuration["API"]}/org/command/excexute/create-officer-profile";
using (var client = new HttpClient()) using (var client = new HttpClient())
{ {
@ -2289,8 +2390,10 @@ namespace BMA.EHR.Placement.Service.Controllers
data = resultData data = resultData
}); });
var _result = await _res.Content.ReadAsStringAsync(); var _result = await _res.Content.ReadAsStringAsync();
Console.WriteLine($"[CandidateReportExcecute] External API response status: {_res.StatusCode}");
if (_res.IsSuccessStatusCode) if (_res.IsSuccessStatusCode)
{ {
Console.WriteLine("[CandidateReportExcecute] External API call successful - updating placement profiles");
placementProfile.ForEach(profile => placementProfile.ForEach(profile =>
{ {
profile.PlacementStatus = "DONE"; profile.PlacementStatus = "DONE";
@ -2304,17 +2407,27 @@ namespace BMA.EHR.Placement.Service.Controllers
profile.templateDoc = req.refIds[0].remark; profile.templateDoc = req.refIds[0].remark;
} }
}); });
Console.WriteLine($"[CandidateReportExcecute] Saving changes to database for {placementProfile.Count} profiles");
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
Console.WriteLine("[CandidateReportExcecute] Database save completed successfully");
}
else
{
Console.Error.WriteLine($"[CandidateReportExcecute] External API call failed with status: {_res.StatusCode}");
Console.Error.WriteLine($"[CandidateReportExcecute] Response content: {_result}");
} }
} }
// // update placementstatus // // update placementstatus
// placementProfile.ForEach(profile => profile.PlacementStatus = "DONE"); // placementProfile.ForEach(profile => profile.PlacementStatus = "DONE");
// await _context.SaveChangesAsync(); // await _context.SaveChangesAsync();
Console.WriteLine($"[CandidateReportExcecute] Process completed successfully at {DateTime.Now}");
return Success(); return Success();
} }
catch catch (Exception ex)
{ {
Console.Error.WriteLine($"[CandidateReportExcecute] Error occurred: {ex.Message}");
Console.Error.WriteLine($"[CandidateReportExcecute] Stack trace: {ex.StackTrace}");
throw; throw;
} }
} }
@ -2498,6 +2611,11 @@ namespace BMA.EHR.Placement.Service.Controllers
var data = await _context.PlacementProfiles var data = await _context.PlacementProfiles
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => profile.PlacementStatus = "DONE");
await _context.SaveChangesAsync();
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
on p.Id.ToString() equals r.refId on p.Id.ToString() equals r.refId
@ -2511,10 +2629,14 @@ namespace BMA.EHR.Placement.Service.Controllers
positionExecutive = p.PositionExecutive, positionExecutive = p.PositionExecutive,
positionExecutiveField = p.positionExecutiveField, positionExecutiveField = p.positionExecutiveField,
positionArea = p.positionArea, positionArea = p.positionArea,
positionTypeId = p.posTypeId,
positionType = p.posTypeName, positionType = p.posTypeName,
positionLevelId = p.posLevelId,
positionLevel = p.posLevelName, positionLevel = p.posLevelName,
posmasterId = p.posmasterId, posmasterId = p.posmasterId,
positionId = p.positionId, positionId = p.positionId,
posExecutiveId = p.posExecutiveId,
positionField = p.positionField,
commandId = r.commandId, commandId = r.commandId,
orgRoot = p.root, orgRoot = p.root,
orgChild1 = p.child1, orgChild1 = p.child1,
@ -2547,12 +2669,12 @@ namespace BMA.EHR.Placement.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); //// var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) //// if (_res.IsSuccessStatusCode)
{ //// {
data.ForEach(profile => profile.PlacementStatus = "DONE"); //// data.ForEach(profile => profile.PlacementStatus = "DONE");
await _context.SaveChangesAsync(); //// await _context.SaveChangesAsync();
} //// }
} }
return Success(); return Success();
} }
@ -2734,6 +2856,11 @@ namespace BMA.EHR.Placement.Service.Controllers
var data = await _context.PlacementProfiles var data = await _context.PlacementProfiles
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => profile.PlacementStatus = "DONE");
await _context.SaveChangesAsync();
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
on p.Id.ToString() equals r.refId on p.Id.ToString() equals r.refId
@ -2747,10 +2874,14 @@ namespace BMA.EHR.Placement.Service.Controllers
positionExecutive = p.PositionExecutive, positionExecutive = p.PositionExecutive,
positionExecutiveField = p.positionExecutiveField, positionExecutiveField = p.positionExecutiveField,
positionArea = p.positionArea, positionArea = p.positionArea,
positionTypeId = p.posTypeId,
positionType = p.posTypeName, positionType = p.posTypeName,
positionLevelId = p.posLevelId,
positionLevel = p.posLevelName, positionLevel = p.posLevelName,
posmasterId = p.posmasterId, posmasterId = p.posmasterId,
positionId = p.positionId, positionId = p.positionId,
posExecutiveId = p.posExecutiveId,
positionField = p.positionField,
commandId = r.commandId, commandId = r.commandId,
orgRoot = p.root, orgRoot = p.root,
orgChild1 = p.child1, orgChild1 = p.child1,
@ -2783,12 +2914,12 @@ namespace BMA.EHR.Placement.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); //// var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) //// if (_res.IsSuccessStatusCode)
{ //// {
data.ForEach(profile => profile.PlacementStatus = "DONE"); //// data.ForEach(profile => profile.PlacementStatus = "DONE");
await _context.SaveChangesAsync(); //// await _context.SaveChangesAsync();
} //// }
} }
return Success(); return Success();
} }
@ -2955,6 +3086,11 @@ namespace BMA.EHR.Placement.Service.Controllers
var data = await _context.PlacementProfiles var data = await _context.PlacementProfiles
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => profile.PlacementStatus = "DONE");
await _context.SaveChangesAsync();
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
on p.Id.ToString() equals r.refId on p.Id.ToString() equals r.refId
@ -2972,6 +3108,8 @@ namespace BMA.EHR.Placement.Service.Controllers
positionLevel = p.posLevelName, positionLevel = p.posLevelName,
posmasterId = p.posmasterId, posmasterId = p.posmasterId,
positionId = p.positionId, positionId = p.positionId,
posExecutiveId = p.posExecutiveId,
positionField = p.positionField,
commandId = r.commandId, commandId = r.commandId,
orgRoot = p.root, orgRoot = p.root,
orgChild1 = p.child1, orgChild1 = p.child1,
@ -3004,12 +3142,12 @@ namespace BMA.EHR.Placement.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); //// var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) //// if (_res.IsSuccessStatusCode)
{ //// {
data.ForEach(profile => profile.PlacementStatus = "DONE"); //// data.ForEach(profile => profile.PlacementStatus = "DONE");
await _context.SaveChangesAsync(); //// await _context.SaveChangesAsync();
} //// }
} }
return Success(); return Success();
} }

View file

@ -493,7 +493,7 @@ namespace BMA.EHR.Placement.Service.Controllers
placementOfficer.positionAreaOld = org.result.positionArea; placementOfficer.positionAreaOld = org.result.positionArea;
placementOfficer.PositionLevelOld = org.result.posLevelName; placementOfficer.PositionLevelOld = org.result.posLevelName;
placementOfficer.PositionTypeOld = org.result.posTypeName; placementOfficer.PositionTypeOld = org.result.posTypeName;
placementOfficer.PositionNumberOld = org.result.nodeShortName + " " + org.result.posMasterNo; placementOfficer.PositionNumberOld = org.result.posNo;
placementOfficer.OrganizationOld = (org.result.child4 == null ? "" : org.result.child4 + "\n") + placementOfficer.OrganizationOld = (org.result.child4 == null ? "" : org.result.child4 + "\n") +
(org.result.child3 == null ? "" : org.result.child3 + "\n") + (org.result.child3 == null ? "" : org.result.child3 + "\n") +
(org.result.child2 == null ? "" : org.result.child2 + "\n") + (org.result.child2 == null ? "" : org.result.child2 + "\n") +
@ -758,6 +758,17 @@ namespace BMA.EHR.Placement.Service.Controllers
var data = await _context.PlacementOfficers var data = await _context.PlacementOfficers
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
var firstRef = req.refIds.FirstOrDefault();
var commandNoText = firstRef != null ? $"{firstRef.commandNo}/{firstRef.commandYear.ToThaiYear()}" : null;
foreach (var profile in data)
{
profile.Status = "DONE";
profile.commandNo = commandNoText;
}
await _context.SaveChangesAsync();
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
on p.Id.ToString() equals r.refId on p.Id.ToString() equals r.refId
@ -809,17 +820,16 @@ namespace BMA.EHR.Placement.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); //// var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) //// if (_res.IsSuccessStatusCode)
{ //// {
//data.ForEach(profile => profile.Status = "DONE"); //// foreach (var profile in data)
foreach (var profile in data) //// {
{ //// profile.Status = "DONE";
profile.Status = "DONE"; //// profile.commandNo = resultData.Count > 0 ? $"{resultData[0].commandNo}/{resultData[0].commandYear.ToThaiYear()}" : null;
profile.commandNo = resultData.Count > 0 ? $"{resultData[0].commandNo}/{resultData[0].commandYear.ToThaiYear()}" : null; //// }
} //// await _context.SaveChangesAsync();
await _context.SaveChangesAsync(); //// }
}
} }
return Success(); return Success();
} }

View file

@ -129,6 +129,7 @@ namespace BMA.EHR.Placement.Service.Controllers
{ {
p.Id, p.Id,
p.prefix, p.prefix,
p.rank,
p.firstName, p.firstName,
p.lastName, p.lastName,
p.citizenId, p.citizenId,
@ -280,6 +281,7 @@ namespace BMA.EHR.Placement.Service.Controllers
// ProfileId = p.Profile.Id, // ProfileId = p.Profile.Id,
p.citizenId, p.citizenId,
p.prefix, p.prefix,
p.rank,
p.firstName, p.firstName,
p.lastName, p.lastName,
p.DateOfBirth, p.DateOfBirth,
@ -377,6 +379,7 @@ namespace BMA.EHR.Placement.Service.Controllers
// data.ProfileId, // data.ProfileId,
data.citizenId, data.citizenId,
data.prefix, data.prefix,
data.rank,
data.firstName, data.firstName,
data.lastName, data.lastName,
data.DateOfBirth, data.DateOfBirth,
@ -484,6 +487,7 @@ namespace BMA.EHR.Placement.Service.Controllers
// Profile = profile, // Profile = profile,
citizenId = req.citizenId, citizenId = req.citizenId,
prefix = req.prefix, prefix = req.prefix,
rank = req.rank,
firstName = req.firstName, firstName = req.firstName,
lastName = req.lastName, lastName = req.lastName,
DateOfBirth = req.BirthDate, DateOfBirth = req.BirthDate,
@ -586,7 +590,7 @@ namespace BMA.EHR.Placement.Service.Controllers
placementReceive.positionAreaOld = org.result.positionArea; placementReceive.positionAreaOld = org.result.positionArea;
placementReceive.PositionLevelOld = org.result.posLevelName; placementReceive.PositionLevelOld = org.result.posLevelName;
placementReceive.PositionTypeOld = org.result.posTypeName; placementReceive.PositionTypeOld = org.result.posTypeName;
placementReceive.PositionNumberOld = org.result.nodeShortName + " " + org.result.posMasterNo; placementReceive.PositionNumberOld = org.result.posNo;
placementReceive.OrganizationOld = (org.result.child4 == null ? "" : org.result.child4 + "\n") + placementReceive.OrganizationOld = (org.result.child4 == null ? "" : org.result.child4 + "\n") +
(org.result.child3 == null ? "" : org.result.child3 + "\n") + (org.result.child3 == null ? "" : org.result.child3 + "\n") +
(org.result.child2 == null ? "" : org.result.child2 + "\n") + (org.result.child2 == null ? "" : org.result.child2 + "\n") +
@ -778,6 +782,7 @@ namespace BMA.EHR.Placement.Service.Controllers
uppdated.posMasterNo = req.posMasterNo; uppdated.posMasterNo = req.posMasterNo;
uppdated.position = req.positionName; uppdated.position = req.positionName;
uppdated.PositionExecutive = req.posExecutiveName; uppdated.PositionExecutive = req.posExecutiveName;
uppdated.posExecutiveId = req.posExecutiveId;
uppdated.positionExecutiveField = req.positionExecutiveField; uppdated.positionExecutiveField = req.positionExecutiveField;
uppdated.positionArea = req.positionArea; uppdated.positionArea = req.positionArea;
uppdated.positionField = req.positionField; uppdated.positionField = req.positionField;
@ -852,6 +857,7 @@ namespace BMA.EHR.Placement.Service.Controllers
uppdated.Gender = req.Gender; uppdated.Gender = req.Gender;
uppdated.citizenId = req.citizenId; uppdated.citizenId = req.citizenId;
uppdated.prefix = req.prefix; uppdated.prefix = req.prefix;
uppdated.rank = req.rank;
uppdated.firstName = req.firstName; uppdated.firstName = req.firstName;
uppdated.lastName = req.lastName; uppdated.lastName = req.lastName;
uppdated.DateOfBirth = req.DateOfBirth; uppdated.DateOfBirth = req.DateOfBirth;
@ -917,6 +923,61 @@ namespace BMA.EHR.Placement.Service.Controllers
return Success(); return Success();
} }
/// <summary>
/// API ลบรายการรับโอน (ADMIN)
/// </summary>
/// <param name="id">Id รับโอน</param>
/// <returns></returns>
/// <response code="200"></response>
/// <response code="400">ค่าตัวแปรที่ส่งมาไม่ถูกต้อง</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpDelete("admin/{id:length(36)}")]
public async Task<ActionResult<ResponseObject>> DeleteForAdminAsync(Guid id)
{
var jsonData = await _permission.GetPermissionWithActingAPIAsync("DELETE", "SYS_TRANSFER_RECEIVE");
if (jsonData!.status != 200)
{
return Error(jsonData.message, StatusCodes.Status403Forbidden);
}
// ตรวจสอบว่า role ต้องเป็น OWNER เท่านั้น
if (jsonData.result.privilege != "OWNER")
{
return Error("ไม่มีสิทธิ์ในการลบรายการรับโอน", StatusCodes.Status403Forbidden);
}
var deleted = await _context.PlacementReceives.AsQueryable()
.Include(x => x.PlacementReceiveDocs)
.ThenInclude(x => x.Document)
.FirstOrDefaultAsync(x => x.Id == id);
if (deleted == null)
return NotFound();
// ห้ามลบเฉพาะสถานะ REPORT, WAITING, DONE
if (new[] { "REPORT", "WAITING", "DONE" }.Contains(deleted.Status))
{
return Error("ไม่สามารถลบรายการรับโอนสถานะนี้ได้");
}
var placementReceiveDocs = new List<dynamic>();
foreach (var doc in deleted.PlacementReceiveDocs)
{
if (doc.Document != null)
placementReceiveDocs.Add(doc.Document.Id);
}
_context.PlacementReceiveDocs.RemoveRange(deleted.PlacementReceiveDocs);
await _context.SaveChangesAsync();
_context.PlacementReceives.Remove(deleted);
foreach (var doc in placementReceiveDocs)
{
if (doc != null)
await _documentService.DeleteFileAsync(doc);
}
await _context.SaveChangesAsync();
return Success();
}
/// <summary> /// <summary>
/// สั่งรายชื่อไปออกคำสั่ง /// สั่งรายชื่อไปออกคำสั่ง
/// </summary> /// </summary>
@ -1124,6 +1185,10 @@ namespace BMA.EHR.Placement.Service.Controllers
.Include(x => x.Avatar) .Include(x => x.Avatar)
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync();
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
on p.Id.ToString() equals r.refId on p.Id.ToString() equals r.refId
@ -1131,7 +1196,7 @@ namespace BMA.EHR.Placement.Service.Controllers
{ {
bodyProfile = new bodyProfile = new
{ {
rank = string.Empty, rank = string.IsNullOrEmpty(p.rank) ? string.Empty : p.rank,
prefix = p.prefix == null ? string.Empty : p.prefix, prefix = p.prefix == null ? string.Empty : p.prefix,
firstName = p.firstName == null ? string.Empty : p.firstName, firstName = p.firstName == null ? string.Empty : p.firstName,
lastName = p.lastName == null ? string.Empty : p.lastName, lastName = p.lastName == null ? string.Empty : p.lastName,
@ -1208,12 +1273,18 @@ namespace BMA.EHR.Placement.Service.Controllers
bodyPosition = new bodyPosition = new
{ {
posmasterId = p.posmasterId, posmasterId = p.posmasterId,
positionId = p.positionId positionId = p.positionId,
positionName = p.position,
positionField = p.positionField,
posTypeId = p.posTypeId,
posLevelId = p.posLevelId,
posExecutiveId = p.posExecutiveId,
positionExecutiveField = p.positionExecutiveField,
positionArea = p.positionArea,
} }
}).ToList(); }).ToList();
var baseAPIOrg = _configuration["API"]; var baseAPIOrg = _configuration["API"];
//var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-current";
var apiUrlOrg = $"{_configuration["API"]}/org/command/excexute/create-officer-profile"; var apiUrlOrg = $"{_configuration["API"]}/org/command/excexute/create-officer-profile";
using (var client = new HttpClient()) using (var client = new HttpClient())
{ {
@ -1223,12 +1294,12 @@ namespace BMA.EHR.Placement.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); // // var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) // // if (_res.IsSuccessStatusCode)
{ // // {
data.ForEach(profile => profile.Status = "DONE"); // // data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync(); // // await _context.SaveChangesAsync();
} // // }
} }
return Success(); return Success();
} }

View file

@ -374,7 +374,7 @@ namespace BMA.EHR.Placement.Service.Controllers
placementRepatriation.positionAreaOld = org.result.positionArea; placementRepatriation.positionAreaOld = org.result.positionArea;
placementRepatriation.PositionLevelOld = org.result.posLevelName; placementRepatriation.PositionLevelOld = org.result.posLevelName;
placementRepatriation.PositionTypeOld = org.result.posTypeName; placementRepatriation.PositionTypeOld = org.result.posTypeName;
placementRepatriation.PositionNumberOld = org.result.nodeShortName + " " + org.result.posMasterNo; placementRepatriation.PositionNumberOld = org.result.posNo;
placementRepatriation.OrganizationOld = (org.result.child4 == null ? "" : org.result.child4 + "\n") + placementRepatriation.OrganizationOld = (org.result.child4 == null ? "" : org.result.child4 + "\n") +
(org.result.child3 == null ? "" : org.result.child3 + "\n") + (org.result.child3 == null ? "" : org.result.child3 + "\n") +
(org.result.child2 == null ? "" : org.result.child2 + "\n") + (org.result.child2 == null ? "" : org.result.child2 + "\n") +
@ -623,6 +623,10 @@ namespace BMA.EHR.Placement.Service.Controllers
var data = await _context.PlacementRepatriations var data = await _context.PlacementRepatriations
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync();
string? _null = null; string? _null = null;
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
@ -675,12 +679,12 @@ namespace BMA.EHR.Placement.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); // // var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) // // if (_res.IsSuccessStatusCode)
{ // // {
data.ForEach(profile => profile.Status = "DONE"); // // data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync(); // // await _context.SaveChangesAsync();
} // // }
} }
return Success(); return Success();
} }

View file

@ -764,7 +764,7 @@ namespace BMA.EHR.Placement.Service.Controllers
placementTransfer.positionAreaOld = org.result.positionArea; placementTransfer.positionAreaOld = org.result.positionArea;
placementTransfer.PositionLevelOld = org.result.posLevelName; placementTransfer.PositionLevelOld = org.result.posLevelName;
placementTransfer.PositionTypeOld = org.result.posTypeName; placementTransfer.PositionTypeOld = org.result.posTypeName;
placementTransfer.PositionNumberOld = org.result.nodeShortName + " " + org.result.posMasterNo; placementTransfer.PositionNumberOld = org.result.posNo;
placementTransfer.OrganizationOld = (org.result.child4 == null ? "" : org.result.child4 + "\n") + placementTransfer.OrganizationOld = (org.result.child4 == null ? "" : org.result.child4 + "\n") +
(org.result.child3 == null ? "" : org.result.child3 + "\n") + (org.result.child3 == null ? "" : org.result.child3 + "\n") +
(org.result.child2 == null ? "" : org.result.child2 + "\n") + (org.result.child2 == null ? "" : org.result.child2 + "\n") +
@ -932,6 +932,60 @@ namespace BMA.EHR.Placement.Service.Controllers
return Success(); return Success();
} }
/// <summary>
/// API ลบรายการคำขอโอน (ADMIN)
/// </summary>
/// <param name="id">Id คำขอโอน</param>
/// <returns></returns>
/// <response code="200"></response>
/// <response code="400">ค่าตัวแปรที่ส่งมาไม่ถูกต้อง</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpDelete("admin/{id:length(36)}")]
public async Task<ActionResult<ResponseObject>> DeleteForAdminAsync(Guid id)
{
var jsonData = await _permission.GetPermissionWithActingAPIAsync("DELETE", "SYS_TRANSFER_REQ");
if (jsonData!.status != 200)
{
return Error(jsonData.message, StatusCodes.Status403Forbidden);
}
// ตรวจสอบว่า role ต้องเป็น OWNER เท่านั้น
if (jsonData.result.privilege != "OWNER")
{
return Error("ไม่มีสิทธิ์ในการลบรายการคำขอโอน", StatusCodes.Status403Forbidden);
}
var deleted = await _context.PlacementTransfers.AsQueryable()
.Include(x => x.PlacementTransferDocs)
.ThenInclude(x => x.Document)
.FirstOrDefaultAsync(x => x.Id == id);
if (deleted == null)
return NotFound();
// ห้ามลบเฉพาะสถานะ REPORT, WAITING, DONE
if (new[] { "REPORT", "WAITING", "DONE" }.Contains(deleted.Status))
{
return Error("ไม่สามารถลบรายการคำขอโอนสถานะนี้ได้");
}
var placementTransferDocs = new List<dynamic>();
foreach (var doc in deleted.PlacementTransferDocs)
{
if (doc.Document != null)
placementTransferDocs.Add(doc.Document.Id);
}
_context.PlacementTransferDocs.RemoveRange(deleted.PlacementTransferDocs);
await _context.SaveChangesAsync();
_context.PlacementTransfers.Remove(deleted);
foreach (var doc in placementTransferDocs)
{
if (doc != null)
await _documentService.DeleteFileAsync(doc);
}
await _context.SaveChangesAsync();
return Success();
}
/// <summary> /// <summary>
/// สั่งรายชื่อไปออกคำสั่ง /// สั่งรายชื่อไปออกคำสั่ง
/// </summary> /// </summary>
@ -1094,6 +1148,10 @@ namespace BMA.EHR.Placement.Service.Controllers
var data = await _context.PlacementTransfers var data = await _context.PlacementTransfers
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync();
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
on p.Id.ToString() equals r.refId on p.Id.ToString() equals r.refId
@ -1146,12 +1204,12 @@ namespace BMA.EHR.Placement.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); // // var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) // // if (_res.IsSuccessStatusCode)
{ // // {
data.ForEach(profile => profile.Status = "DONE"); // // data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync(); // // await _context.SaveChangesAsync();
} // // }
} }
return Success(); return Success();
} }

View file

@ -21,6 +21,7 @@ using System.Text;
using System.Transactions; using System.Transactions;
using BMA.EHR.Placement.Service.Filters; using BMA.EHR.Placement.Service.Filters;
using BMA.EHR.Application.Repositories.Reports; using BMA.EHR.Application.Repositories.Reports;
using BMA.EHR.Application.Repositories;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
{ {
@ -164,6 +165,8 @@ var app = builder.Build();
if (manager != null) if (manager != null)
{ {
manager.AddOrUpdate("แจ้งเตือนระบบทดลองงาน", Job.FromExpression<ProbationReportRepository>(x => x.NotifyProbation()), Cron.Daily(Int32.Parse(builder.Configuration["KeycloakCron:Hour"]), Int32.Parse(builder.Configuration["KeycloakCron:Minute"])), TimeZoneInfo.Local); manager.AddOrUpdate("แจ้งเตือนระบบทดลองงาน", Job.FromExpression<ProbationReportRepository>(x => x.NotifyProbation()), Cron.Daily(Int32.Parse(builder.Configuration["KeycloakCron:Hour"]), Int32.Parse(builder.Configuration["KeycloakCron:Minute"])), TimeZoneInfo.Local);
// Job: อัพเดทสถานะผู้สอบผ่านที่ลาออกไปแล้วแต่ยังไม่ส่งไปออกคำสั่ง ทำงานทุกวันเวลา 05:00 น.
manager.AddOrUpdate("ประมวลผลข้าราชการฯ กทม.", Job.FromExpression<PlacementRepository>(x => x.UpdateStatusPlacementProfiles()), Cron.Daily(5), TimeZoneInfo.Local);
} }
// apply migrations // apply migrations

View file

@ -53,6 +53,7 @@ namespace BMA.EHR.Placement.Service.Requests
public double? Amount { get; set; } public double? Amount { get; set; }
public string? avatarUrl { get; set; } public string? avatarUrl { get; set; }
public bool? isDeputy { get; set; } public bool? isDeputy { get; set; }
public string? posNo { get; set; }
} }
} }

View file

@ -30,6 +30,7 @@ namespace BMA.EHR.Placement.Service.Requests
public string? posLevelName { get; set; } public string? posLevelName { get; set; }
public string? typeCommand { get; set; } public string? typeCommand { get; set; }
public string? posExecutiveName { get; set; } public string? posExecutiveName { get; set; }
public string? posExecutiveId { get; set; }
public string? positionExecutiveField { get; set; } public string? positionExecutiveField { get; set; }
public string? positionArea { get; set; } public string? positionArea { get; set; }
} }

View file

@ -30,6 +30,7 @@ namespace BMA.EHR.Placement.Service.Requests
public string? posLevelName { get; set; } public string? posLevelName { get; set; }
public string? typeCommand { get; set; } public string? typeCommand { get; set; }
public string? posExecutiveName { get; set; } public string? posExecutiveName { get; set; }
public string? posExecutiveId { get; set; }
public string? positionExecutiveField { get; set; } public string? positionExecutiveField { get; set; }
public string? positionArea { get; set; } public string? positionArea { get; set; }
} }

View file

@ -39,6 +39,7 @@ namespace BMA.EHR.Placement.Service.Requests
public string? posLevelName { get; set; } public string? posLevelName { get; set; }
public string? typeCommand { get; set; } public string? typeCommand { get; set; }
public string? posExecutiveName { get; set; } public string? posExecutiveName { get; set; }
public string? posExecutiveId { get; set; }
public string? positionExecutiveField { get; set; } public string? positionExecutiveField { get; set; }
public string? positionArea { get; set; } public string? positionArea { get; set; }

View file

@ -6,7 +6,8 @@ namespace BMA.EHR.Placement.Service.Requests
public class PlacementReceiveEditRequest public class PlacementReceiveEditRequest
{ {
public string citizenId { get; set; } public string citizenId { get; set; }
public string prefix { get; set; } public string? prefix { get; set; }
public string? rank { get; set; }
public string firstName { get; set; } public string firstName { get; set; }
public string lastName { get; set; } public string lastName { get; set; }
public DateTime DateOfBirth { get; set; } public DateTime DateOfBirth { get; set; }

View file

@ -6,7 +6,9 @@ namespace BMA.EHR.Placement.Service.Requests
public class PlacementReceiveRequest public class PlacementReceiveRequest
{ {
public string citizenId { get; set; } public string citizenId { get; set; }
public string prefix { get; set; }
public string? prefix { get; set; }
public string? rank { get; set; }
public string firstName { get; set; } public string firstName { get; set; }
public string lastName { get; set; } public string lastName { get; set; }
public DateTime BirthDate { get; set; } public DateTime BirthDate { get; set; }

View file

@ -40,10 +40,17 @@
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="9.0.3" /> <PackageReference Include="Serilog.Sinks.Elasticsearch" Version="9.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" /> <PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="DocumentFormat.OpenXml" Version="2.20.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\BMA.EHR.Infrastructure\BMA.EHR.Infrastructure.csproj" /> <ProjectReference Include="..\BMA.EHR.Infrastructure\BMA.EHR.Infrastructure.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="Templates\**\*.docx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project> </Project>

View file

@ -7,6 +7,7 @@ using BMA.EHR.Domain.Models.Retirement;
using BMA.EHR.Domain.Shared; using BMA.EHR.Domain.Shared;
using BMA.EHR.Infrastructure.Persistence; using BMA.EHR.Infrastructure.Persistence;
using BMA.EHR.Retirement.Service.Requests; using BMA.EHR.Retirement.Service.Requests;
using BMA.EHR.Retirement.Service.Services;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -37,6 +38,7 @@ namespace BMA.EHR.Retirement.Service.Controllers
private readonly PermissionRepository _permission; private readonly PermissionRepository _permission;
private readonly DisciplineDbContext _contextDiscipline; private readonly DisciplineDbContext _contextDiscipline;
private readonly RetireReportRepository _service; private readonly RetireReportRepository _service;
private readonly RetirementReportService _reportService;
public RetirementController(RetirementRepository repository, public RetirementController(RetirementRepository repository,
NotificationRepository repositoryNoti, NotificationRepository repositoryNoti,
ApplicationDBContext context, ApplicationDBContext context,
@ -46,7 +48,8 @@ namespace BMA.EHR.Retirement.Service.Controllers
IHttpContextAccessor httpContextAccessor, IHttpContextAccessor httpContextAccessor,
PermissionRepository permission, PermissionRepository permission,
DisciplineDbContext contextDiscipline, DisciplineDbContext contextDiscipline,
RetireReportRepository service) RetireReportRepository service,
RetirementReportService reportService)
{ {
_repository = repository; _repository = repository;
_repositoryNoti = repositoryNoti; _repositoryNoti = repositoryNoti;
@ -58,6 +61,7 @@ namespace BMA.EHR.Retirement.Service.Controllers
_permission = permission; _permission = permission;
_contextDiscipline = contextDiscipline; _contextDiscipline = contextDiscipline;
_service = service; _service = service;
_reportService = reportService;
} }
#region " Properties " #region " Properties "
@ -2213,5 +2217,83 @@ namespace BMA.EHR.Retirement.Service.Controllers
} }
} }
#endregion #endregion
#region &
/// <summary>
/// รายงานรายชื่อผู้เกษียณอายุราชการ ข้าราชการ & ลูกจ้างประจำ
/// </summary>
/// <param name="Id">Id ของรอบเกษียณ</param>
/// <param name="exportType">pdf, docx</param>
/// <returns></returns>
/// <response code="200">เมื่อทำการอ่านข้อมูลจาก Relational Database สำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpGet("report/{exportType}/{Id}")]
public async Task<ActionResult<ResponseObject>> 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
} }
} }

View file

@ -569,6 +569,7 @@ namespace BMA.EHR.Retirement.Service.Controllers
uppdated.positionId = req.positionId; uppdated.positionId = req.positionId;
uppdated.posMasterNo = req.posMasterNo; uppdated.posMasterNo = req.posMasterNo;
uppdated.position = req.positionName; uppdated.position = req.positionName;
uppdated.posExecutiveId = req.posExecutiveId;
uppdated.PositionExecutive = req.posExecutiveName; uppdated.PositionExecutive = req.posExecutiveName;
uppdated.positionExecutiveField = req.positionExecutiveField; uppdated.positionExecutiveField = req.positionExecutiveField;
uppdated.positionArea = req.positionArea; uppdated.positionArea = req.positionArea;
@ -845,6 +846,10 @@ namespace BMA.EHR.Retirement.Service.Controllers
var data = await _context.RetirementOthers var data = await _context.RetirementOthers
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync();
string? _null = null; string? _null = null;
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
@ -885,6 +890,9 @@ namespace BMA.EHR.Retirement.Service.Controllers
commandCode = r.commandCode, commandCode = r.commandCode,
commandName = r.commandName, commandName = r.commandName,
remark = r.remark, remark = r.remark,
positionId = p.positionId,
posExecutiveId = p.posExecutiveId,
positionField = p.positionField,
positionTypeNew = p.posTypeId, positionTypeNew = p.posTypeId,
positionLevelNew = p.posLevelId, positionLevelNew = p.posLevelId,
positionNameNew = p.position, positionNameNew = p.position,
@ -914,12 +922,12 @@ namespace BMA.EHR.Retirement.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); // // var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) // // if (_res.IsSuccessStatusCode)
{ // // {
data.ForEach(profile => profile.Status = "DONE"); // // data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync(); // // await _context.SaveChangesAsync();
} // // }
} }
return Success(); return Success();
} }
@ -1090,6 +1098,10 @@ namespace BMA.EHR.Retirement.Service.Controllers
var data = await _context.RetirementOthers var data = await _context.RetirementOthers
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync();
string? _null = null; string? _null = null;
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
@ -1130,6 +1142,9 @@ namespace BMA.EHR.Retirement.Service.Controllers
commandCode = r.commandCode, commandCode = r.commandCode,
commandName = r.commandName, commandName = r.commandName,
remark = r.remark, remark = r.remark,
positionId = p.positionId,
posExecutiveId = p.posExecutiveId,
positionField = p.positionField,
positionTypeNew = p.posTypeId, positionTypeNew = p.posTypeId,
positionLevelNew = p.posLevelId, positionLevelNew = p.posLevelId,
positionNameNew = p.position, positionNameNew = p.position,
@ -1159,12 +1174,12 @@ namespace BMA.EHR.Retirement.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); // // var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) // // if (_res.IsSuccessStatusCode)
{ // // {
data.ForEach(profile => profile.Status = "DONE"); // // data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync(); // // await _context.SaveChangesAsync();
} // // }
} }
return Success(); return Success();
} }

View file

@ -642,6 +642,10 @@ namespace BMA.EHR.Retirement.Service.Controllers
var data = await _context.RetirementOuts var data = await _context.RetirementOuts
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync();
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
on p.Id.ToString() equals r.refId on p.Id.ToString() equals r.refId
@ -701,12 +705,12 @@ namespace BMA.EHR.Retirement.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); // // var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) // // if (_res.IsSuccessStatusCode)
{ // // {
data.ForEach(profile => profile.Status = "DONE"); // // data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync(); // // await _context.SaveChangesAsync();
} // // }
} }
return Success(); return Success();
} }

View file

@ -151,7 +151,7 @@ namespace BMA.EHR.Retirement.Service.Controllers
return Error("ไม่พบหน่วยงานของผู้ใช้งานคนนี้", 404); return Error("ไม่พบหน่วยงานของผู้ใช้งานคนนี้", 404);
var retirementResigns = await _context.RetirementResigns.AsQueryable() var retirementResigns = await _context.RetirementResigns.AsQueryable()
.Where(x => x.profileId == org.result.profileId) .Where(x => x.Status != "DELETE" && x.profileId == org.result.profileId)
.OrderByDescending(x => x.CreatedAt) .OrderByDescending(x => x.CreatedAt)
.Select(p => new .Select(p => new
{ {
@ -1456,10 +1456,12 @@ namespace BMA.EHR.Retirement.Service.Controllers
else if ((retirementResign.posTypeNameOld == "ทั่วไป" && retirementResign.posLevelNameOld == "อาวุโส") || (retirementResign.posTypeNameOld == "วิชาการ" && retirementResign.posLevelNameOld == "ชำนาญการพิเศษ") || (retirementResign.posTypeNameOld == "อำนวยการ" && retirementResign.posLevelNameOld == "ต้น")) else if ((retirementResign.posTypeNameOld == "ทั่วไป" && retirementResign.posLevelNameOld == "อาวุโส") || (retirementResign.posTypeNameOld == "วิชาการ" && retirementResign.posLevelNameOld == "ชำนาญการพิเศษ") || (retirementResign.posTypeNameOld == "อำนวยการ" && retirementResign.posLevelNameOld == "ต้น"))
{ {
retirementResign.Group = "1.2"; retirementResign.Group = "1.2";
retirementResign.ApproveStep = "st3";
} }
else if ((retirementResign.posTypeNameOld == "ทั่วไป" && retirementResign.posLevelNameOld == "ทักษะพิเศษ") || (retirementResign.posTypeNameOld == "วิชาการ" && retirementResign.posLevelNameOld == "เชี่ยวชาญ") || (retirementResign.posTypeNameOld == "วิชาการ" && retirementResign.posLevelNameOld == "ทรงคุณวุฒิ") || (retirementResign.posTypeNameOld == "อำนวยการ" && retirementResign.posLevelNameOld == "สูง") || (retirementResign.posTypeNameOld == "บริหาร" && retirementResign.posLevelNameOld == "ต้น") || (retirementResign.posTypeNameOld == "บริหาร" && retirementResign.posLevelNameOld == "สูง")) else if ((retirementResign.posTypeNameOld == "ทั่วไป" && retirementResign.posLevelNameOld == "ทักษะพิเศษ") || (retirementResign.posTypeNameOld == "วิชาการ" && retirementResign.posLevelNameOld == "เชี่ยวชาญ") || (retirementResign.posTypeNameOld == "วิชาการ" && retirementResign.posLevelNameOld == "ทรงคุณวุฒิ") || (retirementResign.posTypeNameOld == "อำนวยการ" && retirementResign.posLevelNameOld == "สูง") || (retirementResign.posTypeNameOld == "บริหาร" && retirementResign.posLevelNameOld == "ต้น") || (retirementResign.posTypeNameOld == "บริหาร" && retirementResign.posLevelNameOld == "สูง"))
{ {
retirementResign.Group = "2"; retirementResign.Group = "2";
retirementResign.ApproveStep = "st3";
} }
} }
await _context.RetirementResigns.AddAsync(retirementResign); await _context.RetirementResigns.AddAsync(retirementResign);
@ -1809,6 +1811,47 @@ namespace BMA.EHR.Retirement.Service.Controllers
return Success(); return Success();
} }
/// <summary>
/// API ลบรายการลาออก (ADMIN)
/// </summary>
/// <param name="id">Id ลาออก</param>
/// <returns></returns>
/// <response code="200"></response>
/// <response code="400">ค่าตัวแปรที่ส่งมาไม่ถูกต้อง</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpDelete("admin/{id:length(36)}")]
public async Task<ActionResult<ResponseObject>> DeleteForAdminAsync(Guid id)
{
var jsonData = await _permission.GetPermissionWithActingAPIAsync("DELETE", "SYS_RESIGN");
if (jsonData!.status != 200)
{
return Error(jsonData.message, StatusCodes.Status403Forbidden);
}
// ตรวจสอบว่า role ต้องเป็น OWNER เท่านั้น
if (jsonData.result.privilege != "OWNER")
{
return Error("ไม่มีสิทธิ์ในการลบรายการลาออก", StatusCodes.Status403Forbidden);
}
var deleted = await _context.RetirementResigns.AsQueryable()
.FirstOrDefaultAsync(x => x.Id == id);
if (deleted == null)
return Error(GlobalMessages.RetirementResignNotFound, 404);
// ห้ามลบเฉพาะสถานะ REPORT, WAITING, DONE, CANCELING, CANCEL
if (new[] { "REPORT", "WAITING", "DONE", "CANCELING", "CANCEL" }.Contains(deleted.Status))
{
return Error("ไม่สามารถลบรายการลาออกสถานะนี้ได้");
}
deleted.Status = "DELETE";
deleted.LastUpdateFullName = FullName ?? "System Administrator";
deleted.LastUpdateUserId = UserId ?? "";
deleted.LastUpdatedAt = DateTime.Now;
await _context.SaveChangesAsync();
return Success();
}
/// <summary> /// <summary>
/// อนุมัติคำลาออก /// อนุมัติคำลาออก
/// </summary> /// </summary>
@ -2861,6 +2904,10 @@ namespace BMA.EHR.Retirement.Service.Controllers
var data = await _context.RetirementResigns var data = await _context.RetirementResigns
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync();
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
on p.Id.ToString() equals r.refId on p.Id.ToString() equals r.refId
@ -2915,12 +2962,12 @@ namespace BMA.EHR.Retirement.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); // // var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) // // if (_res.IsSuccessStatusCode)
{ // // {
data.ForEach(profile => profile.Status = "DONE"); // // data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync(); // // await _context.SaveChangesAsync();
} // // }
} }
return Success(); return Success();
} }
@ -3060,6 +3107,11 @@ namespace BMA.EHR.Retirement.Service.Controllers
.Include(x => x.RetirementResign) .Include(x => x.RetirementResign)
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => profile.Status = "DONE");
data.ForEach(profile => profile.RetirementResign.Status = "CANCEL");
await _context.SaveChangesAsync();
string? _null = null; string? _null = null;
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
@ -3120,13 +3172,13 @@ namespace BMA.EHR.Retirement.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); // // var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) // // if (_res.IsSuccessStatusCode)
{ // // {
data.ForEach(profile => profile.Status = "DONE"); // // data.ForEach(profile => profile.Status = "DONE");
data.ForEach(profile => profile.RetirementResign.Status = "CANCEL"); // // data.ForEach(profile => profile.RetirementResign.Status = "CANCEL");
await _context.SaveChangesAsync(); // // await _context.SaveChangesAsync();
} // // }
} }
//} //}
//else //else

View file

@ -89,7 +89,7 @@ namespace BMA.EHR.Retirement.Service.Controllers
return Error("ไม่พบหน่วยงานของผู้ใช้งานคนนี้", 404); return Error("ไม่พบหน่วยงานของผู้ใช้งานคนนี้", 404);
var retirementResignEmployees = await _context.RetirementResignEmployees.AsQueryable() var retirementResignEmployees = await _context.RetirementResignEmployees.AsQueryable()
.Where(x => x.profileId == org.result.profileId) .Where(x => x.Status != "DELETE" && x.profileId == org.result.profileId)
.OrderByDescending(x => x.CreatedAt) .OrderByDescending(x => x.CreatedAt)
.Select(p => new .Select(p => new
{ {
@ -1719,6 +1719,47 @@ namespace BMA.EHR.Retirement.Service.Controllers
return Success(); return Success();
} }
/// <summary>
/// API ลบรายการลาออกลูกจ้าง (ADMIN)
/// </summary>
/// <param name="id">Id ลาออก</param>
/// <returns></returns>
/// <response code="200"></response>
/// <response code="400">ค่าตัวแปรที่ส่งมาไม่ถูกต้อง</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpDelete("admin/{id:length(36)}")]
public async Task<ActionResult<ResponseObject>> DeleteForAdminAsync(Guid id)
{
var jsonData = await _permission.GetPermissionWithActingAPIAsync("DELETE", "SYS_RESIGN_EMP");
if (jsonData!.status != 200)
{
return Error(jsonData.message, StatusCodes.Status403Forbidden);
}
// ตรวจสอบว่า role ต้องเป็น OWNER เท่านั้น
if (jsonData.result.privilege != "OWNER")
{
return Error("ไม่มีสิทธิ์ในการลบรายการลาออกลูกจ้าง", StatusCodes.Status403Forbidden);
}
var deleted = await _context.RetirementResignEmployees.AsQueryable()
.FirstOrDefaultAsync(x => x.Id == id);
if (deleted == null)
return Error(GlobalMessages.RetirementResignEmployeeNotFound, 404);
// ห้ามลบเฉพาะสถานะ REPORT, WAITING, DONE, CANCELING, CANCEL
if (new[] { "REPORT", "WAITING", "DONE", "CANCELING", "CANCEL" }.Contains(deleted.Status))
{
return Error("ไม่สามารถลบรายการลาออกลูกจ้างสถานะนี้ได้");
}
deleted.Status = "DELETE";
deleted.LastUpdateFullName = FullName ?? "System Administrator";
deleted.LastUpdateUserId = UserId ?? "";
deleted.LastUpdatedAt = DateTime.Now;
await _context.SaveChangesAsync();
return Success();
}
/// <summary> /// <summary>
/// อนุมัติคำลาออก /// อนุมัติคำลาออก
/// </summary> /// </summary>
@ -2361,6 +2402,10 @@ namespace BMA.EHR.Retirement.Service.Controllers
var data = await _context.RetirementResignEmployees var data = await _context.RetirementResignEmployees
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync();
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
on p.Id.ToString() equals r.refId on p.Id.ToString() equals r.refId
@ -2413,12 +2458,12 @@ namespace BMA.EHR.Retirement.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); // // var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) // // if (_res.IsSuccessStatusCode)
{ // // {
data.ForEach(profile => profile.Status = "DONE"); // // data.ForEach(profile => profile.Status = "DONE");
await _context.SaveChangesAsync(); // // await _context.SaveChangesAsync();
} // // }
} }
return Success(); return Success();
} }
@ -2556,6 +2601,11 @@ namespace BMA.EHR.Retirement.Service.Controllers
.Include(x => x.RetirementResignEmployee) .Include(x => x.RetirementResignEmployee)
.Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString()))
.ToListAsync(); .ToListAsync();
// Task #224 ปรับให้เป็น process ที่ควรบันทึกตามลำดับ
data.ForEach(profile => profile.Status = "DONE");
data.ForEach(profile => profile.RetirementResignEmployee.Status = "CANCEL");
await _context.SaveChangesAsync();
string? _null = null; string? _null = null;
var resultData = (from p in data var resultData = (from p in data
join r in req.refIds join r in req.refIds
@ -2615,13 +2665,13 @@ namespace BMA.EHR.Retirement.Service.Controllers
{ {
data = resultData, data = resultData,
}); });
var _result = await _res.Content.ReadAsStringAsync(); // // var _result = await _res.Content.ReadAsStringAsync();
if (_res.IsSuccessStatusCode) // // if (_res.IsSuccessStatusCode)
{ // // {
data.ForEach(profile => profile.Status = "DONE"); // // data.ForEach(profile => profile.Status = "DONE");
data.ForEach(profile => profile.RetirementResignEmployee.Status = "CANCEL"); // // data.ForEach(profile => profile.RetirementResignEmployee.Status = "CANCEL");
await _context.SaveChangesAsync(); // // await _context.SaveChangesAsync();
} // // }
} }
//} //}
//else //else

View file

@ -3,6 +3,7 @@ using BMA.EHR.Domain.Middlewares;
using BMA.EHR.Infrastructure; using BMA.EHR.Infrastructure;
using BMA.EHR.Infrastructure.Persistence; using BMA.EHR.Infrastructure.Persistence;
using BMA.EHR.Retirement.Service; using BMA.EHR.Retirement.Service;
using BMA.EHR.Retirement.Service.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.ApiExplorer;
@ -86,6 +87,7 @@ var builder = WebApplication.CreateBuilder(args);
builder.Services.AddApplication(); builder.Services.AddApplication();
builder.Services.AddLeaveApplication(); builder.Services.AddLeaveApplication();
builder.Services.AddPersistence(builder.Configuration); builder.Services.AddPersistence(builder.Configuration);
builder.Services.AddScoped<RetirementReportService>();
builder.Services.AddLeavePersistence(builder.Configuration); builder.Services.AddLeavePersistence(builder.Configuration);
builder.Services.AddHttpClient(); builder.Services.AddHttpClient();

View file

@ -29,6 +29,7 @@ namespace BMA.EHR.Retirement.Service.Requests
public string? posLevelId { get; set; } public string? posLevelId { get; set; }
public string? posLevelName { get; set; } public string? posLevelName { get; set; }
public string? typeCommand { get; set; } public string? typeCommand { get; set; }
public string? posExecutiveId { get; set; }
public string? posExecutiveName { get; set; } public string? posExecutiveName { get; set; }
public string? positionExecutiveField { get; set; } public string? positionExecutiveField { get; set; }
public string? positionArea { get; set; } public string? positionArea { get; set; }

View file

@ -0,0 +1,698 @@
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<RetirementReportService> _logger;
private readonly IConfiguration _configuration;
/// <summary>
/// Initializes a new instance of the RetirementReportService class.
/// </summary>
public RetirementReportService(
IWebHostEnvironment environment,
ILogger<RetirementReportService> logger,
IConfiguration configuration)
{
_environment = environment;
_logger = logger;
_configuration = configuration;
}
#region Public Methods
/// <summary>
/// สร้างรายงานจาก Template (.docx)
/// </summary>
public async Task<byte[]> 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<byte[]> 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<byte>();
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<Document, System.Collections.IEnumerable>(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<Table>().FirstOrDefault();
if (table == null) return;
var rows = table.Elements<TableRow>().ToList();
if (rows.Count == 0) return;
var strategy = CreateTableStrategy(rows);
strategy.Process(table, rows, profiles);
}
private static ITableStrategy CreateTableStrategy(List<TableRow> rows)
{
// retire-1 format: 1 row, 1 cell, 1 paragraph
if (IsSingleParagraphFormat(rows))
return new SingleParagraphTableStrategy();
// retire-1 format: 2 rows, 3 columns (root row + data row per profile)
if (IsTwoRowPerProfileFormat(rows))
return new TwoRowPerProfileTableStrategy();
// retire-3 format: 2+ rows (header + template)
return new MultiRowTableStrategy();
}
private static bool IsSingleParagraphFormat(List<TableRow> rows) =>
rows.Count == 1 &&
rows[0].Elements<TableCell>().Count() == 1 &&
rows[0].Elements<TableCell>().First().Elements<Paragraph>().Count() == 1;
private static bool IsTwoRowPerProfileFormat(List<TableRow> rows) =>
rows.Count == 2 &&
rows[0].Elements<TableCell>().Count() == 3 &&
rows[1].Elements<TableCell>().Count() == 3;
#endregion
#region PDF Conversion
private async Task<byte[]> 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<bool>("LibreOffice:UseDocker", false);
var timeout = _configuration.GetValue<int>("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<Document, System.Collections.IEnumerable> tableFiller);
}
internal class DictionaryDataProcessor : IDataProcessor
{
private readonly dynamic _data;
public DictionaryDataProcessor(dynamic data)
{
_data = data;
}
public void Process(Document document, Action<Document, System.Collections.IEnumerable> 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<Document, System.Collections.IEnumerable> tableFiller)
{
var dataType = _data.GetType();
var allProps = dataType.GetProperties();
var validProps = new List<PropertyInfo>();
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<Run>())
{
var textElements = run.Elements<Text>().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<Paragraph>())
{
var allRuns = para.Elements<Run>().ToList();
if (allRuns.Count == 0) continue;
var combinedParaText = string.Concat(allRuns.SelectMany(r => r.Elements<Text>().Select(t => t.Text)));
if (combinedParaText.Contains(oldValue))
{
found = true;
var replacedText = combinedParaText.Replace(oldValue, newValue);
var firstRunTexts = allRuns[0].Elements<Text>().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<Text>())
{
t.Text = string.Empty;
}
}
}
}
// Fallback: Check individual Text elements
if (!found)
{
foreach (var text in document.Descendants<Text>())
{
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<TableCell>())
{
foreach (var para in cell.Elements<Paragraph>())
{
found = ReplaceInParagraph(para, oldValue, newValue) || found;
}
}
// Fallback: Check individual Text elements
if (!found)
{
foreach (var text in row.Descendants<Text>())
{
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<Text>().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<TableRow> rows, System.Collections.IEnumerable profiles);
}
internal class SingleParagraphTableStrategy : ITableStrategy
{
public void Process(Table table, List<TableRow> rows, System.Collections.IEnumerable profiles)
{
var cell = rows[0].Elements<TableCell>().First();
var templatePara = cell.Elements<Paragraph>().First();
var profileList = profiles.Cast<object>().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<TableRow> rows, System.Collections.IEnumerable profiles)
{
var templateRowIndex = rows.Count >= 2 ? 1 : 0;
var templateRow = rows[templateRowIndex];
templateRow.Remove();
var profileList = profiles.Cast<object>().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<object> 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);
}
}
}
}
internal class TwoRowPerProfileTableStrategy : ITableStrategy
{
public void Process(Table table, List<TableRow> rows, System.Collections.IEnumerable profiles)
{
// retire-1 format: 2 rows per profile (root row + data row)
var rootRow = rows[0];
var dataRow = rows[1];
// Remove template rows from table
rootRow.Remove();
dataRow.Remove();
var profileList = profiles.Cast<object>().ToList();
foreach (var profile in profileList)
{
var props = profile.GetType()
.GetProperties()
.Where(p => p.GetIndexParameters().Length == 0)
.ToList();
// Check root value - skip root row if empty or null
var rootValue = props.FirstOrDefault(p => p.Name.Equals("root", StringComparison.OrdinalIgnoreCase))
?.GetValue(profile)?.ToString() ?? string.Empty;
// Clone root row and fill with placeholders (only if root has value)
if (!string.IsNullOrWhiteSpace(rootValue))
{
var newRootRow = (TableRow)rootRow.CloneNode(true);
foreach (var prop in props)
{
var value = prop.GetValue(profile)?.ToString() ?? string.Empty;
var placeholder = $"{{{{{prop.Name}}}}}";
TextReplacer.ReplaceInRow(newRootRow, placeholder, value);
}
table.AppendChild(newRootRow);
}
// Clone data row and fill with placeholders (always show)
var newDataRow = (TableRow)dataRow.CloneNode(true);
foreach (var prop in props)
{
var value = prop.GetValue(profile)?.ToString() ?? string.Empty;
var placeholder = $"{{{{{prop.Name}}}}}";
TextReplacer.ReplaceInRow(newDataRow, placeholder, value);
}
table.AppendChild(newDataRow);
}
}
}
#endregion
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

210
README.md Normal file
View file

@ -0,0 +1,210 @@
# BMA.EHR - HRMS API Backend
ระบบบริหารจัดการทรัพยากรบุคคล (Human Resource Management System) พัฒนาด้วยสถาปัตยกรรม Microservices บน .NET
---
## Tech Stack
| Category | Technology |
| ----------------- | ----------------------------- |
| Framework | .NET 6.0 / 7.0 / 8.0 |
| Architecture | Clean Architecture, DDD, CQRS |
| Database | MySQL (Entity Framework Core) |
| Authentication | JWT + Keycloak |
| Message Queue | RabbitMQ |
| Object Storage | MinIO |
| Background Jobs | Hangfire |
| Logging | Serilog + Elasticsearch |
| Error Tracking | Sentry |
| API Documentation | Swagger / OpenAPI |
| Containerization | Docker |
| Testing | xUnit, k6 (Load Testing) |
---
## โครงสร้างโปรเจกต์ (Project Structure)
```
BMA.EHR.Solution.sln
├── src/ # Core Libraries
│ ├── BMA.EHR.Domain/ # Domain layer (Entities, Business Rules)
│ ├── BMA.EHR.Application/ # Application layer (Use Cases, Interfaces)
│ └── BMA.EHR.Infrastructure/ # Infrastructure layer (Data Access, External Services)
└── Service/ # Microservices
├── BMA.EHR.Command.Service/ # Command/CQRS API Gateway
├── BMA.EHR.MetaData.Service/ # ข้อมูลอ้างอิง (คำนำหน้า, หมู่เลือด, ศาสนา ฯลฯ)
├── BMA.EHR.Placement.Service/ # การบริจาค/สั่งย้าย/แต่งตั้ง
├── BMA.EHR.OrganizationEmployee.Service/ # โครงสร้างองค์กรและบุคลากร
├── BMA.EHR.Discipline.Service/ # การ discipline บุคลากร
├── BMA.EHR.Retirement.Service/ # การเกษียณอายุราชการ
├── BMA.EHR.Report.Service/ # รายงาน
├── BMA.EHR.Insignia/ # เครื่องราชอิสริยาภรณ์
├── BMA.EHR.Leave/ # ระบบลา
└── BMA.EHR.CheckInConsumer/ # ลงเวลาปฏิบัติงาน
```
---
## โมดูลหลัก (Key Modules)
### การบริหารทรัพยากรบุคคล
- **Placement Service** - สั่งย้าย, แต่งตั้ง, เลื่อนตำแหน่ง, โอนย้ายบุคลากร
- **Organization Employee Service** - จัดการโครงสร้างองค์กร, ตำแหน่ง, ข้อมูลบุคลากร
- **Leave Service** - การลางาน, การอนุมัติ, วันหยุดนักขัตฤกษ์, ยอดวันลาคงเหลือ
- **Discipline Service** - การดำเนินการ discipline, การสืบสวน, เอกสารที่เกี่ยวข้อง
- **Retirement Service** - การเกษียณอายุ, เอกสาร, สิทธิประโยชน์
- **Insignia Service** - เครื่องราชอิสริยาภรณ์และรางวัล
- **CheckIn Consumer** - ติดตามการลงเวลาปฏิบัติงาน
### ระบบสนับสนุน
- **Metadata Service** - ข้อมูลอ้างอิง (คำนำหน้าชื่อ, หมู่เลือด, ศาสนา, ระดับการศึกษา ฯลฯ)
- **Report Service** - สร้างรายงาน PDF/Excel
- **Command Service** - API Gateway สำหรับ CQRS command operations
### ฟีเจอร์เด่น
- **Real-time Notifications** - แจ้งเตือนผ่าน WebSocket
- **Background Processing** - งานที่กำหนดเวลาผ่าน Hangfire
- **Event-Driven Communication** - สื่อสารระหว่าง services ผ่าน RabbitMQ
- **Document Generation** - สร้างเอกสาร PDF/Excel
- **Audit Trail** - บันทึกประวัติการเปลี่ยนแปลงข้อมูลทั้งหมด
---
## API Endpoints
API ใช้ versioning และสามารถดูรายละเอียดได้ผ่าน Swagger UI:
```
/api/v1/placement # การบริจาค/สั่งย้าย/แต่งตั้ง
/api/v1/leave # ระบบลา
/api/v1/discipline # การ discipline
/api/v1/organization # โครงสร้างองค์กร
/api/v1/metadata # ข้อมูลอ้างอิง
/api/v1/retirement # การเกษียณอายุ
/api/v1/insignia # เครื่องราชอิสริยาภรณ์
/api/v1/reports # รายงาน
```
---
## Getting Started
### Prerequisites
- .NET SDK 6.0 / 7.0 / 8.0
- MySQL Server
- Keycloak (Authentication Server)
- RabbitMQ
- MinIO (Object Storage)
- Docker (สำหรับ deployment)
### Configuration
แต่ละ service มีไฟล์ `appsettings.json` สำหรับ config ดังนี้:
- **JWT Authentication** - เชื่อมต่อกับ Keycloak
- **Database Connection** - MySQL connection string
- **RabbitMQ** - Message queue connection
- **MinIO** - File storage endpoint
- **Elasticsearch** - Logging endpoint
- **Mail Server** - สำหรับส่งอีเมล
### Build & Run
```bash
# Restore dependencies
dotnet restore
# Build solution
dotnet build
# Run specific service
dotnet run --project Service/BMA.EHR.Command.Service
```
### Docker
```bash
# Build Docker image
docker build -t bma-ehr-service .
# Run container
docker run -d -p 5000:80 bma-ehr-service
```
---
## Architecture
```
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Swagger │ │ Client │ │ WebSocket │
│ UI │ │ Apps │ │ Clients │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
└────────────────────┼────────────────────┘
┌────────▼────────┐
│ API Gateway │
│ (Keycloak JWT) │
└────────┬────────┘
┌────────────────────┼────────────────────┐
│ │ │
┌──────▼──────┐ ┌────────▼──────┐ ┌────────▼──────┐
│ Placement │ │ Leave │ │ Discipline │
│ Service │ │ Service │ │ Service │
└──────┬──────┘ └────────┬──────┘ └────────┬──────┘
│ │ │
└────────────────────┼────────────────────┘
┌─────────────▼──────────────┐
│ MySQL Database │
│ (Entity Framework) │
└────────────────────────────┘
┌────────────────────────────┐
│ RabbitMQ / MinIO / │
│ Elasticsearch / Hangfire │
└────────────────────────────┘
```
---
## Dependencies ที่สำคัญ
| Package | หน้าที่ |
| --------------------- | ------------------------- |
| Entity Framework Core | ORM สำหรับ MySQL |
| Serilog | Structured Logging |
| Swashbuckle | API Documentation |
| Hangfire | Background Job Processing |
| EPPlus | สร้าง/อ่านไฟล์ Excel |
| iTextSharp | สร้างไฟล์ PDF |
| RabbitMQ.Client | Message Queue |
| NEST | Elasticsearch Client |
| ThaiBahtText | แปลงตัวเลขเป็นหน่วยบาทไทย |
| NodaTime | Date/Time Handling |
| Sentry | Error Tracking |
---
## Testing
```bash
# Run unit tests
dotnet test
# Run with coverage
dotnet test --collect:"XPlat Code Coverage"
# Run load tests (k6)
k6 run tests/load/*.js
```
---