diff --git a/WebContent/WEB-INF/view/purchaseOrder/deliveryMngAcceptanceList.jsp b/WebContent/WEB-INF/view/purchaseOrder/deliveryMngAcceptanceList.jsp index b917b69..491d89b 100644 --- a/WebContent/WEB-INF/view/purchaseOrder/deliveryMngAcceptanceList.jsp +++ b/WebContent/WEB-INF/view/purchaseOrder/deliveryMngAcceptanceList.jsp @@ -88,6 +88,7 @@ $(document).ready(function(){ var columns = [ // 요구사항: 품의서 No, 발주서 No, 프로젝트번호, 품번, 품명, 공급업체, 발주수량, 입고수량, 미입고수량, 검사성적서, 입고결과 + {title:'STATUS' ,field:'STATUS' ,visible:false, frozen:true}, {title:'TOTAL_SUPPLY_PRICE' ,field:'TOTAL_SUPPLY_PRICE' ,visible:false, frozen:true}, {title:'TOTAL_DELIVERY_PRICE' ,field:'TOTAL_DELIVERY_PRICE' ,visible:false, frozen:true}, {title:'TOTAL_NOT_DELIVERY_PRICE',field:'TOTAL_NOT_DELIVERY_PRICE',visible:false, frozen:true}, @@ -127,15 +128,36 @@ var columns = [ } }, {headerHozAlign : 'center', hozAlign : 'center', minWidth : 90, widthGrow : 1, title : '입고결과', field : 'DELIVERY_STATUS', - formatter:fnc_createGridAnchorTag, - cellClick:function(e, cell){ + formatter: function(cell, formatterParams, onRendered){ + var status = fnc_checkNull(cell.getData().STATUS); + var deliveryStatus = fnc_checkNull(cell.getValue()); + + // 발주취소 상태인 경우 + if(status === 'orderCancel'){ + return '발주취소'; + } + + // 일반 상태 - 링크로 표시 + if(deliveryStatus != ''){ + return '' + deliveryStatus + ''; + } + return deliveryStatus; + }, + cellClick:function(e, cell){ + var status = fnc_checkNull(cell.getData().STATUS); + + // 발주취소 상태인 경우 팝업 열지 않음 + if(status === 'orderCancel'){ + return; + } + var objId = fnc_checkNull(cell.getData().OBJID); var DELIVERY_STATUS = fnc_checkNull(cell.getData().DELIVERY_STATUS); var purchaseOrderNo = fnc_checkNull(cell.getData().PURCHASE_ORDER_NO); fn_deliveryAcceptanceViewPopUp(objId,DELIVERY_STATUS); } - }, - {headerHozAlign : 'center', hozAlign : 'center', minWidth : 140, widthGrow : 1, title : '매입마감', field : 'PURCHASE_CLOSE_DATE'} + } + // {headerHozAlign : 'center', hozAlign : 'center', minWidth : 140, widthGrow : 1, title : '매입마감', field : 'PURCHASE_CLOSE_DATE'} ]; //var grid; @@ -219,7 +241,14 @@ function fn_deliveryAcceptancePopUp(){ if(selected.length > 1){ Swal.fire("한건씩 등록 가능합니다."); return; - }else{ + }else{ + // 발주취소 상태 체크 + var status = fnc_checkNull(selected[0].STATUS); + if(status === 'orderCancel'){ + Swal.fire("발주취소된 건은 입고등록할 수 없습니다."); + return; + } + var MULTI_MASTER_YN = fnc_checkNull(selected[0].MULTI_MASTER_YN); var MULTI_YN = fnc_checkNull(selected[0].MULTI_YN); @@ -472,7 +501,7 @@ function fn_purchaseClose(){
- + <%-- --%>
diff --git a/WebContent/WEB-INF/view/purchaseOrder/purchaseOrderList_new.jsp b/WebContent/WEB-INF/view/purchaseOrder/purchaseOrderList_new.jsp index 5315041..0e0f59c 100644 --- a/WebContent/WEB-INF/view/purchaseOrder/purchaseOrderList_new.jsp +++ b/WebContent/WEB-INF/view/purchaseOrder/purchaseOrderList_new.jsp @@ -76,6 +76,11 @@ $(document).ready(function(){ fn_sendPurchaseOrder(); }); + // 발주 취소 버튼 클릭 + $("#btnOrderCancel").click(function(){ + fn_orderCancel(); + }); + //수주활동 복사 팝업 $("#btnCopy").click(function(){ var checkedObj = _tabulGrid.getSelectedData(); @@ -352,7 +357,11 @@ var columns = [ {headerHozAlign:'center', hozAlign:'center', widthGrow:1, title:'메일발송', field:'MAIL_SEND_YN', formatter: function(cell, formatterParams, onRendered){ var value = fnc_checkNull(cell.getValue()); - if(value === 'Y'){ + var status = fnc_checkNull(cell.getData().STATUS); + // 발주취소 상태인 경우 + if(status === 'orderCancel'){ + return '발주취소'; + } else if(value === 'Y'){ return '발송완료'; } else { return ''; @@ -771,7 +780,7 @@ function fn_sendPurchaseOrder(){ var mailSendYn = fnc_checkNull(selectedData[0].MAIL_SEND_YN); // 취소 상태 확인 - if(status === "cancel"){ + if(status === "cancel" || status === "orderCancel"){ Swal.fire("취소된 발주서는 발송할 수 없습니다."); return false; } @@ -817,6 +826,119 @@ function fn_openMailFormPopup(purchaseOrderObjId){ window.open(url, "purchaseOrderMailForm", "width="+popup_width+",height="+popup_height+",menubar=no,scrollbars=yes,resizable=yes"); } +// 발주 취소 +function fn_orderCancel(){ + var selectedData = _tabulGrid.getSelectedData(); + + if(selectedData.length < 1){ + Swal.fire("발주 취소할 행을 선택해주세요."); + return false; + } else if(selectedData.length > 1){ + Swal.fire("한번에 한 개의 발주서만 취소 가능합니다."); + return false; + } + + var objId = fnc_checkNull(selectedData[0].OBJID); + var status = fnc_checkNull(selectedData[0].STATUS); + var MULTI_YN = fnc_checkNull(selectedData[0].MULTI_YN); + var MULTI_MASTER_YN = fnc_checkNull(selectedData[0].MULTI_MASTER_YN); + var purchaseOrderNo = fnc_checkNull(selectedData[0].PURCHASE_ORDER_NO); + + // 이미 취소된 상태 확인 + if(status === 'cancel'){ + Swal.fire("이미 취소된 발주서입니다."); + return false; + } + + // 이미 발주취소된 상태 확인 + if(status === 'orderCancel'){ + Swal.fire("이미 발주취소된 발주서입니다."); + return false; + } + + // 동시발주 하위건 확인 + if(MULTI_YN === 'Y' && MULTI_MASTER_YN !== 'Y'){ + Swal.fire("동시발주 하위건은 마스터건으로 취소해주세요."); + return false; + } + + // 입고 여부 확인 후 취소 진행 + $.ajax({ + type: "POST", + url: "/purchaseOrder/checkReceiptForCancel.do", + data: { PURCHASE_ORDER_MASTER_OBJID: objId }, + dataType: "json", + success: function(data){ + if(data.hasReceipt){ + // 입고된 항목이 있는 경우 + Swal.fire({ + title: '취소 불가', + html: '입고된 항목이 있어 발주 취소가 불가합니다.

' + + '입고수량: ' + data.totalReceiptQty + '개', + icon: 'error' + }); + } else { + // 입고된 항목이 없는 경우 - 취소 확인 + Swal.fire({ + title: '발주 취소', + html: '발주서 ' + purchaseOrderNo + '을(를) 취소하시겠습니까?

' + + '취소 후에도 목록에서 확인 가능하며, 메일발송 컬럼에 "발주취소"로 표시됩니다.', + icon: 'warning', + showCancelButton: true, + confirmButtonColor: '#d33', + confirmButtonText: '취소하기', + cancelButtonText: '닫기' + }).then((result) => { + if(result.isConfirmed){ + fn_executeOrderCancel(objId); + } + }); + } + }, + error: function(jqxhr, status, error){ + console.error("입고 확인 오류:", error); + Swal.fire("입고 확인 중 오류가 발생했습니다."); + } + }); +} + +// 발주 취소 실행 +function fn_executeOrderCancel(objId){ + $.ajax({ + type: "POST", + url: "/purchaseOrder/executeOrderCancel.do", + data: { PURCHASE_ORDER_MASTER_OBJID: objId }, + dataType: "json", + beforeSend: function(){ + _startLoading("처리중입니다."); + }, + complete: function(){ + _endLoading(); + }, + success: function(data){ + if(data.result){ + Swal.fire({ + title: '완료', + text: '발주가 취소되었습니다.', + icon: 'success' + }).then(() => { + fn_search(); + }); + } else { + Swal.fire({ + title: '오류', + text: data.message || '발주 취소 중 오류가 발생했습니다.', + icon: 'error' + }); + } + }, + error: function(jqxhr, status, error){ + console.error("발주 취소 오류:", error); + Swal.fire("발주 취소 중 오류가 발생했습니다."); + } + }); +} +
@@ -842,6 +964,7 @@ function fn_openMailFormPopup(purchaseOrderObjId){ --> + diff --git a/WebContent/WEB-INF/view/purchaseOrder/purchaseOrderMailFormPopup.jsp b/WebContent/WEB-INF/view/purchaseOrder/purchaseOrderMailFormPopup.jsp index 8fab87c..fa529e1 100644 --- a/WebContent/WEB-INF/view/purchaseOrder/purchaseOrderMailFormPopup.jsp +++ b/WebContent/WEB-INF/view/purchaseOrder/purchaseOrderMailFormPopup.jsp @@ -191,7 +191,14 @@ String purchaseOrderObjId = request.getParameter("purchaseOrderObjId");
- 첨부파일: 발주서(PDF) 및 도면 파일이 자동으로 첨부됩니다. + 첨부파일: 발주서(PDF)가 자동으로 첨부됩니다. +
+ + +
@@ -291,6 +298,9 @@ function fn_loadPurchaseOrderInfo(){ } else { $("#managerListContainer").html('
공급업체 정보가 없습니다.
'); } + + // 도면 파일 개수 조회 + fn_loadDrawingFileCount(purchaseOrderObjId); } else { Swal.fire({ title: '오류', @@ -313,6 +323,31 @@ function fn_loadPurchaseOrderInfo(){ }); } +// 도면 파일 개수 조회 +function fn_loadDrawingFileCount(purchaseOrderObjId){ + $.ajax({ + url: "/purchaseOrder/getDrawingFileCount.do", + type: "POST", + data: { objId: purchaseOrderObjId }, + dataType: "json", + success: function(data){ + if(data.result === "success"){ + var count = parseInt(data.count) || 0; + if(count > 0){ + $("#drawingFileCount").text("(" + count + "개 파일)"); + } else { + $("#drawingFileCount").text("(파일 없음)"); + $("#includeDrawingFiles").prop("checked", false); + $("#includeDrawingFiles").prop("disabled", true); + } + } + }, + error: function(){ + $("#drawingFileCount").text("(조회 실패)"); + } + }); +} + // 공급업체 담당자 목록 로드 function fn_loadPartnerManagers(partnerObjId){ $.ajax({ @@ -669,7 +704,8 @@ function fn_submitMailForm(){ toEmails: $("#toEmails").val(), ccEmails: $("#ccEmails").val(), subject: $("#subject").val(), - contents: $("#contents").val() + contents: $("#contents").val(), + includeDrawingFiles: $("#includeDrawingFiles").is(":checked") ? "Y" : "N" }; $.ajax({ diff --git a/src/com/pms/controller/PurchaseOrderController.java b/src/com/pms/controller/PurchaseOrderController.java index 6e192fb..09e3374 100644 --- a/src/com/pms/controller/PurchaseOrderController.java +++ b/src/com/pms/controller/PurchaseOrderController.java @@ -2112,6 +2112,44 @@ public class PurchaseOrderController { return resultMap; } + /** + * 도면 파일 개수 조회 (AJAX) + * @param request + * @param paramMap - objId (PURCHASE_ORDER_MASTER_OBJID) + * @return + */ + @ResponseBody + @RequestMapping("/purchaseOrder/getDrawingFileCount.do") + public Map getDrawingFileCount(HttpServletRequest request, @RequestParam Map paramMap){ + Map resultMap = new HashMap(); + + try { + String objId = CommonUtils.checkNull(paramMap.get("objId")); + + if("".equals(objId)){ + resultMap.put("result", "error"); + resultMap.put("message", "잘못된 요청입니다."); + return resultMap; + } + + // 도면 파일 개수 조회 + paramMap.put("PURCHASE_ORDER_MASTER_OBJID", objId); + paramMap.put("MULTI_MASTER_OBJID", objId); + List fileList = commonService.selectList("purchaseOrder.purchaseOrderPartFileListForMail", null, paramMap); + + int count = (fileList != null) ? fileList.size() : 0; + resultMap.put("result", "success"); + resultMap.put("count", count); + + } catch (Exception e) { + e.printStackTrace(); + resultMap.put("result", "error"); + resultMap.put("message", "도면 파일 조회 중 오류가 발생했습니다."); + } + + return resultMap; + } + /** * PDF 청크 업로드 (AJAX) * @param request @@ -2234,4 +2272,48 @@ public class PurchaseOrderController { return resultMap; } + + /** + * 발주 취소 전 입고 여부 확인 + * @param request + * @param paramMap + * @return + */ + @ResponseBody + @RequestMapping("/purchaseOrder/checkReceiptForCancel.do") + public Map checkReceiptForCancel(HttpServletRequest request, @RequestParam Map paramMap){ + Map resultMap = new HashMap(); + + try { + resultMap = purchaseOrderService.checkReceiptForCancel(request, paramMap); + } catch (Exception e) { + e.printStackTrace(); + resultMap.put("hasReceipt", true); + resultMap.put("message", "입고 확인 중 오류가 발생했습니다."); + } + + return resultMap; + } + + /** + * 발주 취소 실행 (입고가 없는 경우만) + * @param request + * @param paramMap + * @return + */ + @ResponseBody + @RequestMapping("/purchaseOrder/executeOrderCancel.do") + public Map executeOrderCancel(HttpServletRequest request, @RequestParam Map paramMap){ + Map resultMap = new HashMap(); + + try { + resultMap = purchaseOrderService.executeOrderCancel(request, paramMap); + } catch (Exception e) { + e.printStackTrace(); + resultMap.put("result", false); + resultMap.put("message", "발주 취소 중 오류가 발생했습니다: " + e.getMessage()); + } + + return resultMap; + } } diff --git a/src/com/pms/mapper/purchaseOrder.xml b/src/com/pms/mapper/purchaseOrder.xml index 56624c4..b509da5 100644 --- a/src/com/pms/mapper/purchaseOrder.xml +++ b/src/com/pms/mapper/purchaseOrder.xml @@ -4246,6 +4246,7 @@ SELECT T.* + + + + + + UPDATE PURCHASE_ORDER_MASTER + SET STATUS = #{STATUS} + WHERE OBJID = #{OBJID} + + + + + UPDATE PURCHASE_ORDER_MASTER + SET STATUS = #{STATUS} + WHERE MULTI_MASTER_OBJID = #{OBJID} + + \ No newline at end of file diff --git a/src/com/pms/service/PurchaseOrderService.java b/src/com/pms/service/PurchaseOrderService.java index cf43668..9b79488 100644 --- a/src/com/pms/service/PurchaseOrderService.java +++ b/src/com/pms/service/PurchaseOrderService.java @@ -2961,6 +2961,8 @@ public class PurchaseOrderService { String contents = CommonUtils.checkNull(paramMap.get("contents")); String writerEmail = CommonUtils.checkNull(paramMap.get("WRITER_EMAIL")); String writer = CommonUtils.checkNull(paramMap.get("WRITER")); + // 도면 파일 첨부 여부 (기본값: Y) + String includeDrawingFiles = CommonUtils.checkNull(paramMap.get("includeDrawingFiles"), "Y"); // 발주서 정보 조회 Map infoParam = new HashMap(); @@ -3035,8 +3037,8 @@ public class PurchaseOrderService { attachFileList.add(excelFile); } - // 2. 도면 파일 압축 첨부 - if(partFileList != null && partFileList.size() > 0) { + // 2. 도면 파일 압축 첨부 (includeDrawingFiles가 Y인 경우에만) + if("Y".equals(includeDrawingFiles) && partFileList != null && partFileList.size() > 0) { File zf = MailUtil.zipFileListMail(zipName, partFileList); if(zf != null && zf.exists()) { HashMap hm = new HashMap(); @@ -3177,4 +3179,126 @@ public class PurchaseOrderService { } return resultMap; } + + /** + * 발주 취소 전 입고 여부 확인 + * @param request + * @param paramMap + * @return hasReceipt: 입고 여부, totalReceiptQty: 총 입고수량 + */ + public Map checkReceiptForCancel(HttpServletRequest request, Map paramMap) throws Exception { + Map resultMap = new HashMap(); + SqlSession sqlSession = null; + + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(); + + String purchaseOrderMasterObjid = CommonUtils.checkNull(paramMap.get("PURCHASE_ORDER_MASTER_OBJID")); + + if(purchaseOrderMasterObjid.isEmpty()) { + resultMap.put("hasReceipt", true); + resultMap.put("message", "발주서 정보가 없습니다."); + return resultMap; + } + + // 입고 수량 조회 (ARRIVAL_PLAN 테이블에서 RECEIPT_QTY 합계) + Map queryParam = new HashMap(); + queryParam.put("PURCHASE_ORDER_MASTER_OBJID", purchaseOrderMasterObjid); + + Map receiptInfo = (Map)sqlSession.selectOne("purchaseOrder.getTotalReceiptQtyForCancel", queryParam); + + int totalReceiptQty = 0; + if(receiptInfo != null) { + // 대소문자 구분 없이 키 찾기 + Object qtyValue = receiptInfo.get("TOTAL_RECEIPT_QTY"); + if(qtyValue == null) { + qtyValue = receiptInfo.get("total_receipt_qty"); + } + if(qtyValue != null) { + totalReceiptQty = Integer.parseInt(qtyValue.toString()); + } + System.out.println("=== 발주취소 입고확인(check) === OBJID: " + purchaseOrderMasterObjid + ", receiptInfo: " + receiptInfo + ", totalReceiptQty: " + totalReceiptQty); + } + + resultMap.put("hasReceipt", totalReceiptQty > 0); + resultMap.put("totalReceiptQty", totalReceiptQty); + + } finally { + if(sqlSession != null) sqlSession.close(); + } + + return resultMap; + } + + /** + * 발주 취소 실행 (입고가 없는 경우만) + * STATUS를 'orderCancel'로 변경 + * @param request + * @param paramMap + * @return + */ + public Map executeOrderCancel(HttpServletRequest request, Map paramMap) throws Exception { + Map resultMap = new HashMap(); + SqlSession sqlSession = null; + + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(false); + + String purchaseOrderMasterObjid = CommonUtils.checkNull(paramMap.get("PURCHASE_ORDER_MASTER_OBJID")); + + if(purchaseOrderMasterObjid.isEmpty()) { + resultMap.put("result", false); + resultMap.put("message", "발주서 정보가 없습니다."); + return resultMap; + } + + // 입고 여부 재확인 + Map queryParam = new HashMap(); + queryParam.put("PURCHASE_ORDER_MASTER_OBJID", purchaseOrderMasterObjid); + + Map receiptInfo = (Map)sqlSession.selectOne("purchaseOrder.getTotalReceiptQtyForCancel", queryParam); + + int totalReceiptQty = 0; + if(receiptInfo != null) { + // 대소문자 구분 없이 키 찾기 + Object qtyValue = receiptInfo.get("TOTAL_RECEIPT_QTY"); + if(qtyValue == null) { + qtyValue = receiptInfo.get("total_receipt_qty"); + } + if(qtyValue != null) { + totalReceiptQty = Integer.parseInt(qtyValue.toString()); + } + System.out.println("=== 발주취소 입고확인 === OBJID: " + purchaseOrderMasterObjid + ", receiptInfo: " + receiptInfo + ", totalReceiptQty: " + totalReceiptQty); + } + + if(totalReceiptQty > 0) { + resultMap.put("result", false); + resultMap.put("message", "입고된 항목이 있어 취소할 수 없습니다. (입고수량: " + totalReceiptQty + ")"); + return resultMap; + } + + // 발주 취소 처리 (STATUS = 'orderCancel') + Map updateParam = new HashMap(); + updateParam.put("OBJID", purchaseOrderMasterObjid); + updateParam.put("STATUS", "orderCancel"); + + sqlSession.update("purchaseOrder.updateOrderCancelStatus", updateParam); + + // 동시발주 하위건도 함께 취소 + sqlSession.update("purchaseOrder.updateOrderCancelStatusMulti", updateParam); + + sqlSession.commit(); + + resultMap.put("result", true); + resultMap.put("message", "발주가 취소되었습니다."); + + } catch(Exception e) { + if(sqlSession != null) sqlSession.rollback(); + throw e; + } finally { + if(sqlSession != null) sqlSession.close(); + } + + return resultMap; + } }