From 05dc5b5d76027a6ca76667c04028307da9200d8b Mon Sep 17 00:00:00 2001 From: "hehihoho3@gmail.com" Date: Tue, 12 Aug 2025 15:49:34 +0900 Subject: [PATCH] =?UTF-8?q?=EC=97=B0=EC=B0=A8=EB=B3=80=EA=B2=BD=EC=9B=90?= =?UTF-8?q?=20=EC=A7=84=ED=96=89=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mapper/domain/LeaveChangeRequestVO.java | 3 + .../itn/leave/mapper/domain/LeavePlanVO.java | 2 + .../itn/leave/service/ItnLeaveService.java | 2 +- .../service/impl/ItnLeaveServiceImpl.java | 75 +++++++-- .../itn/leave/web/ItnLeaveRestController.java | 16 +- .../mapper/itn/leave/ItnLeaveMapper.xml | 18 ++- .../itn/leave/leave_approval_list.html | 14 ++ .../templates/itn/leave/leave_status.html | 144 +++++++++++++++--- 8 files changed, 233 insertions(+), 41 deletions(-) diff --git a/src/main/java/com/itn/admin/itn/leave/mapper/domain/LeaveChangeRequestVO.java b/src/main/java/com/itn/admin/itn/leave/mapper/domain/LeaveChangeRequestVO.java index 84b5942..da3be53 100644 --- a/src/main/java/com/itn/admin/itn/leave/mapper/domain/LeaveChangeRequestVO.java +++ b/src/main/java/com/itn/admin/itn/leave/mapper/domain/LeaveChangeRequestVO.java @@ -5,6 +5,7 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; +import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; @@ -18,6 +19,8 @@ public class LeaveChangeRequestVO { private String userName; private LocalDate originalLeaveDate; private LocalDate requestedLeaveDate; + private BigDecimal originalLeaveDays; // 추가 + private BigDecimal requestedLeaveDays; // 추가 private String requestReason; private String requestStatus; private String requestStatusName; // 추가 diff --git a/src/main/java/com/itn/admin/itn/leave/mapper/domain/LeavePlanVO.java b/src/main/java/com/itn/admin/itn/leave/mapper/domain/LeavePlanVO.java index 8402ffa..2e13882 100644 --- a/src/main/java/com/itn/admin/itn/leave/mapper/domain/LeavePlanVO.java +++ b/src/main/java/com/itn/admin/itn/leave/mapper/domain/LeavePlanVO.java @@ -32,4 +32,6 @@ public class LeavePlanVO { private String requestStatus; // 변경 상태 private String requestStatusName; // 변경 상태 코드 name private String changeCount; // 변경 카운트 + private BigDecimal originalLeaveDays; // 변경 전 일수 + private BigDecimal requestedLeaveDays; // 변경 후 일수 } diff --git a/src/main/java/com/itn/admin/itn/leave/service/ItnLeaveService.java b/src/main/java/com/itn/admin/itn/leave/service/ItnLeaveService.java index aa4ff3a..fc89d89 100644 --- a/src/main/java/com/itn/admin/itn/leave/service/ItnLeaveService.java +++ b/src/main/java/com/itn/admin/itn/leave/service/ItnLeaveService.java @@ -27,7 +27,7 @@ public interface ItnLeaveService { boolean rejectLeavePlan(Long planId, String approverId, String rejectReason); - boolean requestLeaveChange(LeaveChangeRequestVO leaveChangeRequestVO); + RestResponse requestLeaveChange(LeaveChangeRequestVO leaveChangeRequestVO); LeaveChangeRequestVO getLeaveChangeRequestDetail(Long changeRequestId); diff --git a/src/main/java/com/itn/admin/itn/leave/service/impl/ItnLeaveServiceImpl.java b/src/main/java/com/itn/admin/itn/leave/service/impl/ItnLeaveServiceImpl.java index c47484d..f93021c 100644 --- a/src/main/java/com/itn/admin/itn/leave/service/impl/ItnLeaveServiceImpl.java +++ b/src/main/java/com/itn/admin/itn/leave/service/impl/ItnLeaveServiceImpl.java @@ -71,6 +71,8 @@ public class ItnLeaveServiceImpl implements ItnLeaveService { LeaveChangeRequestVO leaveChangeRequestVO = itnLeaveMapper.selectItnLeaveChangeRequestById(changeRequestId); if (leaveChangeRequestVO != null) { leaveChangeRequestVO.setUserName(tCodeUtils.getUserName(leaveChangeRequestVO.getUniqId())); + log.info("originalLeaveDays: {}", leaveChangeRequestVO.getOriginalLeaveDays()); + log.info("requestedLeaveDays: {}", leaveChangeRequestVO.getRequestedLeaveDays()); } return leaveChangeRequestVO; } @@ -79,6 +81,30 @@ public class ItnLeaveServiceImpl implements ItnLeaveService { @Override @Transactional public boolean saveLeavePlan(LeavePlanVO leavePlanVO) { + // ==================== [검증 로직 시작] ==================== + + // 1. 사용자의 촉진 연차 목표치 조회 (itn_leave_status.promoted_leave) + LeaveStatusVO leaveStatus = itnLeaveMapper.selectLeaveStatusByUserIdAndYear(leavePlanVO.getUniqId(), leavePlanVO.getYear()); + BigDecimal promotedLeaveGoal = leaveStatus.getPromotedLeave(); + + // 2. 현재 DB에 저장된 '승인' 및 '대기' 상태의 모든 연차 계획 총합을 계산 + List existingPlans = itnLeaveMapper.selectLeavePlansByUserIdAndYear(leavePlanVO.getUniqId(), leavePlanVO.getYear()); + BigDecimal currentPledgedDays = existingPlans.stream() + .filter(p -> "10".equals(p.getStatus()) || "20".equals(p.getStatus())) // 10: 대기, 20: 승인 + .map(LeavePlanVO::getLeaveDays) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + // 3. 이번에 새로 신청한 연차 일수 + BigDecimal newRequestDays = leavePlanVO.getLeaveDays(); + + // 4. 예상 총합(기존 총합 + 신규 신청)이 목표치를 초과하는지 검증 + if (currentPledgedDays.add(newRequestDays).compareTo(promotedLeaveGoal) > 0) { + // 초과할 경우, 예외를 발생시켜 저장을 막고 사용자에게 에러 메시지 전달 + throw new IllegalArgumentException("신청 가능한 촉진 연차 일수를 초과했습니다."); + } + + // ==================== [검증 로직 종료] ==================== + if (leavePlanVO.getPlanId() == null) { // 신규 신청 leavePlanVO.setStatus(LEAVE_STATUS_PENDING); // 초기 상태는 PENDING @@ -173,18 +199,37 @@ public class ItnLeaveServiceImpl implements ItnLeaveService { // 연차 변경 요청 @Override @Transactional - public boolean requestLeaveChange(LeaveChangeRequestVO leaveChangeRequestVO) { - // 기존 연차 계획의 변경 요청 상태를 10으로 업데이트 -// LeavePlanVO originalLeavePlan = itnLeaveMapper.selectLeavePlanById(leaveChangeRequestVO.getPlanId()); -// if (originalLeavePlan == null) { -// return false; // 원본 연차 계획이 없음 -// } -// leaveChangeRequestVO.setUniqId(originalLeavePlan.getUniqId()); + public RestResponse requestLeaveChange(LeaveChangeRequestVO leaveChangeRequestVO) { + // ==================== [검증 로직 시작] ==================== + // 1. 사용자의 촉진 연차 목표치 조회 (itn_leave_status.promoted_leave) + LeaveStatusVO leaveStatus = itnLeaveMapper.selectLeaveStatusByUserIdAndYear(leaveChangeRequestVO.getUniqId(), String.valueOf(leaveChangeRequestVO.getOriginalLeaveDate().getYear())); + BigDecimal promotedLeaveGoal = leaveStatus.getPromotedLeave(); - // 연차 변경 요청 이력 저장 + // 2. 현재 DB에 저장된 '승인' 및 '대기' 상태의 모든 연차 계획 총합을 계산 + List existingPlans = itnLeaveMapper.selectLeavePlansByUserIdAndYear(leaveChangeRequestVO.getUniqId(), String.valueOf(leaveChangeRequestVO.getOriginalLeaveDate().getYear())); + BigDecimal currentPledgedDays = existingPlans.stream() + .filter(p -> "10".equals(p.getStatus()) || "20".equals(p.getStatus())) // 10: 대기, 20: 승인 + .map(LeavePlanVO::getLeaveDays) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + // 3. 이번 '변경'으로 인해 발생하는 연차 변동량 계산 + // (예: 1.0일 -> 0.5일 변경 시 -0.5 / 0.5일 -> 1.0일 변경 시 +0.5) + BigDecimal changeInDays = leaveChangeRequestVO.getRequestedLeaveDays().subtract(leaveChangeRequestVO.getOriginalLeaveDays()); + + // 4. 예상 총합(기존 총합 + 변동량)이 목표치를 초과하는지 검증 + if (currentPledgedDays.add(changeInDays).compareTo(promotedLeaveGoal) > 0) { + // 초과할 경우, 예외를 발생시켜 저장을 막음 + return new RestResponse(HttpStatus.BAD_REQUEST, "변경 후 총 연차 일수가 촉진 연차 한도를 초과합니다."); + } + + // ==================== [검증 로직 종료] ==================== + + // 5. 검증 통과 시, 기존 로직 수행 leaveChangeRequestVO.setRequestStatus(LEAVE_CHANGE_REQUEST_STATUS_PENDING); - return itnLeaveMapper.insertLeaveChangeRequest(leaveChangeRequestVO) > 0; + itnLeaveMapper.insertLeaveChangeRequest(leaveChangeRequestVO); + + return new RestResponse(HttpStatus.OK, "연차 변경 요청이 성공적으로 제출되었습니다."); } @@ -202,11 +247,21 @@ public class ItnLeaveServiceImpl implements ItnLeaveService { changeRequest.setApproverId(approverId); itnLeaveMapper.updateLeaveChangeRequestStatus(changeRequest); - // 원본 연차 계획 업데이트 (날짜 변경) + // 원본 연차 계획 업데이트 (날짜 및 일수 변경) LeavePlanVO originalLeavePlan = itnLeaveMapper.selectLeavePlanById(changeRequest.getPlanId()); if (originalLeavePlan != null) { originalLeavePlan.setLeaveDate(changeRequest.getRequestedLeaveDate()); + originalLeavePlan.setLeaveDays(changeRequest.getRequestedLeaveDays()); // 일수 변경 반영 itnLeaveMapper.updateLeavePlan(originalLeavePlan); // 기존 updateLeavePlan 사용 + + // 사용된 연차 재계산 및 업데이트 (used_leave는 고정값이므로 수정하지 않음) + // LeaveStatusVO leaveStatus = itnLeaveMapper.selectLeaveStatusByUserIdAndYear(originalLeavePlan.getUniqId(), originalLeavePlan.getYear()); + // if (leaveStatus != null) { + // BigDecimal oldUsedLeave = leaveStatus.getUsedLeave(); + // BigDecimal changedAmount = changeRequest.getOriginalLeaveDays().subtract(changeRequest.getRequestedLeaveDays()); + // leaveStatus.setUsedLeave(oldUsedLeave.subtract(changedAmount)); + // itnLeaveMapper.updateLeaveStatus(leaveStatus); + // } } return true; diff --git a/src/main/java/com/itn/admin/itn/leave/web/ItnLeaveRestController.java b/src/main/java/com/itn/admin/itn/leave/web/ItnLeaveRestController.java index 4c60c85..507482c 100644 --- a/src/main/java/com/itn/admin/itn/leave/web/ItnLeaveRestController.java +++ b/src/main/java/com/itn/admin/itn/leave/web/ItnLeaveRestController.java @@ -89,12 +89,16 @@ public class ItnLeaveRestController { public ResponseEntity submitLeaveChangeRequest(@RequestBody LeaveChangeRequestVO leaveChangeRequestVO , @AuthenticationPrincipal CustomUserDetails loginUser) { leaveChangeRequestVO.setUniqId(loginUser.getUser().getUniqId()); - boolean result = itnLeaveService.requestLeaveChange(leaveChangeRequestVO); - if (result) { - return ResponseEntity.ok().body(new RestResponse(HttpStatus.OK, "연차 변경 요청이 성공적으로 제출되었습니다.")); - } else { - return ResponseEntity.badRequest().body(new RestResponse(HttpStatus.BAD_REQUEST, "연차 변경 요청 제출에 실패했습니다.")); - } +// boolean result = itnLeaveService.requestLeaveChange(leaveChangeRequestVO); + + + + return ResponseEntity.ok().body(itnLeaveService.requestLeaveChange(leaveChangeRequestVO)); +// if (result) { +// return ResponseEntity.ok().body(new RestResponse(HttpStatus.OK, "연차 변경 요청이 성공적으로 제출되었습니다.")); +// } else { +// return ResponseEntity.badRequest().body(new RestResponse(HttpStatus.BAD_REQUEST, "연차 변경 요청 제출에 실패했습니다.")); +// } } // 연차 변경 요청 상세 조회 diff --git a/src/main/resources/mapper/itn/leave/ItnLeaveMapper.xml b/src/main/resources/mapper/itn/leave/ItnLeaveMapper.xml index 87e8cb6..9ea6a6a 100644 --- a/src/main/resources/mapper/itn/leave/ItnLeaveMapper.xml +++ b/src/main/resources/mapper/itn/leave/ItnLeaveMapper.xml @@ -53,6 +53,8 @@ c.change_request_id, c.original_leave_date, c.requested_leave_date, + c.original_leave_days, + c.requested_leave_days, c.request_reason, c.request_status, c.request_dt, @@ -143,6 +145,8 @@ UNIQ_ID, ORIGINAL_LEAVE_DATE, REQUESTED_LEAVE_DATE, + ORIGINAL_LEAVE_DAYS, + REQUESTED_LEAVE_DAYS, REQUEST_REASON, REQUEST_STATUS, REQUEST_DT @@ -151,6 +155,8 @@ #{uniqId}, #{originalLeaveDate}, #{requestedLeaveDate}, + #{originalLeaveDays}, + #{requestedLeaveDays}, #{requestReason}, #{requestStatus}, NOW() @@ -167,6 +173,8 @@ T1.UNIQ_ID, T1.ORIGINAL_LEAVE_DATE, T1.REQUESTED_LEAVE_DATE, + T1.ORIGINAL_LEAVE_DAYS, + T1.REQUESTED_LEAVE_DAYS, T1.REQUEST_REASON, T1.REQUEST_STATUS, (SELECT CODE_NAME FROM COMMON_CODE_DETAIL WHERE CODE_GROUP_ID = 'CHANGE_REQUEST_STATUS' AND CODE_ID = T1.REQUEST_STATUS) AS requestStatusName, @@ -195,6 +203,8 @@ T1.UNIQ_ID, T1.ORIGINAL_LEAVE_DATE, T1.REQUESTED_LEAVE_DATE, + T1.ORIGINAL_LEAVE_DAYS, + T1.REQUESTED_LEAVE_DAYS, T1.REQUEST_REASON, T1.REQUEST_STATUS, (SELECT CODE_NAME FROM COMMON_CODE_DETAIL WHERE CODE_GROUP_ID = 'CHANGE_REQUEST_STATUS' AND CODE_ID = T1.REQUEST_STATUS) AS requestStatusName, @@ -211,11 +221,11 @@ SELECT T1.CHANGE_REQUEST_ID, T1.PLAN_ID, - ilp.YEAR , - ilp.LEAVE_DAYS , T1.UNIQ_ID, T1.ORIGINAL_LEAVE_DATE, T1.REQUESTED_LEAVE_DATE, + T1.ORIGINAL_LEAVE_DAYS, + T1.REQUESTED_LEAVE_DAYS, T1.REQUEST_REASON, T1.REQUEST_STATUS, (SELECT CODE_NAME FROM COMMON_CODE_DETAIL WHERE CODE_GROUP_ID = 'CHANGE_REQUEST_STATUS' AND CODE_ID = T1.REQUEST_STATUS) AS requestStatusName, @@ -224,8 +234,6 @@ T1.REQUEST_DT, T1.APPROVAL_DT FROM ITN_LEAVE_CHANGE_REQUEST T1 - left join itn_leave_plan ilp - on T1.plan_id = ilp.PLAN_ID WHERE T1.CHANGE_REQUEST_ID = #{changeRequestId} @@ -238,6 +246,8 @@ T1.UNIQ_ID, T1.ORIGINAL_LEAVE_DATE, T1.REQUESTED_LEAVE_DATE, + T1.ORIGINAL_LEAVE_DAYS, + T1.REQUESTED_LEAVE_DAYS, T1.REQUEST_REASON, T1.REQUEST_STATUS, (SELECT CODE_NAME FROM COMMON_CODE_DETAIL WHERE CODE_GROUP_ID = 'CHANGE_REQUEST_STATUS' AND CODE_ID = T1.REQUEST_STATUS) AS requestStatusName, diff --git a/src/main/resources/templates/itn/leave/leave_approval_list.html b/src/main/resources/templates/itn/leave/leave_approval_list.html index 0a886ed..0fa7c59 100644 --- a/src/main/resources/templates/itn/leave/leave_approval_list.html +++ b/src/main/resources/templates/itn/leave/leave_approval_list.html @@ -53,6 +53,7 @@ 신청자 기존 연차 일자 요청 변경 일자 + 변경일수 요청 사유 요청 일시 상태 @@ -64,6 +65,10 @@ + + + @@ -179,6 +184,11 @@ $('#leaveChangeRequestDetailModal #detailChangeRequestUserId').val(data.userName); $('#leaveChangeRequestDetailModal #detailOriginalLeaveDate').val(data.originalLeaveDate); $('#leaveChangeRequestDetailModal #detailRequestedLeaveDate').val(data.requestedLeaveDate); + if (data.originalLeaveDays != null && data.requestedLeaveDays != null) { + $('#leaveChangeRequestDetailModal #detailLeaveDaysChange').val(`${data.originalLeaveDays} → ${data.requestedLeaveDays}`); + } else { + $('#leaveChangeRequestDetailModal #detailLeaveDaysChange').val('-'); + } $('#leaveChangeRequestDetailModal #detailRequestReason').val(data.requestReason); $('#leaveChangeRequestDetailModal #detailChangeRequestRequestDt').val(data.requestDt); $('#leaveChangeRequestDetailModal #changeRequestApprovalStatus').val('20'); // 기본값 설정 : 승인 @@ -237,6 +247,10 @@ +
+ + +
diff --git a/src/main/resources/templates/itn/leave/leave_status.html b/src/main/resources/templates/itn/leave/leave_status.html index f61b60e..4b47adc 100644 --- a/src/main/resources/templates/itn/leave/leave_status.html +++ b/src/main/resources/templates/itn/leave/leave_status.html @@ -112,6 +112,56 @@
+ +
+
촉진 연차 현황
+ + + + + + + + + + + + + + + +
촉진 연차 목표10.0일
현재 계획된 연차 총합8.0일
추가 계획 가능 연차2.0일
+ + +
+

디버깅 정보:

+

촉진 연차 목표 (promotedLeave):

+

현재 계획된 연차 총합 (plannedTotal):

+

추가 계획 가능 연차 (remainingPlanDays):

+
+ + +
+ + +
+ + + +
+
📅 연차 사용 계획
@@ -122,6 +172,7 @@ 일자 일수 변경 횟수 + 변경일수 요청 상태 반려 사유 관리 @@ -141,6 +192,10 @@ style="cursor: pointer;"> + + + @@ -158,7 +213,7 @@ 변경 요청 @@ -172,6 +227,14 @@ + + + 합계 + + + + +
@@ -220,7 +283,7 @@ $body.empty(); // 기존 내용 제거 if (historyList.length === 0) { - $body.append('변경 이력이 없습니다.'); + $body.append('변경 이력이 없습니다.'); return; } @@ -230,6 +293,9 @@ : item.requestStatus === '40' ? 'badge-success' : 'badge-light'; + const leaveDaysChangeHtml = (item.originalLeaveDays != null && item.requestedLeaveDays != null) ? + `${item.originalLeaveDays} → ${item.requestedLeaveDays}` : '-'; + const row = ` @@ -237,6 +303,7 @@ ${item.originalLeaveDate} ${item.requestedLeaveDate} + ${leaveDaysChangeHtml} ${item.requestReason} ${item.requestStatusName} @@ -254,11 +321,13 @@ }); } - window.openLeaveChangeRequestModal = function (planId, originalLeaveDate) { + window.openLeaveChangeRequestModal = function (planId, originalLeaveDate, originalLeaveDays) { console.log(' openLeaveChangeRequestModal '); $('#leaveChangeRequestModal #planId').val(planId); $('#leaveChangeRequestModal #originalLeaveDate').val(originalLeaveDate); + $('#leaveChangeRequestModal #originalLeaveDays').val(originalLeaveDays); $('#leaveChangeRequestModal #requestedLeaveDate').val(''); + $('#leaveChangeRequestModal #requestedLeaveDays').val('1.0'); $('#leaveChangeRequestModal #requestReason').val(''); $('#leaveChangeRequestModal').modal('show'); } @@ -266,7 +335,9 @@ function submitLeaveChangeRequest() { const planId = $('#leaveChangeRequestModal #planId').val(); const originalLeaveDate = $('#leaveChangeRequestModal #originalLeaveDate').val(); + const originalLeaveDays = $('#leaveChangeRequestModal #originalLeaveDays').val(); const requestedLeaveDate = $('#leaveChangeRequestModal #requestedLeaveDate').val(); + const requestedLeaveDays = $('#leaveChangeRequestModal #requestedLeaveDays').val(); const requestReason = $('#leaveChangeRequestModal #requestReason').val(); if (!requestedLeaveDate) { @@ -283,7 +354,9 @@ const data = { planId: planId, originalLeaveDate: originalLeaveDate, + originalLeaveDays: originalLeaveDays, requestedLeaveDate: requestedLeaveDate, + requestedLeaveDays: requestedLeaveDays, requestReason: requestReason }; @@ -295,9 +368,15 @@ contentType: 'application/json', data: JSON.stringify(data), success: function (response) { - fn_successAlert(response.msg); - $('#leaveChangeRequestModal').modal('hide'); - location.reload(); + if(response.status == 'BAD_REQUEST'){ + + fn_failedAlert(response.msg); + }else{ + + fn_successAlert(response.msg); + $('#leaveChangeRequestModal').modal('hide'); + location.reload(); + } }, error: function (xhr, status, error) { const errorResponse = JSON.parse(xhr.responseText); @@ -321,6 +400,7 @@ +
+ + +
@@ -351,7 +438,7 @@