Compare commits

...
Sign in to create a new pull request.

204 commits
main ... dev

Author SHA1 Message Date
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
adisak
678329b5df #2392 fix departmentName null
All checks were successful
Build & Deploy Insignia Service / build (push) Successful in 2m13s
2026-04-16 11:31:03 +07:00
Suphonchai Phoonsawat
057b51390e add some code
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m50s
2026-04-09 12:07:11 +07:00
harid
8950073485 ส่งรายชื่อไปออกคำสั่ง C-PM-25, C-PM-26 ให้ปั๊ม commandCode #2377
All checks were successful
Build & Deploy Discipline Service / build (push) Successful in 1m56s
2026-04-03 16:03:01 +07:00
Suphonchai Phoonsawat
bf92f6933e Refactor user profile retrieval method in InsigniaRequestController #2390
All checks were successful
Build & Deploy Insignia Service / build (push) Successful in 1m41s
2026-04-03 12:17:39 +07:00
Suphonchai Phoonsawat
cea1c4b64e Merge branch 'develop' into working
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m39s
2026-04-03 12:09:32 +07:00
Suphonchai Phoonsawat
cef41506a8 Change Amount property to nullable int in GetProfileLeaveByKeycloakDto #2411 2026-04-03 12:09:00 +07:00
bceb4d3096 update emp status resign
All checks were successful
Build & Deploy Retirement Service / build (push) Successful in 1m41s
2026-04-03 11:45:52 +07:00
adisak
06956284d7 #2381
All checks were successful
Build & Deploy Retirement Service / build (push) Successful in 2m1s
2026-04-02 17:50:18 +07:00
Suphonchai Phoonsawat
6b8eddcbc0 Add Insignia launch configuration and improve null checks in InsigniaRequestController #2388
All checks were successful
Build & Deploy Insignia Service / build (push) Successful in 1m54s
2026-04-02 11:36:59 +07:00
Suphonchai Phoonsawat
a4a5d13203 Update InsigniaRequestController to use GetOcByNodeId and include RootDnaId in insigniaNoteProfile #2390
All checks were successful
Build & Deploy Insignia Service / build (push) Successful in 2m3s
2026-04-02 11:09:36 +07:00
Suphonchai Phoonsawat
69b89dfc90 Add GetOcByNodeId method to UserProfileRepository and update InsigniaManageController to use it #2389 2026-04-02 11:00:14 +07:00
Suphonchai Phoonsawat
ea694bfda2 Fix effective date retrieval in LeaveReportController by using ProfileId instead of Id #2400
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m48s
2026-04-02 09:51:45 +07:00
Suphonchai Phoonsawat
6691303ea7 Merge branch 'working' into develop
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m52s
2026-04-01 12:35:00 +07:00
Suphonchai Phoonsawat
8ea572d46c Refactor LeaveReportController to improve duty time retrieval and handle default round logic 2026-04-01 12:30:42 +07:00
Suphonchai Phoonsawat
2ecef0792c Merge branch 'working' into develop
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m46s
2026-04-01 12:24:45 +07:00
Suphonchai Phoonsawat
1cf780ecd0 Refactor LeaveController to streamline profile retrieval and duty time handling 2026-04-01 12:24:00 +07:00
harid
bf6ea555fc fix รายงานมาสาย ข้อมูลไม่แสดง #2395
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m44s
2026-03-31 14:23:51 +07:00
Suphonchai Phoonsawat
8fa105606b Refactor LeaveProcessJobStatusRepository to filter employee records by status and ensure proper task processing
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m48s
2026-03-31 11:45:48 +07:00
Suphonchai Phoonsawat
47c0cfc62a Refactor leave remark generation logic in LeaveReportController to handle single and multi-day leave requests more accurately 2026-03-31 11:32:17 +07:00
Suphonchai Phoonsawat
932d5e75c7 Refactor LeaveProcessJobStatusRepository to update API endpoint paths and comment out JSON file writing logic 2026-03-31 11:28:12 +07:00
Suphonchai Phoonsawat
a50153f32c Refactor LeaveProcessJobStatusRepository to enhance leave remark generation logic and update file export functionality with environment path handling 2026-03-31 11:26:28 +07:00
Suphonchai Phoonsawat
d85bab11b2 Add ProcessEmpTaskAsync method to handle employee task processing and integrate with external API for attendance reporting 2026-03-31 10:20:30 +07:00
Suphonchai Phoonsawat
82c31a0f57 Refactor GenericRepository and GenericLeaveRepository to expose PostExternalAPIAsync method and enhance LeaveProcessJobStatusRepository with API integration for processing employee records 2026-03-31 10:18:06 +07:00
Suphonchai Phoonsawat
2cd7798dd9 Add admin endpoints for processing leave tasks, including retrieval, deletion, and updates 2026-03-31 09:46:44 +07:00
Suphonchai Phoonsawat
759a51ab58 Enhance LeaveProcessJobStatusRepository with detailed processing logic and add new methods in UserProfileRepository for fetching officer and employee profiles by RootDnaId 2026-03-30 15:53:33 +07:00
Suphonchai Phoonsawat
3dee5f7166 Refactor LeaveProcessJobStatusRepository methods and update Hangfire configuration for improved job processing 2026-03-30 12:08:30 +07:00
Suphonchai Phoonsawat
8732c34564 Add scheduled job to process pending jobs in LeaveProcessJobStatusRepository 2026-03-30 10:06:21 +07:00
Suphonchai Phoonsawat
91e6b1b35b Add methods to process pending jobs and update their statuses in LeaveProcessJobStatusRepository 2026-03-30 10:02:57 +07:00
Suphonchai Phoonsawat
c1ac687101 Add CreateLeaveProcessJobDto and implement CreateProcessTaskAsync in LeaveController 2026-03-30 09:52:27 +07:00
Suphonchai Phoonsawat
de1773880b Add LeaveProcessJobStatusRepository and register it in ApplicationServicesRegistration 2026-03-30 09:31:19 +07:00
Suphonchai Phoonsawat
c91e6c8030 Add migration for LeaveProcessJobStatuses table
- Created a new migration to add the LeaveProcessJobStatuses table.
- The table includes fields for job status, timestamps, user information, and error messages.
- Supports tracking of leave process job statuses with relevant metadata.
2026-03-30 09:23:13 +07:00
harid
d8f1126764 fix ส่งรายชื่อผู้ถูกพักราชการไปออกคำสั่ง #2364
All checks were successful
Build & Deploy Discipline Service / build (push) Successful in 2m7s
2026-03-27 14:31:42 +07:00
Suphonchai Phoonsawat
7ba429bb64 Refactor checkout status logic in LeaveController for improved clarity and handling of check-in dates
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m46s
2026-03-27 09:48:10 +07:00
Suphonchai Phoonsawat
3e3bfff7ba Refactor leave date overlap check in LeaveRequestController for improved readability and performance
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m51s
2026-03-26 14:10:37 +07:00
Suphonchai Phoonsawat
19b79a162d Add leave subtype name and couple day level country to leave approval response (Employee)#2366
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m47s
2026-03-26 10:33:44 +07:00
Suphonchai Phoonsawat
a09d5937f9 Add leave subtype and couple day level country to leave approval response #2366 2026-03-26 10:33:00 +07:00
Suphonchai Phoonsawat
aef81e9f4e Add support for multiple child DNA IDs in leave processing and enhance batch creation of duty time changes
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m45s
2026-03-25 15:17:54 +07:00
Suphonchai Phoonsawat
6427cb4344 Comment out probation-related leave limit checks in LeaveRequestController and update appsettings.json to disable unused database connections
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m45s
2026-03-24 09:00:06 +07:00
Suphonchai Phoonsawat
252d8b5fa3 Update SearchProfile method parameters to use string for SelectedNodeId #1555
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m40s
2026-03-23 10:40:54 +07:00
Suphonchai Phoonsawat
58aca3a328 Add SelectedNode parameter to SearchProfile method for enhanced profile retrieval #1555
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m44s
2026-03-23 10:13:13 +07:00
Suphonchai Phoonsawat
818ff38e99 Add SelectedNodeId parameter to SearchProfile method and update related DTO #1555 2026-03-23 10:04:09 +07:00
Suphonchai Phoonsawat
23bbd9791e Add CreateChangeRoundMultipleAsync method for batch processing of duty time changes #1555
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m51s
Build & Deploy Placement Service / build (push) Successful in 1m54s
Build & Deploy Insignia Service / build (push) Successful in 1m51s
Build & Deploy Discipline Service / build (push) Successful in 1m51s
2026-03-23 09:49:17 +07:00
Suphonchai Phoonsawat
7e0f0485fd Add TokenUserInfo class and extend ClaimsPrincipal with methods for user claims retrieval
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m49s
2026-03-17 15:47:03 +07:00
harid
b1df33dc20 fix bug #2183
All checks were successful
Build & Deploy Retirement Service / build (push) Successful in 1m56s
2026-03-11 14:01:34 +07:00
Suphonchai Phoonsawat
6902236f48 Add GetTimeStampHistoryAsync2 method for fiscal year timestamp retrieval
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m50s
2026-03-11 11:57:00 +07:00
Suphonchai Phoonsawat
4562029e6e Update GetTimeStampHistoryAsync call to include pagination and keyword filtering
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 2m8s
2026-03-10 14:10:35 +07:00
Suphonchai Phoonsawat
4650f7a2ab Refactor ScheduleUpdateDnaAsync to handle a list of ScheduleUpdateDnaDto and streamline profile updates #2341
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m59s
2026-02-26 20:36:48 +07:00
Suphonchai Phoonsawat
f866435897 Refactor LeaveBeginningController to simplify duplicate check and comment out LeaveYear property in EditLeaveBeginningDto #2341
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m41s
2026-02-25 16:26:28 +07:00
Suphonchai Phoonsawat
006cea048d Add ScheduleUpdateDna endpoint and DTO for updating DNA information in LeaveBeginningController
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m35s
2026-02-25 15:26:49 +07:00
Suphonchai Phoonsawat
9a74b690cd ทดสอบ
Some checks failed
Build & Deploy Leave Service / build (push) Has been cancelled
2026-02-25 10:13:27 +07:00
Suphonchai Phoonsawat
2ee36af763 Test 2026-02-24 19:43:58 +07:00
Suphonchai Phoonsawat
cd99179621 Enhance leave eligibility check in LeaveRequestController with detailed messaging 2026-02-24 10:24:24 +07:00
Suphonchai Phoonsawat
c20e1b48bd Add GetDifference method to DateTimeExtension and implement TimeCheck endpoint in LeaveRequestController
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m35s
2026-02-23 10:09:36 +07:00
Suphonchai Phoonsawat
5b054f9948 Merge branch 'develop' into working
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m50s
2026-02-20 16:33:33 +07:00
Suphonchai Phoonsawat
3e34aaa178 Refactor file upload logic in LeaveController to handle check-in scenarios more effectively
#2328
2026-02-20 16:32:57 +07:00
harid
ddb35f525a Fix #2319
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m50s
2026-02-20 13:36:50 +07:00
harid
ecca345407 รายชื่อผู้สอบผ่าน กรณี OWNER ให้เห็นรายชื่อเหมือน สกจ. #2319
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m48s
2026-02-20 10:46:15 +07:00
Suphonchai Phoonsawat
7e613ab2e6 Merge branch 'develop' into working
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m43s
2026-02-19 17:34:10 +07:00
Suphonchai Phoonsawat
7eade164e9 Update LeaveRequestController to use GetLastLeaveRequestByTypeForUserAsync2 method with CreatedAt for fetching last leave request 2026-02-19 17:33:37 +07:00
harid
256da24caf แก้ไขสิทธิ์ PARENT ให้เห็นข้อมูลทั้งหมดทุกหน่วยงาน #54
All checks were successful
Build & Deploy Discipline Service / build (push) Successful in 1m55s
Build & Deploy Insignia Service / build (push) Successful in 1m48s
Build & Deploy Leave Service / build (push) Successful in 1m49s
Build & Deploy Placement Service / build (push) Successful in 1m46s
Build & Deploy Retirement Service / build (push) Successful in 1m50s
2026-02-19 17:01:44 +07:00
Suphonchai Phoonsawat
869defcc7e Update LeaveRequestRepository to order leave requests by creation date instead of start date #2305
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m44s
2026-02-19 15:19:41 +07:00
Suphonchai Phoonsawat
65feb994ee Add GetLastLeaveRequestByTypeForUserAsync2 method and update LeaveReportController to use new method for fetching last leave request #2305
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 2m0s
2026-02-19 15:10:44 +07:00
Suphonchai Phoonsawat
d748308419 Enhance LeaveController to implement check-out logic and status validation based on last check-in record 2026-02-19 15:07:04 +07:00
Suphonchai Phoonsawat
b8df2d4024 Add NodaTime package and update LeaveRequestController to use LocalDate for date calculations
#2324
2026-02-19 14:06:59 +07:00
Suphonchai Phoonsawat
c42aaa38f6 Fix API path in UserProfileRepository to remove redundant versioning
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m45s
2026-02-19 10:11:39 +07:00
Suphonchai Phoonsawat
ddaa339e9f Refactor LeaveRequestController and LeaveReportController to use GetSumApproveLeaveTotalByTypeAndRangeForUser2 method and update fiscal year end date calculation #2305
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m41s
2026-02-18 20:24:30 +07:00
Suphonchai Phoonsawat
d70ed254c0 Enhance LeaveRequestController to restore profile checks and implement officer notification logic #2164
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m34s
2026-02-18 16:56:48 +07:00
Suphonchai Phoonsawat
de91fd0fa2 Refactor LeaveBeginningController to restore profile checks and reset leave days to zero 2026-02-18 16:47:14 +07:00
Suphonchai Phoonsawat
7d3ec6c74e Refactor ScheduleUpdateLeaveBeginningAsync to use ScheduleEditLeaveBeginningDto and remove unused profile checks 2026-02-18 16:34:35 +07:00
1b7bdd82e6 #2313
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m10s
2026-02-13 13:04:29 +07:00
Suphonchai Phoonsawat
a8271c8d79 Update leave summary calculation to use BeginningLeaveDays instead of LeaveDaysUsed #2305
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m12s
2026-02-12 20:09:05 +07:00
Suphonchai Phoonsawat
14fd9d5262 Refactor leave limit logic to use IsProbation property instead of govAge check
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m17s
2026-02-11 12:14:44 +07:00
Suphonchai Phoonsawat
c81220a049 Remove unnecessary whitespace and comment out unused code in LeaveReportController
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m18s
2026-02-11 11:56:04 +07:00
Suphonchai Phoonsawat
e5e7c77880 Add GetProfileByProfileIdNoAuthAsync method and update related controller logic 2026-02-11 11:47:49 +07:00
Suphonchai Phoonsawat
a2ac05ed61 Add BeginningLeaveDays and BeginningLeaveCount to LeaveBeginning DTOs and update controller logic #2304 #2305 2026-02-11 11:11:19 +07:00
Suphonchai Phoonsawat
2410574d42 Fix typo in IsProbatin property name and update related condition check in LeaveRequestController #2306 2026-02-11 10:47:01 +07:00
Suphonchai Phoonsawat
682c88c2db Add BeginningLeaveCount and BeginningLeaveDays to LeaveBeginnings table
- Altered LeaveDays column to update its comment.
- Added BeginningLeaveCount column to track the number of leave occurrences.
- Added BeginningLeaveDays column to store the total days of leave carried over.
2026-02-11 09:44:18 +07:00
harid
05ec0cccce Fix Bug จำนวนคนลาไม่แสดงในรายงาน #2299
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m18s
2026-02-10 15:29:35 +07:00
harid
35310f7854 Fix ประกาศเกษียณลูกจ้าง บันทึกข้อมูลวันที่ประกาศ ระบบแจ้ง Error #2260
All checks were successful
Build & Deploy Retirement Service / build (push) Successful in 1m27s
2026-02-10 12:07:32 +07:00
harid
1d8ef79373 api อัพเดทสถานะเป็นบรรจุ เปลี่ยนสิทธิ์จาก super_admin เป็น owner
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m26s
2026-02-06 10:18:47 +07:00
Suphonchai Phoonsawat
c693364fe1 Refactor LeaveReportController to use LeaveCount instead of CountLeaveDay for leave types
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m14s
2026-02-05 12:03:48 +07:00
Suphonchai Phoonsawat
4f18a97d0b Add GetOCStaffAsync method to UserProfileRepository and create GetOcStaff response models 2026-02-05 11:57:19 +07:00
Suphonchai Phoonsawat
d3cc0781cf Add UpdateLeaveCountAsync method to LeaveBeginningRepository and integrate it into leave request logic #2288
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m17s
2026-02-05 11:01:49 +07:00
Suphonchai Phoonsawat
639d41649c Add LeaveCount column to LeaveBeginnings table
- Introduced a new column 'LeaveCount' of type int to the LeaveBeginnings table.
- Set default value to 0 and added a comment for clarity in Thai: "จำนวนครั้งที่ลาสะสม".
2026-02-05 10:54:44 +07:00
Suphonchai Phoonsawat
358fd47b99 Add IsProbatin property to GetProfileByKeycloakIdDto and update leave request logic for probationary users #2266 2026-02-05 10:39:57 +07:00
harid
970319e8c2 API อัพเดทสถานะเป็นบรรจุ เฉพาะ Super_admin
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m57s
2026-02-04 11:05:02 +07:00
Suphonchai Phoonsawat
09a7208074 Update govAge calculation to use DateStart instead of DateAppoint in Leave repositories and controller
Some checks failed
Build & Deploy Leave Service / build (push) Failing after 1h5m56s
2026-02-04 10:49:13 +07:00
Suphonchai Phoonsawat
7775ea85c3 Refactor error handling in LeaveController to return appropriate error responses instead of throwing exceptions
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m18s
2026-02-04 10:32:44 +07:00
Suphonchai Phoonsawat
19000b2e42 Refactor check-out status logic to improve clarity and handle edge cases for same-day and next-day check-outs 2026-02-04 10:18:44 +07:00
Suphonchai Phoonsawat
1a0e712a1c Update leave limit logic and add GovAge property to user leave profile DTO
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m10s
2026-02-03 21:03:40 +07:00
Suphonchai Phoonsawat
c25bef0672 Update leave calculations to use DateAppoint and adjust leave limits based on government age #2266
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m35s
2026-02-03 20:46:20 +07:00
Suphonchai Phoonsawat
659e06a08d Add cancellation token support and extend timeout to 30 minutes for external API calls
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m19s
2026-01-30 13:35:58 +07:00
Suphonchai Phoonsawat
0a170fd259 Configure HttpClient to use a 10-minute timeout for long-running operations
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m14s
2026-01-30 10:09:37 +07:00
Suphonchai Phoonsawat
5c05f1123a Increase HttpClient timeout for long-running operations
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m21s
2026-01-30 09:49:54 +07:00
harid
06b53ddeaa Fix Bug Leave Report Issue #2233
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m10s
2026-01-29 15:09:14 +07:00
harid
46504c9e30 Change Call Org
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m15s
2026-01-29 13:37:38 +07:00
harid
e80f89117c Change Call Org 2026-01-29 13:22:41 +07:00
Suphonchai Phoonsawat
4c189fdc4a Fix null reference for CheckIn and CheckOut location names in LeaveController
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m21s
2026-01-29 10:07:29 +07:00
harid
90eb94cee3 กรองค้นหาข้อมูลรายการลงเวลากรณีพิเศษ #2252
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m24s
2026-01-28 16:24:59 +07:00
harid
b10ff45d07 เปลี่ยนเส้น call api สำหรับเช็ค profileId
All checks were successful
Build & Deploy Retirement Service / build (push) Successful in 1m16s
Build & Deploy Placement Service / build (push) Successful in 1m17s
Build & Deploy Discipline Service / build (push) Successful in 1m32s
2026-01-28 15:06:55 +07:00
harid
02487d91ff เปลี่ยนเส้น api สำหรับเก็บ logs
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m12s
Build & Deploy Discipline Service / build (push) Successful in 1m27s
Build & Deploy Insignia Service / build (push) Successful in 1m25s
Build & Deploy Placement Service / build (push) Successful in 1m17s
Build & Deploy Retirement Service / build (push) Successful in 1m23s
2026-01-28 13:27:25 +07:00
Suphonchai Phoonsawat
839c357842 Update LeaveController to use KeycloakUserId for profile retrieval #2253
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m38s
2026-01-28 11:47:10 +07:00
Suphonchai Phoonsawat
cbc2a1a88d Comment out profile retrieval in LeaveRequestController to prevent unnecessary calls #2250
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m29s
2026-01-27 17:05:07 +07:00
Suphonchai Phoonsawat
e8ffb82ead Comment out profile retrieval and error handling in LeaveRequestController #2250
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m16s
2026-01-27 17:00:22 +07:00
Suphonchai Phoonsawat
54c8152752 Refactor LeaveRequestController to improve filtering logic and update appsettings.json to modify connection strings
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m12s
2026-01-27 16:55:23 +07:00
Suphonchai Phoonsawat
4f28b4e9e0 Add LeaveTotal property to LeaveRequest DTOs for better leave tracking #2245
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m13s
2026-01-26 22:58:37 +07:00
Suphonchai Phoonsawat
982dfc33d1 Refactor LeaveReportController and LeaveRequestController to calculate leave days using repository methods #2246 #2247
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 11s
2026-01-26 22:35:24 +07:00
harid
2e6a81ff31 Task #2233 รายงานใบลา แสดงสำนักงาน ก.ก.
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m23s
2026-01-26 18:21:33 +07:00
Suphonchai Phoonsawat
22a7a8c17c Update LeaveController to refine check-in/check-out logic based on meeting location #2223
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m17s
2026-01-26 12:04:58 +07:00
Suphonchai Phoonsawat
c1d689ebfa Update LeaveController to adjust check-in/check-out times based on location for meetings
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m14s
2026-01-23 21:25:34 +07:00
Suphonchai Phoonsawat
ecf5ada7ed Update LeaveController to conditionally set check-out time based on existing value and duty schedule
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m14s
2026-01-23 20:55:21 +07:00
Suphonchai Phoonsawat
9bd6017ded Update LeaveController to adjust check-in/check-out times based on user duty schedule #2223
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m16s
2026-01-23 20:35:54 +07:00
Suphonchai Phoonsawat
e1c7688913 Update LeaveController to set end times based on duty schedule for check-in/check-out #2228 2026-01-23 20:27:22 +07:00
Suphonchai Phoonsawat
4e4eec3d84 Add job status check for pending or processing check-in/check-out requests
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m19s
2026-01-23 09:32:17 +07:00
Suphonchai Phoonsawat
2f366374fa Refactor user profile retrieval to use new method GetProfileByKeycloakIdNewAsync
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m12s
2026-01-22 12:43:52 +07:00
Suphonchai Phoonsawat
2b737de23b Update ElasticConfiguration URIs and IndexFormats in appsettings for Leave and Insignia
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m12s
2026-01-22 12:24:27 +07:00
Suphonchai Phoonsawat
d945deae4f Add error handling for permission API calls and enhance logging in middleware
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m15s
2026-01-22 11:58:26 +07:00
harid
d3501e831c fix กจ. ไม่เห็นรายชื่อผู้สอบผ่านหลังจากเผยแพร่โครงสร้าง Task #2219
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m11s
2026-01-21 17:00:45 +07:00
Suphonchai Phoonsawat
c3901d56b3 Merge branch 'develop' of github.com:Frappet/hrms-api-backend into develop 2026-01-20 20:55:25 +07:00
Suphonchai Phoonsawat
27b3773c79 load test script 2026-01-20 20:54:47 +07:00
harid
5219934e05 fix #2207
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m19s
2026-01-20 17:30:11 +07:00
harid
15f5d7cae7 Task #2207 กรณีคนขอโอนอยู่ในสำนักปลัดกรุงเทพมหานคร
Some checks failed
Build & Deploy Placement Service / build (push) Failing after 36s
2026-01-20 16:41:22 +07:00
Suphonchai Phoonsawat
d7b257f0ce Merge branch 'develop' into working
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m12s
Build & Deploy Checkin Service / build (push) Successful in 1m13s
2026-01-20 11:20:16 +07:00
Suphonchai Phoonsawat
e3a228773e Add scheduled job to clean up CheckIn Job Status older than 30 days 2026-01-20 11:14:30 +07:00
Suphonchai Phoonsawat
90eea1ac7f Enhance CombinedErrorHandlerAndLoggingMiddleware with caching and improved token handling 2026-01-20 11:09:13 +07:00
Suphonchai Phoonsawat
a463df5716 Add migration to create CheckInJobStatuses table for RMQ task control
- Introduced a new migration that creates the CheckInJobStatuses table.
- The table includes fields for tracking job statuses, timestamps, user information, and error messages.
- Supports various statuses such as PENDING, PROCESSING, COMPLETED, and FAILED.
2026-01-20 10:49:13 +07:00
harid
60602d99c4 api ขอโอน (admin) เพิ่มบันทึกฟิลล์ "ขอโอนตั้งแต่วันที่" #2196
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m23s
2026-01-20 09:54:15 +07:00
harid
9442d3b29f fix #2173
All checks were successful
Build & Deploy Retirement Service / build (push) Successful in 1m15s
2026-01-20 09:32:10 +07:00
Suphonchai Phoonsawat
3532df32fd Refactor message consumption to start after 8:10 AM and implement time checks for operating hours
All checks were successful
Build & Deploy Checkin Service / build (push) Successful in 1m20s
2026-01-20 05:38:42 +07:00
Suphonchai Phoonsawat
6c8e79b1bc Merge branch 'develop' into working
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m9s
Build & Deploy Checkin Service / build (push) Successful in 2m4s
2026-01-19 22:43:24 +07:00
Suphonchai Phoonsawat
0ab75b2a19 Add delay to start message consumption until 8:10 AM and implement time calculation 2026-01-19 22:42:39 +07:00
harid
1aab307f6a fix รายการให้ออกลูกจ้าง ระบบใช้สิทธิ์ API เส้นเดียวกับของขรก. #2173
All checks were successful
Build & Deploy Retirement Service / build (push) Successful in 1m21s
2026-01-19 18:17:11 +07:00
harid
93a83b34e6 ปรับแบบใบขอยกเลิกวันลา
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m17s
2026-01-19 17:15:17 +07:00
Suphonchai Phoonsawat
79e0fe7f1b Merge branch 'develop' into working
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m20s
2026-01-19 14:28:29 +07:00
Suphonchai Phoonsawat
21f82d69e1 Add load testing script for simulating 30,000 requests over 10 minutes 2026-01-19 14:27:43 +07:00
harid
b5c82f4243 ปรับรายงานลา #2195
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m14s
2026-01-19 12:13:45 +07:00
Suphonchai Phoonsawat
b0715e3da6 refactor LeaveController to update check-out status logic based on morning end time #2187
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m12s
2026-01-16 16:19:49 +07:00
Suphonchai Phoonsawat
510f1cd78a fix sort order condition
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m15s
2026-01-16 16:09:23 +07:00
Suphonchai Phoonsawat
83a915f92c refactor LeaveReportController to sort employees by check-in time and remark #2193 2026-01-16 16:01:07 +07:00
harid
094789bfb1 API ขอลาออกและโอนออก ฝั่ง admin #2196
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m22s
Build & Deploy Retirement Service / build (push) Successful in 1m23s
2026-01-16 15:43:55 +07:00
harid
64c1021c52 Migrate เก็บฟิลด์ใช้แสดงในรายงานลาเพิ่ม #2195
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m18s
2026-01-16 09:25:22 +07:00
Suphonchai Phoonsawat
5ec7933b3c refactor LeaveController to correct end time parsing for seminar handling #2199
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m11s
2026-01-14 16:51:21 +07:00
Suphonchai Phoonsawat
9e529ed19b refactor LeaveController to update checkout-check endpoint to accept string for seminar handling #2199 2026-01-14 16:43:35 +07:00
Suphonchai Phoonsawat
3e8c3d998e refactor LeaveController to modify checkout-check endpoint for seminar handling #2199 2026-01-14 16:42:36 +07:00
Suphonchai Phoonsawat
3a6e4501fd refactor LeaveController to update start and end time logic for check-in and check-out #2199 2026-01-14 16:38:55 +07:00
Suphonchai Phoonsawat
5415019b3c refactor LeaveController to update image URL handling for check-in and check-out #2188
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m12s
2026-01-14 10:25:31 +07:00
Suphonchai Phoonsawat
86790cf9f3 refactor LeaveReportController to enhance employee sorting by remark and check-in/check-out times #2193
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m16s
2026-01-14 10:16:42 +07:00
Suphonchai Phoonsawat
127909d29d Merge branch 'develop' into working
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m23s
2026-01-13 17:02:31 +07:00
Suphonchai Phoonsawat
6907607a06 refactor LeaveController to improve sorting and pagination logic #2192 2026-01-13 17:01:20 +07:00
harid
0233d92931 Change function call Org Service
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m12s
2026-01-13 12:05:52 +07:00
harid
90ea986831 fix รายงานใบลา ข้อมูลแสดงในรายงานแสดงไม่ครบ #2184
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m24s
2026-01-12 17:23:08 +07:00
Suphonchai Phoonsawat
1c3ce46bcb update LeaveController to determine status based on leave request and range
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m12s
#2187
2026-01-12 13:41:23 +07:00
398679cbcc rollback
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m15s
2026-01-11 21:10:24 +07:00
d831b208de Merge branch 'develop' into dev
* develop:
  update LeaveController to handle additional leave range options for check-in and check-out statuses
  update LeaveController to pass currentDate parameter to GetLastEffectRound method
  update LeaveReportController to pass date parameter to GetLastEffectRound method
  update GetLastEffectRound method to accept effectiveDate parameter and adjust usage in LeaveReportController
  add noti discord
  fix build report
  fix build report to v2 and add noti
  remove build forgejo, move to dev branch
2026-01-11 20:39:39 +07:00
kittapath
49cb60dee7 report leave api4
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m9s
2026-01-11 16:35:02 +07:00
kittapath
5cdef791f3 report leave 2026-01-11 15:17:02 +07:00
Suphonchai Phoonsawat
7b97cd09a3 update LeaveController to handle additional leave range options for check-in and check-out statuses
Some checks failed
DockerHub Release - Leave Service / release-to-dockerhub (push) Failing after 12s
2026-01-09 20:24:50 +07:00
Suphonchai Phoonsawat
3f13557b31 update LeaveController to pass currentDate parameter to GetLastEffectRound method 2026-01-09 19:11:52 +07:00
Suphonchai Phoonsawat
95cd49ecbc update LeaveReportController to pass date parameter to GetLastEffectRound method 2026-01-09 19:10:16 +07:00
kittapath
6e531e4d16 export report leave
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m8s
2026-01-09 18:59:11 +07:00
Suphonchai Phoonsawat
99accd44e3 update GetLastEffectRound method to accept effectiveDate parameter and adjust usage in LeaveReportController 2026-01-09 18:57:24 +07:00
a540912202 add noti discord 2026-01-09 18:43:59 +07:00
0690337422 fix build report
Some checks failed
DockerHub Release - Report Service / release-to-dockerhub (push) Failing after 10s
2026-01-09 16:35:33 +07:00
8c1a219084 fix build report to v2 and add noti
Some checks failed
DockerHub Release - Report Service / release-to-dockerhub (push) Failing after 12s
2026-01-09 16:29:43 +07:00
9dfd5efafc Merge branch 'develop'
Some checks failed
DockerHub Release - Leave Service / release-to-dockerhub (push) Failing after 11s
* develop:
  remove workflow integration from LeaveRequestController #2164
2026-01-09 11:05:53 +07:00
98adc74792 Merge branch 'develop' into dev
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m11s
* develop:
  remove workflow integration from LeaveRequestController #2164
2026-01-09 11:03:53 +07:00
Suphonchai Phoonsawat
14eb80a30e Merge branch 'develop' into working 2026-01-07 12:07:21 +07:00
Suphonchai Phoonsawat
e8dfe976a2 remove workflow integration from LeaveRequestController #2164 2026-01-07 12:06:49 +07:00
8f491c1b12 Merge branch 'main' of ssh://hrms-git.bangkok.go.th:6611/BMA-HRMS/hrms-api-backend
* 'main' of ssh://hrms-git.bangkok.go.th:6611/BMA-HRMS/hrms-api-backend:
2026-01-07 11:28:51 +07:00
a6de2791c4 Merge branch 'develop'
* develop:
  #2150
  fix รันเครื่องราชแต่หน่วยงานไม่ครบ #1831
  ปรับ query แบบใบขอยกเลิกวันลา
  find org v2
  change to v2
2026-01-07 11:28:39 +07:00
a739d52c46 Merge branch 'develop' into dev
All checks were successful
Build & Deploy Placement Service / build (push) Successful in 1m9s
2026-01-05 16:39:57 +07:00
4865bab7e2 Merge branch 'develop' into adiDev 2026-01-05 16:39:14 +07:00
f9ca9b52af #2150 2026-01-05 16:38:30 +07:00
harid
be57524f1b Merge branch 'develop' into dev
All checks were successful
Build & Deploy Insignia Service / build (push) Successful in 1m10s
2026-01-05 16:22:05 +07:00
harid
517755d758 fix รันเครื่องราชแต่หน่วยงานไม่ครบ #1831 2026-01-05 16:12:11 +07:00
harid
05689b5338 ปรับ query แบบใบขอยกเลิกวันลา 2026-01-05 13:20:00 +07:00
kittapath
a381ff1528 Merge branch 'develop'
Some checks failed
DockerHub Release - Leave Service / release-to-dockerhub (push) Failing after 11s
2026-01-02 21:57:09 +07:00
kittapath
ed04bf8258 Merge branch 'develop' into dev 2026-01-02 21:47:40 +07:00
kittapath
909042445c find org v2
Some checks failed
release-dev / release-dev (push) Failing after 12s
2026-01-02 21:47:26 +07:00
kittapath
cb074e3e25 find org v2 2026-01-02 21:46:39 +07:00
kittapath
7e71f528ab Merge branch 'develop' into dev
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m6s
2026-01-02 10:22:13 +07:00
kittapath
adb8e31308 Merge branch 'develop'
Some checks failed
DockerHub Release - Leave Service / release-to-dockerhub (push) Failing after 12s
2025-12-31 10:08:06 +07:00
kittapath
0b0cc53e07 change to v2 2025-12-31 10:07:01 +07:00
72b9c6e31e remove build forgejo, move to dev branch
Some checks failed
DockerHub Release - Leave Service / release-to-dockerhub (push) Failing after 15s
2025-12-31 08:02:16 +07:00
9fe3c82a9b Merge branch 'develop'
All checks were successful
Build & Deploy Leave Service / build (push) Successful in 1m24s
* develop:
  leave v2
2025-12-31 07:45:50 +07:00
kittapath
8973e1b78f leave v2 2025-12-30 23:55:03 +07:00
Suphonchai Phoonsawat
4a94aae6e3 Merge branch 'develop' 2025-12-26 09:58:00 +07:00
Suphonchai Phoonsawat
7caf1cc8d6 add date range parameters to GetProfileByAdminRole and GetEmployeeByAdminRole methods 2025-12-26 09:51:51 +07:00
c87e467a6e change build to docker hub 2025-12-23 23:32:02 +07:00
d10c86e0cb add: build docker hub 2025-12-23 21:50:04 +07:00
2b1d6852c9 add build placement 2025-12-23 21:18:04 +07:00
bde1aa21c9 test build leave 2025-12-23 21:09:15 +07:00
8bb31b4e73 updated deploy 2025-12-23 20:30:37 +07:00
804a4cf85f add: build leave 2025-12-23 20:21:25 +07:00
117 changed files with 17283 additions and 2595 deletions

View file

@ -1,61 +0,0 @@
# name: Build
# on:
# push:
# tags:
# - "command-dev[0-9]+.[0-9]+.[0-9]+"
# - "command-dev[0-9]+.[0-9]+.[0-9]+*"
# workflow_dispatch:
# env:
# REGISTRY: ${{ vars.CONTAINER_REGISTRY }}
# REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }}
# REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
# CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }}
# IMAGE_VERSION: build
# DISCORD_WEBHOOK: ${{ vars.DISCORD_WEBHOOK }}
# jobs:
# build:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout repository
# uses: actions/checkout@v4
# - name: Set up Docker Buildx
# uses: docker/setup-buildx-action@v2
# with:
# config-inline: |
# [registry."${{ env.REGISTRY }}"]
# ca=["/etc/ssl/certs/ca-certificates.crt"]
# - name: Tag Version
# run: |
# if [[ "${{ github.event_name }}" == "push" ]]; then
# echo "IMAGE_VERSION=${{ github.ref_name }}" | sed 's/command-dev//g' >> $GITHUB_ENV
# else
# echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV
# fi
# - name: Login in to registry
# uses: docker/login-action@v2
# with:
# registry: ${{ env.REGISTRY }}
# username: ${{ env.REGISTRY_USERNAME }}
# password: ${{ env.REGISTRY_PASSWORD }}
# - name: Build and push docker image
# uses: docker/build-push-action@v3
# with:
# platforms: linux/amd64
# context: .
# file: ./BMA.EHR.Command.Service/Dockerfile
# tags: ${{ env.CONTAINER_IMAGE_NAME }}/hrms-api-command:latest,${{ env.CONTAINER_IMAGE_NAME }}/hrms-api-command:${{ env.IMAGE_VERSION }}
# push: true
# - name: Remote Deploy
# uses: appleboy/ssh-action@v1.2.1
# with:
# host: ${{ vars.SSH_DEPLOY_HOST }}
# port: ${{ vars.SSH_DEPLOY_PORT }}
# username: ${{ secrets.SSH_DEPLOY_USER }}
# password: ${{ secrets.SSH_DEPLOY_PASSWORD }}
# script: |
# cd ~/repo
# ./replace-env.sh API_COMMAND "${{ env.IMAGE_VERSION }}"
# ./deploy.sh hrms-api-command

View file

@ -1,84 +0,0 @@
# name: Build
# on:
# push:
# tags:
# - "report-dev[0-9]+.[0-9]+.[0-9]+"
# - "report-dev[0-9]+.[0-9]+.[0-9]+*"
# workflow_dispatch:
# env:
# REGISTRY: ${{ vars.CONTAINER_REGISTRY }}
# REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }}
# REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
# CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }}
# SERVICE_NAME: hrms-api-report
# IMAGE_VERSION: build
# DISCORD_WEBHOOK: ${{ vars.DISCORD_WEBHOOK }}
# jobs:
# build:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout repository
# uses: actions/checkout@v4
# - name: Set up Docker Buildx
# uses: docker/setup-buildx-action@v2
# with:
# config-inline: |
# [registry."${{ env.REGISTRY }}"]
# ca=["/etc/ssl/certs/ca-certificates.crt"]
# - name: Tag Version
# run: |
# if [[ "${{ github.event_name }}" == "push" ]]; then
# echo "IMAGE_VERSION=${{ github.ref_name }}" | sed 's/report-dev//g' >> $GITHUB_ENV
# else
# echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV
# fi
# - name: Login in to registry
# uses: docker/login-action@v2
# with:
# registry: ${{ env.REGISTRY }}
# username: ${{ env.REGISTRY_USERNAME }}
# password: ${{ env.REGISTRY_PASSWORD }}
# - name: Build and push docker image
# uses: docker/build-push-action@v3
# with:
# platforms: linux/amd64
# context: .
# file: ./BMA.EHR.Report.Service/Dockerfile
# tags: ${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:latest,${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:${{ env.IMAGE_VERSION }}
# push: true
# - name: Remote Deploy
# uses: appleboy/ssh-action@v1.2.1
# with:
# host: ${{ vars.SSH_DEPLOY_HOST }}
# port: ${{ vars.SSH_DEPLOY_PORT }}
# username: ${{ secrets.SSH_DEPLOY_USER }}
# password: ${{ secrets.SSH_DEPLOY_PASSWORD }}
# script: |
# cd ~/repo
# ./replace-env.sh API_REPORT "${{ env.IMAGE_VERSION }}"
# ./deploy.sh ${{ env.SERVICE_NAME }}
# - name: Discord Notification
# if: always()
# run: |
# STATUS="${{ job.status == 'success' && '✅ Success' || '❌ Failed' }}"
# COLOR="${{ job.status == 'success' && '3066993' || '15158332' }}"
# TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
# curl -H "Content-Type: application/json" \
# -X POST \
# -d "{
# \"embeds\": [{
# \"title\": \"$STATUS\",
# \"description\": \"**Build & Deploy**\\n- Image: \`${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}\`\\n- Version: \`${{ env.IMAGE_VERSION }}\`\\n- By: \`${{ github.actor }}\`\",
# \"color\": $COLOR,
# \"footer\": {
# \"text\": \"Release Notification\",
# \"icon_url\": \"https://example.com/success-icon.png\"
# },
# \"timestamp\": \"$TIMESTAMP\"
# }]
# }" \
# ${{ env.DISCORD_WEBHOOK }}

View file

@ -1,49 +0,0 @@
name: Build
on:
push:
tags:
- "leave[0-9]+.[0-9]+.[0-9]+"
- "leave[0-9]+.[0-9]+.[0-9]+*"
workflow_dispatch:
env:
REGISTRY: ${{ vars.CONTAINER_REGISTRY }}
REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }}
REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }}
IMAGE_VERSION: build
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
config-inline: |
[registry."${{ env.REGISTRY }}"]
ca=["/etc/ssl/certs/ca-certificates.crt"]
- name: Tag Version
run: |
if [[ "${{ github.event_name }}" == "push" ]]; then
echo "IMAGE_VERSION=${{ github.ref_name }}" | sed 's/leave//g' >> $GITHUB_ENV
else
echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV
fi
- name: Login in to registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ env.REGISTRY_USERNAME }}
password: ${{ env.REGISTRY_PASSWORD }}
- name: Build and push docker image
uses: docker/build-push-action@v3
with:
platforms: linux/amd64
context: .
file: ./BMA.EHR.Leave/Dockerfile
tags: ${{ env.CONTAINER_IMAGE_NAME }}/hrms-leave:latest,${{ env.CONTAINER_IMAGE_NAME }}/hrms-leave:${{ env.IMAGE_VERSION }}
push: true

View file

@ -1,29 +0,0 @@
name: Build
on:
workflow_dispatch:
inputs:
version:
description: "Version to deploy"
type: string
required: false
default: "latest"
env:
IMAGE_VERSION: build
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Remote Deploy
uses: appleboy/ssh-action@v1.2.1
with:
host: ${{ vars.SSH_DEPLOY_HOST }}
port: ${{ vars.SSH_DEPLOY_PORT }}
username: ${{ secrets.SSH_DEPLOY_USER }}
password: ${{ secrets.SSH_DEPLOY_PASSWORD }}
script: |
cd ~/repo
./replace-env.sh API_LEAVE "${{ inputs.version }}"
./deploy.sh hrms-api-leave

View file

@ -0,0 +1,101 @@
name: DockerHub Release - CheckIn Consumer
run-name: DockerHub Release - CheckIn Consumer by ${{ github.actor }}
on:
push:
tags:
- "checkin-[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
inputs:
IMAGE_VER:
description: "Image version (e.g., latest, v1.0.0)"
required: false
default: "latest"
env:
DOCKERHUB_REGISTRY: docker.io
IMAGE_NAME: hrms-api-checkin
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
jobs:
release-to-dockerhub:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set output tags
id: vars
run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- name: Generate version
id: gen_ver
run: |
if [[ $GITHUB_REF == 'refs/tags/'* ]]; then
IMAGE_VER=${{ steps.vars.outputs.tag }}
else
IMAGE_VER=${{ github.event.inputs.IMAGE_VER }}
fi
if [[ $IMAGE_VER == '' ]]; then
IMAGE_VER='test-vBeta'
fi
echo "image_ver=${IMAGE_VER}" >> $GITHUB_OUTPUT
echo "Generated version: ${IMAGE_VER}"
- name: Display version
run: |
echo "Git Ref: $GITHUB_REF"
echo "Image Version: ${{ steps.gen_ver.outputs.image_ver }}"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
registry: ${{env.DOCKERHUB_REGISTRY}}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: BMA.EHR.CheckInConsumer/Dockerfile
platforms: linux/amd64
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.gen_ver.outputs.image_ver }}
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest
labels: |
org.opencontainers.image.title=BMA EHR CheckIn Consumer
org.opencontainers.image.description=HRMS CheckIn Consumer Service
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Notify Discord on success
if: success()
env:
IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }}
run: |
TAG_INFO="Tag: ${IMAGE_VER:-unknown}"
REF_INFO="Ref: ${GITHUB_REF}"
ACTOR_INFO="Actor: ${GITHUB_ACTOR}"
MSG="✅ DockerHub release succeeded\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}"
curl -s -H "Content-Type: application/json" \
-X POST \
-d "{\"content\":\"${MSG}\"}" \
"$DISCORD_WEBHOOK"
- name: Notify Discord on failure
if: failure()
env:
IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }}
run: |
TAG_INFO="Tag: ${IMAGE_VER:-unknown}"
REF_INFO="Ref: ${GITHUB_REF}"
ACTOR_INFO="Actor: ${GITHUB_ACTOR}"
MSG="❌ DockerHub release failed\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}"
curl -s -H "Content-Type: application/json" \
-X POST \
-d "{\"content\":\"${MSG}\"}" \
"$DISCORD_WEBHOOK"

View file

@ -0,0 +1,101 @@
name: DockerHub Release - Command Service
run-name: DockerHub Release - Command Service by ${{ github.actor }}
on:
push:
tags:
- "command-[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
inputs:
IMAGE_VER:
description: "Image version (e.g., latest, v1.0.0)"
required: false
default: "latest"
env:
DOCKERHUB_REGISTRY: docker.io
IMAGE_NAME: hrms-api-command
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
jobs:
release-to-dockerhub:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set output tags
id: vars
run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- name: Generate version
id: gen_ver
run: |
if [[ $GITHUB_REF == 'refs/tags/'* ]]; then
IMAGE_VER=${{ steps.vars.outputs.tag }}
else
IMAGE_VER=${{ github.event.inputs.IMAGE_VER }}
fi
if [[ $IMAGE_VER == '' ]]; then
IMAGE_VER='test-vBeta'
fi
echo "image_ver=${IMAGE_VER}" >> $GITHUB_OUTPUT
echo "Generated version: ${IMAGE_VER}"
- name: Display version
run: |
echo "Git Ref: $GITHUB_REF"
echo "Image Version: ${{ steps.gen_ver.outputs.image_ver }}"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
registry: ${{env.DOCKERHUB_REGISTRY}}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: BMA.EHR.Command.Service/Dockerfile
platforms: linux/amd64
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.gen_ver.outputs.image_ver }}
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest
labels: |
org.opencontainers.image.title=BMA EHR Command Service
org.opencontainers.image.description=HRMS Command API Service
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Notify Discord on success
if: success()
env:
IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }}
run: |
TAG_INFO="Tag: ${IMAGE_VER:-unknown}"
REF_INFO="Ref: ${GITHUB_REF}"
ACTOR_INFO="Actor: ${GITHUB_ACTOR}"
MSG="✅ DockerHub release succeeded\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}"
curl -s -H "Content-Type: application/json" \
-X POST \
-d "{\"content\":\"${MSG}\"}" \
"$DISCORD_WEBHOOK"
- name: Notify Discord on failure
if: failure()
env:
IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }}
run: |
TAG_INFO="Tag: ${IMAGE_VER:-unknown}"
REF_INFO="Ref: ${GITHUB_REF}"
ACTOR_INFO="Actor: ${GITHUB_ACTOR}"
MSG="❌ DockerHub release failed\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}"
curl -s -H "Content-Type: application/json" \
-X POST \
-d "{\"content\":\"${MSG}\"}" \
"$DISCORD_WEBHOOK"

View file

@ -0,0 +1,101 @@
name: DockerHub Release - Discipline Service
run-name: DockerHub Release - Discipline Service by ${{ github.actor }}
on:
push:
tags:
- "discipline-[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
inputs:
IMAGE_VER:
description: "Image version (e.g., latest, v1.0.0)"
required: false
default: "latest"
env:
DOCKERHUB_REGISTRY: docker.io
IMAGE_NAME: hrms-api-discipline
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
jobs:
release-to-dockerhub:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set output tags
id: vars
run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- name: Generate version
id: gen_ver
run: |
if [[ $GITHUB_REF == 'refs/tags/'* ]]; then
IMAGE_VER=${{ steps.vars.outputs.tag }}
else
IMAGE_VER=${{ github.event.inputs.IMAGE_VER }}
fi
if [[ $IMAGE_VER == '' ]]; then
IMAGE_VER='test-vBeta'
fi
echo "image_ver=${IMAGE_VER}" >> $GITHUB_OUTPUT
echo "Generated version: ${IMAGE_VER}"
- name: Display version
run: |
echo "Git Ref: $GITHUB_REF"
echo "Image Version: ${{ steps.gen_ver.outputs.image_ver }}"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
registry: ${{env.DOCKERHUB_REGISTRY}}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: BMA.EHR.Discipline.Service/Dockerfile
platforms: linux/amd64
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.gen_ver.outputs.image_ver }}
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest
labels: |
org.opencontainers.image.title=BMA EHR Discipline Service
org.opencontainers.image.description=HRMS Discipline API Service
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Notify Discord on success
if: success()
env:
IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }}
run: |
TAG_INFO="Tag: ${IMAGE_VER:-unknown}"
REF_INFO="Ref: ${GITHUB_REF}"
ACTOR_INFO="Actor: ${GITHUB_ACTOR}"
MSG="✅ DockerHub release succeeded\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}"
curl -s -H "Content-Type: application/json" \
-X POST \
-d "{\"content\":\"${MSG}\"}" \
"$DISCORD_WEBHOOK"
- name: Notify Discord on failure
if: failure()
env:
IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }}
run: |
TAG_INFO="Tag: ${IMAGE_VER:-unknown}"
REF_INFO="Ref: ${GITHUB_REF}"
ACTOR_INFO="Actor: ${GITHUB_ACTOR}"
MSG="❌ DockerHub release failed\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}"
curl -s -H "Content-Type: application/json" \
-X POST \
-d "{\"content\":\"${MSG}\"}" \
"$DISCORD_WEBHOOK"

View file

@ -0,0 +1,101 @@
name: DockerHub Release - Insignia Service
run-name: DockerHub Release - Insignia Service by ${{ github.actor }}
on:
push:
tags:
- "insignia-[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
inputs:
IMAGE_VER:
description: "Image version (e.g., latest, v1.0.0)"
required: false
default: "latest"
env:
DOCKERHUB_REGISTRY: docker.io
IMAGE_NAME: hrms-api-insignia
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
jobs:
release-to-dockerhub:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set output tags
id: vars
run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- name: Generate version
id: gen_ver
run: |
if [[ $GITHUB_REF == 'refs/tags/'* ]]; then
IMAGE_VER=${{ steps.vars.outputs.tag }}
else
IMAGE_VER=${{ github.event.inputs.IMAGE_VER }}
fi
if [[ $IMAGE_VER == '' ]]; then
IMAGE_VER='test-vBeta'
fi
echo "image_ver=${IMAGE_VER}" >> $GITHUB_OUTPUT
echo "Generated version: ${IMAGE_VER}"
- name: Display version
run: |
echo "Git Ref: $GITHUB_REF"
echo "Image Version: ${{ steps.gen_ver.outputs.image_ver }}"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
registry: ${{env.DOCKERHUB_REGISTRY}}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: BMA.EHR.Insignia/Dockerfile
platforms: linux/amd64
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.gen_ver.outputs.image_ver }}
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest
labels: |
org.opencontainers.image.title=BMA EHR Insignia Service
org.opencontainers.image.description=HRMS Insignia API Service
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Notify Discord on success
if: success()
env:
IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }}
run: |
TAG_INFO="Tag: ${IMAGE_VER:-unknown}"
REF_INFO="Ref: ${GITHUB_REF}"
ACTOR_INFO="Actor: ${GITHUB_ACTOR}"
MSG="✅ DockerHub release succeeded\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}"
curl -s -H "Content-Type: application/json" \
-X POST \
-d "{\"content\":\"${MSG}\"}" \
"$DISCORD_WEBHOOK"
- name: Notify Discord on failure
if: failure()
env:
IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }}
run: |
TAG_INFO="Tag: ${IMAGE_VER:-unknown}"
REF_INFO="Ref: ${GITHUB_REF}"
ACTOR_INFO="Actor: ${GITHUB_ACTOR}"
MSG="❌ DockerHub release failed\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}"
curl -s -H "Content-Type: application/json" \
-X POST \
-d "{\"content\":\"${MSG}\"}" \
"$DISCORD_WEBHOOK"

View file

@ -0,0 +1,119 @@
name: DockerHub Release - Leave Service
run-name: DockerHub Release - Leave Service by ${{ github.actor }}
on:
push:
tags:
- "leave-[0-9]+.[0-9]+.[0-9]+"
# branches:
# - main
# - develop
workflow_dispatch:
inputs:
IMAGE_VER:
description: "Image version (e.g., latest, v1.0.0)"
required: false
default: "latest"
env:
DOCKERHUB_REGISTRY: docker.io
IMAGE_NAME: hrms-api-leave
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
jobs:
release-to-dockerhub:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set output tags
id: vars
run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- name: Generate version
id: gen_ver
run: |
if [[ $GITHUB_REF == 'refs/tags/'* ]]; then
IMAGE_VER=${{ steps.vars.outputs.tag }}
elif [[ $GITHUB_REF == 'refs/heads/'* ]]; then
BRANCH_NAME=${GITHUB_REF#refs/heads/}
IMAGE_VER="${BRANCH_NAME}-latest"
else
IMAGE_VER=${{ github.event.inputs.IMAGE_VER }}
fi
if [[ $IMAGE_VER == '' ]]; then
IMAGE_VER='test-vBeta'
fi
echo "image_ver=${IMAGE_VER}" >> $GITHUB_OUTPUT
echo "Generated version: ${IMAGE_VER}"
- name: Display version
run: |
echo "Git Ref: $GITHUB_REF"
echo "Image Version: ${{ steps.gen_ver.outputs.image_ver }}"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
registry: ${{env.DOCKERHUB_REGISTRY}}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=tag
type=ref,event=branch
type=raw,value=${{ steps.gen_ver.outputs.image_ver }}
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: BMA.EHR.Leave/Dockerfile
platforms: linux/amd64
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.gen_ver.outputs.image_ver }}
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Image digest
run: echo "Image pushed with digest ${{ steps.build.outputs.digest }}"
- name: Notify Discord on success
if: success()
env:
IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }}
run: |
TAG_INFO="Tag: ${IMAGE_VER:-unknown}"
REF_INFO="Ref: ${GITHUB_REF}"
ACTOR_INFO="Actor: ${GITHUB_ACTOR}"
MSG="✅ DockerHub release succeeded\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}"
curl -s -H "Content-Type: application/json" \
-X POST \
-d "{\"content\":\"${MSG}\"}" \
"$DISCORD_WEBHOOK"
- name: Notify Discord on failure
if: failure()
env:
IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }}
run: |
TAG_INFO="Tag: ${IMAGE_VER:-unknown}"
REF_INFO="Ref: ${GITHUB_REF}"
ACTOR_INFO="Actor: ${GITHUB_ACTOR}"
MSG="❌ DockerHub release failed\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}"
curl -s -H "Content-Type: application/json" \
-X POST \
-d "{\"content\":\"${MSG}\"}" \
"$DISCORD_WEBHOOK"

View file

@ -0,0 +1,101 @@
name: DockerHub Release - Placement Service
run-name: DockerHub Release - Placement Service by ${{ github.actor }}
on:
push:
tags:
- "placement-[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
inputs:
IMAGE_VER:
description: "Image version (e.g., latest, v1.0.0)"
required: false
default: "latest"
env:
DOCKERHUB_REGISTRY: docker.io
IMAGE_NAME: hrms-api-placement
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
jobs:
release-to-dockerhub:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set output tags
id: vars
run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- name: Generate version
id: gen_ver
run: |
if [[ $GITHUB_REF == 'refs/tags/'* ]]; then
IMAGE_VER=${{ steps.vars.outputs.tag }}
else
IMAGE_VER=${{ github.event.inputs.IMAGE_VER }}
fi
if [[ $IMAGE_VER == '' ]]; then
IMAGE_VER='test-vBeta'
fi
echo "image_ver=${IMAGE_VER}" >> $GITHUB_OUTPUT
echo "Generated version: ${IMAGE_VER}"
- name: Display version
run: |
echo "Git Ref: $GITHUB_REF"
echo "Image Version: ${{ steps.gen_ver.outputs.image_ver }}"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
registry: ${{env.DOCKERHUB_REGISTRY}}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: BMA.EHR.Placement.Service/Dockerfile
platforms: linux/amd64
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.gen_ver.outputs.image_ver }}
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest
labels: |
org.opencontainers.image.title=BMA EHR Placement Service
org.opencontainers.image.description=HRMS Placement API Service
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Notify Discord on success
if: success()
env:
IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }}
run: |
TAG_INFO="Tag: ${IMAGE_VER:-unknown}"
REF_INFO="Ref: ${GITHUB_REF}"
ACTOR_INFO="Actor: ${GITHUB_ACTOR}"
MSG="✅ DockerHub release succeeded\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}"
curl -s -H "Content-Type: application/json" \
-X POST \
-d "{\"content\":\"${MSG}\"}" \
"$DISCORD_WEBHOOK"
- name: Notify Discord on failure
if: failure()
env:
IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }}
run: |
TAG_INFO="Tag: ${IMAGE_VER:-unknown}"
REF_INFO="Ref: ${GITHUB_REF}"
ACTOR_INFO="Actor: ${GITHUB_ACTOR}"
MSG="❌ DockerHub release failed\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}"
curl -s -H "Content-Type: application/json" \
-X POST \
-d "{\"content\":\"${MSG}\"}" \
"$DISCORD_WEBHOOK"

View file

@ -0,0 +1,101 @@
name: DockerHub Release - Report Service
run-name: DockerHub Release - Report Service by ${{ github.actor }}
on:
push:
tags:
- "reportv2-[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
inputs:
IMAGE_VER:
description: "Image version (e.g., latest, v1.0.0)"
required: false
default: "latest"
env:
DOCKERHUB_REGISTRY: docker.io
IMAGE_NAME: hrms-api-reportv2
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
jobs:
release-to-dockerhub:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set output tags
id: vars
run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- name: Generate version
id: gen_ver
run: |
if [[ $GITHUB_REF == 'refs/tags/'* ]]; then
IMAGE_VER=${{ steps.vars.outputs.tag }}
else
IMAGE_VER=${{ github.event.inputs.IMAGE_VER }}
fi
if [[ $IMAGE_VER == '' ]]; then
IMAGE_VER='test-vBeta'
fi
echo "image_ver=${IMAGE_VER}" >> $GITHUB_OUTPUT
echo "Generated version: ${IMAGE_VER}"
- name: Display version
run: |
echo "Git Ref: $GITHUB_REF"
echo "Image Version: ${{ steps.gen_ver.outputs.image_ver }}"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
registry: ${{env.DOCKERHUB_REGISTRY}}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: BMA.EHR.Report.Service/Dockerfile
platforms: linux/amd64
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.gen_ver.outputs.image_ver }}
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest
labels: |
org.opencontainers.image.title=BMA EHR Report Service
org.opencontainers.image.description=HRMS Report API Service
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Notify Discord on success
if: success()
env:
IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }}
run: |
TAG_INFO="Tag: ${IMAGE_VER:-unknown}"
REF_INFO="Ref: ${GITHUB_REF}"
ACTOR_INFO="Actor: ${GITHUB_ACTOR}"
MSG="✅ DockerHub release succeeded\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}"
curl -s -H "Content-Type: application/json" \
-X POST \
-d "{\"content\":\"${MSG}\"}" \
"$DISCORD_WEBHOOK"
- name: Notify Discord on failure
if: failure()
env:
IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }}
run: |
TAG_INFO="Tag: ${IMAGE_VER:-unknown}"
REF_INFO="Ref: ${GITHUB_REF}"
ACTOR_INFO="Actor: ${GITHUB_ACTOR}"
MSG="❌ DockerHub release failed\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}"
curl -s -H "Content-Type: application/json" \
-X POST \
-d "{\"content\":\"${MSG}\"}" \
"$DISCORD_WEBHOOK"

View file

@ -0,0 +1,101 @@
name: DockerHub Release - Retirement Service
run-name: DockerHub Release - Retirement Service by ${{ github.actor }}
on:
push:
tags:
- "retirement-[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
inputs:
IMAGE_VER:
description: "Image version (e.g., latest, v1.0.0)"
required: false
default: "latest"
env:
DOCKERHUB_REGISTRY: docker.io
IMAGE_NAME: hrms-api-retirement
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
jobs:
release-to-dockerhub:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set output tags
id: vars
run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- name: Generate version
id: gen_ver
run: |
if [[ $GITHUB_REF == 'refs/tags/'* ]]; then
IMAGE_VER=${{ steps.vars.outputs.tag }}
else
IMAGE_VER=${{ github.event.inputs.IMAGE_VER }}
fi
if [[ $IMAGE_VER == '' ]]; then
IMAGE_VER='test-vBeta'
fi
echo "image_ver=${IMAGE_VER}" >> $GITHUB_OUTPUT
echo "Generated version: ${IMAGE_VER}"
- name: Display version
run: |
echo "Git Ref: $GITHUB_REF"
echo "Image Version: ${{ steps.gen_ver.outputs.image_ver }}"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
registry: ${{env.DOCKERHUB_REGISTRY}}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: BMA.EHR.Retirement.Service/Dockerfile
platforms: linux/amd64
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.gen_ver.outputs.image_ver }}
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest
labels: |
org.opencontainers.image.title=BMA EHR Retirement Service
org.opencontainers.image.description=HRMS Retirement API Service
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Notify Discord on success
if: success()
env:
IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }}
run: |
TAG_INFO="Tag: ${IMAGE_VER:-unknown}"
REF_INFO="Ref: ${GITHUB_REF}"
ACTOR_INFO="Actor: ${GITHUB_ACTOR}"
MSG="✅ DockerHub release succeeded\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}"
curl -s -H "Content-Type: application/json" \
-X POST \
-d "{\"content\":\"${MSG}\"}" \
"$DISCORD_WEBHOOK"
- name: Notify Discord on failure
if: failure()
env:
IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }}
run: |
TAG_INFO="Tag: ${IMAGE_VER:-unknown}"
REF_INFO="Ref: ${GITHUB_REF}"
ACTOR_INFO="Actor: ${GITHUB_ACTOR}"
MSG="❌ DockerHub release failed\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}"
curl -s -H "Content-Type: application/json" \
-X POST \
-d "{\"content\":\"${MSG}\"}" \
"$DISCORD_WEBHOOK"

View file

@ -1,9 +1,9 @@
name: release-dev
run-name: release-dev ${{ github.actor }}
on:
push:
tags:
- "retirement-[0-9]+.[0-9]+.[0-9]+"
# push:
# tags:
# - "retirement-[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
env:
REGISTRY: docker.frappet.com

View file

@ -1,9 +1,9 @@
name: release-dev
run-name: release-dev ${{ github.actor }}
on:
push:
tags:
- "consumer-[0-9]+.[0-9]+.[0-9]+"
# push:
# tags:
# - "consumer-[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
env:
REGISTRY: docker.frappet.com

View file

@ -1,9 +1,9 @@
name: release-dev
run-name: release-dev ${{ github.actor }}
on:
push:
tags:
- "command-[0-9]+.[0-9]+.[0-9]+"
# push:
# tags:
# - "command-[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
env:
REGISTRY: docker.frappet.com

View file

@ -1,9 +1,9 @@
name: release-dev
run-name: release-dev ${{ github.actor }}
on:
push:
tags:
- "discipline-[0-9]+.[0-9]+.[0-9]+"
# push:
# tags:
# - "discipline-[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
env:
REGISTRY: docker.frappet.com

View file

@ -1,9 +1,9 @@
name: release-dev
run-name: release-dev ${{ github.actor }}
on:
push:
tags:
- "insignia-[0-9]+.[0-9]+.[0-9]+"
# push:
# tags:
# - "insignia-[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
env:
REGISTRY: docker.frappet.com

View file

@ -1,9 +1,9 @@
name: release-dev
run-name: release-dev ${{ github.actor }}
on:
push:
tags:
- "leave-[0-9]+.[0-9]+.[0-9]+"
# push:
# tags:
# - "leave-[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
env:
REGISTRY: docker.frappet.com

View file

@ -1,9 +1,9 @@
name: release-dev
run-name: release-dev ${{ github.actor }}
on:
push:
tags:
- "placement-[0-9]+.[0-9]+.[0-9]+"
# push:
# tags:
# - "placement-[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
env:
REGISTRY: docker.frappet.com

View file

@ -1,107 +1,107 @@
name: release-dev
run-name: release-dev ${{ github.actor }}
on:
push:
tags:
- "reportv2-[0-9]+.[0-9]+.[0-9]+"
# push:
# tags:
# - "reportv2-[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
env:
REGISTRY: docker.frappet.com
IMAGE_NAME: ehr/bma-ehr-report-v2-service
DEPLOY_HOST: frappet.com
DEPLOY_PORT: 10102
# COMPOSE_PATH: /home/frappet/docker/bma-ehr
COMPOSE_PATH: /home/frappet/docker/bma/bma-ehr-report-v2
TOKEN_LINE: uxuK5hDzS2DsoC5piJBrWRLiz8GgY7iMZZldOWsDDF0
REGISTRY: docker.frappet.com
IMAGE_NAME: ehr/bma-ehr-report-v2-service
DEPLOY_HOST: frappet.com
DEPLOY_PORT: 10102
# COMPOSE_PATH: /home/frappet/docker/bma-ehr
COMPOSE_PATH: /home/frappet/docker/bma/bma-ehr-report-v2
TOKEN_LINE: uxuK5hDzS2DsoC5piJBrWRLiz8GgY7iMZZldOWsDDF0
jobs:
# act workflow_dispatch -W .github/workflows/release_report.yaml --input IMAGE_VER=latest -s DOCKER_USER=admin -s DOCKER_PASS=FPTadmin2357 -s SSH_PASSWORD=FPTadmin2357
release-dev:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set output tags
id: vars
run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- name: Gen Version
id: gen_ver
run: |
if [[ $GITHUB_REF == 'refs/tags/'* ]]; then
IMAGE_VER=${{ steps.vars.outputs.tag }}
else
IMAGE_VER=${{ github.event.inputs.IMAGE_VER }}
fi
if [[ $IMAGE_VER == '' ]]; then
IMAGE_VER='test-vBeta'
fi
echo '::set-output name=image_ver::'$IMAGE_VER
- name: Check Version
run: |
echo $GITHUB_REF
echo ${{ steps.gen_ver.outputs.image_ver }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login in to registry
uses: docker/login-action@v2
with:
registry: ${{env.REGISTRY}}
username: ${{secrets.DOCKER_USER}}
password: ${{secrets.DOCKER_PASS}}
- name: Build and load local docker image
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64
file: BMA.EHR.Report.Service/Dockerfile
push: true
tags: ${{env.REGISTRY}}/${{env.IMAGE_NAME}}:${{ steps.gen_ver.outputs.image_ver }},${{env.REGISTRY}}/${{env.IMAGE_NAME}}:latest
# act workflow_dispatch -W .github/workflows/release_report.yaml --input IMAGE_VER=latest -s DOCKER_USER=admin -s DOCKER_PASS=FPTadmin2357 -s SSH_PASSWORD=FPTadmin2357
release-dev:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set output tags
id: vars
run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- name: Gen Version
id: gen_ver
run: |
if [[ $GITHUB_REF == 'refs/tags/'* ]]; then
IMAGE_VER=${{ steps.vars.outputs.tag }}
else
IMAGE_VER=${{ github.event.inputs.IMAGE_VER }}
fi
if [[ $IMAGE_VER == '' ]]; then
IMAGE_VER='test-vBeta'
fi
echo '::set-output name=image_ver::'$IMAGE_VER
- name: Check Version
run: |
echo $GITHUB_REF
echo ${{ steps.gen_ver.outputs.image_ver }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login in to registry
uses: docker/login-action@v2
with:
registry: ${{env.REGISTRY}}
username: ${{secrets.DOCKER_USER}}
password: ${{secrets.DOCKER_PASS}}
- name: Build and load local docker image
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64
file: BMA.EHR.Report.Service/Dockerfile
push: true
tags: ${{env.REGISTRY}}/${{env.IMAGE_NAME}}:${{ steps.gen_ver.outputs.image_ver }},${{env.REGISTRY}}/${{env.IMAGE_NAME}}:latest
- name: Reload docker compose
uses: appleboy/ssh-action@v0.1.8
with:
host: ${{env.DEPLOY_HOST}}
username: frappet
password: ${{ secrets.SSH_PASSWORD }}
port: ${{env.DEPLOY_PORT}}
script: |
cd "${{env.COMPOSE_PATH}}"
docker compose pull
docker compose up -d
echo "${{ steps.gen_ver.outputs.image_ver }}"> success
- name: Notify Discord Success
if: success()
run: |
curl -H "Content-Type: application/json" \
-X POST \
-d '{
"embeds": [{
"title": "✅ Deployment Success!",
"description": "**Details:**\n- Image: `${{env.IMAGE_NAME}}`\n- Version: `${{ steps.gen_ver.outputs.image_ver }}`\n- Deployed by: `${{github.actor}}`",
"color": 3066993,
"footer": {
"text": "Release Notification",
"icon_url": "https://example.com/success-icon.png"
},
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
}]
}' \
${{ secrets.DISCORD_WEBHOOK }}
- name: Reload docker compose
uses: appleboy/ssh-action@v0.1.8
with:
host: ${{env.DEPLOY_HOST}}
username: frappet
password: ${{ secrets.SSH_PASSWORD }}
port: ${{env.DEPLOY_PORT}}
script: |
cd "${{env.COMPOSE_PATH}}"
docker compose pull
docker compose up -d
echo "${{ steps.gen_ver.outputs.image_ver }}"> success
- name: Notify Discord Success
if: success()
run: |
curl -H "Content-Type: application/json" \
-X POST \
-d '{
"embeds": [{
"title": "✅ Deployment Success!",
"description": "**Details:**\n- Image: `${{env.IMAGE_NAME}}`\n- Version: `${{ steps.gen_ver.outputs.image_ver }}`\n- Deployed by: `${{github.actor}}`",
"color": 3066993,
"footer": {
"text": "Release Notification",
"icon_url": "https://example.com/success-icon.png"
},
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
}]
}' \
${{ secrets.DISCORD_WEBHOOK }}
- name: Notify Discord Failure
if: failure()
run: |
curl -H "Content-Type: application/json" \
-X POST \
-d '{
"embeds": [{
"title": "❌ Deployment Failed!",
"description": "**Details:**\n- Image: `${{env.IMAGE_NAME}}`\n- Version: `${{ steps.gen_ver.outputs.image_ver }}`\n- Attempted by: `${{github.actor}}`",
"color": 15158332,
"footer": {
"text": "Release Notification",
"icon_url": "https://example.com/failure-icon.png"
},
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
}]
}' \
${{ secrets.DISCORD_WEBHOOK }}
- name: Notify Discord Failure
if: failure()
run: |
curl -H "Content-Type: application/json" \
-X POST \
-d '{
"embeds": [{
"title": "❌ Deployment Failed!",
"description": "**Details:**\n- Image: `${{env.IMAGE_NAME}}`\n- Version: `${{ steps.gen_ver.outputs.image_ver }}`\n- Attempted by: `${{github.actor}}`",
"color": 15158332,
"footer": {
"text": "Release Notification",
"icon_url": "https://example.com/failure-icon.png"
},
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
}]
}' \
${{ secrets.DISCORD_WEBHOOK }}

93
.vscode/launch.json vendored
View file

@ -1,35 +1,60 @@
{
"version": "0.2.0",
"configurations": [
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md.
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/BMA.EHR.Leave.Service/bin/Debug/net7.0/BMA.EHR.Leave.Service.dll",
"args": [],
"cwd": "${workspaceFolder}/BMA.EHR.Leave.Service",
"stopAtEntry": false,
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}
"version": "0.2.0",
"configurations": [
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md.
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/BMA.EHR.Leave/bin/Debug/net7.0/BMA.EHR.Leave.dll",
"args": [],
"cwd": "${workspaceFolder}/BMA.EHR.Leave",
"stopAtEntry": false,
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md.
"name": ".NET Core Launch (web) - Insignia",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/BMA.EHR.Insignia/bin/Debug/net7.0/BMA.EHR.Insignia.dll",
"args": [],
"cwd": "${workspaceFolder}/BMA.EHR.Insignia",
"stopAtEntry": false,
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}

View file

@ -53,6 +53,7 @@ namespace BMA.EHR.Application
services.AddTransient<UserDutyTimeRepository>();
services.AddTransient<AdditionalCheckRequestRepository>();
services.AddTransient<UserCalendarRepository>();
services.AddTransient<CheckInJobStatusRepository>();
services.AddTransient<LeaveTypeRepository>();
services.AddTransient<LeaveRequestRepository>();
@ -60,6 +61,8 @@ namespace BMA.EHR.Application
services.AddTransient<MinIOLeaveService>();
services.AddTransient<LeaveProcessJobStatusRepository>();
return services;
}

View file

@ -53,15 +53,18 @@ namespace BMA.EHR.Application.Repositories
#region " For Call External API "
protected async Task<string> GetExternalAPIAsync(string apiPath, string accessToken, string apiKey)
protected async Task<string> GetExternalAPIAsync(string apiPath, string accessToken, string apiKey, CancellationToken cancellationToken = default)
{
try
{
// กำหนด timeout เป็น 30 นาที
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(30));
using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Replace("Bearer ", ""));
client.DefaultRequestHeaders.Add("api-key", apiKey);
var _res = await client.GetAsync(apiPath);
var _res = await client.GetAsync(apiPath,cancellationToken: combinedCts.Token);
if (_res.IsSuccessStatusCode)
{
var _result = await _res.Content.ReadAsStringAsync();
@ -77,10 +80,13 @@ namespace BMA.EHR.Application.Repositories
}
}
protected async Task<string> SendExternalAPIAsync(HttpMethod method, string apiPath, string accessToken, object? body, string apiKey)
protected async Task<string> SendExternalAPIAsync(HttpMethod method, string apiPath, string accessToken, object? body, string apiKey, CancellationToken cancellationToken = default)
{
try
{
// กำหนด timeout เป็น 30 นาที
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(30));
using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
// สร้าง request message
var request = new HttpRequestMessage(method, apiPath);
@ -92,7 +98,7 @@ namespace BMA.EHR.Application.Repositories
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Replace("Bearer ", ""));
client.DefaultRequestHeaders.Add("api-key", apiKey);
var _res = await client.SendAsync(request);
var _res = await client.SendAsync(request, combinedCts.Token);
if (_res.IsSuccessStatusCode)
{
var _result = await _res.Content.ReadAsStringAsync();
@ -109,11 +115,13 @@ namespace BMA.EHR.Application.Repositories
}
protected async Task<string> PostExternalAPIAsync(string apiPath, string accessToken, object? body, string apiKey)
public async Task<string> PostExternalAPIAsync(string apiPath, string accessToken, object? body, string apiKey, CancellationToken cancellationToken = default)
{
try
{
// กำหนด timeout เป็น 30 นาที
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(30));
using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
var json = JsonConvert.SerializeObject(body);
var stringContent = new StringContent(json, Encoding.UTF8, "application/json");
//stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
@ -122,7 +130,7 @@ namespace BMA.EHR.Application.Repositories
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Replace("Bearer ", ""));
client.DefaultRequestHeaders.Add("api-key", apiKey);
var _res = await client.PostAsync(apiPath, stringContent);
var _res = await client.PostAsync(apiPath, stringContent, combinedCts.Token);
if (_res.IsSuccessStatusCode)
{
var _result = await _res.Content.ReadAsStringAsync();
@ -138,10 +146,13 @@ namespace BMA.EHR.Application.Repositories
}
}
protected async Task<bool> PostExternalAPIBooleanAsync(string apiPath, string accessToken, object? body, string apiKey)
protected async Task<bool> PostExternalAPIBooleanAsync(string apiPath, string accessToken, object? body, string apiKey, CancellationToken cancellationToken = default)
{
try
{
// กำหนด timeout เป็น 30 นาที
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(30));
using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
var json = JsonConvert.SerializeObject(body);
var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json");
stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
@ -150,7 +161,7 @@ namespace BMA.EHR.Application.Repositories
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Replace("Bearer ", ""));
client.DefaultRequestHeaders.Add("api-key", apiKey);
var _res = await client.PostAsync(apiPath, stringContent);
var _res = await client.PostAsync(apiPath, stringContent, combinedCts.Token);
return _res.IsSuccessStatusCode;
}
}

File diff suppressed because it is too large Load diff

View file

@ -2,8 +2,11 @@
using BMA.EHR.Domain.Models.Base;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using System.IO.Pipes;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
namespace BMA.EHR.Application.Repositories.Leaves
{
@ -43,6 +46,38 @@ namespace BMA.EHR.Application.Repositories.Leaves
#region " Methods "
public async Task<string> PostExternalAPIAsync(string apiPath, string accessToken, object? body, string apiKey, CancellationToken cancellationToken = default)
{
try
{
// กำหนด timeout เป็น 30 นาที
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(30));
using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
var json = JsonConvert.SerializeObject(body);
var stringContent = new StringContent(json, Encoding.UTF8, "application/json");
//stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Replace("Bearer ", ""));
client.DefaultRequestHeaders.Add("api-key", apiKey);
var _res = await client.PostAsync(apiPath, stringContent, combinedCts.Token);
if (_res.IsSuccessStatusCode)
{
var _result = await _res.Content.ReadAsStringAsync();
return _result;
}
return string.Empty;
}
}
catch
{
throw;
}
}
public virtual async Task<IReadOnlyList<T>> GetAllAsync()
{
return await _dbSet.ToListAsync();
@ -68,6 +103,24 @@ namespace BMA.EHR.Application.Repositories.Leaves
return entity;
}
public virtual async Task<IReadOnlyList<T>> AddRangeAsync(List<T> entities)
{
foreach (var entity in entities)
{
if (entity is EntityBase)
{
(entity as EntityBase).CreatedUserId = UserId ?? "";
(entity as EntityBase).CreatedFullName = FullName ?? "System Administrator";
(entity as EntityBase).CreatedAt = DateTime.Now;
}
}
await _dbSet.AddRangeAsync(entities);
await _dbContext.SaveChangesAsync();
return entities;
}
public virtual async Task<T> UpdateAsync(T entity)
{
if (entity is EntityBase)

View file

@ -79,7 +79,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
public async Task UpdateLeaveUsageAsync(int year, Guid typeId, Guid userId, double day)
{
var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken);
// var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken);
var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
if (pf == null)
{
throw new Exception(GlobalMessages.DataNotFound);
@ -98,9 +99,32 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
await _dbContext.SaveChangesAsync();
}
public async Task UpdateLeaveCountAsync(int year, Guid typeId, Guid userId, int count)
{
// var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken);
var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
if (pf == null)
{
throw new Exception(GlobalMessages.DataNotFound);
}
var data = await _dbContext.Set<LeaveBeginning>()
.Include(x => x.LeaveType)
.FirstOrDefaultAsync(x => x.LeaveYear == year && x.LeaveTypeId == typeId && x.ProfileId == pf.Id);
if (data == null)
{
throw new Exception(GlobalMessages.DataNotFound);
}
data.LeaveCount += count;
await _dbContext.SaveChangesAsync();
}
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.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
if (pf == null)
{
throw new Exception(GlobalMessages.DataNotFound);
@ -238,7 +262,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
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.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
if (pf == null)
{
return null;
@ -413,5 +438,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
public string LastName { get; set; } = string.Empty;
public DateTime? DateStart { get; set; } = null;
public DateTime? DateAppoint { get; set; } = null;
}
}

View file

@ -253,7 +253,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
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.GetProfileByKeycloakIdNew2Async(userId, AccessToken);
if (profile == null)
{
@ -306,11 +307,11 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
rawData = rawData
.Where(x => x.RootDnaId == Guid.Parse(nodeId!));
}
else if (role == "PARENT")
{
rawData = rawData
.Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null);
}
// else if (role == "PARENT")
// {
// rawData = rawData
// .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null);
// }
else if (role == "NORMAL")
{
rawData = rawData
@ -420,11 +421,11 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
rawData = rawData
.Where(x => x.RootDnaId == Guid.Parse(nodeId!));
}
else if (role == "PARENT")
{
rawData = rawData
.Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null);
}
// else if (role == "PARENT")
// {
// rawData = rawData
// .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null);
// }
else if (role == "NORMAL")
{
rawData = rawData
@ -495,7 +496,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
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.GetProfileByKeycloakIdNew2Async(keycloakUserId, AccessToken);
if (pf == null)
throw new Exception(GlobalMessages.DataNotFound);
@ -556,6 +558,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
var data = await _dbContext.Set<LeaveRequest>().AsQueryable().AsNoTracking()
.Include(x => x.Type)
.Where(x => x.LeaveStartDate.Date < beforeDate.Date)
//.Where(x => x.CreatedAt < beforeDate)
.Where(x => x.KeycloakUserId == keycloakUserId)
.Where(x => x.Type.Id == leaveTypeId)
.Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING")
@ -566,6 +569,22 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
return data;
}
public async Task<LeaveRequest?> GetLastLeaveRequestByTypeForUserAsync2(Guid keycloakUserId, Guid leaveTypeId, DateTime beforeDate)
{
var data = await _dbContext.Set<LeaveRequest>().AsQueryable().AsNoTracking()
.Include(x => x.Type)
//.Where(x => x.LeaveStartDate.Date < beforeDate.Date)
.Where(x => x.CreatedAt < beforeDate)
.Where(x => x.KeycloakUserId == keycloakUserId)
.Where(x => x.Type.Id == leaveTypeId)
.Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING")
//.Where(x => x.LeaveStatus != "REJECT" && x.LeaveStatus != "DELETE")
.OrderByDescending(x => x.CreatedAt)
.FirstOrDefaultAsync();
return data;
}
public async Task<List<LeaveRequest>> GetCancelLeaveRequestForAdminAsync(int year, Guid type, string status, string role, string? nodeId, int? node)
{
var rawData = _dbContext.Set<LeaveRequest>().AsNoTracking()
@ -608,11 +627,11 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
rawData = rawData
.Where(x => x.RootDnaId == Guid.Parse(nodeId!));
}
else if (role == "PARENT")
{
rawData = rawData
.Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null);
}
// else if (role == "PARENT")
// {
// rawData = rawData
// .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null);
// }
else if (role == "NORMAL")
{
rawData = rawData
@ -631,7 +650,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
{
try
{
var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken ?? "");
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken ?? "");
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(data.KeycloakUserId, AccessToken ?? "");
if (profile == null)
{
throw new Exception(GlobalMessages.DataNotFound);
@ -656,6 +676,9 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
thisYear = thisYear + 1;
}
await _leaveBeginningRepository.UpdateLeaveUsageAsync(thisYear, data.Type.Id, data.KeycloakUserId, -1 * data.LeaveTotal);
// update leave count ลดลง 1 ครั้ง
await _leaveBeginningRepository.UpdateLeaveCountAsync(thisYear, data.Type.Id, data.KeycloakUserId, -1);
var _baseAPI = _configuration["API"];
var apiUrlSalary = $"{_baseAPI}/org/profile/leave/cancel/{data.Id}";
@ -704,7 +727,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
throw new Exception(GlobalMessages.DataNotFound);
}
var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken ?? "");
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken ?? "");
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(rawData.KeycloakUserId, AccessToken ?? "");
if (profile == null)
{
throw new Exception(GlobalMessages.DataNotFound);
@ -729,6 +753,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
}
await _leaveBeginningRepository.UpdateLeaveUsageAsync(thisYear, rawData.Type.Id, rawData.KeycloakUserId, -1 * rawData.LeaveTotal);
// update leave count ลดลง 1 ครั้ง
await _leaveBeginningRepository.UpdateLeaveCountAsync(thisYear, rawData.Type.Id, rawData.KeycloakUserId, -1);
var _baseAPI = _configuration["API"];
var apiUrlSalary = $"{_baseAPI}/org/profile/leave/cancel/{rawData.Id}";
@ -790,7 +816,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
throw new Exception(GlobalMessages.DataNotFound);
}
var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken ?? "");
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken ?? "");
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(rawData.KeycloakUserId, AccessToken ?? "");
if (profile == null)
{
throw new Exception(GlobalMessages.DataNotFound);
@ -1214,7 +1241,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
}
else
{
var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken);
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken);
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(rawData.KeycloakUserId, AccessToken);
if (profile == null)
{
throw new Exception(GlobalMessages.DataNotFound);
@ -1235,6 +1263,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
// TODO : Update ไปตาราง beginning
await _leaveBeginningRepository.UpdateLeaveUsageAsync(thisYear, rawData.Type.Id, rawData.KeycloakUserId, rawData.LeaveTotal);
// update leave count เพิ่มขึ้น 1 ครั้ง
await _leaveBeginningRepository.UpdateLeaveCountAsync(thisYear, rawData.Type.Id, rawData.KeycloakUserId, 1);
var _baseAPI = _configuration["API"];
@ -1258,6 +1288,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
status = "approve",
reason = rawData.LeaveDetail,
leaveId = rawData.Id,
leaveSubTypeName = rawData.LeaveSubTypeName,
coupleDayLevelCountry = rawData.CoupleDayLevelCountry,
});
// var _result = await _res.Content.ReadAsStringAsync();
}
@ -1281,6 +1313,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
status = "approve",
reason = rawData.LeaveDetail,
leaveId = rawData.Id,
leaveSubTypeName = rawData.LeaveSubTypeName,
coupleDayLevelCountry = rawData.CoupleDayLevelCountry,
});
}
}
@ -1377,7 +1411,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
}
else
{
var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken);
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken);
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(rawData.KeycloakUserId, AccessToken);
if (profile == null)
{
throw new Exception(GlobalMessages.DataNotFound);
@ -1664,10 +1699,10 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
{
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).ToList();
}
// else if (role == "PARENT")
// {
// data = data.Where(x => x.RootDnaId == Guid.Parse(nodeId) && x.Child1DnaId != null).ToList();
// }
else if (role == "NORMAL")
{
data = data.Where(x =>
@ -1683,7 +1718,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
if (role == "ROOT" || role == "OWNER" || role == "CHILD" || role == "BROTHER" || role == "PARENT")
{
data = data
.Where(x => nodeByReq == 4 ? x.Child4Id == Guid.Parse(nodeIdByReq) : nodeByReq == 3 ? x.Child3Id == Guid.Parse(nodeIdByReq) : nodeByReq == 2 ? x.Child2Id == Guid.Parse(nodeIdByReq) : nodeByReq == 1 ? x.Child1Id == Guid.Parse(nodeIdByReq) : nodeByReq == 0 ? x.RootId == Guid.Parse(nodeIdByReq) : true)
.Where(x => nodeByReq == 4 ? x.Child4DnaId == Guid.Parse(nodeIdByReq) : nodeByReq == 3 ? x.Child3DnaId == Guid.Parse(nodeIdByReq) : nodeByReq == 2 ? x.Child2DnaId == Guid.Parse(nodeIdByReq) : nodeByReq == 1 ? x.Child1DnaId == Guid.Parse(nodeIdByReq) : nodeByReq == 0 ? x.RootDnaId == Guid.Parse(nodeIdByReq) : true)
.ToList();
}
// รายงานการลางานจำแนกตามเพศฯ Template ให้หน่วยงานแสดงก่อนส่วนราชการ
@ -1824,6 +1859,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
.Include(x => x.Type)
.Where(x => x.KeycloakUserId == keycloakUserId)
.Where(x => x.Type.Id == leaveTypeId)
//.Where(x => x.CreatedAt >= startDate && x.CreatedAt <= endDate)
.Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date)
.Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING")
.ToListAsync();
@ -1834,6 +1870,23 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
return 0;
}
public async Task<double> GetSumApproveLeaveTotalByTypeAndRangeForUser2(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.CreatedAt.Date >= startDate && 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> GetCountApproveLeaveByTypeAndRangeForUser(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate)
{
var data = await _dbContext.Set<LeaveRequest>().AsQueryable().AsNoTracking()
@ -1841,6 +1894,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
.Where(x => x.KeycloakUserId == keycloakUserId)
.Where(x => x.Type.Id == leaveTypeId)
.Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date)
//.Where(x => x.CreatedAt >= startDate && x.CreatedAt <= endDate)
.Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING")
.ToListAsync();

View file

@ -74,7 +74,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants
await base.AddAsync(entity);
var userId = UserId != null ? Guid.Parse(UserId) : Guid.Empty;
var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken ?? "");
// var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken ?? "");
var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken ?? "");
// fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลากรณีพิเศษ (ไม่มีแจ้งเตือนไปยังผู้บังคับบัญชา) #969
// send noti + inbox + mail
@ -144,7 +145,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants
}
}
public async Task<List<AdditionalCheckRequest>> GetAdditionalCheckRequestsByAdminRole(int year, int month, string role, string nodeId, int? node)
public async Task<List<AdditionalCheckRequest>> GetAdditionalCheckRequestsByAdminRole(int year, int month, string role, string nodeId, int? node, string? keyword)
{
try
{
@ -152,6 +153,17 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants
.Where(x => (x.CheckDate.Year == year && x.CheckDate.Month == month))
.OrderByDescending(x => x.CreatedAt.Date)
.ToListAsync();
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;
@ -173,11 +185,11 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants
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 == "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 =>

View file

@ -0,0 +1,135 @@
using BMA.EHR.Application.Common.Interfaces;
using BMA.EHR.Domain.Models.Leave.TimeAttendants;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants
{
public class CheckInJobStatusRepository : GenericLeaveRepository<Guid, CheckInJobStatus>
{
#region " Fields "
private readonly ILeaveDbContext _dbContext;
#endregion
#region " Constructor and Destructor "
public CheckInJobStatusRepository(ILeaveDbContext dbContext,
IHttpContextAccessor httpContextAccessor) : base(dbContext, httpContextAccessor)
{
_dbContext = dbContext;
}
#endregion
#region " Methods "
/// <summary>
/// ดึงข้อมูล Job Status จาก TaskId
/// </summary>
public async Task<CheckInJobStatus?> GetByTaskIdAsync(Guid taskId)
{
var data = await _dbContext.Set<CheckInJobStatus>()
.Where(x => x.TaskId == taskId)
.FirstOrDefaultAsync();
return data;
}
/// <summary>
/// ดึงข้อมูล Job Status จาก UserId และสถานะ
/// </summary>
public async Task<List<CheckInJobStatus>> GetByUserIdAndStatusAsync(Guid userId, string status)
{
var data = await _dbContext.Set<CheckInJobStatus>()
.Where(x => x.KeycloakUserId == userId && x.Status == status)
.OrderByDescending(x => x.CreatedDate)
.ToListAsync();
return data;
}
/// <summary>
/// ดึงข้อมูล Job Status ที่ยัง pending หรือ processing
/// </summary>
public async Task<List<CheckInJobStatus>> GetPendingOrProcessingJobsAsync(Guid userId)
{
var data = await _dbContext.Set<CheckInJobStatus>()
.Where(x => x.KeycloakUserId == userId &&
(x.Status == "PENDING" || x.Status == "PROCESSING"))
//.OrderByDescending(x => x.CreatedDate)
.ToListAsync();
return data;
}
/// <summary>
/// อัปเดตสถานะเป็น Processing
/// </summary>
public async Task<CheckInJobStatus> UpdateToProcessingAsync(Guid taskId)
{
var job = await GetByTaskIdAsync(taskId);
if (job != null)
{
job.Status = "PROCESSING";
job.ProcessingDate = DateTime.Now;
await UpdateAsync(job);
}
return job!;
}
/// <summary>
/// อัปเดตสถานะเป็น Completed
/// </summary>
public async Task<CheckInJobStatus> UpdateToCompletedAsync(Guid taskId, string? additionalData = null)
{
var job = await GetByTaskIdAsync(taskId);
if (job != null)
{
job.Status = "COMPLETED";
job.CompletedDate = DateTime.Now;
if (!string.IsNullOrEmpty(additionalData))
{
job.AdditionalData = additionalData;
}
await UpdateAsync(job);
}
return job!;
}
/// <summary>
/// อัปเดตสถานะเป็น Failed
/// </summary>
public async Task<CheckInJobStatus> UpdateToFailedAsync(Guid taskId, string errorMessage)
{
var job = await GetByTaskIdAsync(taskId);
if (job != null)
{
job.Status = "FAILED";
job.CompletedDate = DateTime.Now;
job.ErrorMessage = errorMessage;
await UpdateAsync(job);
}
return job!;
}
/// <summary>
/// ล้างข้อมูล Job Status ที่เก่าเกิน X วัน
/// </summary>
public async Task<int> CleanupOldJobsAsync(int daysOld = 30)
{
var cutoffDate = DateTime.Now.AddDays(-daysOld);
var oldJobs = await _dbContext.Set<CheckInJobStatus>()
.Where(x => x.CreatedDate < cutoffDate)
.ToListAsync();
_dbContext.Set<CheckInJobStatus>().RemoveRange(oldJobs);
await _dbContext.SaveChangesAsync();
return oldJobs.Count;
}
#endregion
}
}

View file

@ -61,9 +61,12 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants
return await _dbContext.Set<DutyTime>().Where(x => x.IsActive).ToListAsync();
}
public async Task<DutyTime?> GetDefaultAsync()
public async Task<DutyTime?> GetDefaultAsync(CancellationToken cancellationToken = default)
{
return await _dbContext.Set<DutyTime>().Where(x => x.IsDefault).FirstOrDefaultAsync();
// กำหนด timeout เป็น 30 นาที
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(30));
using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
return await _dbContext.Set<DutyTime>().Where(x => x.IsDefault).FirstOrDefaultAsync(combinedCts.Token);
}
#endregion

View file

@ -0,0 +1,795 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Text.Json;
using System.IO;
using BMA.EHR.Application.Common.Interfaces;
using BMA.EHR.Application.Repositories.Leaves.LeaveRequests;
using BMA.EHR.Application.Repositories.MetaData;
using BMA.EHR.Application.Responses.Profiles;
using BMA.EHR.Domain.Extensions;
using BMA.EHR.Domain.Models.Leave.TimeAttendants;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants
{
public class LeaveProcessJobStatusRepository: GenericLeaveRepository<Guid, LeaveProcessJobStatus>
{
#region " Fields "
private readonly ILeaveDbContext _dbContext;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly UserProfileRepository _userProfileRepository;
private readonly HolidayRepository _holidayRepository;
private readonly DutyTimeRepository _dutyTimeRepository;
private readonly UserDutyTimeRepository _userDutyTimeRepository;
private readonly ProcessUserTimeStampRepository _processUserTimeStampRepository;
private readonly LeaveRequestRepository _leaveRequestRepository;
private readonly IConfiguration _configuration;
private readonly IWebHostEnvironment _env;
#endregion
#region " Constructor and Destructor "
public LeaveProcessJobStatusRepository(ILeaveDbContext dbContext,
IHttpContextAccessor httpContextAccessor,
UserProfileRepository userProfileRepository,
HolidayRepository holidayRepository,
DutyTimeRepository dutyTimeRepository,
UserDutyTimeRepository userDutyTimeRepository,
ProcessUserTimeStampRepository processUserTimeStampRepository,
LeaveRequestRepository leaveRequestRepository,
IConfiguration configuration,
IWebHostEnvironment env) : base(dbContext, httpContextAccessor)
{
_dbContext = dbContext;
_httpContextAccessor = httpContextAccessor;
_userProfileRepository = userProfileRepository;
_holidayRepository = holidayRepository;
_configuration = configuration;
_leaveRequestRepository = leaveRequestRepository;
_dutyTimeRepository = dutyTimeRepository;
_userDutyTimeRepository = userDutyTimeRepository;
_processUserTimeStampRepository = processUserTimeStampRepository;
_env = env;
}
#endregion
#region " Methods "
/// <summary>
/// ดึงข้อมูล Job Status จาก TaskId
/// </summary>
public async Task<LeaveProcessJobStatus?> GetByTaskIdAsync(Guid id)
{
var data = await _dbContext.Set<LeaveProcessJobStatus>()
.Where(x => x.Id == id)
.FirstOrDefaultAsync();
return data;
}
/// <summary>
/// ดึงข้อมูล Job Status จาก UserId และสถานะ
/// </summary>
public async Task<List<LeaveProcessJobStatus>> GetByUserIdAndStatusAsync(Guid userId, string status)
{
var data = await _dbContext.Set<LeaveProcessJobStatus>()
.Where(x => x.CreatedUserId == userId.ToString("D") && x.Status == status)
.OrderByDescending(x => x.CreatedDate)
.ToListAsync();
return data;
}
/// <summary>
/// ดึงข้อมูล Job Status จาก UserId
/// </summary>
public async Task<List<LeaveProcessJobStatus>> GetByUserIdAsync(Guid userId)
{
var data = await _dbContext.Set<LeaveProcessJobStatus>()
.Where(x => x.CreatedUserId == userId.ToString("D"))
.OrderByDescending(x => x.CreatedDate)
.ToListAsync();
return data;
}
/// <summary>
/// ดึงข้อมูล Job Status ที่ยัง pending หรือ processing
/// </summary>
public async Task<List<LeaveProcessJobStatus>> GetPendingOrProcessingJobsAsync(Guid userId)
{
var data = await _dbContext.Set<LeaveProcessJobStatus>()
.Where(x => x.CreatedUserId == userId.ToString("D") &&
(x.Status == "PENDING" || x.Status == "PROCESSING"))
//.OrderByDescending(x => x.CreatedDate)
.ToListAsync();
return data;
}
public async Task<List<LeaveProcessJobStatus>> GetPendingJobsAsync()
{
var data = await _dbContext.Set<LeaveProcessJobStatus>()
.Where(x => x.Status == "PENDING")
.ToListAsync();
return data;
}
/// <summary>
/// อัปเดตสถานะเป็น Processing
/// </summary>
public async Task<LeaveProcessJobStatus> UpdateToProcessingAsync(Guid id)
{
var job = await GetByTaskIdAsync(id);
if (job != null)
{
job.Status = "PROCESSING";
job.ProcessingDate = DateTime.Now;
await UpdateAsync(job);
}
return job!;
}
/// <summary>
/// อัปเดตสถานะเป็น Completed
/// </summary>
public async Task<LeaveProcessJobStatus> UpdateToCompletedAsync(Guid id, string? additionalData = null)
{
var job = await GetByTaskIdAsync(id);
if (job != null)
{
job.Status = "COMPLETED";
job.CompletedDate = DateTime.Now;
await UpdateAsync(job);
}
return job!;
}
/// <summary>
/// อัปเดตสถานะเป็น Failed
/// </summary>
public async Task<LeaveProcessJobStatus> UpdateToFailedAsync(Guid id, string errorMessage)
{
var job = await GetByTaskIdAsync(id);
if (job != null)
{
job.Status = "FAILED";
job.CompletedDate = DateTime.Now;
job.ErrorMessage = errorMessage;
await UpdateAsync(job);
}
return job!;
}
public async Task ProcessTaskAsync(Guid rootDnaId, DateTime? startDate, DateTime? endDate)
{
var profiles = new List<GetProfileByKeycloakIdRootDto>();
var dateStart = startDate?.Date ?? DateTime.Now.Date;
var dateEnd = endDate?.Date ?? DateTime.Now.Date;
var holidays = await _holidayRepository.GetHolidayAsync(dateStart, dateEnd);
var weekend = _holidayRepository.GetWeekEnd(dateStart, dateEnd);
var excludeDates = holidays.Union(weekend).ToList();
var dateList = new List<LoopDate>();
for (DateTime i = dateStart; i <= dateEnd; i = i.AddDays(1))
{
if (holidays.Contains(i))
{
var d = await _holidayRepository.GetHolidayAsync(i);
dateList.Add(new LoopDate
{
date = i,
isHoliday = true,
isWeekEnd = false,
dateRemark = d
});
}
else if (weekend.Contains(i))
{
dateList.Add(new LoopDate
{
date = i,
isHoliday = true,
isWeekEnd = false,
dateRemark = "วันหยุด"
});
}
else
{
dateList.Add(new LoopDate
{
date = i,
isHoliday = false,
isWeekEnd = false,
dateRemark = ""
});
}
}
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
if (defaultRound == null)
{
throw new Exception("ไม่พบรอบการลงเวลา Default");
}
var employees = new List<DateResultReport>();
foreach (var dd in dateList.Where(x => !x.isHoliday && !x.isWeekEnd))
{
profiles = await _userProfileRepository.GetAllOfficerByRootDnaId(rootDnaId.ToString(),dd.date);
foreach (var p in profiles)
{
var count = 1;
var keycloakUserId = p.Keycloak ?? Guid.Empty;
var timeStamps = await _processUserTimeStampRepository.GetTimestampByDateAsync(keycloakUserId, dd.date);
var fullName = $"{p.Prefix}{p.FirstName} {p.LastName}";
var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id, dd.date);
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
var duty = userRound ?? defaultRound;
// check วันลาของแต่ละคน
var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(keycloakUserId, dd.date);
var remarkStr = string.Empty;
var status = string.Empty;
var stampType = string.Empty;
var stampAmount = 0.0;
if (leaveReq != null)
{
switch (leaveReq.Type.Code.ToUpper())
{
case "LV-001":
case "LV-002":
case "LV-005":
remarkStr += leaveReq.Type.Name;
var leaveRange = leaveReq.LeaveRange == null ? "" : leaveReq.LeaveRange.ToUpper();
if(leaveReq.LeaveStartDate.Date == leaveReq.LeaveEndDate.Date)
{
if (leaveRange == "MORNING")
remarkStr += "ครึ่งวันเช้า";
else if (leaveRange == "AFTERNOON")
remarkStr += "ครึ่งวันบ่าย";
// var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper();
// if (leaveRangeEnd == "MORNING")
// remarkStr += "ครึ่งวันเช้า";
// else if (leaveRangeEnd == "AFTERNOON")
// remarkStr += "ครึ่งวันบ่าย";
var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper();
if (leaveRange != leaveRangeEnd)
{
if (leaveRangeEnd == "MORNING")
remarkStr += " - ครึ่งวันเช้า";
else if (leaveRangeEnd == "AFTERNOON")
remarkStr += " - ครึ่งวันบ่าย";
}
}
else
{
if(dd.date == leaveReq.LeaveStartDate.Date)
{
if (leaveRange == "MORNING")
remarkStr += "ครึ่งวันเช้า";
else if (leaveRange == "AFTERNOON")
remarkStr += "ครึ่งวันบ่าย";
}
else if(dd.date == leaveReq.LeaveEndDate.Date)
{
var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper();
if (leaveRangeEnd == "MORNING")
remarkStr += "ครึ่งวันเช้า";
else if (leaveRangeEnd == "AFTERNOON")
remarkStr += "ครึ่งวันบ่าย";
else
remarkStr += "เต็มวัน";
}
else
{
remarkStr += "เต็มวัน";
}
}
break;
default:
remarkStr += leaveReq.Type.Name;
break;
}
status = "LEAVE";
if(leaveReq.LeaveStartDate.Date == dd.date)
{
stampType = leaveReq.LeaveRange ?? "";
stampAmount = leaveReq.LeaveRange != "ALL" ? 0.5 : 1;
}
else if(leaveReq.LeaveEndDate.Date == dd.date)
{
stampAmount = leaveReq.LeaveRangeEnd != "ALL" ? 0.5 : 1;
stampType = leaveReq.LeaveRangeEnd ?? "";
}
else
stampAmount = leaveReq.LeaveRange != "ALL" || leaveReq.LeaveRangeEnd != "ALL" ? 0.5 : 1;
if(stampType == "ALL") stampType = "FULL_DAY";
}
else
{
if (timeStamps == null)
{
if (dd.date <= DateTime.Now.Date)
{
remarkStr = "ขาดราชการ";
status = "ABSENT";
stampType = "FULL_DAY";
stampAmount = 1;
if (dd.isHoliday == true)
{
remarkStr = $"วันหยุด ({dd.dateRemark})";
status = "HOLIDAY";
}
else if (dd.isWeekEnd)
{
remarkStr = dd.dateRemark;
status = "WEEKEND";
}
}
else remarkStr = "";
}
else
{
// check status ของการลงเวลา
if (timeStamps.CheckOut != null)
{
if (timeStamps.CheckOutStatus == "ABSENT")
{
remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckOut ? $" (นอกสถานที่:{timeStamps.CheckOutLocationName})".Trim() : "");
status = "ABSENT";
stampType = "FULL_DAY";
stampAmount = 1;
}
else if (timeStamps.CheckInStatus == "ABSENT")
{
remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : "");
status = "ABSENT";
stampType = "FULL_DAY";
stampAmount = 1;
}
else if (timeStamps.CheckInStatus == "LATE")
{
remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : "");
status = "LATE";
stampType = "FULL_DAY";
stampAmount = 1;
//lateTotal += 1;
}
else
remarkStr = !timeStamps.IsLocationCheckIn ? $" นอกสถานที่:{timeStamps.CheckInLocationName}".Trim() : "";
}
else
{
if (timeStamps.CheckInStatus == "ABSENT")
{
status = "ABSENT";
stampType = "FULL_DAY";
stampAmount = 1;
remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : "");
}
else if (timeStamps.CheckInStatus == "LATE")
{
status = "LATE";
stampType = "FULL_DAY";
stampAmount = 1;
remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : "");
//lateTotal += 1;
}
else
remarkStr = !timeStamps.IsLocationCheckIn ? $" นอกสถานที่:{timeStamps.CheckInLocationName}".Trim() : "";
}
}
}
var emp = new DateResultReport
{
profileId = p.Id.ToString(),
stampDate = dd.date,
stampType = stampType,
stampAmount = stampAmount,
remark = remarkStr,
status = status
};
employees.Add(emp);
count++;
}
// Write employees to JSON file
// var fileName = $"employees_{DateTime.Now:yyyyMMdd_HHmmss}.txt";
// var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Exports", fileName);
// // Ensure directory exists
// var directory = Path.GetDirectoryName(filePath);
// if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
// {
// Directory.CreateDirectory(directory);
// }
// var jsonOptions = new JsonSerializerOptions
// {
// WriteIndented = true,
// Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
// };
// var jsonContent = JsonSerializer.Serialize(employees, jsonOptions);
// await File.WriteAllTextAsync(filePath, jsonContent);
}
//call api
var apiPath = $"{_configuration["API"]}/org/unauthorize/profile/absent-late/batch";
var apiKey = _configuration["API_KEY"];
var body = new
{
records = employees.Where(x => x.status == "ABSENT" || x.status == "LATE").ToList()
};
var apiResult = await PostExternalAPIAsync(apiPath, AccessToken ?? "", body, apiKey);
if(apiResult == "")
{
throw new Exception($"เรียก API {apiPath} ไม่สำเร็จ");
}
}
public async Task ProcessEmpTaskAsync(Guid rootDnaId, DateTime? startDate, DateTime? endDate)
{
var profiles = new List<GetProfileByKeycloakIdRootDto>();
var dateStart = startDate?.Date ?? DateTime.Now.Date;
var dateEnd = endDate?.Date ?? DateTime.Now.Date;
var holidays = await _holidayRepository.GetHolidayAsync(dateStart, dateEnd);
var weekend = _holidayRepository.GetWeekEnd(dateStart, dateEnd);
var excludeDates = holidays.Union(weekend).ToList();
var dateList = new List<LoopDate>();
for (DateTime i = dateStart; i <= dateEnd; i = i.AddDays(1))
{
if (holidays.Contains(i))
{
var d = await _holidayRepository.GetHolidayAsync(i);
dateList.Add(new LoopDate
{
date = i,
isHoliday = true,
isWeekEnd = false,
dateRemark = d
});
}
else if (weekend.Contains(i))
{
dateList.Add(new LoopDate
{
date = i,
isHoliday = true,
isWeekEnd = false,
dateRemark = "วันหยุด"
});
}
else
{
dateList.Add(new LoopDate
{
date = i,
isHoliday = false,
isWeekEnd = false,
dateRemark = ""
});
}
}
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
if (defaultRound == null)
{
throw new Exception("ไม่พบรอบการลงเวลา Default");
}
var employees = new List<DateResultReport>();
foreach (var dd in dateList.Where(x => !x.isHoliday && !x.isWeekEnd))
{
profiles = await _userProfileRepository.GetAllEmployeeByRootDnaId(rootDnaId.ToString(),dd.date);
foreach (var p in profiles)
{
var count = 1;
var keycloakUserId = p.Keycloak ?? Guid.Empty;
var timeStamps = await _processUserTimeStampRepository.GetTimestampByDateAsync(keycloakUserId, dd.date);
var fullName = $"{p.Prefix}{p.FirstName} {p.LastName}";
var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id, dd.date);
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
var duty = userRound ?? defaultRound;
// check วันลาของแต่ละคน
var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(keycloakUserId, dd.date);
var remarkStr = string.Empty;
var status = string.Empty;
var stampType = string.Empty;
var stampAmount = 0.0;
if (leaveReq != null)
{
switch (leaveReq.Type.Code.ToUpper())
{
case "LV-001":
case "LV-002":
case "LV-005":
remarkStr += leaveReq.Type.Name;
var leaveRange = leaveReq.LeaveRange == null ? "" : leaveReq.LeaveRange.ToUpper();
if(leaveReq.LeaveStartDate.Date == leaveReq.LeaveEndDate.Date)
{
if (leaveRange == "MORNING")
remarkStr += "ครึ่งวันเช้า";
else if (leaveRange == "AFTERNOON")
remarkStr += "ครึ่งวันบ่าย";
// var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper();
// if (leaveRangeEnd == "MORNING")
// remarkStr += "ครึ่งวันเช้า";
// else if (leaveRangeEnd == "AFTERNOON")
// remarkStr += "ครึ่งวันบ่าย";
var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper();
if (leaveRange != leaveRangeEnd)
{
if (leaveRangeEnd == "MORNING")
remarkStr += " - ครึ่งวันเช้า";
else if (leaveRangeEnd == "AFTERNOON")
remarkStr += " - ครึ่งวันบ่าย";
}
}
else
{
if(dd.date == leaveReq.LeaveStartDate.Date)
{
if (leaveRange == "MORNING")
remarkStr += "ครึ่งวันเช้า";
else if (leaveRange == "AFTERNOON")
remarkStr += "ครึ่งวันบ่าย";
}
else if(dd.date == leaveReq.LeaveEndDate.Date)
{
var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper();
if (leaveRangeEnd == "MORNING")
remarkStr += "ครึ่งวันเช้า";
else if (leaveRangeEnd == "AFTERNOON")
remarkStr += "ครึ่งวันบ่าย";
}
else
{
remarkStr += "เต็มวัน";
}
}
break;
default:
remarkStr += leaveReq.Type.Name;
break;
}
status = "LEAVE";
if(leaveReq.LeaveStartDate.Date == dd.date)
{
stampType = leaveReq.LeaveRange ?? "";
stampAmount = leaveReq.LeaveRange != "ALL" ? 0.5 : 1;
}
else if(leaveReq.LeaveEndDate.Date == dd.date)
{
stampAmount = leaveReq.LeaveRangeEnd != "ALL" ? 0.5 : 1;
stampType = leaveReq.LeaveRangeEnd ?? "";
}
else
stampAmount = leaveReq.LeaveRange != "ALL" || leaveReq.LeaveRangeEnd != "ALL" ? 0.5 : 1;
if(stampType == "ALL") stampType = "FULL_DAY";
//stampAmount = leaveReq.LeaveRange != "ALL" || leaveReq.LeaveRangeEnd != "ALL" ? 0.5 : 1;
}
else
{
if (timeStamps == null)
{
if (dd.date <= DateTime.Now.Date)
{
remarkStr = "ขาดราชการ";
status = "ABSENT";
stampType = "FULL_DAY";
stampAmount = 1;
if (dd.isHoliday == true)
{
remarkStr = $"วันหยุด ({dd.dateRemark})";
status = "HOLIDAY";
}
else if (dd.isWeekEnd)
{
remarkStr = dd.dateRemark;
status = "WEEKEND";
}
}
else remarkStr = "";
}
else
{
// check status ของการลงเวลา
if (timeStamps.CheckOut != null)
{
if (timeStamps.CheckOutStatus == "ABSENT")
{
remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckOut ? $" (นอกสถานที่:{timeStamps.CheckOutLocationName})".Trim() : "");
status = "ABSENT";
stampType = "FULL_DAY";
stampAmount = 1;
}
else if (timeStamps.CheckInStatus == "ABSENT")
{
remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : "");
status = "ABSENT";
stampType = "FULL_DAY";
stampAmount = 1;
}
else if (timeStamps.CheckInStatus == "LATE")
{
remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : "");
status = "LATE";
stampType = "FULL_DAY";
stampAmount = 1;
//lateTotal += 1;
}
else
remarkStr = !timeStamps.IsLocationCheckIn ? $" นอกสถานที่:{timeStamps.CheckInLocationName}".Trim() : "";
}
else
{
if (timeStamps.CheckInStatus == "ABSENT")
{
status = "ABSENT";
stampType = "FULL_DAY";
stampAmount = 1;
remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : "");
}
else if (timeStamps.CheckInStatus == "LATE")
{
status = "LATE";
stampType = "FULL_DAY";
stampAmount = 1;
remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : "");
//lateTotal += 1;
}
else
remarkStr = !timeStamps.IsLocationCheckIn ? $" นอกสถานที่:{timeStamps.CheckInLocationName}".Trim() : "";
}
}
}
var emp = new DateResultReport
{
profileId = p.Id.ToString(),
stampDate = dd.date,
stampType = stampType,
stampAmount = stampAmount,
remark = remarkStr,
status = status
};
employees.Add(emp);
count++;
}
// Write employees to JSON file
// var fileName = $"employees_{DateTime.Now:yyyyMMdd_HHmmss}.txt";
// var filePath = Path.Combine(_env.ContentRootPath, "Exports", fileName);
// // Ensure directory exists
// var directory = Path.GetDirectoryName(filePath);
// if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
// {
// Directory.CreateDirectory(directory);
// }
// var jsonOptions = new JsonSerializerOptions
// {
// WriteIndented = true,
// Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
// };
// var jsonContent = JsonSerializer.Serialize(employees, jsonOptions);
// Console.WriteLine($"Writing file to: {filePath}");
// await File.WriteAllTextAsync(filePath, jsonContent);
// Console.WriteLine($"File written successfully: {fileName}");
}
// call api
var apiPath = $"{_configuration["API"]}/org/unauthorize/profile-employee/absent-late/batch";
var apiKey = _configuration["API_KEY"];
var body = new
{
records = employees.Where(x => x.status == "ABSENT" || x.status == "LATE").ToList()
};
var apiResult = await PostExternalAPIAsync(apiPath, AccessToken ?? "", body, apiKey);
if(apiResult == "")
{
throw new Exception($"เรียก API {apiPath} ไม่สำเร็จ");
}
}
public async Task ProcessPendingJobsAsync()
{
var pendingJobs = await GetPendingJobsAsync();
Console.WriteLine($"พบงานที่ค้างอยู่ในสถานะ PENDING จำนวน {pendingJobs.Count} งาน");
foreach (var job in pendingJobs)
{
try
{
// อัปเดตสถานะเป็น Processing
await UpdateToProcessingAsync(job.Id);
// ทำงานที่ต้องการที่นี่ (เช่น เรียก API, ประมวลผลข้อมูล ฯลฯ)
await ProcessTaskAsync(job.RootDnaId,job.StartDate, job.EndDate);
await ProcessEmpTaskAsync(job.RootDnaId,job.StartDate, job.EndDate);
// อัปเดตสถานะเป็น Completed
await UpdateToCompletedAsync(job.Id);
}
catch (Exception ex)
{
// หากเกิดข้อผิดพลาด อัปเดตสถานะเป็น Failed พร้อมข้อความแสดงข้อผิดพลาด
await UpdateToFailedAsync(job.Id, ex.Message);
}
}
}
#endregion
}
class LoopDate
{
public DateTime date { get; set; }
public bool isHoliday { get; set; }
public bool isWeekEnd { get; set; }
public string dateRemark { get; set; }
}
class DateResultReport
{
public string? profileId { get; set; }
public DateTime stampDate { get; set; }
public string stampType { get; set; }
public double stampAmount { get; set; }
public string remark { get; set; }
public string status { get; set; }
}
}

View file

@ -172,10 +172,10 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants
{
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).ToList();
}
// else if (role == "PARENT")
// {
// data = data.Where(x => x.RootDnaId == Guid.Parse(nodeId) && x.Child1DnaId != null).ToList();
// }
else if (role == "NORMAL")
{
data = data.Where(x =>
@ -191,11 +191,11 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants
if (role == "ROOT" || role == "OWNER" || role == "CHILD" || role == "BROTHER" || role == "PARENT")
{
data = data.Where(x =>
nodeByReq == 4 ? x.Child4Id == Guid.Parse(nodeIdByReq) :
nodeByReq == 3 ? x.Child3Id == Guid.Parse(nodeIdByReq) :
nodeByReq == 2 ? x.Child2Id == Guid.Parse(nodeIdByReq) :
nodeByReq == 1 ? x.Child1Id == Guid.Parse(nodeIdByReq) :
nodeByReq == 0 ? x.RootId == Guid.Parse(nodeIdByReq) : true
nodeByReq == 4 ? x.Child4DnaId == Guid.Parse(nodeIdByReq) :
nodeByReq == 3 ? x.Child3DnaId == Guid.Parse(nodeIdByReq) :
nodeByReq == 2 ? x.Child2DnaId == Guid.Parse(nodeIdByReq) :
nodeByReq == 1 ? x.Child1DnaId == Guid.Parse(nodeIdByReq) :
nodeByReq == 0 ? x.RootDnaId == Guid.Parse(nodeIdByReq) : true
).ToList();
}
return data;
@ -227,6 +227,19 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants
return data;
}
public async Task<List<ProcessUserTimeStamp>> GetTimeStampHistoryAsync2(Guid keycloakId, int year)
{
var fiscalDateStart = new DateTime(year - 1, 10, 1);
var fiscalDateEnd = new DateTime(year, 9, 30);
var data = await _dbContext.Set<ProcessUserTimeStamp>()
.Where(u => u.KeycloakUserId == keycloakId)
.Where(u => u.CheckIn.Date >= fiscalDateStart && u.CheckIn.Date <= fiscalDateEnd)
.OrderByDescending(u => u.CheckIn.Date)
.ToListAsync();
return data;
}
public async Task<int> GetTimeStampHistoryForAdminCountAsync(DateTime startDate, DateTime endDate)
{
var data = await _dbContext.Set<ProcessUserTimeStamp>()
@ -288,12 +301,12 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants
.Where(x => x.RootDnaId == Guid.Parse(nodeId!))
.ToList();
}
else if (role == "PARENT")
{
data = data
.Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null)
.ToList();
}
// else if (role == "PARENT")
// {
// data = data
// .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null)
// .ToList();
// }
else if (role == "NORMAL")
{
data = data.Where(x =>

View file

@ -101,14 +101,17 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants
return data;
}
public async Task<UserDutyTime?> GetLastEffectRound(Guid profileId)
public async Task<UserDutyTime?> GetLastEffectRound(Guid profileId, DateTime? effectiveDate = null, CancellationToken cancellationToken = default)
{
// กำหนด timeout เป็น 30 นาที
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(30));
using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
effectiveDate ??= DateTime.Now;
var data = await _dbContext.Set<UserDutyTime>()
.Where(x => x.ProfileId == profileId)
.Where(x => x.IsProcess)
.Where(x => x.EffectiveDate.Value.Date <= DateTime.Now.Date)
.Where(x => x.EffectiveDate.Value.Date <= effectiveDate.Value.Date)
.OrderByDescending(x => x.EffectiveDate)
.FirstOrDefaultAsync();
.FirstOrDefaultAsync(combinedCts.Token);
return data;
}

View file

@ -74,12 +74,16 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants
return data;
}
public async Task<UserTimeStamp?> GetLastRecord(Guid keycloakId)
public async Task<UserTimeStamp?> GetLastRecord(Guid keycloakId, CancellationToken cancellationToken = default)
{
// กำหนด timeout เป็น 30 นาที
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(30));
using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
var data = await _dbContext.Set<UserTimeStamp>()
.Where(u => u.KeycloakUserId == keycloakId)
.OrderByDescending(u => u.CheckIn)
.FirstOrDefaultAsync();
.FirstOrDefaultAsync(combinedCts.Token);
return data;
}
@ -136,12 +140,12 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants
.Where(x => x.RootDnaId == Guid.Parse(nodeId!))
.ToList();
}
else if (role == "PARENT")
{
data = data
.Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null)
.ToList();
}
// else if (role == "PARENT")
// {
// data = data
// .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null)
// .ToList();
// }
else if (role == "NORMAL")
{
data = data.Where(x =>

View file

@ -51,7 +51,8 @@ namespace BMA.EHR.Application.Repositories.MessageQueue
// // throw new Exception(GlobalMessages.DataNotFound);
// }
var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position";
//var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position";
var apiUrl = $"{_configuration["API"]}/org/dotnet/get-profileId";
var profileId = "";
using (var client = new HttpClient())
{

View file

@ -55,7 +55,8 @@ namespace BMA.EHR.Application.Repositories.MessageQueue
// // throw new Exception(GlobalMessages.DataNotFound);
// }
var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position";
//var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position";
var apiUrl = $"{_configuration["API"]}/org/dotnet/get-profileId";
var profileId = "";
using (var client = new HttpClient())
{
@ -131,7 +132,8 @@ namespace BMA.EHR.Application.Repositories.MessageQueue
// {
// return 0;
// }
var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position";
//var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position";
var apiUrl = $"{_configuration["API"]}/org/dotnet/get-profileId";
var profileId = "";
using (var client = new HttpClient())
{

View file

@ -10,6 +10,7 @@ using System.Net.Http.Headers;
using Microsoft.Extensions.Configuration;
using System.Security.Claims;
using System.Net.Http.Json;
using BMA.EHR.Application.Responses.Leaves;
namespace BMA.EHR.Application.Repositories
{
@ -62,6 +63,10 @@ namespace BMA.EHR.Application.Repositories
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 res = await req.Content.ReadAsStringAsync();
return res;
}
@ -72,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)
{
try

View file

@ -893,7 +893,7 @@ namespace BMA.EHR.Application.Repositories.Reports
select new
{
RowNo = 1,
DepartmentName = _userProfileRepository.GetOc(g.Key.OcId, 0, AccessToken).Root, //_organizationCommonRepository.GetOrganizationNameFullPath(g.Key.OcId, false, false),
DepartmentName = _userProfileRepository.GetOc(g.Key.OcId, 0, AccessToken)?.Root ?? "-", //_organizationCommonRepository.GetOrganizationNameFullPath(g.Key.OcId, false, false),
G1Male = g.Sum(x => x.Gendor == "ชาย" && x.RequestInsigniaName == "เหรียญจักรพรรดิมาลา" ? 1 : 0),
G1Female = g.Sum(x => x.Gendor == "หญิง" && x.RequestInsigniaName == "เหรียญจักรพรรดิมาลา" ? 1 : 0),
G2Male = g.Sum(x => x.Gendor == "ชาย" ? 1 : 0),

View file

@ -157,7 +157,7 @@ namespace BMA.EHR.Application.Repositories
return null;
}
catch(Exception ex)
catch (Exception ex)
{
throw;
}
@ -186,6 +186,55 @@ namespace BMA.EHR.Application.Repositories
}
}
public async Task<GetProfileByKeycloakIdDto?> GetProfileByKeycloakIdNewAsync(Guid keycloakId, string? accessToken,CancellationToken cancellationToken = default)
{
try
{
var apiPath = $"{_configuration["API"]}/org/dotnet/by-keycloak/{keycloakId}";
var apiKey = _configuration["API_KEY"];
var apiResult = await GetExternalAPIAsync(apiPath, accessToken ?? "", apiKey, cancellationToken);
if (apiResult != null)
{
var raw = JsonConvert.DeserializeObject<GetProfileByKeycloakIdResultDto>(apiResult);
if (raw != null)
return raw.Result;
}
return null;
}
catch
{
throw;
}
}
public async Task<GetProfileByKeycloakIdDto?> GetProfileByKeycloakIdNew2Async(Guid keycloakId, string? accessToken)
{
try
{
var apiPath = $"{_configuration["API"]}/org/dotnet/by-keycloak2/{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)
{
try
@ -209,6 +258,66 @@ namespace BMA.EHR.Application.Repositories
}
}
public async Task<List<GetOcStaff>?> GetOCStaffAsync(Guid profileId, string? accessToken)
{
try
{
var apiPath = $"{_configuration["API"]}/org/dotnet/find-staff";
var apiKey = _configuration["API_KEY"];
var body = new
{
assignId = "SYS_LEAVE_LIST",
profileId = profileId
};
//var profiles = new List<GetOcStaff>();
var apiResult = await PostExternalAPIAsync(apiPath, accessToken ?? "", body, apiKey);
if (apiResult != null)
{
var raw = JsonConvert.DeserializeObject<GetOcStaffResultDto>(apiResult);
if (raw != null)
return raw.Result;
}
return null;
}
catch
{
throw;
}
}
public async Task<GetProfileLeaveByKeycloakDto?> GetProfileLeaveReportByKeycloakIdAsync(Guid keycloakId, string? accessToken, string? report)
{
try
{
var apiPath = $"{_configuration["API"]}/org/dotnet/profile-leave/keycloak";
var apiKey = _configuration["API_KEY"];
var body = new
{
keycloakId = keycloakId,
report = report
};
//var profiles = new List<SearchProfileDto>();
var apiResult = await PostExternalAPIAsync(apiPath, accessToken, body, apiKey);
if (apiResult != null)
{
var raw = JsonConvert.DeserializeObject<GetProfileLeaveByKeycloakResultDto>(apiResult);
if (raw != null)
return raw.Result;
}
return null;
}
catch
{
throw;
}
}
public async Task<GetProfileByKeycloakIdDto?> GetProfileByProfileIdAsync(Guid profileId, string? accessToken)
{
try
@ -232,6 +341,31 @@ namespace BMA.EHR.Application.Repositories
}
}
public async Task<GetProfileByKeycloakIdDto?> GetProfileByProfileIdNoAuthAsync(Guid profileId, string? accessToken)
{
try
{
var apiPath = $"{_configuration["API"]}/org/unauthorize/profile/{profileId}";
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<bool> UpdateDutyTimeAsync(Guid profileId, Guid roundId, DateTime effectiveDate, string? accessToken)
{
try
@ -520,7 +654,7 @@ namespace BMA.EHR.Application.Repositories
}
}
public async Task<List<GetProfileByKeycloakIdRootDto>> GetProfileByAdminRole(string? accessToken, int? node, string? nodeId, string role, string? revisionId, int? reqNode, string? reqNodeId)
public async Task<List<GetProfileByKeycloakIdRootDto>> GetProfileByAdminRole(string? accessToken, int? node, string? nodeId, string role, string? revisionId, int? reqNode, string? reqNodeId, DateTime? startDate, DateTime? endDate)
{
try
{
@ -533,7 +667,9 @@ namespace BMA.EHR.Application.Repositories
role = role,
revisionId = revisionId,
reqNode = reqNode,
reqNodeId = reqNodeId
reqNodeId = reqNodeId,
//startDate = startDate,
//endDate = endDate
};
var profiles = new List<SearchProfileDto>();
@ -554,6 +690,187 @@ namespace BMA.EHR.Application.Repositories
}
}
public async Task<List<GetProfileByKeycloakIdRootDto>> GetProfileByAdminRolev2(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/officer-by-admin-rolev2";
var apiKey = _configuration["API_KEY"];
var body = new
{
node = node,
nodeId = nodeId,
role = role,
// revisionId = revisionId,
reqNode = reqNode,
reqNodeId = reqNodeId,
// startDate = startDate,
// endDate = endDate
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<List<GetProfileByKeycloakIdRootDto>> GetProfileByAdminRolev3(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/officer-by-admin-rolev3";
var apiKey = _configuration["API_KEY"];
var body = new
{
node = node,
nodeId = nodeId,
role = role,
// revisionId = revisionId,
reqNode = reqNode,
reqNodeId = reqNodeId,
startDate = startDate,
endDate = endDate
};
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 null;
}
catch
{
throw;
}
}
public async Task<List<GetProfileByKeycloakIdRootDto>> GetAllOfficerByRootDnaId(string? rootDnaId, DateTime date)
{
try
{
var apiPath = $"{_configuration["API"]}/org/unauthorize/officer-list";
var apiKey = _configuration["API_KEY"];
var body = new
{
reqNode = 0,
reqNodeId = rootDnaId,
date = date
};
//Console.WriteLine(body);
var profiles = new List<SearchProfileDto>();
var apiResult = await PostExternalAPIAsync(apiPath, "", body, apiKey);
if (apiResult != null)
{
var raw = JsonConvert.DeserializeObject<GetListProfileByKeycloakIdRootResultDto>(apiResult);
if (raw != null)
return raw.Result;
else
return new List<GetProfileByKeycloakIdRootDto>();
}
else
return new List<GetProfileByKeycloakIdRootDto>();
}
catch
{
throw;
}
}
public async Task<List<GetProfileByKeycloakIdRootDto>> GetAllEmployeeByRootDnaId(string? rootDnaId, DateTime date)
{
try
{
var apiPath = $"{_configuration["API"]}/org/unauthorize/employee-list";
var apiKey = _configuration["API_KEY"];
var body = new
{
reqNode = 0,
reqNodeId = rootDnaId,
startDate = date,
endDate = date
};
//Console.WriteLine(body);
var profiles = new List<SearchProfileDto>();
var apiResult = await PostExternalAPIAsync(apiPath, "", body, apiKey);
if (apiResult != null)
{
var raw = JsonConvert.DeserializeObject<GetListProfileByKeycloakIdRootResultDto>(apiResult);
if (raw != null)
return raw.Result;
else
return new List<GetProfileByKeycloakIdRootDto>();
}
else
return new List<GetProfileByKeycloakIdRootDto>();
}
catch
{
throw;
}
}
public async Task<List<GetProfileByKeycloakIdRootDto>> GetProfileByAdminRolev4(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/officer-by-admin-rolev4";
var apiKey = _configuration["API_KEY"];
var body = new
{
node = node,
nodeId = nodeId,
role = role,
// revisionId = revisionId,
reqNode = reqNode,
reqNodeId = reqNodeId,
// startDate = startDate,
// endDate = endDate
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<List<GetProfileByKeycloakIdRootDto>> GetProfileWithKeycloakAllOfficerRetireFilterAndRevision(string? accessToken, int? node, string? nodeId, bool isAll, bool? isRetirement, string? revisionId)
{
try
@ -686,7 +1003,7 @@ namespace BMA.EHR.Application.Repositories
}
}
public async Task<List<GetProfileByKeycloakIdRootDto>> GetEmployeeByAdminRole(string? accessToken, int? node, string? nodeId, string role, string? revisionId, int? reqNode, string? reqNodeId)
public async Task<List<GetProfileByKeycloakIdRootDto>> GetEmployeeByAdminRole(string? accessToken, int? node, string? nodeId, string role, string? revisionId, int? reqNode, string? reqNodeId, DateTime? startDate, DateTime? endDate)
{
try
{
@ -699,7 +1016,9 @@ namespace BMA.EHR.Application.Repositories
role = role,
revisionId = revisionId,
reqNode = reqNode,
reqNodeId = reqNodeId
reqNodeId = reqNodeId,
startDate = startDate,
endDate = endDate
};
var profiles = new List<SearchProfileDto>();
@ -720,7 +1039,7 @@ namespace BMA.EHR.Application.Repositories
}
}
public async Task<GetProfileByKeycloakIdRootAddTotalDto> SearchProfile(string? citizenId, string? firstName, string? lastName, string accessToken, int page, int pageSize, string? role, string? nodeId, int? node)
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
{
@ -736,6 +1055,8 @@ namespace BMA.EHR.Application.Repositories
node = node,
page = page,
pageSize = pageSize,
selectedNodeId = selectedNodeId,
selectedNode = selectedNode
};
var profiles = new List<GetProfileByKeycloakIdRootDto>();
@ -938,7 +1259,7 @@ namespace BMA.EHR.Application.Repositories
}
}
public GetOrganizationResponseDTO? GetOc(Guid ocId, int level, string? accessToken)
public GetOrganizationResponseDTO? GetOcByNodeId(Guid ocId, int level, string? accessToken)
{
try
{
@ -969,6 +1290,38 @@ namespace BMA.EHR.Application.Repositories
}
}
public GetOrganizationResponseDTO? GetOc(Guid ocId, int level, string? accessToken)
{
try
{
var apiPath = $"{_configuration["API"]}/org/find/allv2";
var apiKey = _configuration["API_KEY"];
var body = new
{
nodeId = ocId,
node = level
};
var apiResult = PostExternalAPIAsync(apiPath, accessToken ?? "", body, apiKey).Result;
if (apiResult != null)
{
var raw = JsonConvert.DeserializeObject<GetOrganizationResponseResultDTO>(apiResult);
if (raw != null && raw.Result != null)
{
return raw.Result;
}
}
return null;
}
catch
{
throw;
}
}
public GetProfileByIdDto GetOfficerProfileById(Guid id, string? accessToken)
{
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

@ -2,19 +2,27 @@
{
public class GetProfileLeaveByKeycloakDto
{
public string ProfileType { get; set; }
public string Prefix { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string CitizenId { get; set; }
public DateTime BirthDate { get; set; }
public DateTime RetireDate { get; set; }
public string GovAge { get; set; } = string.Empty;
public string Age { get; set; } = string.Empty;
public DateTime DateAppoint { get; set; }
public DateTime DateCurrent { get; set; }
public int Amount { get; set; }
public int? Amount { get; set; } = 0;
public string? TelephoneNumber { get; set; } = string.Empty;
public string PositionName { get; set; } = string.Empty;
public string Position { get; set; } = string.Empty;
public string PosLevel { get; set; } = string.Empty;
public string PosType { get; set; } = string.Empty;
public string? PositionLeaveName { get; set; }
public string? PosExecutiveName { get; set; }
public string CurrentAddress { get; set; } = string.Empty;
public string Oc { get; set; } = string.Empty;
public bool isCommission { get; set; } = false;
public string Root { get; set; } = string.Empty;
public string? Child1 { get; set; }
public string? Child2 { get; set; }

View file

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace BMA.EHR.Application.Responses.Profiles
{
public class GetOcStaff
{
public Guid ProfileId { get; set; }
public Guid Keycloak { get; set; }
public string FullName { get; set; } = null!;
public Guid? RootId { get; set; }
public Guid? OrgChild1Id { get; set; }
public Guid? OrgChild2Id { get; set; }
public Guid? OrgChild3Id { get; set; }
public Guid? OrgChild4Id { get; set; }
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 GetOcStaffResultDto
{
public string Message { get; set; } = string.Empty;
public int Status { get; set; } = -1;
public List<GetOcStaff> Result { get; set; } = new();
}
}

View file

@ -44,6 +44,8 @@ namespace BMA.EHR.Application.Responses.Profiles
public string? ProfileType { get; set; }
public bool? IsLeave { get; set; }
public bool? IsProbation { get; set; }
public string? Root { get; set; }
public string? Child1 { get; set; }
public string? Child2 { get; set; }
@ -80,6 +82,8 @@ namespace BMA.EHR.Application.Responses.Profiles
public string? CurrentZipCode { get; set; }
public string? PositionLeaveName { get; set; }
public string? PosExecutiveName { get; set; }
public string? CommanderPositionName { get; set; } = string.Empty;

View file

@ -25,6 +25,12 @@ namespace BMA.EHR.Application.Responses.Profiles
public DateTime? DateStart { get; set; }
public DateTime? DateAppoint { get; set; }
public string? RootDnaId { get; set; }
public string? Child1DnaId { get; set; }
public string? Child2DnaId { get; set; }
public string? Child3DnaId { get; set; }
public string? Child4DnaId { get; set; }
}
public class GetProfileByKeycloakIdRootAddTotalDto

View file

@ -95,7 +95,6 @@ async Task CallRestApi(string requestData)
}
}
public class ResponseObject
{
[JsonPropertyName("status")]

View file

@ -1,9 +1,9 @@
{
"Rabbit": {
"Host": "192.168.1.40",
"User": "admin",
"Password": "Test123456",
"Queue": "bma-checkin-queue"
},
"API": "https://localhost:7283/api/v1"
}
"Rabbit": {
"Host": "192.168.1.63",
"User": "admin",
"Password": "12345678",
"Queue": "hrms-checkin-queue-dev"
},
"API": "https://localhost:7283/api/v1"
}

View file

@ -93,7 +93,8 @@ namespace BMA.EHR.DisciplineComplaint_Appeal.Service.Controllers
public async Task<ActionResult<ResponseObject>> GetDisciplineUser(string status = "ALL", string type = "ALL", int year = 0, int page = 1, int pageSize = 25, string keyword = "", string? sortBy = null, bool descending = false)
{
var id = "";
var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position";
//var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position";
var apiUrl = $"{_configuration["API"]}/org/dotnet/get-profileId";
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", ""));
@ -357,7 +358,8 @@ namespace BMA.EHR.DisciplineComplaint_Appeal.Service.Controllers
[HttpPost()]
public async Task<ActionResult<ResponseObject>> CreateDiscipline([FromForm] DisciplineComplaint_AppealRequest req)
{
var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position";
//var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position";
var apiUrl = $"{_configuration["API"]}/org/dotnet/get-profileId";
var id = "";
var type = "";
using (var client = new HttpClient())
@ -784,7 +786,7 @@ namespace BMA.EHR.DisciplineComplaint_Appeal.Service.Controllers
? profileAdmin?.RootDnaId
: "";
}
else if (role == "ROOT" || role == "PARENT")
else if (role == "ROOT" /*|| role == "PARENT"*/)
{
nodeId = profileAdmin?.RootDnaId;
}
@ -824,11 +826,11 @@ namespace BMA.EHR.DisciplineComplaint_Appeal.Service.Controllers
data_search = data_search
.Where(x => x.rootDnaId == nodeId).ToList();
}
else if (role == "PARENT")
{
data_search = data_search
.Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList();
}
// else if (role == "PARENT")
// {
// data_search = data_search
// .Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList();
// }
else if (role == "NORMAL")
{
data_search = data_search.Where(x =>

View file

@ -392,7 +392,7 @@ namespace BMA.EHR.DisciplineDirector.Service.Controllers
return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound);
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, token.Replace("Bearer ", ""));
var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, token.Replace("Bearer ", ""));
if (profile == null)
return Error(GlobalMessages.DataNotFound);

View file

@ -1242,14 +1242,22 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("command25/report")]
public async Task<ActionResult<ResponseObject>> PostReportCommand25([FromBody] ReportPersonRequest req)
public async Task<ActionResult<ResponseObject>> PostReportCommand25([FromBody] ReportPersonAndCommandRequest req)
{
try
{
var data = await _context.DisciplineReport_Profiles
.Where(x => req.refIds.Contains(x.Id.ToString()))
.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();
return Success();
}
@ -1276,7 +1284,13 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
.Where(x => req.refIds.Contains(x.Id.ToString()))
// .Where(x => x.Status.ToUpper() == "REPORT")
.ToListAsync();
data.ForEach(profile => profile.Status = "PENDING");
// data.ForEach(profile => profile.Status = "PENDING");
data.ForEach(profile =>
{
profile.Status = "PENDING";
profile.CommandTypeId = null;
profile.CommandCode = null;
});
await _context.SaveChangesAsync();
return Success();
}
@ -1429,14 +1443,22 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpPost("command26/report")]
public async Task<ActionResult<ResponseObject>> PostReportCommand26([FromBody] ReportPersonRequest req)
public async Task<ActionResult<ResponseObject>> PostReportCommand26([FromBody] ReportPersonAndCommandRequest req)
{
try
{
var data = await _context.DisciplineReport_Profiles
.Where(x => req.refIds.Contains(x.Id.ToString()))
.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();
return Success();
}
@ -1463,7 +1485,13 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers
.Where(x => req.refIds.Contains(x.Id.ToString()))
// .Where(x => x.Status.ToUpper() == "REPORT")
.ToListAsync();
data.ForEach(profile => profile.Status = "PENDING");
// data.ForEach(profile => profile.Status = "PENDING");
data.ForEach(profile =>
{
profile.Status = "PENDING";
profile.CommandTypeId = null;
profile.CommandCode = null;
});
await _context.SaveChangesAsync();
return Success();
}

View file

@ -71,6 +71,13 @@ namespace BMA.EHR.DisciplineSuspend.Service.Controllers
{
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
}
// ถ้า FE ส่ง status = PENDING กรอง start/end suspend not null
bool isPending =
!string.IsNullOrEmpty(profileType) &&
!string.IsNullOrEmpty(status) &&
status.Trim().ToUpper() == "PENDING";
// กรองสิทธิ์
string role = jsonData["result"]?.ToString() ?? "";
var nodeId = string.Empty;
@ -102,14 +109,14 @@ namespace BMA.EHR.DisciplineSuspend.Service.Controllers
? profileAdmin?.RootDnaId
: "";
}
else if (role == "ROOT" || role == "PARENT")
else if (role == "ROOT" /*|| role == "PARENT"*/)
{
nodeId = profileAdmin?.RootDnaId;
}
var data_search = (from x in _context.DisciplineReport_Profiles.Include(x => x.DisciplineDisciplinary)
where
(
endDate != null && startDate != null?
endDate != null && startDate != null ?
(
(x.StartDateSuspend.Value.Date >= startDate.Value.Date && x.StartDateSuspend.Value.Date <= endDate.Value.Date) ||
(x.EndDateSuspend.Value.Date >= startDate.Value.Date && x.EndDateSuspend.Value.Date <= endDate.Value.Date) ||
@ -137,14 +144,19 @@ namespace BMA.EHR.DisciplineSuspend.Service.Controllers
(
!string.IsNullOrEmpty(status) ? x.Status!.Trim().ToUpper() == status : true
)
// ถ้า FE ส่ง status = PENDING กรอง start/end suspend not null
&&
(
isPending
? x.StartDateSuspend != null && x.EndDateSuspend != null
: true
)
&&
(
role == "OWNER"
? true
: role == "ROOT"
? x.rootDnaId == nodeId
: role == "PARENT"
? x.rootDnaId == nodeId && x.child1DnaId != null
: role == "CHILD"
? (
profileAdmin.Node == 4 ? x.child4DnaId == nodeId :
@ -177,125 +189,119 @@ namespace BMA.EHR.DisciplineSuspend.Service.Controllers
)
select x).ToList();
var query = data_search
.Select(x => new
{
Id = x.Id,
CitizenId = x.CitizenId,
Prefix = x.Prefix,
FirstName = x.FirstName,
LastName = x.LastName,
ProfileId = x.PersonId,
Organization = x.Organization,
root = x.root,
rootId = x.rootId,
rootDnaId = x.rootDnaId,
rootShortName = x.rootShortName,
child1 = x.child1,
child1Id = x.child1Id,
child1DnaId = x.child1DnaId,
child1ShortName = x.child1ShortName,
child2 = x.child2,
child2Id = x.child2Id,
child2DnaId = x.child2DnaId,
child2ShortName = x.child2ShortName,
child3 = x.child3,
child3Id = x.child3Id,
child3DnaId = x.child3DnaId,
child3ShortName = x.child3ShortName,
child4 = x.child4,
child4Id = x.child4Id,
child4DnaId = x.child4DnaId,
child4ShortName = x.child4ShortName,
posMasterNo = x.posMasterNo,
posTypeId = x.posTypeId,
posTypeName = x.posTypeName,
posLevelId = x.posLevelId,
posLevelName = x.posLevelName,
.Select(x => new
{
Id = x.Id,
CitizenId = x.CitizenId,
Prefix = x.Prefix,
FirstName = x.FirstName,
LastName = x.LastName,
ProfileId = x.PersonId,
Organization = x.Organization,
root = x.root,
rootId = x.rootId,
rootDnaId = x.rootDnaId,
rootShortName = x.rootShortName,
child1 = x.child1,
child1Id = x.child1Id,
child1DnaId = x.child1DnaId,
child1ShortName = x.child1ShortName,
child2 = x.child2,
child2Id = x.child2Id,
child2DnaId = x.child2DnaId,
child2ShortName = x.child2ShortName,
child3 = x.child3,
child3Id = x.child3Id,
child3DnaId = x.child3DnaId,
child3ShortName = x.child3ShortName,
child4 = x.child4,
child4Id = x.child4Id,
child4DnaId = x.child4DnaId,
child4ShortName = x.child4ShortName,
posMasterNo = x.posMasterNo,
posTypeId = x.posTypeId,
posTypeName = x.posTypeName,
posLevelId = x.posLevelId,
posLevelName = x.posLevelName,
Position = x.Position,
PosNo = x.PosNo,
PositionLevel = x.PositionLevel == null ? "" : x.PositionLevel,
PositionType = x.PositionType == null ? "" : x.PositionType,
Salary = x.Salary,
Status = x.Status,
DescriptionSuspend = x.DescriptionSuspend,
StartDateSuspend = x.StartDateSuspend,
EndDateSuspend = x.EndDateSuspend,
Title = x.DisciplineDisciplinary.Title,
OffenseDetails = x.DisciplineDisciplinary.OffenseDetails,//ลักษณะความผิด
DisciplinaryFaultLevel = x.DisciplineDisciplinary.DisciplinaryFaultLevel,//ระดับโทษความผิด
DisciplinaryCaseFault = x.DisciplineDisciplinary.DisciplinaryCaseFault,//กรณีความผิด
profileType = x.profileType,
CreatedAt = x.CreatedAt,
});
Position = x.Position,
PosNo = x.PosNo,
PositionLevel = x.PositionLevel == null ? "" : x.PositionLevel,
PositionType = x.PositionType == null ? "" : x.PositionType,
Salary = x.Salary,
Status = x.Status,
DescriptionSuspend = x.DescriptionSuspend,
StartDateSuspend = x.StartDateSuspend,
EndDateSuspend = x.EndDateSuspend,
Title = x.DisciplineDisciplinary.Title,
OffenseDetails = x.DisciplineDisciplinary.OffenseDetails,//ลักษณะความผิด
DisciplinaryFaultLevel = x.DisciplineDisciplinary.DisciplinaryFaultLevel,//ระดับโทษความผิด
DisciplinaryCaseFault = x.DisciplineDisciplinary.DisciplinaryCaseFault,//กรณีความผิด
profileType = x.profileType,
CreatedAt = x.CreatedAt,
});
bool desc = descending ?? false;
if (!string.IsNullOrEmpty(sortBy))
{
if (sortBy == "title")
{
query = desc ? query.OrderByDescending(x => x.Title)
: query.OrderBy(x => x.Title);
}
else if (sortBy == "prefix" || sortBy == "firstName" || sortBy == "lastName")
{
query = desc ?
query
//.OrderByDescending(x => x.Prefix)
.OrderByDescending(x => x.FirstName)
.ThenByDescending(x => x.LastName) :
query
//.OrderBy(x => x.Prefix)
.OrderBy(x => x.FirstName)
.ThenBy(x => x.LastName);
}
else if (sortBy == "position")
{
query = desc ? query.OrderByDescending(x => x.Position)
: query.OrderBy(x => x.Position);
}
else if (sortBy == "positionType" || sortBy == "positionLevel")
{
query = desc ?
query
.OrderByDescending(x => x.PositionType)
.ThenByDescending(x => x.PositionLevel) :
query
.OrderBy(x => x.PositionType)
.ThenBy(x => x.PositionLevel);
}
else if (sortBy == "organization")
{
query = desc ? query.OrderByDescending(x => x.Organization)
: query.OrderBy(x => x.Organization);
}
else if (sortBy == "startDateSuspend")
{
query = desc ? query.OrderByDescending(x => x.StartDateSuspend)
: query.OrderBy(x => x.StartDateSuspend);
}
else if (sortBy == "endDateSuspend")
{
query = desc ? query.OrderByDescending(x => x.EndDateSuspend)
: query.OrderBy(x => x.EndDateSuspend);
}
else if (sortBy == "descriptionSuspend")
{
query = desc ? query.OrderByDescending(x => x.DescriptionSuspend)
: query.OrderBy(x => x.DescriptionSuspend);
}
else
{
query = query.OrderByDescending(x => x.profileType)
.ThenByDescending(x => x.CreatedAt)
.ThenByDescending(x => x.CitizenId);
}
}
bool desc = descending ?? false;
if (!string.IsNullOrEmpty(sortBy))
{
if (sortBy == "title")
{
query = desc ? query.OrderByDescending(x => x.Title)
: query.OrderBy(x => x.Title);
}
else if (sortBy == "prefix" || sortBy == "firstName" || sortBy == "lastName")
{
query = desc ?
query.OrderByDescending(x => x.FirstName).ThenByDescending(x => x.LastName) :
query.OrderBy(x => x.FirstName).ThenBy(x => x.LastName);
}
else if (sortBy == "position")
{
query = desc ? query.OrderByDescending(x => x.Position)
: query.OrderBy(x => x.Position);
}
else if (sortBy == "positionType" || sortBy == "positionLevel")
{
query = desc ?
query
.OrderByDescending(x => x.PositionType)
.ThenByDescending(x => x.PositionLevel) :
query
.OrderBy(x => x.PositionType)
.ThenBy(x => x.PositionLevel);
}
else if (sortBy == "organization")
{
query = desc ? query.OrderByDescending(x => x.Organization)
: query.OrderBy(x => x.Organization);
}
else if (sortBy == "startDateSuspend")
{
query = desc ? query.OrderByDescending(x => x.StartDateSuspend)
: query.OrderBy(x => x.StartDateSuspend);
}
else if (sortBy == "endDateSuspend")
{
query = desc ? query.OrderByDescending(x => x.EndDateSuspend)
: query.OrderBy(x => x.EndDateSuspend);
}
else if (sortBy == "descriptionSuspend")
{
query = desc ? query.OrderByDescending(x => x.DescriptionSuspend)
: query.OrderBy(x => x.DescriptionSuspend);
}
else
{
query = query.OrderByDescending(x => x.profileType)
.ThenByDescending(x => x.CreatedAt)
.ThenByDescending(x => x.CitizenId);
}
}
var data = query
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToList();
var data = query
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToList();
return Success(new { data, total = data_search.Count() });
}

View file

@ -8,4 +8,12 @@ namespace BMA.EHR.Discipline.Service.Requests
public string[] refIds { get; set; }
public string? status { get; set; }
}
public class ReportPersonAndCommandRequest
{
public string[] refIds { get; set; }
public string? status { get; set; }
public string? commandTypeId { get; set; }
public string? commandCode { get; set; }
}
}

View file

@ -1,4 +1,5 @@
using BMA.EHR.Domain.Shared;
using BMA.EHR.Domain.Extensions;
using BMA.EHR.Domain.Shared;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@ -81,6 +82,20 @@ namespace BMA.EHR.Domain.Common
}
#endregion
#region " Properties "
protected string? EmpType => User.GetEmpType();
protected Guid? OrgChild1DnaId => User.GetOrgChild1DnaId();
protected Guid? OrgChild2DnaId => User.GetOrgChild2DnaId();
protected Guid? OrgChild3DnaId => User.GetOrgChild3DnaId();
protected Guid? OrgChild4DnaId => User.GetOrgChild4DnaId();
protected Guid? OrgRootDnaId => User.GetOrgRootDnaId();
protected Guid? ProfileId => User.GetProfileId();
protected string? Prefix => User.GetPrefix();
protected string? FullNameFromClaim => User.GetName();
#endregion
#endregion

View file

@ -0,0 +1,39 @@
namespace BMA.EHR.Domain.Common
{
public class TokenUserInfo
{
// Existing properties
public string KeycloakId { get; set; } = string.Empty;
public string? PreferredUsername { get; set; }
public string? GivenName { get; set; }
public string? FamilyName { get; set; }
// New properties to add
public string? EmpType { get; set; }
public Guid? OrgChild1DnaId { get; set; }
public Guid? OrgChild2DnaId { get; set; }
public Guid? OrgChild3DnaId { get; set; }
public Guid? OrgChild4DnaId { get; set; }
public Guid? OrgRootDnaId { get; set; }
public Guid? ProfileId { get; set; }
public string? Prefix { get; set; }
public string? Name { get; set; }
}
// Claim type constants
public static class BmaClaimTypes
{
public const string EmpType = "empType";
public const string OrgChild1DnaId = "orgChild1DnaId";
public const string OrgChild2DnaId = "orgChild2DnaId";
public const string OrgChild3DnaId = "orgChild3DnaId";
public const string OrgChild4DnaId = "orgChild4DnaId";
public const string OrgRootDnaId = "orgRootDnaId";
public const string ProfileId = "profileId";
public const string Prefix = "prefix";
public const string Name = "name";
public const string GivenName = "given_name";
public const string FamilyName = "family_name";
public const string PreferredUsername = "preferred_username";
}
}

View file

@ -0,0 +1,30 @@
using BMA.EHR.Domain.Common;
using System.Security.Claims;
namespace BMA.EHR.Domain.Extensions
{
public static class ClaimsPrincipalExtensions
{
public static string? GetClaimValue(this ClaimsPrincipal user, string claimType)
{
return user?.FindFirst(claimType)?.Value;
}
public static Guid? GetGuidClaim(this ClaimsPrincipal user, string claimType)
{
var value = user?.GetClaimValue(claimType);
return Guid.TryParse(value, out var guid) ? guid : null;
}
// Convenience methods for common claims
public static string? GetEmpType(this ClaimsPrincipal user) => user.GetClaimValue(BmaClaimTypes.EmpType);
public static Guid? GetOrgChild1DnaId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.OrgChild1DnaId);
public static Guid? GetOrgChild2DnaId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.OrgChild2DnaId);
public static Guid? GetOrgChild3DnaId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.OrgChild3DnaId);
public static Guid? GetOrgChild4DnaId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.OrgChild4DnaId);
public static Guid? GetOrgRootDnaId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.OrgRootDnaId);
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? GetName(this ClaimsPrincipal user) => user.GetClaimValue(BmaClaimTypes.Name);
}
}

View file

@ -174,6 +174,29 @@ namespace BMA.EHR.Domain.Extensions
}
}
public static (int Years, int Months, int Days) GetDifference(this DateTime from, DateTime to)
{
if (from > to) (from, to) = (to, from); // swap ถ้าลำดับสลับ
int years = to.Year - from.Year;
int months = to.Month - from.Month;
int days = to.Day - from.Day;
if (days < 0)
{
months--;
days += DateTime.DaysInMonth(to.Year, to.Month == 1 ? 12 : to.Month - 1);
}
if (months < 0)
{
years--;
months += 12;
}
return (years, months, days);
}
public static int CalculateAge(this DateTime date, int plusYear = 0, int subtractYear = 0)
{
try

View file

@ -18,6 +18,10 @@ namespace BMA.EHR.Domain.Middlewares
{
private readonly RequestDelegate _next;
private readonly IConfiguration _configuration;
private static ElasticClient? _elasticClient;
private static readonly object _lock = new object();
private static readonly Dictionary<string, (GetProfileByKeycloakIdLocal Profile, DateTime ExpiryTime)> _profileCache = new();
private static readonly TimeSpan _cacheExpiry = TimeSpan.FromMinutes(10);
private string Uri = "";
private string IndexFormat = "";
@ -31,19 +35,28 @@ namespace BMA.EHR.Domain.Middlewares
Uri = _configuration["ElasticConfiguration:Uri"] ?? "http://192.168.1.40:9200";
IndexFormat = _configuration["ElasticConfiguration:IndexFormat"] ?? "bma-ehr-log-index";
SystemName = _configuration["ElasticConfiguration:SystemName"] ?? "Unknown";
// สร้าง ElasticClient แค่ครั้งเดียว
if (_elasticClient == null)
{
lock (_lock)
{
if (_elasticClient == null)
{
var settings = new ConnectionSettings(new Uri(Uri))
.DefaultIndex(IndexFormat)
.DisableDirectStreaming() // เพิ่มประสิทธิภาพ
.RequestTimeout(TimeSpan.FromSeconds(5)); // กำหนด timeout
_elasticClient = new ElasticClient(settings);
}
}
}
}
public async Task Invoke(HttpContext context)
{
Console.WriteLine("=== CombinedErrorHandlerAndLoggingMiddleware Start ===");
var settings = new ConnectionSettings(new Uri(Uri))
.DefaultIndex(IndexFormat);
var client = new ElasticClient(settings);
var startTime = DateTime.UtcNow;
var stopwatch = Stopwatch.StartNew();
string? responseBodyJson = null;
string? requestBodyJson = null;
Exception? caughtException = null;
@ -64,27 +77,41 @@ namespace BMA.EHR.Domain.Middlewares
string keycloakId = Guid.Empty.ToString("D");
var token = context.Request.Headers["Authorization"];
GetProfileByKeycloakIdLocal? pf = null;
var tokenUserInfo = await ExtractTokenUserInfoAsync(token);
// ลองดึง keycloakId จาก JWT token ก่อน (ถ้ามี)
try
{
keycloakId = await ExtractKeycloakIdFromToken(token);
}
catch (Exception ex)
{
Console.WriteLine($"Error extracting keycloakId from token: {ex.Message}");
}
// Store tokenUserInfo in HttpContext.Items for controllers to use
context.Items["TokenUserInfo"] = tokenUserInfo;
try
// ดึง keycloakId จาก JWT token
keycloakId = tokenUserInfo.KeycloakId;
// ดึง profile จาก claims หรือ cache หรือ API
if (Guid.TryParse(keycloakId, out var parsedId) && parsedId != Guid.Empty)
{
if (Guid.TryParse(keycloakId, out var parsedId) && parsedId != Guid.Empty)
// Build profile from token claims if available
if (tokenUserInfo.OrgRootDnaId.HasValue && tokenUserInfo.ProfileId.HasValue)
{
pf = await GetProfileByKeycloakIdAsync(parsedId, token);
pf = new GetProfileByKeycloakIdLocal
{
Id = tokenUserInfo.ProfileId.Value,
CitizenId = tokenUserInfo.PreferredUsername,
Prefix = tokenUserInfo.Prefix,
FirstName = tokenUserInfo.GivenName,
LastName = tokenUserInfo.FamilyName,
RootDnaId = tokenUserInfo.OrgRootDnaId,
Child1DnaId = tokenUserInfo.OrgChild1DnaId,
Child2DnaId = tokenUserInfo.OrgChild2DnaId,
Child3DnaId = tokenUserInfo.OrgChild3DnaId,
Child4DnaId = tokenUserInfo.OrgChild4DnaId,
};
Console.WriteLine($"[INFO] Using claims for profile - OrgRootDnaId: {pf.RootDnaId}, ProfileId: {pf.Id}");
}
else
{
// Fallback to API only if critical claims are missing
Console.WriteLine("[WARN] Critical claims missing, falling back to API call");
pf = await GetProfileWithCacheAsync(parsedId, token);
}
}
catch (Exception ex)
{
Console.WriteLine($"Error getting profile: {ex.Message}");
}
try
@ -103,17 +130,17 @@ namespace BMA.EHR.Domain.Middlewares
Console.WriteLine($"Updated keycloakId from authenticated user: {keycloakId}");
// อัพเดต profile ด้วย keycloakId ที่ถูกต้อง
try
{
if (Guid.TryParse(keycloakId, out var parsedId))
{
pf = await GetProfileByKeycloakIdAsync(parsedId, token);
}
}
catch (Exception ex)
{
Console.WriteLine($"Error updating profile after authentication: {ex.Message}");
}
// try
// {
// if (Guid.TryParse(keycloakId, out var parsedId))
// {
// //pf = await GetProfileByKeycloakIdAsync(parsedId, token);
// }
// }
// catch (Exception ex)
// {
// Console.WriteLine($"Error updating profile after authentication: {ex.Message}");
// }
}
}
@ -142,14 +169,48 @@ namespace BMA.EHR.Domain.Middlewares
finally
{
stopwatch.Stop();
await LogRequest(context, client, startTime, stopwatch, pf, keycloakId, requestBodyJson, memoryStream, caughtException);
// เขียนข้อมูลกลับไปยัง original Response body
// อ่านข้อมูล response ก่อนที่ stream จะถูก dispose
string? responseBodyForLogging = null;
if (memoryStream.Length > 0)
{
memoryStream.Seek(0, SeekOrigin.Begin);
using var reader = new StreamReader(memoryStream, leaveOpen: true);
responseBodyForLogging = await reader.ReadToEndAsync();
memoryStream.Seek(0, SeekOrigin.Begin);
}
// เก็บข้อมูลที่จำเป็นจาก HttpContext ก่อนที่มันจะถูก dispose
var logData = new
{
RemoteIpAddress = context.Connection.RemoteIpAddress?.ToString(),
HostValue = context.Request.Host.Value,
Method = context.Request.Method,
Path = context.Request.Path.ToString(),
QueryString = context.Request.QueryString.ToString(),
StatusCode = context.Response.StatusCode,
ContentType = context.Response.ContentType ?? ""
};
// เขียนข้อมูลกลับไปยัง original Response body ก่อน
if (memoryStream.Length > 0)
{
memoryStream.Seek(0, SeekOrigin.Begin);
await memoryStream.CopyToAsync(originalBodyStream);
}
// ทำ logging แบบ await
Console.WriteLine("[DEBUG] Starting logging...");
try
{
await LogRequestAsync(_elasticClient!, startTime, stopwatch, pf, keycloakId, requestBodyJson, responseBodyForLogging, caughtException, logData);
Console.WriteLine("[DEBUG] Logging completed successfully");
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Logging error: {ex.Message}");
Console.WriteLine($"[ERROR] Stack trace: {ex.StackTrace}");
}
}
}
@ -379,15 +440,16 @@ namespace BMA.EHR.Domain.Middlewares
}
}
private async Task LogRequest(HttpContext context, ElasticClient client, DateTime startTime, Stopwatch stopwatch,
GetProfileByKeycloakIdLocal? pf, string keycloakId, string? requestBodyJson, MemoryStream memoryStream, Exception? caughtException)
private async Task LogRequestAsync(ElasticClient client, DateTime startTime, Stopwatch stopwatch,
GetProfileByKeycloakIdLocal? pf, string keycloakId, string? requestBodyJson, string? responseBodyForLogging, Exception? caughtException, dynamic contextData)
{
Console.WriteLine("[DEBUG] LogRequestAsync called");
try
{
var processTime = stopwatch.ElapsedMilliseconds;
var endTime = DateTime.UtcNow;
var statusCode = caughtException != null ? (int)HttpStatusCode.InternalServerError : context.Response.StatusCode;
var statusCode = caughtException != null ? (int)HttpStatusCode.InternalServerError : (int)contextData.StatusCode;
var logType = caughtException != null ? "error" : statusCode switch
{
@ -399,58 +461,41 @@ namespace BMA.EHR.Domain.Middlewares
string? message = null;
string? responseBodyJson = null;
// อ่านข้อมูลจาก Response
if (memoryStream.Length > 0)
// ใช้ response body ที่ส่งมาจากการอ่านก่อนหน้า
if (!string.IsNullOrEmpty(responseBodyForLogging))
{
memoryStream.Seek(0, SeekOrigin.Begin);
var responseBody = new StreamReader(memoryStream).ReadToEnd();
var contentType = (string)contextData.ContentType;
var isFileResponse = !contentType.StartsWith("application/json") && !contentType.StartsWith("text/html") && (
contentType.StartsWith("application/") ||
contentType.StartsWith("image/") ||
contentType.StartsWith("audio/")
);
if (!string.IsNullOrEmpty(responseBody))
if (isFileResponse)
{
var contentType = context.Response.ContentType;
var isFileResponse = !contentType.StartsWith("application/json") && !contentType.StartsWith("text/html") && (
contentType.StartsWith("application/") ||
contentType.StartsWith("image/") ||
contentType.StartsWith("audio/") ||
context.Response.Headers.ContainsKey("Content-Disposition")
);
if (isFileResponse)
responseBodyJson = "";
message = "success";
}
else
{
// ใช้ response body ที่มีอยู่แล้วโดยไม่ serialize ซ้ำ
responseBodyJson = responseBodyForLogging;
try
{
responseBodyJson = "";
message = "success";
var json = JsonSerializer.Deserialize<JsonElement>(responseBodyForLogging);
if (json.ValueKind == JsonValueKind.Array)
{
message = "success";
}
else if (json.TryGetProperty("message", out var messageElement))
{
message = messageElement.GetString();
}
}
else
catch
{
try
{
var jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = true,
Converters = { new DateTimeFixConverter() }
};
responseBodyJson = JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(responseBody), jsonOptions);
var json = JsonSerializer.Deserialize<JsonElement>(responseBody);
if (json.ValueKind == JsonValueKind.Array)
{
message = "success";
}
else
{
if (json.TryGetProperty("message", out var messageElement))
{
message = messageElement.GetString();
}
}
}
catch
{
responseBodyJson = responseBody;
message = caughtException?.Message ?? "Unknown error";
}
message = caughtException?.Message ?? "Unknown error";
}
}
}
@ -463,15 +508,16 @@ namespace BMA.EHR.Domain.Middlewares
var logData = new
{
logType = logType,
ip = context.Connection.RemoteIpAddress?.ToString(),
rootId = pf?.RootId,
ip = (string)contextData.RemoteIpAddress,
//rootId = pf?.RootId,
rootId = pf?.RootDnaId,
systemName = SystemName,
startTimeStamp = startTime.ToString("o"),
endTimeStamp = endTime.ToString("o"),
processTime = processTime,
host = context.Request.Host.Value,
method = context.Request.Method,
endpoint = context.Request.Path + context.Request.QueryString,
host = (string)contextData.HostValue,
method = (string)contextData.Method,
endpoint = (string)contextData.Path + (string)contextData.QueryString,
responseCode = statusCode == 304 ? "200" : statusCode.ToString(),
responseDescription = message,
input = requestBodyJson,
@ -482,11 +528,19 @@ namespace BMA.EHR.Domain.Middlewares
exception = caughtException?.ToString()
};
await client.IndexDocumentAsync(logData);
Console.WriteLine($"[DEBUG] Sending log to Elasticsearch: {logType} - {(string)contextData.Method} {(string)contextData.Path}");
var response = await client.IndexDocumentAsync(logData);
Console.WriteLine($"[DEBUG] Elasticsearch response: IsValid={response.IsValid}, Index={response.Index}");
if (!response.IsValid)
{
Console.WriteLine($"[ERROR] Elasticsearch error: {response.OriginalException?.Message ?? response.ServerError?.ToString()}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Error logging request: {ex.Message}");
Console.WriteLine($"[ERROR] Error logging request: {ex.Message}");
Console.WriteLine($"[ERROR] Stack trace: {ex.StackTrace}");
}
}
@ -536,11 +590,19 @@ namespace BMA.EHR.Domain.Middlewares
private async Task<string> ExtractKeycloakIdFromToken(string? authorizationHeader)
{
var tokenInfo = await ExtractTokenUserInfoAsync(authorizationHeader);
return tokenInfo.KeycloakId;
}
private async Task<TokenUserInfo> ExtractTokenUserInfoAsync(string? authorizationHeader)
{
var defaultResult = new TokenUserInfo { KeycloakId = Guid.Empty.ToString("D") };
try
{
if (string.IsNullOrEmpty(authorizationHeader) || !authorizationHeader.StartsWith("Bearer "))
{
return Guid.Empty.ToString("D");
return defaultResult;
}
var token = authorizationHeader.Replace("Bearer ", "");
@ -549,7 +611,7 @@ namespace BMA.EHR.Domain.Middlewares
var parts = token.Split('.');
if (parts.Length != 3)
{
return Guid.Empty.ToString("D");
return defaultResult;
}
// Decode Base64Url payload (JWT uses Base64Url encoding, not standard Base64)
@ -570,31 +632,136 @@ namespace BMA.EHR.Domain.Middlewares
Console.WriteLine($"JWT Payload: {payloadJson}");
// Parse JSON และดึง sub (subject) claim
// Parse JSON และดึง claims ต่างๆ
var jsonDoc = JsonDocument.Parse(payloadJson);
var result = new TokenUserInfo();
// ลองหา keycloak ID ใน claims ต่างๆ
string? keycloakId = null;
// ดึง keycloak ID
if (jsonDoc.RootElement.TryGetProperty("sub", out var subElement))
{
keycloakId = subElement.GetString();
result.KeycloakId = subElement.GetString() ?? Guid.Empty.ToString("D");
}
else if (jsonDoc.RootElement.TryGetProperty("nameid", out var nameidElement))
{
keycloakId = nameidElement.GetString();
result.KeycloakId = nameidElement.GetString() ?? Guid.Empty.ToString("D");
}
else if (jsonDoc.RootElement.TryGetProperty("user_id", out var userIdElement))
{
keycloakId = userIdElement.GetString();
result.KeycloakId = userIdElement.GetString() ?? Guid.Empty.ToString("D");
}
else
{
result.KeycloakId = Guid.Empty.ToString("D");
}
return keycloakId ?? Guid.Empty.ToString("D");
// ดึง preferred_username
if (jsonDoc.RootElement.TryGetProperty("preferred_username", out var preferredUsernameElement))
{
result.PreferredUsername = preferredUsernameElement.GetString();
Console.WriteLine($"Extracted preferred_username: {result.PreferredUsername}");
}
// ดึง given_name
if (jsonDoc.RootElement.TryGetProperty("given_name", out var givenNameElement))
{
result.GivenName = givenNameElement.GetString();
Console.WriteLine($"Extracted given_name: {result.GivenName}");
}
// ดึง family_name
if (jsonDoc.RootElement.TryGetProperty("family_name", out var familyNameElement))
{
result.FamilyName = familyNameElement.GetString();
Console.WriteLine($"Extracted family_name: {result.FamilyName}");
}
// ดึง empType
if (jsonDoc.RootElement.TryGetProperty("empType", out var empTypeElement))
{
result.EmpType = empTypeElement.GetString();
Console.WriteLine($"Extracted empType: {result.EmpType}");
}
// ดึง orgChild1DnaId
if (jsonDoc.RootElement.TryGetProperty("orgChild1DnaId", out var orgChild1Element))
{
if (Guid.TryParse(orgChild1Element.GetString(), out var orgChild1Guid))
{
result.OrgChild1DnaId = orgChild1Guid;
Console.WriteLine($"Extracted orgChild1DnaId: {result.OrgChild1DnaId}");
}
}
// ดึง orgChild2DnaId
if (jsonDoc.RootElement.TryGetProperty("orgChild2DnaId", out var orgChild2Element))
{
if (Guid.TryParse(orgChild2Element.GetString(), out var orgChild2Guid))
{
result.OrgChild2DnaId = orgChild2Guid;
Console.WriteLine($"Extracted orgChild2DnaId: {result.OrgChild2DnaId}");
}
}
// ดึง orgChild3DnaId
if (jsonDoc.RootElement.TryGetProperty("orgChild3DnaId", out var orgChild3Element))
{
if (Guid.TryParse(orgChild3Element.GetString(), out var orgChild3Guid))
{
result.OrgChild3DnaId = orgChild3Guid;
Console.WriteLine($"Extracted orgChild3DnaId: {result.OrgChild3DnaId}");
}
}
// ดึง orgChild4DnaId
if (jsonDoc.RootElement.TryGetProperty("orgChild4DnaId", out var orgChild4Element))
{
if (Guid.TryParse(orgChild4Element.GetString(), out var orgChild4Guid))
{
result.OrgChild4DnaId = orgChild4Guid;
Console.WriteLine($"Extracted orgChild4DnaId: {result.OrgChild4DnaId}");
}
}
// ดึง orgRootDnaId
if (jsonDoc.RootElement.TryGetProperty("orgRootDnaId", out var orgRootElement))
{
if (Guid.TryParse(orgRootElement.GetString(), out var orgRootGuid))
{
result.OrgRootDnaId = orgRootGuid;
Console.WriteLine($"Extracted orgRootDnaId: {result.OrgRootDnaId}");
}
}
// ดึง profileId
if (jsonDoc.RootElement.TryGetProperty("profileId", out var profileIdElement))
{
if (Guid.TryParse(profileIdElement.GetString(), out var profileIdGuid))
{
result.ProfileId = profileIdGuid;
Console.WriteLine($"Extracted profileId: {result.ProfileId}");
}
}
// ดึง prefix
if (jsonDoc.RootElement.TryGetProperty("prefix", out var prefixElement))
{
result.Prefix = prefixElement.GetString();
Console.WriteLine($"Extracted prefix: {result.Prefix}");
}
// ดึง name
if (jsonDoc.RootElement.TryGetProperty("name", out var nameElement))
{
result.Name = nameElement.GetString();
Console.WriteLine($"Extracted name: {result.Name}");
}
return result;
}
catch (Exception ex)
{
Console.WriteLine($"Error extracting keycloak ID from token: {ex.Message}");
return Guid.Empty.ToString("D");
Console.WriteLine($"Error extracting token user info: {ex.Message}");
return defaultResult;
}
}
@ -625,7 +792,8 @@ namespace BMA.EHR.Domain.Middlewares
{
try
{
var apiPath = $"{_configuration["API"]}/org/dotnet/keycloak/{keycloakId}";
//var apiPath = $"{_configuration["API"]}/org/dotnet/by-keycloak/{keycloakId}";
var apiPath = $"{_configuration["API"]}/org/dotnet/user-logs/{keycloakId}";
var apiKey = _configuration["API_KEY"];
var apiResult = await GetExternalAPIAsync(apiPath, accessToken ?? "", apiKey);
@ -640,7 +808,58 @@ namespace BMA.EHR.Domain.Middlewares
}
catch
{
throw;
return null;
}
}
private async Task<GetProfileByKeycloakIdLocal?> GetProfileWithCacheAsync(Guid keycloakId, string? accessToken)
{
var cacheKey = keycloakId.ToString();
// ตรวจสอบ cache
lock (_profileCache)
{
if (_profileCache.TryGetValue(cacheKey, out var cached))
{
if (cached.ExpiryTime > DateTime.UtcNow)
{
return cached.Profile;
}
// ลบ cache ที่หมดอายุ
_profileCache.Remove(cacheKey);
}
}
// ดึงข้อมูลจาก API
try
{
var profile = await GetProfileByKeycloakIdAsync(keycloakId, accessToken);
if (profile != null)
{
// เก็บใน cache
lock (_profileCache)
{
_profileCache[cacheKey] = (profile, DateTime.UtcNow.Add(_cacheExpiry));
// ลบ cache เก่าที่เกิน 1000 รายการ
if (_profileCache.Count > 1000)
{
var expiredKeys = _profileCache
.Where(x => x.Value.ExpiryTime < DateTime.UtcNow)
.Select(x => x.Key)
.ToList();
foreach (var key in expiredKeys)
{
_profileCache.Remove(key);
}
}
}
}
return profile;
}
catch
{
return null;
}
}

View file

@ -24,12 +24,15 @@ namespace BMA.EHR.Domain.Models.Leave.Requests
[Required, Comment("ปีงบประมาณ")]
public int LeaveYear { get; set; } = 0;
[Required, Comment("จำนวนวันลายกมา")]
[Required, Comment("จำนวนวันลาทั้งหมด")]
public double LeaveDays { get; set; } = 0.0;
[Required, Comment("จำนวนวันลาที่ใช้ไป")]
public double LeaveDaysUsed { get; set; } = 0.0;
[Comment("จำนวนครั้งที่ลาสะสม")]
public int LeaveCount { get; set; } = 0;
public Guid? RootDnaId { get; set; }
public Guid? Child1DnaId { get; set; }
@ -39,5 +42,11 @@ namespace BMA.EHR.Domain.Models.Leave.Requests
public Guid? Child3DnaId { get; set; }
public Guid? Child4DnaId { get; set; }
[Required, Comment("จำนวนวันลายกมา")]
public double BeginningLeaveDays { get; set; } = 0.0;
[Comment("จำนวนครั้งที่ลายกมา")]
public int BeginningLeaveCount { get; set; } = 0;
}
}

View file

@ -17,6 +17,15 @@ namespace BMA.EHR.Domain.Models.Leave.Requests
public string PositionName { get; set; } = string.Empty;
[Comment("ประเภทระดับตำแหน่ง")]
public string PositionLevelName { get; set; } = string.Empty;
[Comment("ตำแหน่งทางการบริหาร")]
public string PosExecutiveName { get; set; } = string.Empty;
[Comment("สังกัด")]
public string OrganizationName { get; set; } = string.Empty;
[Comment("ตำแหน่งใต้ลายเช็นต์")]
public string? PositionSign { get; set; } = string.Empty;

View file

@ -0,0 +1,39 @@
using BMA.EHR.Domain.Models.Base;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
namespace BMA.EHR.Domain.Models.Leave.TimeAttendants
{
public class CheckInJobStatus : EntityBase
{
[Required, Comment("Task ID สำหรับติดตามสถานะงาน")]
public Guid TaskId { get; set; } = Guid.Empty;
[Required, Comment("รหัส User ของ Keycloak")]
public Guid KeycloakUserId { get; set; } = Guid.Empty;
[Comment("วันเวลาที่สร้างงาน")]
public DateTime CreatedDate { get; set; } = DateTime.Now;
[Comment("วันเวลาที่เริ่มประมวลผล")]
public DateTime? ProcessingDate { get; set; }
[Comment("วันเวลาที่เสร็จสิ้นการประมวลผล")]
public DateTime? CompletedDate { get; set; }
[Required, Comment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED")]
public string Status { get; set; } = "PENDING";
[Comment("ประเภทการลงเวลา: CHECK_IN, CHECK_OUT")]
public string? CheckType { get; set; }
[Comment("CheckInId สำหรับ Check-Out")]
public Guid? CheckInId { get; set; }
[Comment("ข้อความแสดงข้อผิดพลาด")]
public string? ErrorMessage { get; set; }
[Comment("ข้อมูลเพิ่มเติม (JSON)")]
public string? AdditionalData { get; set; }
}
}

View file

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using BMA.EHR.Domain.Models.Base;
using Microsoft.EntityFrameworkCore;
namespace BMA.EHR.Domain.Models.Leave.TimeAttendants
{
public class LeaveProcessJobStatus: EntityBase
{
[Required, Comment("วันเริ่มต้น")]
public DateTime StartDate { get; set; }
[Required, Comment("วันสิ้นสุด")]
public DateTime EndDate { get; set; }
[Required, Comment("รหัส Root DNA Id")]
public Guid RootDnaId { get; set; } = Guid.Empty;
[Comment("วันเวลาที่สร้างงาน")]
public DateTime CreatedDate { get; set; } = DateTime.Now;
[Comment("วันเวลาที่เริ่มประมวลผล")]
public DateTime? ProcessingDate { get; set; }
[Comment("วันเวลาที่เสร็จสิ้นการประมวลผล")]
public DateTime? CompletedDate { get; set; }
[Required, Comment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED")]
public string Status { get; set; } = "PENDING";
[Comment("ข้อความแสดงข้อผิดพลาด")]
public string? ErrorMessage { get; set; }
}
}

View file

@ -8,6 +8,8 @@
public static readonly string DataNotFound = "ไม่พบข้อมูลในระบบ";
public static readonly string ProfileNotFound = "ไม่พบข้อมูลในระบบทะเบียนประวัติ";
public static readonly string NotAuthorized = "กรุณาเข้าสู่ระบบก่อนใช้งาน!";
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 "CHILD";
}
return reader.Value;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value);
}
}
}

View file

@ -0,0 +1,54 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
{
/// <inheritdoc />
public partial class add_fields_table_eaveequestpprover : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "OrganizationName",
table: "LeaveRequestApprovers",
type: "longtext",
nullable: false,
comment: "สังกัด")
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<string>(
name: "PosExecutiveName",
table: "LeaveRequestApprovers",
type: "longtext",
nullable: false,
comment: "ตำแหน่งทางการบริหาร")
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<string>(
name: "PositionLevelName",
table: "LeaveRequestApprovers",
type: "longtext",
nullable: false,
comment: "ประเภทระดับตำแหน่ง")
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "OrganizationName",
table: "LeaveRequestApprovers");
migrationBuilder.DropColumn(
name: "PosExecutiveName",
table: "LeaveRequestApprovers");
migrationBuilder.DropColumn(
name: "PositionLevelName",
table: "LeaveRequestApprovers");
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,58 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
{
/// <inheritdoc />
public partial class AddRMQTaskControl : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "CheckInJobStatuses",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, comment: "PrimaryKey", collation: "ascii_general_ci"),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false, comment: "สร้างข้อมูลเมื่อ"),
CreatedUserId = table.Column<string>(type: "varchar(40)", maxLength: 40, nullable: false, comment: "User Id ที่สร้างข้อมูล")
.Annotation("MySql:CharSet", "utf8mb4"),
LastUpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true, comment: "แก้ไขข้อมูลล่าสุดเมื่อ"),
LastUpdateUserId = table.Column<string>(type: "varchar(40)", maxLength: 40, nullable: false, comment: "User Id ที่แก้ไขข้อมูลล่าสุด")
.Annotation("MySql:CharSet", "utf8mb4"),
CreatedFullName = table.Column<string>(type: "varchar(200)", maxLength: 200, nullable: false, comment: "ชื่อ User ที่สร้างข้อมูล")
.Annotation("MySql:CharSet", "utf8mb4"),
LastUpdateFullName = table.Column<string>(type: "varchar(200)", maxLength: 200, nullable: false, comment: "ชื่อ User ที่แก้ไขข้อมูลล่าสุด")
.Annotation("MySql:CharSet", "utf8mb4"),
TaskId = table.Column<Guid>(type: "char(36)", nullable: false, comment: "Task ID สำหรับติดตามสถานะงาน", collation: "ascii_general_ci"),
KeycloakUserId = table.Column<Guid>(type: "char(36)", nullable: false, comment: "รหัส User ของ Keycloak", collation: "ascii_general_ci"),
CreatedDate = table.Column<DateTime>(type: "datetime(6)", nullable: false, comment: "วันเวลาที่สร้างงาน"),
ProcessingDate = table.Column<DateTime>(type: "datetime(6)", nullable: true, comment: "วันเวลาที่เริ่มประมวลผล"),
CompletedDate = table.Column<DateTime>(type: "datetime(6)", nullable: true, comment: "วันเวลาที่เสร็จสิ้นการประมวลผล"),
Status = table.Column<string>(type: "longtext", nullable: false, comment: "สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED")
.Annotation("MySql:CharSet", "utf8mb4"),
CheckType = table.Column<string>(type: "longtext", nullable: true, comment: "ประเภทการลงเวลา: CHECK_IN, CHECK_OUT")
.Annotation("MySql:CharSet", "utf8mb4"),
CheckInId = table.Column<Guid>(type: "char(36)", nullable: true, comment: "CheckInId สำหรับ Check-Out", collation: "ascii_general_ci"),
ErrorMessage = table.Column<string>(type: "longtext", nullable: true, comment: "ข้อความแสดงข้อผิดพลาด")
.Annotation("MySql:CharSet", "utf8mb4"),
AdditionalData = table.Column<string>(type: "longtext", nullable: true, comment: "ข้อมูลเพิ่มเติม (JSON)")
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_CheckInJobStatuses", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "CheckInJobStatuses");
}
}
}

View file

@ -0,0 +1,30 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
{
/// <inheritdoc />
public partial class AddLeaveCounttoLeaveBeginning : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "LeaveCount",
table: "LeaveBeginnings",
type: "int",
nullable: false,
defaultValue: 0,
comment: "จำนวนครั้งที่ลาสะสม");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "LeaveCount",
table: "LeaveBeginnings");
}
}
}

View file

@ -0,0 +1,62 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
{
/// <inheritdoc />
public partial class AddBeginningLeaveandLeaveCounttoLeaveBeginning : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<double>(
name: "LeaveDays",
table: "LeaveBeginnings",
type: "double",
nullable: false,
comment: "จำนวนวันลาทั้งหมด",
oldClrType: typeof(double),
oldType: "double",
oldComment: "จำนวนวันลายกมา");
migrationBuilder.AddColumn<int>(
name: "BeginningLeaveCount",
table: "LeaveBeginnings",
type: "int",
nullable: false,
defaultValue: 0,
comment: "จำนวนครั้งที่ลายกมา");
migrationBuilder.AddColumn<double>(
name: "BeginningLeaveDays",
table: "LeaveBeginnings",
type: "double",
nullable: false,
defaultValue: 0.0,
comment: "จำนวนวันลายกมา");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "BeginningLeaveCount",
table: "LeaveBeginnings");
migrationBuilder.DropColumn(
name: "BeginningLeaveDays",
table: "LeaveBeginnings");
migrationBuilder.AlterColumn<double>(
name: "LeaveDays",
table: "LeaveBeginnings",
type: "double",
nullable: false,
comment: "จำนวนวันลายกมา",
oldClrType: typeof(double),
oldType: "double",
oldComment: "จำนวนวันลาทั้งหมด");
}
}
}

View file

@ -0,0 +1,54 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
{
/// <inheritdoc />
public partial class AddLeaveProcessJobStatus : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "LeaveProcessJobStatuses",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, comment: "PrimaryKey", collation: "ascii_general_ci"),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false, comment: "สร้างข้อมูลเมื่อ"),
CreatedUserId = table.Column<string>(type: "varchar(40)", maxLength: 40, nullable: false, comment: "User Id ที่สร้างข้อมูล")
.Annotation("MySql:CharSet", "utf8mb4"),
LastUpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true, comment: "แก้ไขข้อมูลล่าสุดเมื่อ"),
LastUpdateUserId = table.Column<string>(type: "varchar(40)", maxLength: 40, nullable: false, comment: "User Id ที่แก้ไขข้อมูลล่าสุด")
.Annotation("MySql:CharSet", "utf8mb4"),
CreatedFullName = table.Column<string>(type: "varchar(200)", maxLength: 200, nullable: false, comment: "ชื่อ User ที่สร้างข้อมูล")
.Annotation("MySql:CharSet", "utf8mb4"),
LastUpdateFullName = table.Column<string>(type: "varchar(200)", maxLength: 200, nullable: false, comment: "ชื่อ User ที่แก้ไขข้อมูลล่าสุด")
.Annotation("MySql:CharSet", "utf8mb4"),
StartDate = table.Column<DateTime>(type: "datetime(6)", nullable: false, comment: "วันเริ่มต้น"),
EndDate = table.Column<DateTime>(type: "datetime(6)", nullable: false, comment: "วันสิ้นสุด"),
RootDnaId = table.Column<Guid>(type: "char(36)", nullable: false, comment: "รหัส Root DNA Id", collation: "ascii_general_ci"),
CreatedDate = table.Column<DateTime>(type: "datetime(6)", nullable: false, comment: "วันเวลาที่สร้างงาน"),
ProcessingDate = table.Column<DateTime>(type: "datetime(6)", nullable: true, comment: "วันเวลาที่เริ่มประมวลผล"),
CompletedDate = table.Column<DateTime>(type: "datetime(6)", nullable: true, comment: "วันเวลาที่เสร็จสิ้นการประมวลผล"),
Status = table.Column<string>(type: "longtext", nullable: false, comment: "สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED")
.Annotation("MySql:CharSet", "utf8mb4"),
ErrorMessage = table.Column<string>(type: "longtext", nullable: true, comment: "ข้อความแสดงข้อผิดพลาด")
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_LeaveProcessJobStatuses", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "LeaveProcessJobStatuses");
}
}
}

View file

@ -128,6 +128,14 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
.HasComment("PrimaryKey")
.HasAnnotation("Relational:JsonPropertyName", "id");
b.Property<int>("BeginningLeaveCount")
.HasColumnType("int")
.HasComment("จำนวนครั้งที่ลายกมา");
b.Property<double>("BeginningLeaveDays")
.HasColumnType("double")
.HasComment("จำนวนวันลายกมา");
b.Property<Guid?>("Child1DnaId")
.HasColumnType("char(36)");
@ -184,9 +192,13 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
.HasColumnOrder(102)
.HasComment("แก้ไขข้อมูลล่าสุดเมื่อ");
b.Property<int>("LeaveCount")
.HasColumnType("int")
.HasComment("จำนวนครั้งที่ลาสะสม");
b.Property<double>("LeaveDays")
.HasColumnType("double")
.HasComment("จำนวนวันลายกมา");
.HasComment("จำนวนวันลาทั้งหมด");
b.Property<double>("LeaveDaysUsed")
.HasColumnType("double")
@ -727,6 +739,21 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
b.Property<Guid>("LeaveRequestId")
.HasColumnType("char(36)");
b.Property<string>("OrganizationName")
.IsRequired()
.HasColumnType("longtext")
.HasComment("สังกัด");
b.Property<string>("PosExecutiveName")
.IsRequired()
.HasColumnType("longtext")
.HasComment("ตำแหน่งทางการบริหาร");
b.Property<string>("PositionLevelName")
.IsRequired()
.HasColumnType("longtext")
.HasComment("ประเภทระดับตำแหน่ง");
b.Property<string>("PositionName")
.IsRequired()
.HasColumnType("longtext");
@ -867,6 +894,99 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
b.ToTable("AdditionalCheckRequests");
});
modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.CheckInJobStatus", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)")
.HasColumnOrder(0)
.HasComment("PrimaryKey")
.HasAnnotation("Relational:JsonPropertyName", "id");
b.Property<string>("AdditionalData")
.HasColumnType("longtext")
.HasComment("ข้อมูลเพิ่มเติม (JSON)");
b.Property<Guid?>("CheckInId")
.HasColumnType("char(36)")
.HasComment("CheckInId สำหรับ Check-Out");
b.Property<string>("CheckType")
.HasColumnType("longtext")
.HasComment("ประเภทการลงเวลา: CHECK_IN, CHECK_OUT");
b.Property<DateTime?>("CompletedDate")
.HasColumnType("datetime(6)")
.HasComment("วันเวลาที่เสร็จสิ้นการประมวลผล");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)")
.HasColumnOrder(100)
.HasComment("สร้างข้อมูลเมื่อ");
b.Property<DateTime>("CreatedDate")
.HasColumnType("datetime(6)")
.HasComment("วันเวลาที่สร้างงาน");
b.Property<string>("CreatedFullName")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("varchar(200)")
.HasColumnOrder(104)
.HasComment("ชื่อ User ที่สร้างข้อมูล");
b.Property<string>("CreatedUserId")
.IsRequired()
.HasMaxLength(40)
.HasColumnType("varchar(40)")
.HasColumnOrder(101)
.HasComment("User Id ที่สร้างข้อมูล");
b.Property<string>("ErrorMessage")
.HasColumnType("longtext")
.HasComment("ข้อความแสดงข้อผิดพลาด");
b.Property<Guid>("KeycloakUserId")
.HasColumnType("char(36)")
.HasComment("รหัส User ของ Keycloak");
b.Property<string>("LastUpdateFullName")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("varchar(200)")
.HasColumnOrder(105)
.HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด");
b.Property<string>("LastUpdateUserId")
.IsRequired()
.HasMaxLength(40)
.HasColumnType("varchar(40)")
.HasColumnOrder(103)
.HasComment("User Id ที่แก้ไขข้อมูลล่าสุด");
b.Property<DateTime?>("LastUpdatedAt")
.HasColumnType("datetime(6)")
.HasColumnOrder(102)
.HasComment("แก้ไขข้อมูลล่าสุดเมื่อ");
b.Property<DateTime?>("ProcessingDate")
.HasColumnType("datetime(6)")
.HasComment("วันเวลาที่เริ่มประมวลผล");
b.Property<string>("Status")
.IsRequired()
.HasColumnType("longtext")
.HasComment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED");
b.Property<Guid>("TaskId")
.HasColumnType("char(36)")
.HasComment("Task ID สำหรับติดตามสถานะงาน");
b.HasKey("Id");
b.ToTable("CheckInJobStatuses");
});
modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", b =>
{
b.Property<Guid>("Id")
@ -952,6 +1072,91 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
b.ToTable("DutyTimes");
});
modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.LeaveProcessJobStatus", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)")
.HasColumnOrder(0)
.HasComment("PrimaryKey")
.HasAnnotation("Relational:JsonPropertyName", "id");
b.Property<DateTime?>("CompletedDate")
.HasColumnType("datetime(6)")
.HasComment("วันเวลาที่เสร็จสิ้นการประมวลผล");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)")
.HasColumnOrder(100)
.HasComment("สร้างข้อมูลเมื่อ");
b.Property<DateTime>("CreatedDate")
.HasColumnType("datetime(6)")
.HasComment("วันเวลาที่สร้างงาน");
b.Property<string>("CreatedFullName")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("varchar(200)")
.HasColumnOrder(104)
.HasComment("ชื่อ User ที่สร้างข้อมูล");
b.Property<string>("CreatedUserId")
.IsRequired()
.HasMaxLength(40)
.HasColumnType("varchar(40)")
.HasColumnOrder(101)
.HasComment("User Id ที่สร้างข้อมูล");
b.Property<DateTime>("EndDate")
.HasColumnType("datetime(6)")
.HasComment("วันสิ้นสุด");
b.Property<string>("ErrorMessage")
.HasColumnType("longtext")
.HasComment("ข้อความแสดงข้อผิดพลาด");
b.Property<string>("LastUpdateFullName")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("varchar(200)")
.HasColumnOrder(105)
.HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด");
b.Property<string>("LastUpdateUserId")
.IsRequired()
.HasMaxLength(40)
.HasColumnType("varchar(40)")
.HasColumnOrder(103)
.HasComment("User Id ที่แก้ไขข้อมูลล่าสุด");
b.Property<DateTime?>("LastUpdatedAt")
.HasColumnType("datetime(6)")
.HasColumnOrder(102)
.HasComment("แก้ไขข้อมูลล่าสุดเมื่อ");
b.Property<DateTime?>("ProcessingDate")
.HasColumnType("datetime(6)")
.HasComment("วันเวลาที่เริ่มประมวลผล");
b.Property<Guid>("RootDnaId")
.HasColumnType("char(36)")
.HasComment("รหัส Root DNA Id");
b.Property<DateTime>("StartDate")
.HasColumnType("datetime(6)")
.HasComment("วันเริ่มต้น");
b.Property<string>("Status")
.IsRequired()
.HasColumnType("longtext")
.HasComment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED");
b.HasKey("Id");
b.ToTable("LeaveProcessJobStatuses");
});
modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.ProcessUserTimeStamp", b =>
{
b.Property<Guid>("Id")

View file

@ -22,6 +22,8 @@ namespace BMA.EHR.Infrastructure.Persistence
public DbSet<UserCalendar> UserCalendars { get; set; }
public DbSet<CheckInJobStatus> CheckInJobStatuses { get; set; }
#endregion
#region " Leave System "
@ -38,6 +40,8 @@ namespace BMA.EHR.Infrastructure.Persistence
#endregion
public DbSet<LeaveProcessJobStatus> LeaveProcessJobStatuses { get; set; }
public LeaveDbContext(DbContextOptions<LeaveDbContext> options) : base(options)
{

View file

@ -331,7 +331,7 @@ namespace BMA.EHR.Insignia.Service.Controllers
if (req.Total + total > insigniaManage.Total)
return Error(GlobalMessages.InsigniaManageOrgLimit);
var ocData = _userProfileRepository.GetOc(req.OrganizationOrganizationId, 0, AccessToken);
var ocData = _userProfileRepository.GetOcByNodeId(req.OrganizationOrganizationId, 0, AccessToken);
var root = ocData?.Root ?? null;
var rootDnaId = ocData?.RootDnaId ?? null;
await _context.InsigniaManageOrganiations.AddAsync(
@ -407,6 +407,10 @@ namespace BMA.EHR.Insignia.Service.Controllers
if (uppdated == null)
return Error(GlobalMessages.InsigniaManageNotFound);
var ocData = _userProfileRepository.GetOcByNodeId(uppdated.OrganizationId, 0, AccessToken);
var root = ocData?.Root ?? null;
var rootDnaId = ocData?.RootDnaId ?? null;
var insigniaManage = await _context.InsigniaManages.AsQueryable()
.Include(x => x.InsigniaManageOrganiations)
.FirstOrDefaultAsync(x => x.Id == uppdated.InsigniaManage.Id);
@ -416,6 +420,9 @@ namespace BMA.EHR.Insignia.Service.Controllers
if (req.Total + total > insigniaManage.Total)
return Error(GlobalMessages.InsigniaManageOrgLimit);
uppdated.Organization = root;
uppdated.RootDnaId = rootDnaId;
uppdated.Total = req.Total;
uppdated.LastUpdateFullName = FullName ?? "System Administrator";
uppdated.LastUpdateUserId = UserId ?? "";
@ -639,7 +646,7 @@ namespace BMA.EHR.Insignia.Service.Controllers
? profileAdmin?.RootDnaId
: "";
}
else if (role == "ROOT" || role == "PARENT")
else if (role == "ROOT" /*|| role == "PARENT"*/)
{
nodeId = profileAdmin?.RootDnaId;
}
@ -724,11 +731,11 @@ namespace BMA.EHR.Insignia.Service.Controllers
rawData = rawData
.Where(x => x.RootDnaId == Guid.Parse(nodeId!)).ToList();
}
else if (role == "PARENT")
{
rawData = rawData
.Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null).ToList();
}
// else if (role == "PARENT")
// {
// rawData = rawData
// .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null).ToList();
// }
else if (role == "NORMAL")
{
rawData = rawData.Where(x =>
@ -943,7 +950,7 @@ namespace BMA.EHR.Insignia.Service.Controllers
? profileAdmin?.RootDnaId
: "";
}
else if (role == "ROOT" || role == "PARENT")
else if (role == "ROOT" /*|| role == "PARENT"*/)
{
nodeId = profileAdmin?.RootDnaId;
}
@ -1026,11 +1033,11 @@ namespace BMA.EHR.Insignia.Service.Controllers
rawData = rawData
.Where(x => x.RootDnaId == Guid.Parse(nodeId!)).ToList();
}
else if (role == "PARENT")
{
rawData = rawData
.Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null).ToList();
}
// else if (role == "PARENT")
// {
// rawData = rawData
// .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null).ToList();
// }
else if (role == "NORMAL")
{
rawData = rawData.Where(x =>

View file

@ -2641,6 +2641,8 @@ namespace BMA.EHR.Insignia.Service.Controllers
{
if (item.CitizanId == null) continue;
var _profile = await _userProfileRepository.GetOfficerProfileByCitizenId(item.CitizanId, AccessToken);
if (_profile == null)
continue;
var profile = insigniaNote.InsigniaNoteProfiles.FirstOrDefault(x => x.ProfileId == _profile.Id);
if (profile == null)
{
@ -3096,7 +3098,7 @@ namespace BMA.EHR.Insignia.Service.Controllers
var doc = await _documentService.UploadFileAsync(file, file.FileName);
insigniaNoteProfile.DocReturnInsignia = doc;
}
var root = _userProfileRepository.GetOc(req.OrgId, 0, AccessToken)?.Root ?? null;
var root = _userProfileRepository.GetOcByNodeId(req.OrgId, 0, AccessToken)?.Root ?? null;
if (req.OrgId != Guid.Parse("00000000-0000-0000-0000-000000000000"))
{
if (root == null)
@ -3146,7 +3148,10 @@ namespace BMA.EHR.Insignia.Service.Controllers
var doc = await _documentService.UploadFileAsync(file, file.FileName);
insigniaNoteProfile.DocReceiveInsignia = doc;
}
var root = _userProfileRepository.GetOc(req.OrgId, 0, AccessToken)?.Root ?? null;
var orgData = _userProfileRepository.GetOcByNodeId(req.OrgId,0, AccessToken);
var root = orgData?.Root ?? null;
var rootDnaId = orgData?.RootDnaId ?? null;
if (req.OrgId != Guid.Parse("00000000-0000-0000-0000-000000000000"))
{
if (root == null)
@ -3157,6 +3162,7 @@ namespace BMA.EHR.Insignia.Service.Controllers
root = "สำนักนายกรัฐมนตรี";
}
insigniaNoteProfile.OrgReceiveInsignia = root;
insigniaNoteProfile.RootDnaId = rootDnaId;
insigniaNoteProfile.OrgReceiveInsigniaId = req.OrgId;
insigniaNoteProfile.DateReceiveInsignia = req.Date;
insigniaNoteProfile.LastUpdateFullName = FullName ?? "System Administrator";

View file

@ -131,7 +131,7 @@ var builder = WebApplication.CreateBuilder(args);
{
options.ServerName = "Insignia-Server"; // ← ระบุชื่อ server
options.WorkerCount = 5; // ←
options.Queues = new[] { "insignia" }; // ← worker จะรันเฉพาะ queue "insignia"
options.Queues = new[] { "insignia","default" }; // ← worker จะรันเฉพาะ queue "insignia"
});

View file

@ -9,8 +9,8 @@
}
},
"ElasticConfiguration": {
"Uri": "http://192.168.1.40:9200",
"IndexFormat": "bma-ehr-log-index",
"Uri": "http://192.168.1.63:9200",
"IndexFormat": "hrms-log-index",
"SystemName": "insignia"
},
"AllowedHosts": "*",
@ -31,10 +31,11 @@
//"DisciplineConnection": "server=hrms.chin.in.th;user=root;password=ey2qVVyyqGYw8CyA7h8X72559r2Ad84K;port=53636;database=hrms_discipline;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;"
},
"Jwt": {
//"Key": "j7C9RO_p4nRtuwCH4z9Db_A_6We42tkD_p4lZtDrezc",
//"Issuer": "https://hrms-id.chin.in.th/realms/hrms"
"Key": "HP-FnQMUj9msHMSD3T9HtdEnphAKoCJLEl85CIqROFI",
"Issuer": "https://id.frappet.synology.me/realms/hrms"
//"Key": "HP-FnQMUj9msHMSD3T9HtdEnphAKoCJLEl85CIqROFI",
"Key": "j7C9RO_p4nRtuwCH4z9Db_A_6We42tkD_p4lZtDrezc",
"Issuer": "https://hrmsbkk-id.case-collection.com/realms/hrms"
//"Key": "xY2VR-EFvvNPsMs39u8ooVBWQL6mPwrNJOh3koJFTgU",
//"Issuer": "https://hrms-id.bangkok.go.th/realms/hrms"
},
"EPPlus": {
"ExcelPackage": {
@ -55,11 +56,17 @@
"Node": {
"API": "https://bma-ehr.frappet.synology.me/api/v1/probation"
},
"API": "https://bma-ehr.frappet.synology.me/api/v1",
"RabbitMQ": {
"URL": "localhost",
"UserName": "frappet",
"Password": "FPTadmin2357"
},
"Domain": "https://hrmsbkk.case-collection.com",
"APIPROBATION": "https://hrmsbkk.case-collection.com/api/v1/probation",
"API": "https://hrmsbkk.case-collection.com/api/v1",
"APIV2": "https://hrmsbkk.case-collection.com/api/v2",
"VITE_URL_MGT": "https://hrmsbkk-mgt.case-collection.com",
//"API": "https://bma-ehr.frappet.synology.me/api/v1",
//"API": "https://bma-hrms.bangkok.go.th/api/v1",
"API_KEY": "fKRL16yyEgbyTEJdsMw2h64tGSCmkW685PRtM3CygzX1JOSdptT9UJtpgWwKM8FybRTJups3GTFwj27ZRvlPdIkv3XgCoVJaD5LmR06ozuEPvCCRSdp2WFthg08V5xHc56fTPfZLpr1VmXrhd6dvYhHIqKkQUJR02Rlkss11cLRWEQOssEFVA4xdu2J5DIRO1EM5m7wRRvEwcDB4mYRXD9HH52SMq6iYqUWEWsMwLdbk7QW9yYESUEuzMW5gWrb6vIeWZxJV5bTz1PcWUyR7eO9Fyw1F5DiQYc9JgzTC1mW7cv31fEtTtrfbJYKIb5EbWilqIEUKC6A0UKBDDek35ML0006cqRVm0pvdOH6jeq7VQyYrhdXe59dBEyhYGUIfozoVBvW7Up4QBuOMjyPjSqJPlMBKwaseptfrblxQV1AOOivSBpf1ZcQyOZ8JktRtKUDSuXsmG0lsXwFlI3JCeSHdpVdgZWFYcJPegqfrB6KotR02t9AVkpLs1ZWrixwz"
}

View file

@ -45,6 +45,7 @@
<PackageReference Include="Microsoft.IdentityModel.Logging" Version="6.31.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.18.1" />
<PackageReference Include="NEST" Version="7.17.5" />
<PackageReference Include="NodaTime" Version="3.3.0" />
<PackageReference Include="runtime.osx.10.10-x64.CoreCompat.System.Drawing" Version="6.0.5.128" />
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />

View file

@ -141,7 +141,7 @@ namespace BMA.EHR.Leave.Service.Controllers
? profileAdmin?.RootDnaId
: "";
}
else if (role == "ROOT" || role == "PARENT")
else if (role == "ROOT" /*|| role == "PARENT"*/)
{
nodeId = profileAdmin?.RootDnaId;
}
@ -166,11 +166,11 @@ namespace BMA.EHR.Leave.Service.Controllers
resData = resData
.Where(x => x.RootDnaId == Guid.Parse(nodeId!)).ToList();
}
else if (role == "PARENT")
{
resData = resData
.Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null).ToList();
}
// else if (role == "PARENT")
// {
// resData = resData
// .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null).ToList();
// }
else if (role == "NORMAL")
{
resData = resData
@ -201,6 +201,9 @@ namespace BMA.EHR.Leave.Service.Controllers
item.LeaveYear,
item.LeaveDays,
item.LeaveDaysUsed,
item.LeaveCount,
item.BeginningLeaveDays,
item.BeginningLeaveCount,
item.CreatedAt,
item.CreatedFullName,
item.LastUpdatedAt,
@ -394,6 +397,9 @@ namespace BMA.EHR.Leave.Service.Controllers
leaveBeginning.LeaveYear = req.LeaveYear;
leaveBeginning.LeaveDays = req.LeaveDays;
leaveBeginning.LeaveDaysUsed = req.LeaveDaysUsed;
leaveBeginning.LeaveCount = req.LeaveCount;
leaveBeginning.BeginningLeaveDays = req.BeginningLeaveDays;
leaveBeginning.BeginningLeaveCount = req.BeginningLeaveCount;
leaveBeginning.ProfileId = req.ProfileId;
leaveBeginning.Prefix = profile.Prefix;
@ -462,6 +468,9 @@ namespace BMA.EHR.Leave.Service.Controllers
leaveBeginning.LeaveYear = req.LeaveYear;
leaveBeginning.LeaveDays = req.LeaveDays;
leaveBeginning.LeaveDaysUsed = req.LeaveDaysUsed;
leaveBeginning.LeaveCount = req.LeaveCount;
leaveBeginning.BeginningLeaveDays = req.BeginningLeaveDays;
leaveBeginning.BeginningLeaveCount = req.BeginningLeaveCount;
leaveBeginning.ProfileId = req.ProfileId;
leaveBeginning.Prefix = profile.Prefix;
@ -489,6 +498,126 @@ namespace BMA.EHR.Leave.Service.Controllers
}
}
[HttpPut("schedule")]
[AllowAnonymous]
public async Task<ActionResult<ResponseObject>> ScheduleUpdateLeaveBeginningAsync([FromBody] ScheduleEditLeaveBeginningDto req)
{
try
{
var profile = await _userProfileRepository.GetProfileByProfileIdNoAuthAsync(req.ProfileId, AccessToken);
if(profile == null)
{
return Error("ไม่พบข้อมูลข้าราชการหรือลูกจ้าง", StatusCodes.Status404NotFound);
}
// check duplicate
var oldData = await _context.LeaveBeginnings.FirstOrDefaultAsync(x => x.ProfileId == req.ProfileId
&& x.LeaveTypeId == req.LeaveTypeId
&& x.LeaveYear == req.LeaveYear);
if (oldData is not null)
{
//return Error("ไม่สามารถบันทึกข้อมูล เนื่องจากมีข้อมูลในระบบแล้ว");
oldData.LeaveTypeId = req.LeaveTypeId;
oldData.LeaveYear = req.LeaveYear;
oldData.LeaveDays = req.LeaveDays;
// oldData.LeaveDaysUsed = req.LeaveDaysUsed;
// oldData.LeaveCount = req.LeaveCount;
// oldData.BeginningLeaveDays = req.BeginningLeaveDays;
// oldData.BeginningLeaveCount = req.BeginningLeaveCount;
oldData.ProfileId = req.ProfileId;
oldData.Prefix = profile.Prefix;
oldData.FirstName = profile.FirstName;
oldData.LastName = profile.LastName;
oldData.RootDnaId = profile.RootDnaId;
oldData.Child1DnaId = profile.Child1DnaId;
oldData.Child2DnaId = profile.Child2DnaId;
oldData.Child3DnaId = profile.Child3DnaId;
oldData.Child4DnaId = profile.Child4DnaId;
oldData.LastUpdateUserId = "";
oldData.LastUpdateFullName = "System";
oldData.LastUpdatedAt = DateTime.Now;
await _leaveBeginningRepository.UpdateAsync(oldData);
}
else
{
var leaveBeginning = new LeaveBeginning();
leaveBeginning.LeaveTypeId = req.LeaveTypeId;
leaveBeginning.LeaveYear = req.LeaveYear;
leaveBeginning.LeaveDays = req.LeaveDays;
leaveBeginning.LeaveDaysUsed = 0;
leaveBeginning.LeaveCount = 0;
leaveBeginning.BeginningLeaveDays = 0;
leaveBeginning.BeginningLeaveCount = 0;
leaveBeginning.ProfileId = req.ProfileId;
leaveBeginning.Prefix = profile.Prefix;
leaveBeginning.FirstName = profile.FirstName;
leaveBeginning.LastName = profile.LastName;
leaveBeginning.RootDnaId = profile.RootDnaId;
leaveBeginning.Child1DnaId = profile.Child1DnaId;
leaveBeginning.Child2DnaId = profile.Child2DnaId;
leaveBeginning.Child3DnaId = profile.Child3DnaId;
leaveBeginning.Child4DnaId = profile.Child4DnaId;
leaveBeginning.CreatedUserId = "";
leaveBeginning.CreatedFullName = "System";
leaveBeginning.CreatedAt = DateTime.Now;
await _leaveBeginningRepository.AddAsync(leaveBeginning);
}
return Success();
}
catch (Exception ex)
{
return Error(ex);
}
}
[HttpPut("schedule/update-dna")]
[AllowAnonymous]
public async Task<ActionResult<ResponseObject>> ScheduleUpdateDnaAsync([FromBody] List<ScheduleUpdateDnaDto> req)
{
try
{
foreach(var item in req)
{
// var profile = await _userProfileRepository.GetProfileByProfileIdNoAuthAsync(item.ProfileId, AccessToken);
// if(profile == null)
// {
// return Error("ไม่พบข้อมูลข้าราชการหรือลูกจ้าง", StatusCodes.Status404NotFound);
// }
// check duplicate
var oldData = await _context.LeaveBeginnings.Where(x => x.ProfileId == item.ProfileId).ToListAsync();
foreach(var o in oldData)
{
o.RootDnaId = item.RootDnaId;
o.Child1DnaId = item.Child1DnaId;
o.Child2DnaId = item.Child2DnaId;
o.Child3DnaId = item.Child3DnaId;
o.Child4DnaId = item.Child4DnaId;
o.LastUpdateUserId = "";
o.LastUpdateFullName = "System";
o.LastUpdatedAt = DateTime.Now;
await _leaveBeginningRepository.UpdateAsync(o);
}
}
return Success();
}
catch (Exception ex)
{
return Error(ex);
}
}
#endregion
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -12,4 +12,25 @@ namespace BMA.EHR.Leave.Service.DTOs.ChangeRound
public string Remark { get; set; }
}
public class CreateChangeRoundMultipleDto
{
public Guid ProfileId { get; set; }
public Guid RoundId { get; set; }
public DateTime EffectiveDate { get; set; }
public string Remark { get; set; }
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 string? Prefix { get; set; }
public string? FirstName { get; set; }
public string? LastName { get; set; }
}
}

View file

@ -17,5 +17,9 @@
public string? sortBy { get; set; }
public bool? descending { get; set; }
public Guid? SelectedNodeId { get; set; }
public int? SelectedNode { get; set; }
}
}

View file

@ -17,5 +17,11 @@
public string LeaveTimeAfterNoon { get;set; }
public DateTime? EffectiveDate { get; set; }
public string? RootDnaId { get; set; }
public string? Child1DnaId { get; set; }
public string? Child2DnaId { get; set; }
public string? Child3DnaId { get; set; }
public string? Child4DnaId { get; set; }
}
}

View file

@ -54,6 +54,7 @@ namespace BMA.EHR.Leave.Service.DTOs.CheckIn
public Guid? CheckInId { get; set; }
public Guid? TaskId { get; set; }
public double Lat { get; set; } = 0;

View file

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

View file

@ -19,5 +19,45 @@ namespace BMA.EHR.Leave.Service.DTOs.LeaveBeginnings
[Required, Comment("จำนวนวันลาที่ใช้ไป")]
public double LeaveDaysUsed { get; set; } = 0.0;
[Required, Comment("จำนวนครั้งที่ลาสะสม")]
public int LeaveCount { get; set; } = 0;
[Required, Comment("จำนวนวันลายกมา")]
public double BeginningLeaveDays { get; set; } = 0.0;
[Comment("จำนวนครั้งที่ลายกมา")]
public int BeginningLeaveCount { get; set; } = 0;
}
public class ScheduleEditLeaveBeginningDto
{
[Required]
public Guid ProfileId { get; set; } = Guid.Empty;
[Required]
public Guid LeaveTypeId { get; set; } = Guid.Empty;
[Required, Comment("ปีงบประมาณ")]
public int LeaveYear { get; set; } = 0;
[Required, Comment("จำนวนวันลายกมา")]
public double LeaveDays { get; set; } = 0.0;
}
public class ScheduleUpdateDnaDto
{
[Required]
public Guid ProfileId { get; set; } = Guid.Empty;
// [Required, Comment("ปีงบประมาณ")]
// public int LeaveYear { get; set; } = 0;
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; }
}
}

View file

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace BMA.EHR.Leave.Service.DTOs.LeaveRequest
{
/// <summary>
/// ข้อมูลสำหรับสร้าง Job ประมวลผลวันลา โดยมีช่วงวันที่เริ่มต้นและสิ้นสุดของการประมวลผลวันลา
/// </summary>
public class CreateLeaveProcessJobDto
{
/// <summary>
/// วันที่เริ่มต้นของการประมวลผลวันลา
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// วันที่สิ้นสุดของการประมวลผลวันลา
/// </summary>
public DateTime EndDate { get; set; }
}
}

View file

@ -19,5 +19,7 @@
public DateTime LeaveEndDate { get; set; }
public Guid KeycloakId { get; set; }
public double LeaveTotal { get; set; }
}
}

View file

@ -37,5 +37,7 @@
public bool? HajjDayStatus { get; set; }
public string? ProfileType { get; set; }
public double LeaveTotal { get; set; }
}
}

View file

@ -51,5 +51,7 @@
public string? CurrentProvince { get; set; }
public string? CurrentZipCode { get; set; }
public int GovAge { get; set; } = 0;
}
}

View file

@ -27,5 +27,15 @@ namespace BMA.EHR.Leave.Service.DTOs.LeaveRequest
[JsonProperty("keycloakId")]
public Guid KeycloakId { get; set; } = Guid.Empty;
[JsonProperty("positionLeaveName")]
public string PositionLeaveName { get; set; } = string.Empty;
[JsonProperty("posExecutiveName")]
public string PosExecutiveName { get; set; } = string.Empty;
[JsonProperty("organizationName")]
public string OrganizationName { get; set; } = string.Empty;
}
}

View file

@ -96,7 +96,14 @@ builder.Services.AddPersistence(builder.Configuration);
builder.Services.AddLeavePersistence(builder.Configuration);
builder.Services.AddTransient<HolidayService>();
// Configure HttpClient with increased timeout for long-running operations (e.g., RabbitMQ Management API)
builder.Services.AddHttpClient();
builder.Services.AddTransient(sp =>
{
var httpClient = sp.GetRequiredService<IHttpClientFactory>().CreateClient();
httpClient.Timeout = TimeSpan.FromMinutes(10); // Set timeout to 10 minutes
return httpClient;
});
builder.Services.AddControllers(options =>
{
@ -112,7 +119,7 @@ builder.Services.AddHealthChecks();
builder.Services.AddRabbitMqConnectionPooling(builder.Configuration);
// Add Hangfire services.
var defaultConnection = builder.Configuration.GetConnectionString("DefaultConnection");
var hangfireConnection = builder.Configuration.GetConnectionString("defaultConnection");
builder.Services.AddHangfire(configuration => configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
@ -120,19 +127,24 @@ builder.Services.AddHangfire(configuration => configuration
.UseRecommendedSerializerSettings()
.UseStorage(
new MySqlStorage(
defaultConnection,
hangfireConnection,
new MySqlStorageOptions
{
TransactionIsolationLevel = IsolationLevel.ReadCommitted,
QueuePollInterval = TimeSpan.FromSeconds(15),
JobExpirationCheckInterval = TimeSpan.FromHours(1),
CountersAggregateInterval = TimeSpan.FromMinutes(5),
PrepareSchemaIfNecessary = true,
DashboardJobListLimit = 50000,
TransactionTimeout = TimeSpan.FromMinutes(1),
TablesPrefix = "Hangfire"
InvisibilityTimeout = TimeSpan.FromHours(3),
TablesPrefix = "Hangfire_Leave"
})));
builder.Services.AddHangfireServer();
builder.Services.AddHangfireServer(options =>
{
options.ServerName = "Leave-Server"; // ← ระบุชื่อ server
options.WorkerCount = 5; // ←
options.Queues = new[] { "leave","default" }; // ← worker จะรันเฉพาะ queue "leave"
});
var app = builder.Build();
@ -181,6 +193,15 @@ var manager = new RecurringJobManager();
if (manager != null)
{
manager.AddOrUpdate("ปรับปรุงรอบการลงเวลาทำงาน", Job.FromExpression<UserDutyTimeRepository>(x => x.UpdateUserDutyTime()), "0 1 * * *", bangkokTimeZone);
// ทำความสะอาดข้อมูล CheckIn Job Status ที่เก่ากว่า 30 วัน - รันทุกวันเวลา 02:00 น.
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 * * *",
new RecurringJobOptions
{
TimeZone = bangkokTimeZone,
QueueName = "leave" // ← กำหนด queue
});
}
// apply migrations

View file

@ -9,8 +9,8 @@
}
},
"ElasticConfiguration": {
"Uri": "http://192.168.1.40:9200",
"IndexFormat": "bma-ehr-log-index",
"Uri": "http://192.168.1.63:9200",
"IndexFormat": "hrms-log-index",
"SystemName": "leave"
},
"AllowedHosts": "*",
@ -19,20 +19,19 @@
// "ExamConnection": "server=192.168.1.80;user=root;password=adminVM123;port=3306;database=hrms_exam;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;",
// "LeaveConnection": "server=192.168.1.80;user=root;password=adminVM123;port=3306;database=hrms_leave;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;"
"DefaultConnection": "server=192.168.1.63;user=root;password=12345678;port=3306;database=hrms;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;",
"ExamConnection": "server=192.168.1.63;user=root;password=12345678;port=3306;database=hrms_exam;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;",
"LeaveConnection": "server=192.168.1.63;user=root;password=12345678;port=3306;database=hrms_leave;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;"
"DefaultConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;Database=hrms;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;"
//"DefaultConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;",
//"ExamConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms_exam;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;",
//"LeaveConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms_leave;Convert Zero Datetime=True;Allow User Variables=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;",
// "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;",
// "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;"
},
"Jwt": {
//"Key": "HP-FnQMUj9msHMSD3T9HtdEnphAKoCJLEl85CIqROFI",
"Key": "j7C9RO_p4nRtuwCH4z9Db_A_6We42tkD_p4lZtDrezc",
"Issuer": "https://hrmsbkk-id.case-collection.com/realms/hrms"
//"Key": "xY2VR-EFvvNPsMs39u8ooVBWQL6mPwrNJOh3koJFTgU",
//"Issuer": "https://hrms-id.bangkok.go.th/realms/hrms"
// "Key": "xY2VR-EFvvNPsMs39u8ooVBWQL6mPwrNJOh3koJFTgU",
// "Issuer": "https://hrms-id.bangkok.go.th/realms/hrms"
},
"EPPlus": {
"ExcelPackage": {
@ -53,8 +52,13 @@
"Host": "192.168.1.63",
"User": "admin",
"Password": "12345678",
"Queue": "hrms-checkin-queue",
"Queue": "hrms-checkin-queue-dev",
"URL": "http://192.168.1.63:9122/api/queues/%2F/"
// "Host": "172.27.17.68",
// "User": "admin",
// "Password": "admin123456",
// "Queue": "hrms-checkin-queue",
// "URL": "http://172.27.17.68:9122/api/queues/%2F/"
},
"Mail": {
"Server": "mail.bangkok.go.th",
@ -68,7 +72,10 @@
"API": "https://hrmsbkk.case-collection.com/api/v1",
"APIV2": "https://hrmsbkk.case-collection.com/api/v2",
"VITE_URL_MGT": "https://hrmsbkk-mgt.case-collection.com",
//"API": "https://bma-ehr.frappet.synology.me/api/v1",
//"API": "https://bma-hrms.bangkok.go.th/api/v1",
// "Domain": "https://hrms-exam.bangkok.go.th",
// "APIPROBATION": "https://hrms.bangkok.go.th/api/v1/probation",
// "API": "https://hrms.bangkok.go.th/api/v1",
// "APIV2": "https://hrms.bangkok.go.th/api/v2",
// "VITE_URL_MGT": "https://hrms-mgt.bangkok.go.th",
"API_KEY": "fKRL16yyEgbyTEJdsMw2h64tGSCmkW685PRtM3CygzX1JOSdptT9UJtpgWwKM8FybRTJups3GTFwj27ZRvlPdIkv3XgCoVJaD5LmR06ozuEPvCCRSdp2WFthg08V5xHc56fTPfZLpr1VmXrhd6dvYhHIqKkQUJR02Rlkss11cLRWEQOssEFVA4xdu2J5DIRO1EM5m7wRRvEwcDB4mYRXD9HH52SMq6iYqUWEWsMwLdbk7QW9yYESUEuzMW5gWrb6vIeWZxJV5bTz1PcWUyR7eO9Fyw1F5DiQYc9JgzTC1mW7cv31fEtTtrfbJYKIb5EbWilqIEUKC6A0UKBDDek35ML0006cqRVm0pvdOH6jeq7VQyYrhdXe59dBEyhYGUIfozoVBvW7Up4QBuOMjyPjSqJPlMBKwaseptfrblxQV1AOOivSBpf1ZcQyOZ8JktRtKUDSuXsmG0lsXwFlI3JCeSHdpVdgZWFYcJPegqfrB6KotR02t9AVkpLs1ZWrixwz"
}

View file

@ -112,7 +112,7 @@ namespace BMA.EHR.Placement.Service.Controllers
? profileAdmin?.RootDnaId
: "";
}
else if (role == "ROOT" || role == "PARENT")
else if (role == "ROOT" /*|| role == "PARENT"*/)
{
nodeId = profileAdmin?.RootDnaId;
}
@ -239,11 +239,11 @@ namespace BMA.EHR.Placement.Service.Controllers
placementAppointments = placementAppointments
.Where(x => x.rootDnaId == nodeId).ToList();
}
else if (role == "PARENT")
{
placementAppointments = placementAppointments
.Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList();
}
// else if (role == "PARENT")
// {
// placementAppointments = placementAppointments
// .Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList();
// }
else if (role == "NORMAL")
{
placementAppointments = placementAppointments.Where(x =>

View file

@ -110,7 +110,7 @@ namespace BMA.EHR.Placement.Service.Controllers
? profileAdmin?.RootDnaId
: "";
}
else if (role == "ROOT" || role == "PARENT")
else if (role == "ROOT" /*|| role == "PARENT"*/)
{
nodeId = profileAdmin?.RootDnaId;
}
@ -232,11 +232,11 @@ namespace BMA.EHR.Placement.Service.Controllers
placementAppointments = placementAppointments
.Where(x => x.rootDnaId == nodeId).ToList();
}
else if (role == "PARENT")
{
placementAppointments = placementAppointments
.Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList();
}
// else if (role == "PARENT")
// {
// placementAppointments = placementAppointments
// .Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList();
// }
else if (role == "NORMAL")
{
placementAppointments = placementAppointments.Where(x =>

View file

@ -62,9 +62,9 @@ namespace BMA.EHR.Placement.Service.Controllers
#region " Properties "
private string? UserId => _httpContextAccessor?.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
private string? FullName => _httpContextAccessor?.HttpContext?.User?.FindFirst("name")?.Value;
private string? token => _httpContextAccessor.HttpContext.Request.Headers["Authorization"];
//private bool isSuperAdmin => _httpContextAccessor?.HttpContext?.User?.IsInRole("SUPER_ADMIN") ?? false;
#endregion
@ -140,6 +140,7 @@ namespace BMA.EHR.Placement.Service.Controllers
public async Task<ActionResult<ResponseObject>> GetExamByPlacement(Guid examId)
{
var getWorkflow = await _permission.GetPermissionAPIWorkflowAsync(examId.ToString(), "SYS_PLACEMENT_PASS");
var role = string.Empty;
if (getWorkflow == false)
{
var getPermission = await _permission.GetPermissionAPIAsync("GET", "SYS_PLACEMENT_PASS");
@ -148,6 +149,7 @@ namespace BMA.EHR.Placement.Service.Controllers
{
return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden);
}
role = jsonData["result"]?.ToString();
}
var rootId = "";
@ -155,6 +157,7 @@ namespace BMA.EHR.Placement.Service.Controllers
var child2Id = "";
var child3Id = "";
var child4Id = "";
var rootDnaId = "";
var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position-act";
using (var client = new HttpClient())
{
@ -166,18 +169,19 @@ namespace BMA.EHR.Placement.Service.Controllers
if (_res.IsSuccessStatusCode)
{
var org = JsonConvert.DeserializeObject<OrgRequestAct>(_result);
if (org.result.isOfficer == false)
if (org.result.isOfficer == false && role?.Trim().ToUpper() != "OWNER")
{
rootId = org.result.rootId == null ? "" : org.result.rootId;
// child1Id = org.result.child1Id == null ? "" : org.result.child1Id;
// child2Id = org.result.child2Id == null ? "" : org.result.child2Id;
// child3Id = org.result.child3Id == null ? "" : org.result.child3Id;
// child4Id = org.result.child4Id == null ? "" : org.result.child4Id;
rootDnaId = org.result.rootDnaId == null ? "" : org.result.rootDnaId;
var data1 = await _context.PlacementProfiles
.Where(x => x.Placement.Id == examId)
.Where(x => x.Draft == true)
.Where(x => x.PlacementStatus != "UN-CONTAIN")
.Where(x => rootId == "" ? true : (child1Id == "" ? x.rootId == rootId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id)))))
.Where(x => rootDnaId == "" ? true : (child1Id == "" ? x.rootDnaId == rootDnaId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id)))))
.Select(x => new
{
Id = x.Id,
@ -300,7 +304,7 @@ namespace BMA.EHR.Placement.Service.Controllers
}
return Success(result1);
}
if (org.result.isOfficer == true)
if (org.result.isOfficer == true || role?.Trim().ToUpper() == "OWNER")
{
var data = await _context.PlacementProfiles.Where(x => x.Placement.Id == examId).Select(x => new
{
@ -688,12 +692,20 @@ namespace BMA.EHR.Placement.Service.Controllers
public async Task<ActionResult<ResponseObject>> GetDashboardByPlacement(Guid examId)
{
var role = string.Empty;
var getPermission = await _permission.GetPermissionAPIAsync("GET", "SYS_PLACEMENT_PASS");
var jsonData = JsonConvert.DeserializeObject<JObject>(getPermission);
if (jsonData["status"]?.ToString() == "200")
{
role = jsonData["result"]?.ToString();
}
var rootId = "";
var child1Id = "";
var child2Id = "";
var child3Id = "";
var child4Id = "";
var rootDnaId = "";
var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position-act";
using (var client = new HttpClient())
{
@ -706,30 +718,31 @@ namespace BMA.EHR.Placement.Service.Controllers
if (_res.IsSuccessStatusCode)
{
var org = JsonConvert.DeserializeObject<OrgRequestAct>(_result);
if (org.result.isOfficer == false)
if (org.result.isOfficer == false && role?.Trim().ToUpper() != "OWNER")
{
rootId = org.result.rootId == null ? "" : org.result.rootId;
// child1Id = org.result.child1Id == null ? "" : org.result.child1Id;
// child2Id = org.result.child2Id == null ? "" : org.result.child2Id;
// child3Id = org.result.child3Id == null ? "" : org.result.child3Id;
// child4Id = org.result.child4Id == null ? "" : org.result.child4Id;
rootDnaId = org.result.rootDnaId == null ? "" : org.result.rootDnaId;
var placement = await _context.Placements
.Where(x => x.Id == examId)
.Select(x => new
{
Total = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootId == "" ? true : (child1Id == "" ? x.rootId == rootId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Count(),
UnContain = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootId == "" ? true : (child1Id == "" ? x.rootId == rootId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "UN-CONTAIN").Count(),
PrepareContain = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootId == "" ? true : (child1Id == "" ? x.rootId == rootId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "PREPARE-CONTAIN").Count(),
Report = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootId == "" ? true : (child1Id == "" ? x.rootId == rootId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "REPORT").Count(),
Done = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootId == "" ? true : (child1Id == "" ? x.rootId == rootId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "DONE").Count(),
Disclaim = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootId == "" ? true : (child1Id == "" ? x.rootId == rootId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "DISCLAIM").Count(),
Total = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootDnaId == "" ? true : (child1Id == "" ? x.rootDnaId == rootDnaId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Count(),
UnContain = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootDnaId == "" ? true : (child1Id == "" ? x.rootDnaId == rootDnaId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "UN-CONTAIN").Count(),
PrepareContain = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootDnaId == "" ? true : (child1Id == "" ? x.rootDnaId == rootDnaId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "PREPARE-CONTAIN").Count(),
Report = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootDnaId == "" ? true : (child1Id == "" ? x.rootDnaId == rootDnaId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "REPORT").Count(),
Done = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootDnaId == "" ? true : (child1Id == "" ? x.rootDnaId == rootDnaId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "DONE").Count(),
Disclaim = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootDnaId == "" ? true : (child1Id == "" ? x.rootDnaId == rootDnaId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "DISCLAIM").Count(),
}).FirstOrDefaultAsync();
if (placement == null)
return Error(GlobalMessages.DataNotFound, 404);
return Success(placement);
}
if (org.result.isOfficer == true)
if (org.result.isOfficer == true || role?.Trim().ToUpper() == "OWNER")
{
var placement = await _context.Placements
.Where(x => x.Id == examId)
@ -848,6 +861,91 @@ namespace BMA.EHR.Placement.Service.Controllers
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("pass/update-status")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> PersonUpdateStatus([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);
person.PlacementStatus = "DONE";
person.LastUpdateFullName = FullName ?? "System Administrator";
person.LastUpdateUserId = UserId ?? "";
person.LastUpdatedAt = DateTime.Now;
await _context.SaveChangesAsync();
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)}")]
public async Task<ActionResult<ResponseObject>> GetPersonDeferment(Guid personalId)
{
@ -1745,7 +1843,7 @@ namespace BMA.EHR.Placement.Service.Controllers
isLeave = false,
dateRetire = (DateTime?)null,
dateAppoint = r.commandDateAffect,
dateStart = r.commandDateAffect,
dateStart = p.ReportingDate,
govAgeAbsent = 0,
govAgePlus = 0,
birthDate = (p.DateOfBirth == null || p.DateOfBirth == DateTime.MinValue) ? (DateTime?)null : p.DateOfBirth,
@ -1947,7 +2045,7 @@ namespace BMA.EHR.Placement.Service.Controllers
.Where(x => req.refIds.Contains(x.Id.ToString()))
// .Where(x => x.PlacementStatus.ToUpper() == "REPORT")
.ToListAsync();
placementProfiles.ForEach(profile => profile.PlacementStatus = "PREPARE-CONTAI");
placementProfiles.ForEach(profile => profile.PlacementStatus = "PREPARE-CONTAIN");
await _context.SaveChangesAsync();
return Success();
}

View file

@ -111,7 +111,7 @@ namespace BMA.EHR.Placement.Service.Controllers
? profileAdmin?.RootDnaId
: "";
}
else if (role == "ROOT" || role == "PARENT")
else if (role == "ROOT" /*|| role == "PARENT"*/)
{
nodeId = profileAdmin?.RootDnaId;
}
@ -193,11 +193,11 @@ namespace BMA.EHR.Placement.Service.Controllers
placementOfficers = placementOfficers
.Where(x => x.rootDnaOldId == nodeId).ToList();
}
else if (role == "PARENT")
{
placementOfficers = placementOfficers
.Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList();
}
// else if (role == "PARENT")
// {
// placementOfficers = placementOfficers
// .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList();
// }
else if (role == "NORMAL")
{
placementOfficers = placementOfficers.Where(x =>
@ -223,7 +223,8 @@ namespace BMA.EHR.Placement.Service.Controllers
[HttpGet("keycloak")]
public async Task<ActionResult<ResponseObject>> GetListByKeycloak()
{
var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position";
// var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position";
var apiUrl = $"{_configuration["API"]}/org/dotnet/get-profileId";
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", ""));

Some files were not shown because too many files have changed in this diff Show more