주문서관리 수주취소 기능 추가(부분 취소만 가능)

This commit is contained in:
2026-02-27 12:19:40 +09:00
parent 8dfa81d279
commit 6f07267e18
4 changed files with 289 additions and 7 deletions

View File

@@ -151,6 +151,28 @@ $(document).ready(function(){
document.form1.submit();
});
// 수주취소
$("#btnOrderCancel").click(function(){
var selectedData = _tabulGrid.getSelectedData();
if(selectedData.length < 1){
Swal.fire("수주취소할 행을 선택해주십시오.");
return false;
} else if(selectedData.length > 1){
Swal.fire("한번에 한개의 수주만 취소 가능합니다.");
return false;
}
var contractObjId = fnc_checkNull(selectedData[0].OBJID);
var orderQty = parseInt(selectedData[0].ORDER_QUANTITY) || 0;
if(orderQty === 0){
Swal.fire("수주 수량이 없는 건은 취소할 수 없습니다.");
return false;
}
fn_openOrderCancelPopup(contractObjId);
});
fn_search();
});
@@ -203,6 +225,14 @@ var columns = [
return Number(value).toLocaleString();
}
},
// 8-1. 수주취소
{headerHozAlign : 'center', hozAlign : 'right', minWidth : 60, widthGrow: 0.7, title : '수주취소', field : 'CANCEL_QTY_SUM',
formatter: function(cell) {
var value = cell.getValue();
if(!value || value === '' || value === 0 || value === '0') return '';
return "<span style='color:#e74c3c; font-weight:bold;'>" + Number(value).toLocaleString() + "</span>";
}
},
// 9. 유/무상
{headerHozAlign : 'center', hozAlign : 'center', minWidth : 45, widthGrow: 0.5, title : '유/무상', field : 'PAID_TYPE' },
// 10. 수주상태
@@ -656,6 +686,131 @@ function fn_showSerialNoPopup(serialNoString){
});
}
// 수주취소 팝업 - 품목별 취소 수량 입력
function fn_openOrderCancelPopup(contractObjId){
$.ajax({
url: "/contractMgmt/getContractItems.do",
type: "POST",
data: { contractObjId: contractObjId },
dataType: "json",
success: function(data){
if(data.result !== "success" || !data.items || data.items.length === 0){
Swal.fire("수주 품목 정보가 없습니다.");
return;
}
var items = data.items;
var html = '<div style="max-height: 400px; overflow-y: auto;">';
html += '<table style="width: 100%; border-collapse: collapse;">';
html += '<thead><tr style="background-color: #f0f0f0;">';
html += '<th style="border: 1px solid #ddd; padding: 8px;">품번</th>';
html += '<th style="border: 1px solid #ddd; padding: 8px;">품명</th>';
html += '<th style="border: 1px solid #ddd; padding: 8px;">수주수량</th>';
html += '<th style="border: 1px solid #ddd; padding: 8px;">취소수량</th>';
html += '</tr></thead><tbody>';
items.forEach(function(item, idx){
var objid = item.OBJID || item.objid || '';
var partNo = item.PART_NO || item.part_no || '';
var partName = item.PART_NAME || item.part_name || '';
var orderQty = item.ORDER_QUANTITY || item.order_quantity || '0';
var cancelQty = item.CANCEL_QTY || item.cancel_qty || '';
var orderQtyNum = parseInt(orderQty) || 0;
var maxCancel = orderQtyNum > 0 ? orderQtyNum - 1 : 0;
html += '<tr>';
html += '<td style="border: 1px solid #ddd; padding: 8px; text-align: center;">' + partNo + '</td>';
html += '<td style="border: 1px solid #ddd; padding: 8px; text-align: left;">' + partName + '</td>';
html += '<td style="border: 1px solid #ddd; padding: 8px; text-align: right;">' + (orderQtyNum > 0 ? Number(orderQtyNum).toLocaleString() : '-') + '</td>';
html += '<td style="border: 1px solid #ddd; padding: 8px; text-align: center;">';
if(orderQtyNum > 0){
html += '<input type="number" class="cancel-qty-input" data-objid="' + objid + '" data-order-qty="' + orderQtyNum + '" ';
html += 'value="' + (cancelQty || '') + '" min="0" max="' + maxCancel + '" ';
html += 'style="width: 80px; text-align: right; padding: 4px;" placeholder="0">';
} else {
html += '-';
}
html += '</td>';
html += '</tr>';
});
html += '</tbody></table>';
html += '<p style="margin-top: 10px; color: #888; font-size: 12px;">※ 전체 수량 취소는 불가하며, 부분 수량만 취소 가능합니다.</p>';
html += '</div>';
Swal.fire({
title: '수주취소 수량 입력',
html: html,
width: '700px',
showCancelButton: true,
confirmButtonText: '저장',
cancelButtonText: '닫기',
confirmButtonColor: '#e74c3c',
preConfirm: function(){
var inputs = document.querySelectorAll('.cancel-qty-input');
var itemObjIds = [];
var cancelQtys = [];
var orderQtys = [];
var hasError = false;
inputs.forEach(function(input){
var val = input.value.trim();
var orderQty = parseInt(input.getAttribute('data-order-qty'));
var cancelVal = val === '' ? 0 : parseInt(val);
if(cancelVal < 0){
Swal.showValidationMessage('취소 수량은 0 이상이어야 합니다.');
hasError = true;
return;
}
if(cancelVal >= orderQty){
Swal.showValidationMessage('취소 수량(' + cancelVal + ')은 수주수량(' + orderQty + ')보다 적어야 합니다.');
hasError = true;
return;
}
itemObjIds.push(input.getAttribute('data-objid'));
cancelQtys.push(cancelVal);
orderQtys.push(orderQty);
});
if(hasError) return false;
return { itemObjIds: itemObjIds, cancelQtys: cancelQtys, orderQtys: orderQtys };
}
}).then(function(result){
if(result.isConfirmed && result.value){
var val = result.value;
$.ajax({
url: "/contractMgmt/saveOrderCancelQty.do",
type: "POST",
data: {
itemObjIds: val.itemObjIds.join(","),
cancelQtys: val.cancelQtys.join(","),
orderQtys: val.orderQtys.join(",")
},
dataType: "json",
success: function(res){
if(res.result === "true"){
Swal.fire({ title: '저장 완료', text: res.msg, icon: 'success' });
fn_search();
} else {
Swal.fire({ title: '저장 실패', text: res.msg, icon: 'error' });
}
},
error: function(){
Swal.fire("수주취소 저장 중 오류가 발생했습니다.");
}
});
}
});
},
error: function(){
Swal.fire("품목 정보 조회 중 오류가 발생했습니다.");
}
});
}
//코드값을 받아와서 동적으로 selectbox 생성
function optionJobGroup(code){
var val=code;
@@ -790,11 +945,12 @@ function openProjectFormPopUp(objId){
<h2>
<span>영업관리_주문서관리</span>
</h2>
<div class="btnArea">
<input type="button" value="조회" class="plm_btns" id="btnSearch" name="btnSearch">
<input type="button" value="수주등록" class="plm_btns btnRegist">
<input type="button" value="결재상신" class="plm_btns" id="btnApproval">
</div>
<div class="btnArea">
<input type="button" value="조회" class="plm_btns" id="btnSearch" name="btnSearch">
<input type="button" value="수주등록" class="plm_btns btnRegist">
<input type="button" value="수주취소" class="plm_btns" id="btnOrderCancel" style="background-color:#e74c3c; color:#fff;">
<input type="button" value="결재상신" class="plm_btns" id="btnApproval">
</div>
</div>
<div id="plmSearchZon">
<!-- 검색필터: 주문유형, 발주번호, 고객사, 품번, 품명, S/N, 수주상태, 발주일(기간), 요청납기(기간) -->

View File

@@ -2853,6 +2853,25 @@ public class ContractMgmtController {
return resultMap;
}
/**
* 수주취소 수량 저장
*/
@ResponseBody
@RequestMapping(value="/contractMgmt/saveOrderCancelQty.do", method=RequestMethod.POST)
public Map saveOrderCancelQty(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
Map resultMap = new HashMap();
try {
resultMap = contractMgmtService.saveOrderCancelQty(request, paramMap);
} catch (Exception e) {
e.printStackTrace();
resultMap.put("result", "false");
resultMap.put("msg", "수주취소 저장 중 오류가 발생했습니다.");
}
return resultMap;
}
@RequestMapping("/contractMgmt/FileRegistPopup.do")
public String FileRegistPopup(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
request.setAttribute("docType", CommonUtils.checkNull(paramMap.get("docType")));

View File

@@ -717,6 +717,16 @@
AND ORDER_QUANTITY != ''
AND ORDER_QUANTITY != '0'
) AS HAS_ORDER_DATA
-- 수주취소 수량 합계
,(
SELECT COALESCE(SUM(CAST(NULLIF(CANCEL_QTY, '') AS NUMERIC)), 0)
FROM CONTRACT_ITEM
WHERE CONTRACT_OBJID = T.OBJID
AND STATUS = 'ACTIVE'
AND CANCEL_QTY IS NOT NULL
AND CANCEL_QTY != ''
AND CANCEL_QTY != '0'
) AS CANCEL_QTY_SUM
FROM
CONTRACT_MGMT AS T
LEFT OUTER JOIN
@@ -5092,7 +5102,8 @@ WHERE
CI.ORDER_UNIT_PRICE,
CI.ORDER_SUPPLY_PRICE,
CI.ORDER_VAT,
CI.ORDER_TOTAL_AMOUNT
CI.ORDER_TOTAL_AMOUNT,
CI.CANCEL_QTY
FROM
CONTRACT_ITEM CI
LEFT JOIN PART_MNG PM ON CI.PART_OBJID = PM.OBJID
@@ -5117,7 +5128,8 @@ WHERE
CI.ORDER_UNIT_PRICE,
CI.ORDER_SUPPLY_PRICE,
CI.ORDER_VAT,
CI.ORDER_TOTAL_AMOUNT
CI.ORDER_TOTAL_AMOUNT,
CI.CANCEL_QTY
ORDER BY CI.SEQ
</select>
@@ -5133,6 +5145,16 @@ WHERE
WHERE OBJID = #{contractItemObjId}
</update>
<!-- 수주취소 수량 업데이트 -->
<update id="updateContractItemCancelQty" parameterType="map">
UPDATE CONTRACT_ITEM
SET
CANCEL_QTY = #{cancelQty},
CHGDATE = NOW(),
CHG_USER_ID = #{userId}
WHERE OBJID = #{itemObjId}
</update>
<!-- ====================================
품목 관리 쿼리
==================================== -->

View File

@@ -3865,4 +3865,89 @@ private String encodeImageToBase64(String imagePath) {
e.printStackTrace();
}
}
/**
* 수주취소 수량 저장
* 품목별로 부분 취소 수량을 업데이트한다. (전체 취소 불가, 부분만 가능)
*/
public Map<String, Object> saveOrderCancelQty(HttpServletRequest request, Map<String, Object> paramMap) {
Map<String, Object> resultMap = new HashMap<String, Object>();
SqlSession sqlSession = null;
try {
sqlSession = SqlMapConfig.getInstance().getSqlSession();
HttpSession session = request.getSession();
PersonBean person = (PersonBean) session.getAttribute(Constants.PERSON_BEAN);
String userId = person.getUserId();
String itemObjIdsStr = CommonUtils.checkNull(paramMap.get("itemObjIds"));
String cancelQtysStr = CommonUtils.checkNull(paramMap.get("cancelQtys"));
String orderQtysStr = CommonUtils.checkNull(paramMap.get("orderQtys"));
if ("".equals(itemObjIdsStr) || "".equals(cancelQtysStr)) {
resultMap.put("result", "false");
resultMap.put("msg", "취소 수량 정보가 없습니다.");
return resultMap;
}
String[] itemObjIds = itemObjIdsStr.split(",");
String[] cancelQtys = cancelQtysStr.split(",");
String[] orderQtys = orderQtysStr.split(",");
for (int i = 0; i < itemObjIds.length; i++) {
String cancelQty = cancelQtys[i].trim();
String orderQty = orderQtys[i].trim();
// 빈 값이면 스킵
if ("".equals(cancelQty) || "0".equals(cancelQty)) {
// 취소수량 0이면 초기화
Map<String, Object> updateParam = new HashMap<String, Object>();
updateParam.put("itemObjId", itemObjIds[i].trim());
updateParam.put("cancelQty", "");
updateParam.put("userId", userId);
sqlSession.update("contractMgmt.updateContractItemCancelQty", updateParam);
continue;
}
int cancelQtyInt = Integer.parseInt(cancelQty);
int orderQtyInt = Integer.parseInt(orderQty);
// 전체 수량 취소 불가 (부분만 가능)
if (cancelQtyInt >= orderQtyInt) {
resultMap.put("result", "false");
resultMap.put("msg", "수주취소 수량은 수주수량(" + orderQtyInt + ")보다 적어야 합니다.");
sqlSession.rollback();
return resultMap;
}
if (cancelQtyInt < 0) {
resultMap.put("result", "false");
resultMap.put("msg", "수주취소 수량은 0 이상이어야 합니다.");
sqlSession.rollback();
return resultMap;
}
Map<String, Object> updateParam = new HashMap<String, Object>();
updateParam.put("itemObjId", itemObjIds[i].trim());
updateParam.put("cancelQty", cancelQty);
updateParam.put("userId", userId);
sqlSession.update("contractMgmt.updateContractItemCancelQty", updateParam);
}
sqlSession.commit();
resultMap.put("result", "true");
resultMap.put("msg", "수주취소 수량이 저장되었습니다.");
} catch (Exception e) {
e.printStackTrace();
if (sqlSession != null) sqlSession.rollback();
resultMap.put("result", "false");
resultMap.put("msg", "수주취소 저장 중 오류가 발생했습니다.");
} finally {
if (sqlSession != null) sqlSession.close();
}
return resultMap;
}
}