jichoi / calendar star
박정하 박정하 10 hours ago
250811 박정하 휴가/출장 취소 기능 추가
@1d71c8ec77bd7bc9cdd5334455acbb1fa7dd5d55
client/resources/api/bsrp.js
--- client/resources/api/bsrp.js
+++ client/resources/api/bsrp.js
@@ -33,4 +33,9 @@
 // 출장 정보, 출장 품의 재신청
 export const reapplyBsrpProc = (id, data) => {
   return apiClient.post(`/bsrp/${id}/reapplyBsrp.json`, data);
+}
+
+// 출장 정보, 출장 품의 수정
+export const cancelBsrpCnsulProc = (data) => {
+  return apiClient.put(`/bsrp/${data}/cancelBsrpCnsul.json`);
 }
(파일 끝에 줄바꿈 문자 없음)
client/resources/api/bsrpRport.js
--- client/resources/api/bsrpRport.js
+++ client/resources/api/bsrpRport.js
@@ -28,4 +28,9 @@
 // 출장 복명 재신청
 export const reapplyBsrpRport = data => {
   return fileClient.post('/bsrpRport/reapplyBsrpRport.file', data);
+}
+
+// 출장 복명 수정
+export const cancelBsrpRportProc = data => {
+  return fileClient.put(`/bsrpRport/${data}/cancelBsrpRport.json`);
 }
(파일 끝에 줄바꿈 문자 없음)
client/resources/api/vcatn.js
--- client/resources/api/vcatn.js
+++ client/resources/api/vcatn.js
@@ -43,4 +43,9 @@
 // 휴가 재신청
 export const reapplyVcatnProc = (id, data) => {
   return apiClient.post(`/vcatn/${id}/reapplyVcatn.json`, data);
+}
+
+// 휴가 취소
+export const cancelVcatnProc = (data) => {
+  return apiClient.put(`/vcatn/${data}/cancelVcatn.json`);
 }
(파일 끝에 줄바꿈 문자 없음)
client/views/component/bsrp/BsrpRportView.vue
--- client/views/component/bsrp/BsrpRportView.vue
+++ client/views/component/bsrp/BsrpRportView.vue
@@ -53,6 +53,9 @@
         <button type="button" class="btn sm secondary" @click="handleNavigation('bsrpRportReapply', pageId)">복명서 재신청</button>
       </template>
     </template>
+    <template v-if="isCancelable">
+      <button type="button" class="btn sm secondary" @click="handleCancel">취소</button>
+    </template>
     <button type="button" class="btn sm tertiary" @click="handleNavigation('list')">목록</button>
   </div>
   <ReturnPopup v-if="showReportPopup" @close="showReportPopup = false" @confirm="handleRejection" />
@@ -144,6 +147,15 @@
       );
       return mySanctn && mySanctn.confmAt === 'W' && this.pageMode === 'sanctns';
     },
+
+    // 취소 가능 여부
+    isCancelable() {
+      const sanctnList = this.bsrpRport.sanctnList;
+      const mySanctn = sanctnList.find(
+        item => item.confmerId == this.$store.state.userInfo.userId
+      );
+      return ((mySanctn && this.pageMode === 'sanctns') || this.isApplicant) && this.reportSanctnStatus === 'approved';
+    },
   },
 
   mounted() {
@@ -208,6 +220,20 @@
       }
     },
 
+    // 복명 취소 (결재 승인 취소)
+    async handleCancel() {
+      const isCheck = confirm("출장 복명을 취소하시겠습니까?");
+      if (!isCheck) return;
+
+      try {
+        await cancelVcatnProc(this.pageId);
+        alert("출장 복명이 취소되었습니다.");
+        this.handleNavigation('view', this.pageId);
+      } catch (error) {
+        this.handleError(error);
+      }
+    },
+
     // 첨부 파일 다운로드 핸들러
     async handleDownload(fileOrdr) {
       const selectedFile = this.bsrpRport.fileList.find(file => file.ordr == fileOrdr);
client/views/pages/Manager/attendance/BsrpView.vue
--- client/views/pages/Manager/attendance/BsrpView.vue
+++ client/views/pages/Manager/attendance/BsrpView.vue
@@ -100,6 +100,9 @@
             <button type="button" class="btn sm primary" @click="handleNavigation('bsrpRportInsert', pageId)">복명서 작성</button>
           </template>
         </template>
+        <template v-if="isCancelable">
+          <button type="button" class="btn sm secondary" @click="handleCancel">취소</button>
+        </template>
         <button type="button" class="btn sm tertiary" @click="handleNavigation('list')">목록</button>
       </div>
       <!-- 출장 복명서 -->
@@ -114,7 +117,7 @@
 import ViewerComponent from '../../../component/editor/ViewerComponent.vue';
 import BsrpRportView from '../../../component/bsrp/BsrpRportView.vue';
 // API
-import { findBsrpProc, deleteBsrpProc } from '../../../../resources/api/bsrp';
+import { findBsrpProc, deleteBsrpProc, cancelBsrpCnsulProc } from '../../../../resources/api/bsrp';
 import { updateConfmAtProc } from '../../../../resources/api/sanctns';
 
 export default {
@@ -207,7 +210,16 @@
       }
 
       return userList;
-    }
+    },
+
+    // 취소 가능 여부
+    isCancelable() {
+      const sanctnList = this.bsrpCnsul.sanctnList;
+      const mySanctn = sanctnList.find(
+        item => item.confmerId == this.$store.state.userInfo.userId
+      );
+      return ((mySanctn && this.pageMode === 'sanctns') || this.isApplicant) && this.sanctnStatus === 'approved';
+    },
   },
 
   async created() {
@@ -285,6 +297,20 @@
       }
     },
 
+    // 출장 취소 (결재 승인 취소)
+    async handleCancel() {
+      const isCheck = confirm("출장 품의를 취소하시겠습니까?");
+      if (!isCheck) return;
+
+      try {
+        await cancelBsrpCnsulProc(this.pageId);
+        alert("출장 품의를 취소했습니다.");
+        this.handleNavigation('view', this.pageId);
+      } catch (error) {
+        this.handleError(error);
+      }
+    },
+
     // 반려 결재 핸들러
     async handleRejection(reason) {
       this.returnResn = reason;
client/views/pages/Manager/attendance/VcatnInsert.vue
--- client/views/pages/Manager/attendance/VcatnInsert.vue
+++ client/views/pages/Manager/attendance/VcatnInsert.vue
@@ -566,7 +566,6 @@
         endMnt: this.vacationInfo.endMnt,
         detailCn: this.vacationCnsul.detailCn,
         sanctnList: sanctnList,
-        excessReqstAt: this.excessReqst ? 'Y' : 'N',
       };
     },
   },
client/views/pages/Manager/attendance/VcatnView.vue
--- client/views/pages/Manager/attendance/VcatnView.vue
+++ client/views/pages/Manager/attendance/VcatnView.vue
@@ -63,6 +63,9 @@
             <button type="button" class="btn sm secondary" @click="handleNavigation('reapply', pageId)">재신청</button>
           </template>
         </template>
+        <template v-if="isCancelable">
+          <button type="button" class="btn sm secondary" @click="handleCancel">취소</button>
+        </template>
         <button type="button" class="btn sm tertiary" @click="handleNavigation('list')">목록</button>
       </div>
     </div>
@@ -74,7 +77,7 @@
 import ViewerComponent from '../../../component/editor/ViewerComponent.vue';
 import SanctnViewList from '../../../component/Sanctn/SanctnViewList.vue';
 // API
-import { findVcatnProc, deleteVcatnProc } from '../../../../resources/api/vcatn';
+import { findVcatnProc, deleteVcatnProc, cancelVcatnProc } from '../../../../resources/api/vcatn';
 import { updateConfmAtProc } from '../../../../resources/api/sanctns';
 
 export default {
@@ -145,6 +148,15 @@
         item => item.confmerId == this.$store.state.userInfo.userId
       );
       return mySanctn && mySanctn.confmAt === 'W' && this.pageMode === 'sanctns';
+    },
+
+    // 취소 가능 여부
+    isCancelable() {
+      const sanctnList = this.vacationInfo.sanctnList;
+      const mySanctn = sanctnList.find(
+        item => item.confmerId == this.$store.state.userInfo.userId
+      );
+      return ((mySanctn && this.pageMode === 'sanctns') || this.isApplicant) && this.approvalStatus === 'approved';
     },
   },
 
@@ -223,6 +235,20 @@
       }
     },
 
+    // 휴가 취소 (결재 승인 취소)
+    async handleCancel() {
+      const isCheck = confirm("휴가를 취소하시겠습니까?");
+      if (!isCheck) return;
+
+      try {
+        await cancelVcatnProc(this.pageId);
+        alert("휴가가 취소되었습니다.");
+        this.handleNavigation('view', this.pageId);
+      } catch (error) {
+        this.handleError(error);
+      }
+    },
+
     // 페이지 이동 핸들러
     handleNavigation(type, id) {
       const routeMap = {
Add a comment
List