From bb544393f9c7bb58f8a9cf7e88b005f37de36ebc Mon Sep 17 00:00:00 2001 From: hjjeong Date: Fri, 6 Mar 2026 15:57:34 +0900 Subject: [PATCH] =?UTF-8?q?SerialNo=20=EB=8F=99=EA=B8=B0=ED=99=94=20(?= =?UTF-8?q?=EC=98=81=EC=97=85,=20=EC=83=9D=EC=82=B0,=20=ED=8C=90=EB=A7=A4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../productionplanning/prodPlanFormPopup.jsp | 16 +++- .../pms/common/utils/SerialNoSyncUtil.java | 80 +++++++++++++++++++ src/com/pms/mapper/productionplanning.xml | 12 ++- src/com/pms/salesmgmt/mapper/contractMgmt.xml | 21 +++-- .../salesmgmt/mapper/salesNcollectMgmt.xml | 40 ++++++---- .../service/SalesNcollectMgmtService.java | 10 +++ .../service/ProductionPlanningService.java | 15 +++- 7 files changed, 167 insertions(+), 27 deletions(-) create mode 100644 src/com/pms/common/utils/SerialNoSyncUtil.java diff --git a/WebContent/WEB-INF/view/productionplanning/prodPlanFormPopup.jsp b/WebContent/WEB-INF/view/productionplanning/prodPlanFormPopup.jsp index bcef2e7..14c0391 100644 --- a/WebContent/WEB-INF/view/productionplanning/prodPlanFormPopup.jsp +++ b/WebContent/WEB-INF/view/productionplanning/prodPlanFormPopup.jsp @@ -307,7 +307,19 @@ function fn_loadProjectInfo(projectObjid){ $("#REQ_DEL_DATE").val(fnc_checkNull(info.req_del_date)); // S/N - $("#SERIAL_NO").val(fnc_checkNull(info.serial_no)); + var serialNoVal = fnc_checkNull(info.serial_no); + $("#SERIAL_NO").val(serialNoVal); + // snList 동기화 (AJAX 로드 시에도 팝업 목록 갱신) + snList = []; + snCounter = 1; + if(serialNoVal && serialNoVal.trim() != '') { + var snArr = serialNoVal.split(','); + for(var si = 0; si < snArr.length; si++) { + if(snArr[si].trim() != '') { + snList.push({ id: snCounter++, value: snArr[si].trim() }); + } + } + } // 수주수량 $("#ORDER_QTY").val(fnc_checkNull(info.order_qty) || 0); @@ -479,7 +491,7 @@ function fn_openSnManagePopup() { width: '700px', showConfirmButton: false, showCloseButton: true, - didOpen: function() { + onOpen: function() { setTimeout(function() { fn_renderSnList(); // 엔터키로 추가 diff --git a/src/com/pms/common/utils/SerialNoSyncUtil.java b/src/com/pms/common/utils/SerialNoSyncUtil.java new file mode 100644 index 0000000..b54d1c3 --- /dev/null +++ b/src/com/pms/common/utils/SerialNoSyncUtil.java @@ -0,0 +1,80 @@ +package com.pms.common.utils; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.ibatis.session.SqlSession; + +/** + * S/N 동기화 유틸리티 + * - 모든 화면에서 S/N 저장 시 CONTRACT_ITEM_SERIAL(마스터)에 동기화 + * - 프로젝트 연결이 없는 경우(독립 생산계획 등)는 동기화 skip + */ +public class SerialNoSyncUtil { + + /** + * S/N 텍스트를 파싱하여 CONTRACT_ITEM_SERIAL 테이블에 동기화 + * + * @param sqlSession 현재 트랜잭션의 SqlSession + * @param projectNo 프로젝트번호 (PROJECT_MGMT.PROJECT_NO) + * @param serialNoText 쉼표 구분 S/N 문자열 (예: "SN001, SN002, SN003") + * @param writer 작성자 ID + */ + public static void syncSerialToContractItemSerial( + SqlSession sqlSession, String projectNo, String serialNoText, String writer) { + + if (projectNo == null || projectNo.trim().isEmpty()) { + return; + } + + // 1. 프로젝트번호로 CONTRACT_ITEM 조회 + Map param = new HashMap(); + param.put("projectNo", projectNo); + + Map contractItem = sqlSession.selectOne( + "contractMgmt.getContractItemByProject", param); + + if (contractItem == null) { + // CONTRACT_ITEM이 없으면 동기화 불가 (프로젝트-계약 연결 없음) + return; + } + + String itemObjId = String.valueOf(contractItem.get("item_objid")); + if (itemObjId == null || "null".equals(itemObjId) || itemObjId.trim().isEmpty()) { + itemObjId = String.valueOf(contractItem.get("ITEM_OBJID")); + } + if (itemObjId == null || "null".equals(itemObjId) || itemObjId.trim().isEmpty()) { + return; + } + + // 2. 기존 S/N 비활성화 + Map deleteParam = new HashMap(); + deleteParam.put("itemObjId", itemObjId); + sqlSession.update("contractMgmt.deleteItemSerials", deleteParam); + + // 3. 새 S/N 삽입 + if (serialNoText == null || serialNoText.trim().isEmpty()) { + return; + } + + String[] serialNumbers = serialNoText.split(","); + int seq = 1; + + for (String sn : serialNumbers) { + String trimmedSn = sn.trim(); + if (trimmedSn.isEmpty()) { + continue; + } + + Map insertParam = new HashMap(); + insertParam.put("objId", CommonUtils.createObjId()); + insertParam.put("itemObjId", itemObjId); + insertParam.put("seq", seq); + insertParam.put("serialNo", trimmedSn); + insertParam.put("writer", writer); + + sqlSession.insert("contractMgmt.insertContractItemSerial", insertParam); + seq++; + } + } +} diff --git a/src/com/pms/mapper/productionplanning.xml b/src/com/pms/mapper/productionplanning.xml index 3b8b4f7..e6f793a 100644 --- a/src/com/pms/mapper/productionplanning.xml +++ b/src/com/pms/mapper/productionplanning.xml @@ -4776,7 +4776,17 @@ PP.REQ_DEL_DATE, PP.PART_NO, PP.PART_NAME, - PP.SERIAL_NO, + -- S/N: CONTRACT_ITEM_SERIAL(마스터) 우선, 없으면 PRODUCTION_PLAN 텍스트 fallback + COALESCE( + (SELECT STRING_AGG(CIS.SERIAL_NO, ', ' ORDER BY CIS.SERIAL_NO) + FROM PROJECT_MGMT PM + JOIN CONTRACT_ITEM CI ON CI.CONTRACT_OBJID = PM.CONTRACT_OBJID + AND CI.PART_OBJID = PM.PART_OBJID AND CI.STATUS = 'ACTIVE' + JOIN CONTRACT_ITEM_SERIAL CIS ON CIS.ITEM_OBJID = CI.OBJID + AND UPPER(CIS.STATUS) = 'ACTIVE' AND CIS.SERIAL_NO IS NOT NULL + WHERE PM.OBJID::VARCHAR = PP.PROJECT_OBJID), + PP.SERIAL_NO + ) AS SERIAL_NO, PP.ORDER_QTY, PP.EXTRA_PROD_QTY, PP.TOTAL_PROD_QTY, diff --git a/src/com/pms/salesmgmt/mapper/contractMgmt.xml b/src/com/pms/salesmgmt/mapper/contractMgmt.xml index 9386ede..3749e46 100644 --- a/src/com/pms/salesmgmt/mapper/contractMgmt.xml +++ b/src/com/pms/salesmgmt/mapper/contractMgmt.xml @@ -5685,12 +5685,11 @@ WHERE WHERE OBJID = #{itemObjId} - - - UPDATE CONTRACT_ITEM_SERIAL - SET STATUS = 'INACTIVE' + + + DELETE FROM CONTRACT_ITEM_SERIAL WHERE ITEM_OBJID = #{itemObjId} - + + + + \ No newline at end of file diff --git a/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml b/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml index b666876..e021d57 100644 --- a/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml +++ b/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml @@ -844,20 +844,21 @@ FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID) AS PAYMENT_TYPE, T.PART_NO AS PRODUCT_NO, T.PART_NAME AS PRODUCT_NAME, - -- S/N: 판매등록 S/N 우선, 없으면 수주 아이템 S/N 표시 - COALESCE(SR.serial_no, + -- S/N: CONTRACT_ITEM_SERIAL(마스터) 우선, 없으면 판매등록 텍스트 fallback (그리드 요약용) + COALESCE( (SELECT CASE - WHEN COUNT(*) = 0 THEN '' + WHEN COUNT(*) = 0 THEN NULL WHEN COUNT(*) = 1 THEN MIN(CIS.SERIAL_NO) ELSE MIN(CIS.SERIAL_NO) || ' 외 ' || (COUNT(*) - 1)::TEXT || '건' END FROM CONTRACT_ITEM CI LEFT JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND UPPER(CIS.STATUS) = 'ACTIVE' - WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID + WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID AND CI.PART_OBJID = T.PART_OBJID AND CI.STATUS = 'ACTIVE' - AND CIS.SERIAL_NO IS NOT NULL) + AND CIS.SERIAL_NO IS NOT NULL), + SR.serial_no ) AS SERIAL_NO, COALESCE(T.QUANTITY::numeric, 0) AS ORDER_QUANTITY, -- 요청납기: CONTRACT_ITEM 우선, 없으면 PROJECT_MGMT, 없으면 CONTRACT_MGMT @@ -1339,7 +1340,18 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC AND T.CUSTOMER_OBJID = #{customer_objid} - AND SR.serial_no LIKE '%' || #{serialNo} || '%' + AND ( + EXISTS ( + SELECT 1 FROM CONTRACT_ITEM CI + JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID + WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID + AND CI.PART_OBJID = T.PART_OBJID + AND CI.STATUS = 'ACTIVE' + AND UPPER(CIS.STATUS) = 'ACTIVE' + AND UPPER(CIS.SERIAL_NO) LIKE UPPER('%' || #{serialNo} || '%') + ) + OR SR.serial_no LIKE '%' || #{serialNo} || '%' + ) AND EXISTS (SELECT 1 FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID AND CM.PO_NO LIKE '%' || #{poNo} || '%') @@ -1543,20 +1555,16 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID) AS PAYMENT_TYPE, T.PART_NO AS PRODUCT_NO, T.PART_NAME AS PRODUCT_NAME, - -- S/N: 판매등록 S/N 우선, 없으면 수주 아이템 S/N 표시 - COALESCE(SR.serial_no, - (SELECT - CASE - WHEN COUNT(*) = 0 THEN '' - WHEN COUNT(*) = 1 THEN MIN(CIS.SERIAL_NO) - ELSE MIN(CIS.SERIAL_NO) || ' 외 ' || (COUNT(*) - 1)::TEXT || '건' - END + -- S/N: CONTRACT_ITEM_SERIAL(마스터) 우선, 전체 콤마 리스트 (판매등록 폼 파싱용) + COALESCE( + (SELECT STRING_AGG(CIS.SERIAL_NO, ', ' ORDER BY CIS.SERIAL_NO) FROM CONTRACT_ITEM CI LEFT JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND UPPER(CIS.STATUS) = 'ACTIVE' - WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID + WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID AND CI.PART_OBJID = T.PART_OBJID AND CI.STATUS = 'ACTIVE' - AND CIS.SERIAL_NO IS NOT NULL) + AND CIS.SERIAL_NO IS NOT NULL), + SR.serial_no ) AS SERIAL_NO, COALESCE(T.QUANTITY::numeric, 0) AS ORDER_QUANTITY, -- 요청납기: CONTRACT_ITEM 우선, 없으면 PROJECT_MGMT, 없으면 CONTRACT_MGMT diff --git a/src/com/pms/salesmgmt/service/SalesNcollectMgmtService.java b/src/com/pms/salesmgmt/service/SalesNcollectMgmtService.java index c53a3b5..a73b024 100644 --- a/src/com/pms/salesmgmt/service/SalesNcollectMgmtService.java +++ b/src/com/pms/salesmgmt/service/SalesNcollectMgmtService.java @@ -29,6 +29,7 @@ import com.pms.common.SqlMapConfig; import com.pms.common.bean.PersonBean; import com.pms.common.utils.CommonUtils; import com.pms.common.utils.Constants; +import com.pms.common.utils.SerialNoSyncUtil; import com.pms.api.SalesSlipApiClient; /** @@ -422,6 +423,15 @@ public Map saveSaleRegistration(HttpServletRequest request, Map< sqlSession.update("salesNcollectMgmt.updateSaleRegistration", paramMap); } } + + // S/N → CONTRACT_ITEM_SERIAL 동기화 + String serialNo = CommonUtils.nullToEmpty((String) paramMap.get("serialNo")); + String writer = CommonUtils.nullToEmpty((String) paramMap.get("cretEmpNo")); + + if(projectNo != null && !projectNo.isEmpty()) { + SerialNoSyncUtil.syncSerialToContractItemSerial( + sqlSession, projectNo, serialNo, writer); + } sqlSession.commit(); resultMap.put("result", true); diff --git a/src/com/pms/service/ProductionPlanningService.java b/src/com/pms/service/ProductionPlanningService.java index 5602f2e..4dd5edd 100644 --- a/src/com/pms/service/ProductionPlanningService.java +++ b/src/com/pms/service/ProductionPlanningService.java @@ -18,6 +18,7 @@ import com.pms.common.SqlMapConfig; import com.pms.common.bean.PersonBean; import com.pms.common.utils.CommonUtils; import com.pms.common.utils.Constants; +import com.pms.common.utils.SerialNoSyncUtil; @Service public class ProductionPlanningService { @@ -1897,20 +1898,28 @@ public class ProductionPlanningService { SqlSession sqlSession = null; try { - sqlSession = SqlMapConfig.getInstance().getSqlSession(); + sqlSession = SqlMapConfig.getInstance().getSqlSession(false); String objid = CommonUtils.nullToEmpty((String)paramMap.get("OBJID")); String actionType = CommonUtils.nullToEmpty((String)paramMap.get("actionType")); if("regist".equals(actionType) || "".equals(objid)) { - // 신규 등록 paramMap.put("OBJID", CommonUtils.createObjId()); sqlSession.insert("productionplanning.insertProdPlan", paramMap); } else { - // 수정 sqlSession.update("productionplanning.updateProdPlan", paramMap); } + // S/N → CONTRACT_ITEM_SERIAL 동기화 (프로젝트 연결 시) + String projectNo = CommonUtils.nullToEmpty((String)paramMap.get("PROJECT_NO")); + String serialNo = CommonUtils.nullToEmpty((String)paramMap.get("SERIAL_NO")); + String writer = CommonUtils.nullToEmpty((String)paramMap.get("userId")); + + if(!projectNo.isEmpty()) { + SerialNoSyncUtil.syncSerialToContractItemSerial( + sqlSession, projectNo, serialNo, writer); + } + sqlSession.commit(); result = true; } catch(Exception e) {