From c4eeec14504d85ae8b3b6c2aadad7d1f30b150dc Mon Sep 17 00:00:00 2001 From: hjjeong Date: Fri, 20 Mar 2026 17:42:59 +0900 Subject: [PATCH 01/11] =?UTF-8?q?=EA=B5=AC=EB=A7=A4=EA=B4=80=EB=A6=AC=20?= =?UTF-8?q?=EC=9E=85=EA=B3=A0/=EB=A7=A4=EC=9E=85=EB=A7=88=EA=B0=90=20?= =?UTF-8?q?=EA=B8=88=EC=95=A1=20=EC=86=8C=EC=88=98=EC=A0=90=202=EC=9E=90?= =?UTF-8?q?=EB=A6=AC=20=ED=8F=AC=EB=A7=B7=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - deliveryMngAcceptanceList: 발주/입고/미입고 금액 및 합계 소수점 2자리 적용 - purchaseCloseList: 입고금액/환율/관세/수입부가세 그리드 포맷 적용 - purchaseCloseList: 마감정보입력 팝업 금액 입력/표시/저장 포맷 처리 Made-with: Cursor --- .../deliveryMngAcceptanceList.jsp | 68 +++++++++---------- .../view/purchaseOrder/purchaseCloseList.jsp | 64 ++++++++++------- 2 files changed, 75 insertions(+), 57 deletions(-) diff --git a/WebContent/WEB-INF/view/purchaseOrder/deliveryMngAcceptanceList.jsp b/WebContent/WEB-INF/view/purchaseOrder/deliveryMngAcceptanceList.jsp index 5521539..ebcc627 100644 --- a/WebContent/WEB-INF/view/purchaseOrder/deliveryMngAcceptanceList.jsp +++ b/WebContent/WEB-INF/view/purchaseOrder/deliveryMngAcceptanceList.jsp @@ -133,37 +133,37 @@ var columns = [ else if(cn.includes('유로') || cn === 'EUR') s = '€'; else if(cn.includes('엔') || cn === 'JPY') s = '¥'; else if(cn.includes('위안') || cn === 'CNY') s = '¥'; - return s + Number(value).toLocaleString(); - } - }, - {headerHozAlign : 'center', hozAlign : 'right', minWidth : 90, widthGrow : 1, title : '입고금액', field : 'TOTAL_DELIVERY_PRICE', - formatter: function(cell) { - var value = cell.getValue(); - if(!value || value === '' || value === '0') return ''; - var cn = cell.getRow().getData().CURRENCY_NAME || ''; - var s = ''; - if(cn.includes('원') || cn === 'KRW') s = '₩'; - else if(cn.includes('달러') || cn === 'USD') s = '$'; - else if(cn.includes('유로') || cn === 'EUR') s = '€'; - else if(cn.includes('엔') || cn === 'JPY') s = '¥'; - else if(cn.includes('위안') || cn === 'CNY') s = '¥'; - return s + Number(value).toLocaleString(); - } - }, - {headerHozAlign : 'center', hozAlign : 'right', minWidth : 90, widthGrow : 1, title : '미입고금액', field : 'TOTAL_NOT_DELIVERY_PRICE', - formatter: function(cell) { - var value = cell.getValue(); - if(!value || value === '' || value === '0') return ''; - var cn = cell.getRow().getData().CURRENCY_NAME || ''; - var s = ''; - if(cn.includes('원') || cn === 'KRW') s = '₩'; - else if(cn.includes('달러') || cn === 'USD') s = '$'; - else if(cn.includes('유로') || cn === 'EUR') s = '€'; - else if(cn.includes('엔') || cn === 'JPY') s = '¥'; - else if(cn.includes('위안') || cn === 'CNY') s = '¥'; - return s + Number(value).toLocaleString(); - } - }, + return s + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}); + } + }, + {headerHozAlign : 'center', hozAlign : 'right', minWidth : 90, widthGrow : 1, title : '입고금액', field : 'TOTAL_DELIVERY_PRICE', + formatter: function(cell) { + var value = cell.getValue(); + if(!value || value === '' || value === '0') return ''; + var cn = cell.getRow().getData().CURRENCY_NAME || ''; + var s = ''; + if(cn.includes('원') || cn === 'KRW') s = '₩'; + else if(cn.includes('달러') || cn === 'USD') s = '$'; + else if(cn.includes('유로') || cn === 'EUR') s = '€'; + else if(cn.includes('엔') || cn === 'JPY') s = '¥'; + else if(cn.includes('위안') || cn === 'CNY') s = '¥'; + return s + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}); + } + }, + {headerHozAlign : 'center', hozAlign : 'right', minWidth : 90, widthGrow : 1, title : '미입고금액', field : 'TOTAL_NOT_DELIVERY_PRICE', + formatter: function(cell) { + var value = cell.getValue(); + if(!value || value === '' || value === '0') return ''; + var cn = cell.getRow().getData().CURRENCY_NAME || ''; + var s = ''; + if(cn.includes('원') || cn === 'KRW') s = '₩'; + else if(cn.includes('달러') || cn === 'USD') s = '$'; + else if(cn.includes('유로') || cn === 'EUR') s = '€'; + else if(cn.includes('엔') || cn === 'JPY') s = '¥'; + else if(cn.includes('위안') || cn === 'CNY') s = '¥'; + return s + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}); + } + }, {headerHozAlign : 'center', hozAlign : 'center', minWidth : 100, widthGrow : 1, title : '업체성적서', field : 'INSPECTION_FILE_CNT', formatter:fnc_subInfoValueFormatter, cellClick:function(e, cell){ @@ -269,9 +269,9 @@ function fn_calculateTotalAmount(){ } // 합계 표시 - $("#totalOrderAmount").text(Number(Math.round(totalOrderAmount)).toLocaleString()); - $("#deliveredAmount").text(Number(Math.round(totalDeliveredAmount)).toLocaleString()); - $("#notDeliveredAmount").text(Number(Math.round(totalNotDeliveredAmount)).toLocaleString()); + $("#totalOrderAmount").text(Number(totalOrderAmount).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})); + $("#deliveredAmount").text(Number(totalDeliveredAmount).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})); + $("#notDeliveredAmount").text(Number(totalNotDeliveredAmount).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})); } //수입검사등록 diff --git a/WebContent/WEB-INF/view/purchaseOrder/purchaseCloseList.jsp b/WebContent/WEB-INF/view/purchaseOrder/purchaseCloseList.jsp index de1823b..2b661b1 100644 --- a/WebContent/WEB-INF/view/purchaseOrder/purchaseCloseList.jsp +++ b/WebContent/WEB-INF/view/purchaseOrder/purchaseCloseList.jsp @@ -172,27 +172,39 @@ $(document).ready(function(){ changeMonth: true, changeYear: true }); - // 기존 데이터 세팅 - if(taxType) $('#swal_taxType').val(taxType); - if(taxInvoiceDate) $('#swal_taxInvoiceDate').val(taxInvoiceDate); - if(exportDeclNo) $('#swal_exportDeclNo').val(exportDeclNo); - if(loadingDate) $('#swal_loadingDate').val(loadingDate); - if(foreignType) $('#swal_foreignType').val(foreignType); - if(duty) $('#swal_duty').val(duty); - if(importVat) $('#swal_importVat').val(importVat); - if(exchangeRate) $('#swal_exchangeRate').val(exchangeRate); + // 기존 데이터 세팅 + if(taxType) $('#swal_taxType').val(taxType); + if(taxInvoiceDate) $('#swal_taxInvoiceDate').val(taxInvoiceDate); + if(exportDeclNo) $('#swal_exportDeclNo').val(exportDeclNo); + if(loadingDate) $('#swal_loadingDate').val(loadingDate); + if(foreignType) $('#swal_foreignType').val(foreignType); + if(duty) $('#swal_duty').val(formatMoney(duty)); + if(importVat) $('#swal_importVat').val(formatMoney(importVat)); + if(exchangeRate) $('#swal_exchangeRate').val(formatMoney(exchangeRate)); + + // 금액 입력 필드 포맷 (keyup: 천단위 콤마, blur: 소수점 2자리) + $('#swal_exchangeRate, #swal_duty, #swal_importVat').on('keyup', function(){ + var val = this.value.replace(/[^0-9.]/g, ''); + var parts = val.split('.'); + if(parts.length > 2) val = parts[0] + '.' + parts.slice(1).join(''); + if(parts[0]) parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ','); + this.value = parts.join('.'); + }).on('blur', function(){ + var v = this.value.replace(/,/g, ''); + if(v && !isNaN(v)) this.value = formatMoney(v); + }); }, preConfirm: function() { - return { - taxType: $('#swal_taxType').val(), - taxInvoiceDate: $('#swal_taxInvoiceDate').val(), - exportDeclNo: $('#swal_exportDeclNo').val(), - loadingDate: $('#swal_loadingDate').val(), - foreignType: $('#swal_foreignType').val(), - duty: $('#swal_duty').val(), - importVat: $('#swal_importVat').val(), - exchangeRate: $('#swal_exchangeRate').val() - }; + return { + taxType: $('#swal_taxType').val(), + taxInvoiceDate: $('#swal_taxInvoiceDate').val(), + exportDeclNo: $('#swal_exportDeclNo').val(), + loadingDate: $('#swal_loadingDate').val(), + foreignType: $('#swal_foreignType').val(), + duty: removeComma($('#swal_duty').val()), + importVat: removeComma($('#swal_importVat').val()), + exchangeRate: removeComma($('#swal_exchangeRate').val()) + }; } }).then(function(result) { if (result.isConfirmed) { @@ -307,7 +319,7 @@ var columns = [ else if(cn.includes('유로') || cn === 'EUR') s = '€'; else if(cn.includes('엔') || cn === 'JPY') s = '¥'; else if(cn.includes('위안') || cn === 'CNY') s = '¥'; - return s + Number(value).toLocaleString(); + return s + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}); } }, // {headerHozAlign:'center', hozAlign:'right', minWidth:90, widthGrow:1, title:'미입고금액', field:'TOTAL_NOT_DELIVERY_PRICE', @@ -322,13 +334,19 @@ var columns = [ }, {headerHozAlign:'center', hozAlign:'center', minWidth:100, widthGrow:1.2, title:'계정과목', field:'SUB_LOCATION_NAME'}, {headerHozAlign:'center', hozAlign:'center', minWidth:100, widthGrow:1.2, title:'국내/해외', field:'FOREIGN_TYPE_NAME'}, - {headerHozAlign:'center', hozAlign:'right', minWidth:100, widthGrow:1.2, title:'환율', field:'EXCHANGE_RATE'}, + {headerHozAlign:'center', hozAlign:'right', minWidth:100, widthGrow:1.2, title:'환율', field:'EXCHANGE_RATE', + formatter: function(cell){ var v = cell.getValue(); if(!v || v === '') return ''; return formatMoney(v); } + }, {headerHozAlign:'center', hozAlign:'center', minWidth:100, widthGrow:1.2, title:'과세구분', field:'TAX_TYPE_NAME'}, {headerHozAlign:'center', hozAlign:'center', minWidth:100, widthGrow:1.2, title:'세금계산서발행일', field:'TAX_INVOICE_DATE'}, {headerHozAlign:'center', hozAlign:'center', minWidth:100, widthGrow:1.2, title:'수출신고필증신고번호', field:'EXPORT_DECL_NO'}, {headerHozAlign:'center', hozAlign:'center', minWidth:100, widthGrow:1.2, title:'선적일자', field:'LOADING_DATE'}, - {headerHozAlign:'center', hozAlign:'right', minWidth:100, widthGrow:1.2, title:'관세', field:'DUTY'}, - {headerHozAlign:'center', hozAlign:'right', minWidth:100, widthGrow:1.2, title:'수입부가세', field:'IMPORT_VAT'}, + {headerHozAlign:'center', hozAlign:'right', minWidth:100, widthGrow:1.2, title:'관세', field:'DUTY', + formatter: function(cell){ var v = cell.getValue(); if(!v || v === '') return ''; return formatMoney(v); } + }, + {headerHozAlign:'center', hozAlign:'right', minWidth:100, widthGrow:1.2, title:'수입부가세', field:'IMPORT_VAT', + formatter: function(cell){ var v = cell.getValue(); if(!v || v === '') return ''; return formatMoney(v); } + }, {headerHozAlign:'center', hozAlign:'center', minWidth:85, widthGrow:0.6, title:'매입마감', field:'PURCHASE_CLOSE_DATE'} ]; From ed261664ef503f94a0df19946901ec4f68808b45 Mon Sep 17 00:00:00 2001 From: hjjeong Date: Fri, 20 Mar 2026 17:48:45 +0900 Subject: [PATCH 02/11] =?UTF-8?q?=EC=88=98=EC=A3=BC=EB=8B=A8=EA=B0=80=20?= =?UTF-8?q?=ED=95=84=EC=88=98=EC=BB=AC=EB=9F=BC=20=ED=95=B4=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebContent/WEB-INF/view/contractMgmt/orderRegistFormPopup.jsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebContent/WEB-INF/view/contractMgmt/orderRegistFormPopup.jsp b/WebContent/WEB-INF/view/contractMgmt/orderRegistFormPopup.jsp index 1809215..79535ce 100644 --- a/WebContent/WEB-INF/view/contractMgmt/orderRegistFormPopup.jsp +++ b/WebContent/WEB-INF/view/contractMgmt/orderRegistFormPopup.jsp @@ -265,7 +265,7 @@ html += ''; } // ORDER_UNIT_PRICE 수정 가능 - html += ''; + html += ''; // ORDER_SUPPLY_PRICE 자동 계산 html += ''; // ORDER_VAT 수정 가능 From d9ca95718b854fc7d513d9e6df7246285945eb73 Mon Sep 17 00:00:00 2001 From: hjjeong Date: Mon, 23 Mar 2026 16:01:42 +0900 Subject: [PATCH 03/11] =?UTF-8?q?=EC=9D=B4=EB=B4=84=20csv=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EC=8B=9C=20=EA=B8=B0=EC=A1=B4=20=ED=8C=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=A0=95=EB=B3=B4=20=EC=83=88=EB=A1=9C=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8=20(=EC=9E=84=EC=8B=9C=EB=A1=9C!=20?= =?UTF-8?q?=EB=82=98=EC=A4=91=EC=97=90=20=EB=8B=A4=EC=8B=9C=20=EC=9B=90?= =?UTF-8?q?=EB=B3=B5=EC=98=88=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/pms/mapper/partMng.xml | 41 +++++++++++++++++++++++-- src/com/pms/service/PartMngService.java | 5 +++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/com/pms/mapper/partMng.xml b/src/com/pms/mapper/partMng.xml index f73d71e..23dec61 100644 --- a/src/com/pms/mapper/partMng.xml +++ b/src/com/pms/mapper/partMng.xml @@ -7704,8 +7704,45 @@ SELECT T1.LEV, T1.BOM_REPORT_OBJID, T1.ROOT_PART_NO, T1.PATH, T1.LEAF, T2.* ,NULLIF(#{UNIT_QTY}, '') ) - - + + + + UPDATE PART_MNG + SET PART_NAME = #{PART_NAME} + ,UNIT = #{UNIT} + ,SPEC = #{SPEC} + ,MATERIAL = #{MATERIAL} + ,THICKNESS = NULLIF(#{THICKNESS}, '') + ,WIDTH = NULLIF(#{WIDTH}, '') + ,HEIGHT = NULLIF(#{HEIGHT}, '') + ,OUT_DIAMETER = NULLIF(#{OUT_DIAMETER}, '') + ,IN_DIAMETER = NULLIF(#{IN_DIAMETER}, '') + ,LENGTH = NULLIF(#{LENGTH}, '') + ,REMARK = #{REMARK} + ,PART_TYPE = #{PART_TYPE} + ,SUPPLY_CODE = #{SUPPLY_CODE} + ,MAKER = #{MAKER} + ,POST_PROCESSING = #{POST_PROCESSING} + ,HEAT_TREATMENT_HARDNESS = #{HEAT_TREATMENT_HARDNESS} + ,HEAT_TREATMENT_METHOD = #{HEAT_TREATMENT_METHOD} + ,SURFACE_TREATMENT = #{SURFACE_TREATMENT} + ,ACCTFG = #{ACCTFG} + ,ODRFG = #{ODRFG} + ,UNIT_DC = #{UNIT_DC} + ,UNITMANG_DC = #{UNITMANG_DC} + ,UNITCHNG_NB = COALESCE(NULLIF(#{UNITCHNG_NB}, ''), '0')::NUMERIC + ,LOT_FG = COALESCE(#{LOT_FG}, '0') + ,USE_YN = COALESCE(#{USE_YN}, '1') + ,QC_FG = COALESCE(#{QC_FG}, '0') + ,SETITEM_FG = COALESCE(#{SETITEM_FG}, '0') + ,REQ_FG = COALESCE(#{REQ_FG}, '0') + ,UNIT_LENGTH = NULLIF(#{UNIT_LENGTH}, '') + ,UNIT_QTY = NULLIF(#{UNIT_QTY}, '') + ,EDIT_DATE = now() + WHERE OBJID = #{PART_OBJID} + + + '; + popupHtml += ''; + popupHtml += '' + (i+1) + ''; + popupHtml += '' + availableSns[i] + ''; + popupHtml += ''; + } + + popupHtml += ' '; + popupHtml += ' '; + popupHtml += '
'; + popupHtml += ' 선택한 S/N 개수가 판매수량에 자동 반영됩니다.'; + popupHtml += '
'; + popupHtml += '
'; + popupHtml += ' '; + popupHtml += ' '; + popupHtml += '
'; + popupHtml += ''; + + Swal.fire({ + html: popupHtml, + width: '500px', + showConfirmButton: false, + showCloseButton: true + }); + } + + // 전체선택 + function fn_splitSnSelectAll() { + $(".swal2-html-container .split-sn-chk").prop('checked', true); + } + + // 전체해제 + function fn_splitSnDeselectAll() { + $(".swal2-html-container .split-sn-chk").prop('checked', false); + } + + // 분할S/N 선택 확인 + function fn_confirmSplitSn() { + selectedSplitSns = []; + $(".swal2-html-container .split-sn-chk:checked").each(function() { + selectedSplitSns.push($(this).val()); + }); + + // hidden 필드에 저장 + $("#splitSerialNoHidden").val(selectedSplitSns.join(',')); + + // 판매수량 자동 업데이트 + $("#salesQuantity").val(selectedSplitSns.length); + fn_calculateSupplyPrice(); + + fn_updateSplitSnDisplay(); + Swal.close(); + } + + // 분할S/N 표시 업데이트 + function fn_updateSplitSnDisplay() { + if(selectedSplitSns.length > 0) { + var displayText = selectedSplitSns.join(', '); + if(displayText.length > 80) { + displayText = displayText.substring(0, 77) + '... (' + selectedSplitSns.length + '건)'; + } + $("#splitSnDisplay").text(displayText).css('color', '#333'); + } else { + var serialNoVal = $("#serialNo").val(); + if(serialNoVal && serialNoVal.trim() !== '') { + $("#splitSnDisplay").text('클릭하여 분할S/N 선택').css('color', '#999'); + } else { + $("#splitSnDisplay").text('S/N을 먼저 등록하세요').css('color', '#999'); + } + } + } + function fn_save() { // 출하지시 상태는 자동으로 설정됨 (hidden 필드에 이미 "출하지시" 값 설정) @@ -735,7 +899,19 @@ function fn_calculateSelectedItem() { readonly /> - + + + + + + +
+ S/N을 먼저 등록하세요 +
+ + + diff --git a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/shippingDetailPopup.jsp b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/shippingDetailPopup.jsp index 3dafa70..7b4cbcc 100644 --- a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/shippingDetailPopup.jsp +++ b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/shippingDetailPopup.jsp @@ -95,13 +95,14 @@ function fn_openEditPopup(logId, orderNo) { 출하수량 출하지시상태 S/N + 분할S/N - 출하 내역이 없습니다. + 출하 내역이 없습니다. @@ -116,6 +117,7 @@ function fn_openEditPopup(logId, orderNo) { ${item.shipping_quantity} ${item.shipping_order_status} ${item.serial_no} + ${item.split_serial_no} diff --git a/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java b/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java index 0ac0e66..93520cd 100644 --- a/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java +++ b/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java @@ -441,7 +441,28 @@ public class SalesNcollectMgmtController { // orderInfo로 견적 정보 전달 (saleInfo가 이미 모든 필요한 정보를 포함) request.setAttribute("orderInfo", saleInfo); } - + + // 이미 사용된 분할S/N 조회 (현재 수정 중인 logId 제외) + if(paramMap.get("orderNo") != null && !paramMap.get("orderNo").equals("")) { + Map usedSnParam = new HashMap(); + usedSnParam.put("projectNo", paramMap.get("orderNo")); + if(paramMap.get("logId") != null && !paramMap.get("logId").equals("")) { + usedSnParam.put("excludeLogId", paramMap.get("logId")); + } + List> usedSnList = salesNcollectMgmtService.getUsedSplitSerialNos(usedSnParam); + + // 이미 사용된 S/N들을 하나의 콤마 구분 문자열로 합침 + StringBuilder usedSnBuilder = new StringBuilder(); + for(Map usedSn : usedSnList) { + String splitSn = (String) usedSn.get("split_serial_no"); + if(splitSn != null && !splitSn.isEmpty()) { + if(usedSnBuilder.length() > 0) usedSnBuilder.append(","); + usedSnBuilder.append(splitSn); + } + } + request.setAttribute("usedSplitSerialNos", usedSnBuilder.toString()); + } + // 수정 모드: saleInfo에서 담당자 선택값 반영 if(saleInfo != null && saleInfo.get("MANAGER") != null) { String selectedManager = saleInfo.get("MANAGER").toString(); diff --git a/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml b/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml index 10618c4..e9b12f2 100644 --- a/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml +++ b/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml @@ -844,14 +844,9 @@ 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: CONTRACT_ITEM_SERIAL(마스터) 우선, 없으면 판매등록 텍스트 fallback (그리드 요약용) + -- S/N: CONTRACT_ITEM_SERIAL(마스터) 우선, 없으면 판매등록 텍스트 fallback (콤마 구분 전체 목록) COALESCE( - (SELECT - CASE - WHEN COUNT(*) = 0 THEN NULL - WHEN COUNT(*) = 1 THEN MIN(CIS.SERIAL_NO) - ELSE MIN(CIS.SERIAL_NO) || ' 외 ' || (COUNT(*) - 1)::TEXT || '건' - END + (SELECT STRING_AGG(CIS.SERIAL_NO, ',' ORDER BY CIS.SEQ) 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 @@ -860,6 +855,15 @@ AND CIS.SERIAL_NO IS NOT NULL), SR.serial_no ) AS SERIAL_NO, + -- 분할S/N: shipment_log에서 해당 프로젝트의 모든 분할S/N 집계 + COALESCE( + (SELECT STRING_AGG(SL_SN.split_serial_no, ',' ORDER BY SL_SN.log_id) + FROM shipment_log SL_SN + WHERE SL_SN.target_objid = T.PROJECT_NO + AND SL_SN.split_serial_no IS NOT NULL + AND SL_SN.split_serial_no != ''), + '' + ) AS SPLIT_SERIAL_NO, COALESCE(NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::numeric, 0) AS ORDER_QUANTITY, -- 요청납기: CONTRACT_ITEM 우선, 없으면 PROJECT_MGMT, 없으면 CONTRACT_MGMT COALESCE( @@ -1902,7 +1906,7 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC remaining_quantity, shipping_status, shipping_date, shipping_method, sales_unit_price, sales_supply_price, sales_vat, sales_total_amount, sales_currency, sales_exchange_rate, manager_user_id, incoterms, - serial_no, parent_sale_no, reg_user_id + serial_no, parent_sale_no, reg_user_id, split_serial_no ) VALUES ( #{targetObjid}, 'SPLIT_SHIPMENT', '분할 출하', #{salesQuantity}::integer, #{originalQuantity}::integer, #{remainingQuantity}::integer, @@ -1916,7 +1920,7 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC #{shippingMethod}, #{salesUnitPrice}::numeric, #{salesSupplyPrice}::numeric, #{salesVat}::numeric, #{salesTotalAmount}::numeric, #{salesCurrency}, #{salesExchangeRate}::numeric, #{managerUserId}, #{incoterms}, #{serialNo}, - #{parentSaleNo}::integer, #{cretEmpNo} + #{parentSaleNo}::integer, #{cretEmpNo}, #{splitSerialNo} ) @@ -1994,6 +1998,7 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC WHERE PM.PROJECT_NO = SL.target_objid AND CI.STATUS = 'ACTIVE' ), '-') AS serial_no, SL.target_objid AS project_no, + COALESCE(SL.split_serial_no, '-') AS split_serial_no, TO_CHAR(SL.reg_date, 'YYYY-MM-DD HH24:MI:SS') AS reg_date FROM shipment_log SL WHERE SL.target_objid = #{projectNo} @@ -2025,6 +2030,7 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC COALESCE(SL.sales_exchange_rate, 0) AS SALES_EXCHANGE_RATE, COALESCE(SL.manager_user_id, '') AS MANAGER, COALESCE(SL.incoterms, '') AS INCOTERMS, + COALESCE(SL.split_serial_no, '') AS SPLIT_SERIAL_NO, COALESCE(SL.original_quantity, 0) AS ORDER_QUANTITY, COALESCE(SL.remaining_quantity, 0) AS REMAINING_QUANTITY FROM shipment_log SL @@ -2070,6 +2076,9 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC , incoterms = #{incoterms} + + , split_serial_no = #{splitSerialNo} + WHERE log_id = #{logId}::integer @@ -2406,6 +2415,7 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID AND CI.PART_OBJID = T.PART_OBJID AND CI.STATUS = 'ACTIVE' ), '') AS SERIAL_NO, + COALESCE(SL.split_serial_no, '') AS SPLIT_SERIAL_NO, COALESCE(NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::numeric, 0) AS ORDER_QUANTITY, (SELECT CM.PO_NO FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID) AS PO_NO, COALESCE(T.CONTRACT_DATE, (SELECT CM.order_date FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID)) AS ORDER_DATE, @@ -2742,6 +2752,22 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC WHERE PROJECT_NO = #{projectNo} + + + diff --git a/src/com/pms/salesmgmt/service/SalesNcollectMgmtService.java b/src/com/pms/salesmgmt/service/SalesNcollectMgmtService.java index 8506a28..dfaed98 100644 --- a/src/com/pms/salesmgmt/service/SalesNcollectMgmtService.java +++ b/src/com/pms/salesmgmt/service/SalesNcollectMgmtService.java @@ -401,6 +401,10 @@ public Map saveSaleRegistration(HttpServletRequest request, Map< if(logId != null && !"".equals(logId)) { System.out.println("=== shipment_log 수정 모드 (logId: " + logId + ") ==="); paramMap.put("shippingOrderStatus", "출하지시"); + // splitSerialNo가 있으면 전달 + if(paramMap.get("splitSerialNo") != null) { + paramMap.put("splitSerialNo", paramMap.get("splitSerialNo")); + } sqlSession.update("salesNcollectMgmt.updateShipmentLog", paramMap); } else { // 신규 등록 → shipment_log에 INSERT @@ -424,6 +428,7 @@ public Map saveSaleRegistration(HttpServletRequest request, Map< logParam.put("managerUserId", paramMap.get("managerUserId")); logParam.put("incoterms", paramMap.get("incoterms")); logParam.put("serialNo", paramMap.get("serialNo")); + logParam.put("splitSerialNo", paramMap.get("splitSerialNo")); logParam.put("parentSaleNo", null); logParam.put("cretEmpNo", paramMap.get("cretEmpNo")); @@ -518,6 +523,23 @@ public Map saveSaleRegistration(HttpServletRequest request, Map< return resultMap; } + /** + * 프로젝트의 이미 사용된 분할S/N 목록 조회 + */ + public List> getUsedSplitSerialNos(Map paramMap) { + SqlSession sqlSession = null; + List> resultList = new ArrayList>(); + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(true); + resultList = sqlSession.selectList("salesNcollectMgmt.getUsedSplitSerialNos", paramMap); + } catch(Exception e) { + e.printStackTrace(); + } finally { + if(sqlSession != null) sqlSession.close(); + } + return resultList; + } + /** *
 	 * 분할출하 처리 (로그 기반)

From a756123105864ac8a0fef5d24a6d2ce073f5b263 Mon Sep 17 00:00:00 2001
From: hjjeong 
Date: Tue, 24 Mar 2026 16:32:44 +0900
Subject: [PATCH 08/11] =?UTF-8?q?=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20?=
 =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=8B=9C=20contract=5Fitem.objid=20=EB=8F=84?=
 =?UTF-8?q?=20=ED=8F=AC=ED=95=A8=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?=
 =?UTF-8?q?=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../estimateAndOrderRegistFormPopup.jsp       |   1 +
 src/com/pms/mapper/productionplanning.xml     | 116 +++++++++++++++---
 src/com/pms/mapper/project.xml                |  14 ++-
 .../service/ContractMgmtService.java          |  36 ++++--
 4 files changed, 139 insertions(+), 28 deletions(-)

diff --git a/WebContent/WEB-INF/view/contractMgmt/estimateAndOrderRegistFormPopup.jsp b/WebContent/WEB-INF/view/contractMgmt/estimateAndOrderRegistFormPopup.jsp
index 61d3cbf..c2f7eb3 100644
--- a/WebContent/WEB-INF/view/contractMgmt/estimateAndOrderRegistFormPopup.jsp
+++ b/WebContent/WEB-INF/view/contractMgmt/estimateAndOrderRegistFormPopup.jsp
@@ -245,6 +245,7 @@
 			}
 			
 			var item = {
+				objId: $row.find(".item-objid").val() || '',
 				partObjId: $row.find(".item-part-objid").val(),
 				partNo: $row.find(".item-part-no").val() ? $row.find(".item-part-no").val().trim() : "",
 				partName: $row.find(".item-part-name").val() ? $row.find(".item-part-name").val().trim() : "",
diff --git a/src/com/pms/mapper/productionplanning.xml b/src/com/pms/mapper/productionplanning.xml
index b800a75..108d047 100644
--- a/src/com/pms/mapper/productionplanning.xml
+++ b/src/com/pms/mapper/productionplanning.xml
@@ -3031,10 +3031,12 @@
 		FROM 
 			PROJECT_MGMT PM
 			LEFT JOIN CONTRACT_MGMT CM ON PM.CONTRACT_OBJID = CM.OBJID
-			LEFT OUTER JOIN CONTRACT_ITEM CI ON PM.CONTRACT_OBJID = CI.CONTRACT_OBJID
-			AND PM.PART_OBJID = CI.PART_OBJID
-			-- CONTRACT_ITEM과 LEFT JOIN하여 품목별로 펼쳐서 보이기
-			-- LEFT JOIN CONTRACT_ITEM CI ON CM.OBJID::VARCHAR = CI.CONTRACT_OBJID 
+			LEFT OUTER JOIN CONTRACT_ITEM CI ON (
+				CASE
+					WHEN PM.CONTRACT_ITEM_OBJID IS NOT NULL THEN CI.OBJID::VARCHAR = PM.CONTRACT_ITEM_OBJID
+					ELSE CI.CONTRACT_OBJID = PM.CONTRACT_OBJID AND CI.PART_OBJID = PM.PART_OBJID
+				END
+			)
 				AND CI.STATUS = 'ACTIVE'
 		WHERE 1=1
 			AND PM.PROJECT_NO IS NOT NULL
@@ -4597,8 +4599,12 @@
 			FROM 
 				PROJECT_MGMT PM
 				LEFT JOIN CONTRACT_MGMT CM ON PM.CONTRACT_OBJID = CM.OBJID
-				LEFT OUTER JOIN CONTRACT_ITEM CI ON PM.CONTRACT_OBJID = CI.CONTRACT_OBJID
-					AND PM.PART_OBJID = CI.PART_OBJID
+				LEFT OUTER JOIN CONTRACT_ITEM CI ON (
+					CASE
+						WHEN PM.CONTRACT_ITEM_OBJID IS NOT NULL THEN CI.OBJID::VARCHAR = PM.CONTRACT_ITEM_OBJID
+						ELSE CI.CONTRACT_OBJID = PM.CONTRACT_OBJID AND CI.PART_OBJID = PM.PART_OBJID
+					END
+				)
 					AND CI.STATUS = 'ACTIVE'
 				LEFT OUTER JOIN PRODUCTION_PLAN PP ON PP.PROJECT_OBJID = PM.OBJID
 					AND PP.STATUS = 'active'
@@ -4731,8 +4737,13 @@
 			COALESCE(CI.PART_NAME, PM.PART_NAME, '') AS PART_NAME
 		FROM PROJECT_MGMT PM
 			LEFT JOIN CONTRACT_MGMT CM ON PM.CONTRACT_OBJID = CM.OBJID
-			LEFT OUTER JOIN CONTRACT_ITEM CI ON PM.CONTRACT_OBJID = CI.CONTRACT_OBJID
-				AND PM.PART_OBJID = CI.PART_OBJID AND CI.STATUS = 'ACTIVE'
+			LEFT OUTER JOIN CONTRACT_ITEM CI ON (
+				CASE
+					WHEN PM.CONTRACT_ITEM_OBJID IS NOT NULL THEN CI.OBJID::VARCHAR = PM.CONTRACT_ITEM_OBJID
+					ELSE CI.CONTRACT_OBJID = PM.CONTRACT_OBJID AND CI.PART_OBJID = PM.PART_OBJID
+				END
+			)
+				AND CI.STATUS = 'ACTIVE'
 		WHERE PM.OBJID::VARCHAR = #{projectObjid}::VARCHAR
 	
 
@@ -4935,7 +4946,13 @@
 			FROM 
 				PROJECT_MGMT PM
 				LEFT JOIN CONTRACT_MGMT CM ON PM.CONTRACT_OBJID = CM.OBJID
-				LEFT OUTER JOIN CONTRACT_ITEM CI ON PM.CONTRACT_OBJID = CI.CONTRACT_OBJID
+				LEFT OUTER JOIN CONTRACT_ITEM CI ON (
+			CASE
+				WHEN PM.CONTRACT_ITEM_OBJID IS NOT NULL THEN CI.OBJID::VARCHAR = PM.CONTRACT_ITEM_OBJID
+				ELSE CI.CONTRACT_OBJID = PM.CONTRACT_OBJID AND CI.PART_OBJID = PM.PART_OBJID
+			END
+		)
+			AND CI.STATUS = 'ACTIVE'
 					AND PM.PART_OBJID = CI.PART_OBJID
 					AND CI.STATUS = 'ACTIVE'
 				LEFT OUTER JOIN PRODUCTION_PLAN PP ON PP.PROJECT_OBJID = PM.OBJID
@@ -5008,8 +5025,12 @@
 				), '') AS SERIAL_NO
 			FROM PROJECT_MGMT PM
 			LEFT JOIN CONTRACT_MGMT CM ON PM.CONTRACT_OBJID = CM.OBJID
-			LEFT JOIN CONTRACT_ITEM CI ON CI.CONTRACT_OBJID = PM.CONTRACT_OBJID 
-				AND CI.PART_OBJID = PM.PART_OBJID 
+			LEFT JOIN CONTRACT_ITEM CI ON (
+				CASE
+					WHEN PM.CONTRACT_ITEM_OBJID IS NOT NULL THEN CI.OBJID::VARCHAR = PM.CONTRACT_ITEM_OBJID
+					ELSE CI.CONTRACT_OBJID = PM.CONTRACT_OBJID AND CI.PART_OBJID = PM.PART_OBJID
+				END
+			)
 				AND CI.STATUS = 'ACTIVE'
 			WHERE PM.OBJID::VARCHAR = #{projectObjid}
 			
@@ -5051,8 +5072,12 @@
 			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 CI ON (
+					CASE
+						WHEN PM.CONTRACT_ITEM_OBJID IS NOT NULL THEN CI.OBJID::VARCHAR = PM.CONTRACT_ITEM_OBJID
+						ELSE CI.CONTRACT_OBJID = PM.CONTRACT_OBJID AND CI.PART_OBJID = PM.PART_OBJID
+					END
+				) 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),
@@ -5346,7 +5371,13 @@
 		FROM MBOM_HISTORY MH
 		INNER JOIN MBOM_HEADER MHD ON MH.MBOM_HEADER_OBJID = MHD.OBJID
 		INNER JOIN PROJECT_MGMT PM ON MHD.PROJECT_OBJID = PM.OBJID::VARCHAR
-		LEFT OUTER JOIN CONTRACT_ITEM CI ON PM.CONTRACT_OBJID = CI.CONTRACT_OBJID
+		LEFT OUTER JOIN CONTRACT_ITEM CI ON (
+			CASE
+				WHEN PM.CONTRACT_ITEM_OBJID IS NOT NULL THEN CI.OBJID::VARCHAR = PM.CONTRACT_ITEM_OBJID
+				ELSE CI.CONTRACT_OBJID = PM.CONTRACT_OBJID AND CI.PART_OBJID = PM.PART_OBJID
+			END
+		)
+			AND CI.STATUS = 'ACTIVE'
 		LEFT JOIN CONTRACT_MGMT CM ON PM.CONTRACT_OBJID = CM.OBJID
 		WHERE 1=1
 		
@@ -5394,5 +5425,60 @@
 		INNER JOIN PROJECT_MGMT PM ON MHD.PROJECT_OBJID = PM.OBJID::VARCHAR
 		WHERE MH.OBJID::VARCHAR = #{historyObjId}
 	
-	
+
+	
+	
+
+	
+	
+
+
 
diff --git a/src/com/pms/mapper/project.xml b/src/com/pms/mapper/project.xml
index 35b5794..bac22cc 100644
--- a/src/com/pms/mapper/project.xml
+++ b/src/com/pms/mapper/project.xml
@@ -7470,6 +7470,7 @@ SELECT
 							,PART_NO
 							,PART_NAME
 							,QUANTITY
+							,CONTRACT_ITEM_OBJID
                         )
          	 		   
          	 		   (
@@ -7611,12 +7612,21 @@ SELECT
 							,#{part_no}
 							,#{part_name}
 							,#{quantity}
+							,#{contract_item_objid}
                       FROM CONTRACT_MGMT
                       WHERE OBJID=#{objId}
 					    )	
 	
-	
-	
+
+	
+	
+		UPDATE PROJECT_MGMT
+		SET CONTRACT_ITEM_OBJID = #{contract_item_objid}
+		WHERE CONTRACT_OBJID = #{contract_objid}
+		  AND PART_OBJID = #{part_objid}
+		  AND CONTRACT_ITEM_OBJID IS NULL
+	
+
 	
 	
 				INSERT INTO
diff --git a/src/com/pms/salesmgmt/service/ContractMgmtService.java b/src/com/pms/salesmgmt/service/ContractMgmtService.java
index c3ac5b9..8bb9f01 100644
--- a/src/com/pms/salesmgmt/service/ContractMgmtService.java
+++ b/src/com/pms/salesmgmt/service/ContractMgmtService.java
@@ -2699,21 +2699,19 @@ private String encodeImageToBase64(String imagePath) {
 					JSONParser parser = new JSONParser();
 					JSONArray jsonArray = (JSONArray) parser.parse(itemsJson);
 					
-			// 기존 품목 삭제 (전체 삭제 후 재등록 방식)
-			Map deleteParam = new HashMap();
-			deleteParam.put("contractObjId", contract_objid);
-			deleteParam.put("userId", person.getUserId());
-			sqlSession.update("contractMgmt.deleteContractItems", deleteParam);
-					
+			// UPSERT 방식으로 품목 저장 (OBJID 유지 - CONTRACT_ITEM_OBJID 매핑 보존)
+			Set currentItemObjIds = new HashSet();
+
 					for(int i = 0; i < jsonArray.size(); i++) {
 						JSONObject item = (JSONObject) jsonArray.get(i);
 						Map itemMap = new HashMap();
 						
-						// 품목 기본 정보
+						// 품목 기본 정보 - 기존 OBJID가 있으면 유지
 						String itemObjId = item.get("objId") != null ? item.get("objId").toString() : "";
 						if("".equals(itemObjId) || itemObjId.startsWith("temp_")) {
 							itemObjId = CommonUtils.createObjId();
 						}
+						currentItemObjIds.add(itemObjId);
 						
 						itemMap.put("objId", itemObjId);
 						itemMap.put("contractObjId", contract_objid);
@@ -2741,8 +2739,13 @@ private String encodeImageToBase64(String imagePath) {
 						itemMap.put("orderTotalAmount", orderTotalAmount);
 						itemMap.put("writer", person.getUserId());
 						
-						// 품목 저장 (통합 등록용 - 수주 정보 포함)
-						sqlSession.insert("contractMgmt.insertContractItemWithOrder", itemMap);
+						// 품목 UPSERT (기존 OBJID면 UPDATE, 새 OBJID면 INSERT)
+						sqlSession.insert("contractMgmt.upsertContractItemWithOrder", itemMap);
+						
+						// 기존 S/N 삭제 후 재등록
+						Map deleteSnParam = new HashMap();
+						deleteSnParam.put("itemObjId", itemObjId);
+						sqlSession.delete("contractMgmt.deleteItemSerials", deleteSnParam);
 						
 						// S/N 저장 (별도 테이블)
 						JSONArray snArray = (JSONArray) item.get("snList");
@@ -2758,7 +2761,6 @@ private String encodeImageToBase64(String imagePath) {
 								sqlSession.insert("contractMgmt.insertContractItemSerial", snMap);
 							}
 						}
-						
 						// 합계 계산 (소수점 지원)
 						try {
 							totalSupplyPrice += Double.parseDouble(orderSupplyPrice);
@@ -2768,12 +2770,21 @@ private String encodeImageToBase64(String imagePath) {
 							System.out.println("합계 계산 실패 - supply:" + orderSupplyPrice + ", vat:" + orderVat + ", total:" + orderTotalAmount);
 						}
 					}
+
+					// 프론트에서 전달되지 않은 품목들은 INACTIVE 처리 (삭제된 품목)
+					if(!currentItemObjIds.isEmpty()) {
+						Map inactiveParam = new HashMap();
+						inactiveParam.put("contractObjId", contract_objid);
+						inactiveParam.put("currentItemObjIds", new ArrayList(currentItemObjIds));
+						inactiveParam.put("userId", person.getUserId());
+						sqlSession.update("contractMgmt.inactivateRemovedItems", inactiveParam);
+					}
 				} catch (Exception e) {
 					e.printStackTrace();
 					throw new Exception("품목 정보 저장 중 오류가 발생했습니다.");
 				}
 			}
-			
+
 			// 3. 수주 합계 업데이트
 			paramMap.put("order_supply_price", String.valueOf(totalSupplyPrice));
 			paramMap.put("order_vat", String.valueOf(totalVat));
@@ -2835,6 +2846,7 @@ private String encodeImageToBase64(String imagePath) {
 								projectParam.put("OBJID", CommonUtils.createObjId());
 								projectParam.put("is_temp", '1');
 								projectParam.put("part_objid", item.get("PART_OBJID"));
+								projectParam.put("contract_item_objid", String.valueOf(item.get("OBJID")));
 								projectParam.put("part_no", item.get("PART_NO"));
 								projectParam.put("part_name", item.get("PART_NAME"));
 								// Machine인 경우 각 프로젝트의 수량은 1, 아니면 원래 수량
@@ -3068,6 +3080,8 @@ private String encodeImageToBase64(String imagePath) {
 							// 품목별 정보 설정
 							projectParam.put("OBJID", CommonUtils.createObjId());
 							projectParam.put("is_temp", '1');
+							projectParam.put("contract_item_objid", String.valueOf(item.get("OBJID")));
+							System.out.println("CONTRACT_ITEM_OBJID 매핑: " + item.get("OBJID") + " (type: " + (item.get("OBJID") != null ? item.get("OBJID").getClass().getName() : "null") + ")");
 							projectParam.put("part_objid", item.get("PART_OBJID"));
 							projectParam.put("part_no", item.get("PART_NO"));
 							projectParam.put("part_name", item.get("PART_NAME"));

From 20d3ec9b965808c1affe0633f26e4a11acec6742 Mon Sep 17 00:00:00 2001
From: hjjeong 
Date: Tue, 24 Mar 2026 17:01:18 +0900
Subject: [PATCH 09/11] =?UTF-8?q?a756123=EC=97=90=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/com/pms/salesmgmt/mapper/contractMgmt.xml | 61 +++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/src/com/pms/salesmgmt/mapper/contractMgmt.xml b/src/com/pms/salesmgmt/mapper/contractMgmt.xml
index 7153949..0621672 100644
--- a/src/com/pms/salesmgmt/mapper/contractMgmt.xml
+++ b/src/com/pms/salesmgmt/mapper/contractMgmt.xml
@@ -5629,6 +5629,67 @@ WHERE
 		STATUS = 'ACTIVE'
 
 
+
+
+	INSERT INTO CONTRACT_ITEM (
+		OBJID,
+		CONTRACT_OBJID,
+		SEQ,
+		PART_OBJID,
+		PART_NO,
+		PART_NAME,
+		QUANTITY,
+		DUE_DATE,
+		CUSTOMER_REQUEST,
+		RETURN_REASON,
+		REGDATE,
+		WRITER,
+		STATUS,
+		ORDER_QUANTITY,
+		ORDER_UNIT_PRICE,
+		ORDER_SUPPLY_PRICE,
+		ORDER_VAT,
+		ORDER_TOTAL_AMOUNT
+	) VALUES (
+		#{objId},
+		#{contractObjId},
+		#{seq},
+		#{partObjId},
+		#{partNo},
+		#{partName},
+		CASE WHEN #{quantity} = '' OR #{quantity} IS NULL THEN NULL ELSE #{quantity}::INTEGER END,
+		#{dueDate},
+		#{customerRequest},
+		#{returnReason},
+		NOW(),
+		#{writer},
+		'ACTIVE',
+		#{orderQuantity},
+		#{orderUnitPrice},
+		#{orderSupplyPrice},
+		#{orderVat},
+		#{orderTotalAmount}
+	)
+	ON CONFLICT (OBJID) DO UPDATE
+	SET
+		SEQ = #{seq},
+		PART_OBJID = #{partObjId},
+		PART_NO = #{partNo},
+		PART_NAME = #{partName},
+		QUANTITY = CASE WHEN #{quantity} = '' OR #{quantity} IS NULL THEN NULL ELSE #{quantity}::INTEGER END,
+		DUE_DATE = #{dueDate},
+		CUSTOMER_REQUEST = #{customerRequest},
+		RETURN_REASON = #{returnReason},
+		CHGDATE = NOW(),
+		CHG_USER_ID = #{writer},
+		STATUS = 'ACTIVE',
+		ORDER_QUANTITY = #{orderQuantity},
+		ORDER_UNIT_PRICE = #{orderUnitPrice},
+		ORDER_SUPPLY_PRICE = #{orderSupplyPrice},
+		ORDER_VAT = #{orderVat},
+		ORDER_TOTAL_AMOUNT = #{orderTotalAmount}
+
+
 
 
 	INSERT INTO CONTRACT_ITEM_SERIAL (

From dd7b810e348f8ba45def3b1a90aca9f62f7e95b2 Mon Sep 17 00:00:00 2001
From: hjjeong 
Date: Wed, 25 Mar 2026 11:30:17 +0900
Subject: [PATCH 10/11] =?UTF-8?q?=EC=98=81=EC=97=85=5F=EC=A3=BC=EB=AC=B8?=
 =?UTF-8?q?=EC=84=9C=EA=B4=80=EB=A6=AC=20=EC=88=98=EC=A3=BC=ED=99=95?=
 =?UTF-8?q?=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20=EB=B6=84=EB=A6=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../estimateAndOrderRegistFormPopup.jsp       |  11 +-
 .../view/contractMgmt/orderMgmtList.jsp       |  87 ++-
 .../contractMgmt/orderRegistFormPopup.jsp     |  26 +-
 src/com/pms/salesmgmt/mapper/contractMgmt.xml |  24 +-
 .../service/ContractMgmtService.java          | 499 ++++++------------
 5 files changed, 271 insertions(+), 376 deletions(-)

diff --git a/WebContent/WEB-INF/view/contractMgmt/estimateAndOrderRegistFormPopup.jsp b/WebContent/WEB-INF/view/contractMgmt/estimateAndOrderRegistFormPopup.jsp
index c2f7eb3..2fdfb32 100644
--- a/WebContent/WEB-INF/view/contractMgmt/estimateAndOrderRegistFormPopup.jsp
+++ b/WebContent/WEB-INF/view/contractMgmt/estimateAndOrderRegistFormPopup.jsp
@@ -1418,22 +1418,17 @@
 							
 
                             
-								
-								
-									
-								
 								
 								
 									
 								
                                 
-								
+								
 									
 								
+								
 							
+							
 						
 					
 				
diff --git a/WebContent/WEB-INF/view/contractMgmt/orderMgmtList.jsp b/WebContent/WEB-INF/view/contractMgmt/orderMgmtList.jsp
index 5ab2aa4..ee85be9 100644
--- a/WebContent/WEB-INF/view/contractMgmt/orderMgmtList.jsp
+++ b/WebContent/WEB-INF/view/contractMgmt/orderMgmtList.jsp
@@ -151,6 +151,23 @@ $(document).ready(function(){
 		document.form1.submit();
 	});
 	
+	// 수주확정
+	$("#btnOrderConfirm").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 currentStatus = fnc_checkNull(selectedData[0].CONTRACT_RESULT_NAME);
+
+		fn_openOrderConfirmPopup(contractObjId, currentStatus);
+	});
+
 	// 수주취소
 	$("#btnOrderCancel").click(function(){
 		var selectedData = _tabulGrid.getSelectedData();
@@ -710,6 +727,73 @@ function fn_displaySerialNoList(serialNumbers){
 	});
 }
 
+// 수주확정 팝업 - 수주상태 선택하여 저장
+function fn_openOrderConfirmPopup(contractObjId, currentStatus){
+	// 검색 필터의 수주상태 select에서 옵션 목록을 가져와서 SweetAlert에서 사용
+	var optionsHtml = '';
+	$("#contract_result option").each(function(){
+		var val = $(this).val();
+		var text = $(this).text();
+		if(val !== ''){
+			optionsHtml += '';
+		}
+	});
+
+	Swal.fire({
+		title: '수주확정',
+		html: '
' + + '
현재 수주상태: ' + (currentStatus || '-') + '
' + + '
' + + '' + + '
', + width: 400, + showCancelButton: true, + confirmButtonColor: '#3085d6', + cancelButtonColor: '#d33', + confirmButtonText: '확정', + cancelButtonText: '취소', + preConfirm: function() { + var selectedVal = document.getElementById('swal_contract_result').value; + if(!selectedVal) { + Swal.showValidationMessage('수주상태를 선택해주세요.'); + return false; + } + return selectedVal; + } + }).then(function(result) { + if(result.isConfirmed) { + $.ajax({ + url: "/contractMgmt/updateOrderStatus.do", + type: "POST", + data: { + objId: contractObjId, + contract_result: result.value + }, + dataType: "json", + success: function(data){ + if(data.result === "SUCCESS"){ + Swal.fire({ + title: '수주확정 완료', + text: '수주상태가 변경되었습니다.', + icon: 'success' + }).then(function(){ + fn_search(); + }); + } else { + Swal.fire("수주확정 저장 중 오류가 발생했습니다."); + } + }, + error: function(){ + Swal.fire("서버 통신 오류가 발생했습니다."); + } + }); + } + }); +} + // 수주취소 팝업 - 품목별 취소 수량 입력 function fn_openOrderCancelPopup(contractObjId){ $.ajax({ @@ -971,7 +1055,8 @@ function openProjectFormPopUp(objId){
- + +
diff --git a/WebContent/WEB-INF/view/contractMgmt/orderRegistFormPopup.jsp b/WebContent/WEB-INF/view/contractMgmt/orderRegistFormPopup.jsp index 79535ce..5065589 100644 --- a/WebContent/WEB-INF/view/contractMgmt/orderRegistFormPopup.jsp +++ b/WebContent/WEB-INF/view/contractMgmt/orderRegistFormPopup.jsp @@ -444,27 +444,21 @@ - + - + - + - - - - - - - + + + + - - - - - + diff --git a/src/com/pms/salesmgmt/mapper/contractMgmt.xml b/src/com/pms/salesmgmt/mapper/contractMgmt.xml index 0621672..d680e10 100644 --- a/src/com/pms/salesmgmt/mapper/contractMgmt.xml +++ b/src/com/pms/salesmgmt/mapper/contractMgmt.xml @@ -1560,10 +1560,12 @@ ,CONTRACT_DEL_DATE = #{contract_del_date} ,CONTRACT_COMPANY = #{contract_company} ,CONTRACT_DATE = #{contract_date} - ,PO_NO = #{po_no} - ,MANUFACTURE_PLANT = #{manufacture_plant} - ,CONTRACT_RESULT = #{contract_result} - ,PROJECT_NAME = #{project_name} + ,PO_NO = #{po_no} + ,MANUFACTURE_PLANT = #{manufacture_plant} + + ,CONTRACT_RESULT = #{contract_result} + + ,PROJECT_NAME = #{project_name} ,SPEC_USER_ID = #{spec_user_id} ,SPEC_PLAN_DATE = #{spec_plan_date} ,SPEC_COMP_DATE = #{spec_comp_date} @@ -1730,10 +1732,12 @@ ,CONTRACT_DEL_DATE = #{contract_del_date} ,CONTRACT_COMPANY = #{contract_company} ,CONTRACT_DATE = #{contract_date} - ,PO_NO = #{po_no} - ,MANUFACTURE_PLANT = #{manufacture_plant} - ,CONTRACT_RESULT = #{contract_result} - ,PROJECT_NAME = #{project_name} + ,PO_NO = #{po_no} + ,MANUFACTURE_PLANT = #{manufacture_plant} + + ,CONTRACT_RESULT = #{contract_result} + + ,PROJECT_NAME = #{project_name} ,AREA_CD = #{area_cd} --> @@ -4991,7 +4995,9 @@ WHERE UPDATE CONTRACT_MGMT SET + CONTRACT_RESULT = #{contract_result}, + PO_NO = #{po_no}, ORDER_DATE = #{order_date}, CONTRACT_CURRENCY = #{contract_currency}, @@ -5083,7 +5089,9 @@ WHERE PAID_TYPE = #{paid_type}, RECEIPT_DATE = #{receipt_date}, REQ_DEL_DATE = #{req_del_date}, + CONTRACT_RESULT = #{contract_result}, + PO_NO = #{po_no}, ORDER_DATE = #{order_date}, CONTRACT_CURRENCY = #{contract_currency}, diff --git a/src/com/pms/salesmgmt/service/ContractMgmtService.java b/src/com/pms/salesmgmt/service/ContractMgmtService.java index 8bb9f01..72288d3 100644 --- a/src/com/pms/salesmgmt/service/ContractMgmtService.java +++ b/src/com/pms/salesmgmt/service/ContractMgmtService.java @@ -603,75 +603,17 @@ public class ContractMgmtService { PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN); paramMap.put("writer", person.getUserId()); int cnt = sqlSession.update("contractMgmt.saveContractMgmtInfo", paramMap); - - //영업 수주 완료시 자동 프로젝트 등록 로직 - String result_cd= CommonUtils.checkNull(paramMap.get("contract_result")); - String contract_objid= CommonUtils.checkNull(paramMap.get("objId")); - String category_cd= CommonUtils.checkNull(paramMap.get("category_cd")); - String target_project_no= CommonUtils.checkNull(paramMap.get("target_project_no_direct")); - int overhaul_order = Integer.parseInt(CommonUtils.checkNull(paramMap.get("overhaul_order"),"1")); - int project_cnt= Integer.parseInt(CommonUtils.checkNull(paramMap.get("facility_qty"), "1")); - double contract_price_currency= Double.parseDouble(CommonUtils.checkNull(paramMap.get("contract_price_currency"), "0").replace(",", "")); - double contract_price= Double.parseDouble(CommonUtils.checkNull(paramMap.get("contract_price"), "0").replace(",", "")); - - //수주가와 금액은 대수로 나누어서 등록 - paramMap.put("contract_price_currency", String.valueOf(contract_price_currency/project_cnt)); - paramMap.put("contract_price", String.valueOf(contract_price/project_cnt)); - - if("0000964".equals(result_cd)){ - resultList = sqlSession.selectOne("contractMgmt.getProjectListBycontractObjid", paramMap); - System.out.println("resultList:::"+resultList); - //resultList = sqlSession.selectOne("contractMgmt.getProjectCnt", paramMap); - if(null==resultList){ - for (int i=0; i projectInfo = (Map)sqlSession.selectOne("project.getProjectMngInfo", paramMap); - - paramMap.put("contract_objid", paramMap.get("objId")); - paramMap.put("customer_product", paramMap.get("mechanical_type")); - List> taskUnitList = (ArrayList)sqlSession.selectList("project.getWbsTaskListByProject", paramMap); - if(CommonUtils.isNotEmpty(taskUnitList) && !taskUnitList.isEmpty()){ - String projectNo = (String)projectInfo.get("project_no"); - String filepath = Constants.FILE_STORAGE+"\\PART_DATA\\"; - for (Map map : taskUnitList) { - File file = new File(filepath+File.separator+projectNo+File.separator+CommonUtils.checkNull((String)map.get("unit_no"))+"-"+CommonUtils.checkNull((String)map.get("task_name"))); - if(!file.exists()){ - file.mkdirs(); - } - } - } - } - }else{ - sqlSession.update("project.ModifyProjectByContract", paramMap); - } + // 프로젝트 생성은 수주확정(updateOrderStatus)에서 처리 + // 프로젝트가 이미 존재하는 경우에는 수량/납기 등 업데이트 반영 + resultList = sqlSession.selectOne("contractMgmt.getProjectListBycontractObjid", paramMap); + if(resultList != null) { + sqlSession.update("project.ModifyProjectByContract", paramMap); } + if(cnt > 0){ - //계약완료 일시 메일 - if("0000964".equals(CommonUtils.checkNull(paramMap.get("contract_result")))){ - commonService.SendMail(paramMap,"CONTRACT_COMP",CommonUtils.checkNull(paramMap.get("pm_user_id"))); - //그냥 등록일때 메일 - }else{ - if("regist".equals(CommonUtils.checkNull(paramMap.get("actionType")))){ + if("regist".equals(CommonUtils.checkNull(paramMap.get("actionType")))){ commonService.SendMail(paramMap,"CONTRACT_REG",CommonUtils.checkNull(paramMap.get(""))); - } } } resultMap.put("result", true); @@ -2792,39 +2734,23 @@ private String encodeImageToBase64(String imagePath) { // 합계만 별도 업데이트 (기본 정보는 이미 saveEstimateAndOrderInfo에서 저장됨) sqlSession.update("contractMgmt.updateOrderTotalAmounts", paramMap); - - // 4. 프로젝트 생성 로직 (수주 또는 수주(FCST)인 경우) - String result_cd = CommonUtils.checkNull(paramMap.get("contract_result")); - String category_cd = CommonUtils.checkNull(paramMap.get("category_cd")); - String target_project_no = CommonUtils.checkNull(paramMap.get("target_project_no_direct")); - - // 수주(0000964) 또는 수주(FCST)(0000968)인 경우 프로젝트 생성 - if("0000964".equals(result_cd) || "0000968".equals(result_cd)){ - // CONTRACT_OBJID로 프로젝트 존재 여부 확인 - Map resultList = sqlSession.selectOne("contractMgmt.getProjectListBycontractObjid", paramMap); - boolean hasProject = (resultList != null); - - // 제품구분 확인 (DB에서 조회) + + // 프로젝트 생성은 수주확정(updateOrderStatus)에서 처리 + // 프로젝트가 이미 존재하는 경우에는 수량/납기 등 업데이트 반영 + String contract_objid_for_project = CommonUtils.checkNull(paramMap.get("objId")); + Map projectCheckResult = sqlSession.selectOne("contractMgmt.getProjectListBycontractObjid", paramMap); + if(projectCheckResult != null) { Map contractInfo = (Map) sqlSession.selectOne("contractMgmt.getContractBasicInfo", paramMap); contractInfo = CommonUtils.toUpperCaseMapKey(contractInfo); - String product_cd = contractInfo != null ? CommonUtils.checkNull(contractInfo.get("PRODUCT")) : ""; boolean isMachine = "0000928".equals(product_cd); - - if(isMachine) { - System.out.println("제품구분: Machine(0000928) - 품목별 수량만큼 프로젝트 생성"); - } - - // 품목별로 프로젝트 생성 또는 업데이트 (같은 트랜잭션의 sqlSession 사용) - paramMap.put("contractObjId", contract_objid); - List contractItemsRaw = sqlSession.selectList("contractMgmt.getContractItems", paramMap); - List> contractItems = CommonUtils.toUpperCaseMapKey(contractItemsRaw); - + + paramMap.put("contractObjId", contract_objid_for_project); + List contractItemsRaw = sqlSession.selectList("contractMgmt.getContractItems", paramMap); + List> contractItems = CommonUtils.toUpperCaseMapKey(contractItemsRaw); + if(contractItems != null && !contractItems.isEmpty()) { - System.out.println("품목 개수: " + contractItems.size() + "개 - 프로젝트 " + (hasProject ? "업데이트" : "생성") + " 시작" + (isMachine ? " (Machine - 수량별 생성)" : "")); - for(Map item : contractItems) { - // 수량 가져오기 (소수점 형태 "2.00"도 처리) Object quantityObj = item.get("ORDER_QUANTITY") != null ? item.get("ORDER_QUANTITY") : item.get("QUANTITY"); int itemQuantity = 1; try { @@ -2832,109 +2758,22 @@ private String encodeImageToBase64(String imagePath) { } catch (Exception e) { itemQuantity = 1; } - - // Machine인 경우 수량만큼 반복, 아니면 1번만 실행 - int loopCount = (isMachine && !hasProject) ? itemQuantity : 1; - - for(int q = 0; q < loopCount; q++) { - if(!hasProject) { - // 프로젝트가 없으면 모든 품목에 대해 생성 - Map projectParam = new HashMap(); - projectParam.putAll(paramMap); // 기본 정보 복사 - - // 품목별 정보 설정 - projectParam.put("OBJID", CommonUtils.createObjId()); - projectParam.put("is_temp", '1'); - projectParam.put("part_objid", item.get("PART_OBJID")); - projectParam.put("contract_item_objid", String.valueOf(item.get("OBJID"))); - projectParam.put("part_no", item.get("PART_NO")); - projectParam.put("part_name", item.get("PART_NAME")); - // Machine인 경우 각 프로젝트의 수량은 1, 아니면 원래 수량 - projectParam.put("quantity", isMachine ? "1" : String.valueOf(itemQuantity)); - projectParam.put("due_date", item.get("DUE_DATE")); - - if("0000170".equals(category_cd) || "0000171".equals(category_cd)){ - projectParam.put("overhaul_project_no", target_project_no); - } - - if(isMachine) { - System.out.println("프로젝트 생성 [" + (q+1) + "/" + loopCount + "] - PART_OBJID: " + item.get("PART_OBJID") + ", 품번: " + item.get("PART_NO") + ", 품명: " + item.get("PART_NAME") + ", 수량: 1"); - } else { - System.out.println("프로젝트 생성 - PART_OBJID: " + item.get("PART_OBJID") + ", 품번: " + item.get("PART_NO") + ", 품명: " + item.get("PART_NAME") + ", 수량: " + itemQuantity); - } - - // 프로젝트 등록 - cnt = sqlSession.update("project.createProject", projectParam); - // WBS 자동생성 주석처리 - // // 프로젝트 TASK 등록 - // cnt = sqlSession.insert("contractMgmt.insertProjectTask", projectParam); - // // 프로젝트 SETUP_TASK 등록 - // cnt = sqlSession.insert("contractMgmt.insertProjectSetupTask", projectParam); - - // 동일 품번 M-BOM 자동 복사 제거 - 수주 시점에는 총생산수량 미확정이므로 - // M-BOM관리에서 생산계획 입력 후 수동 복사하도록 변경 - // if(!isMachine) { - // copyMbomIfSamePartNoExists(sqlSession, - // (String)projectParam.get("OBJID"), - // CommonUtils.checkNull(item.get("PART_NO")), - // CommonUtils.checkNull(item.get("PART_NAME")), - // person.getUserId(), - // itemQuantity); - // } - - // WBS 폴더 생성 주석처리 - // // project_no - unit 폴더 생성 - // Map projectInfo = (Map)sqlSession.selectOne("project.getProjectMngInfo", projectParam); - // - // projectParam.put("contract_objid", contract_objid); - // projectParam.put("customer_product", projectParam.get("mechanical_type")); - // List> taskUnitList = (ArrayList)sqlSession.selectList("project.getWbsTaskListByProject", projectParam); - // if(CommonUtils.isNotEmpty(taskUnitList) && !taskUnitList.isEmpty()){ - // String projectNo = (String)projectInfo.get("project_no"); - // String filepath = Constants.FILE_STORAGE+"\\PART_DATA\\"; - // for (Map map : taskUnitList) { - // File file = new File(filepath+File.separator+projectNo+File.separator+CommonUtils.checkNull((String)map.get("unit_no"))+"-"+CommonUtils.checkNull((String)map.get("task_name"))); - // if(!file.exists()){ - // file.mkdirs(); - // } - // } - // } - } else { - // 프로젝트가 있으면 모든 품목 업데이트 (수량, 금액 등만) - Map updateParam = new HashMap(); - updateParam.putAll(paramMap); - updateParam.put("part_objid", item.get("PART_OBJID")); - // Machine인 경우 수량은 변경하지 않음 (프로젝트별 1로 유지) - if(isMachine) { - updateParam.remove("quantity"); - } else { - updateParam.put("quantity", String.valueOf(itemQuantity)); - } - updateParam.put("due_date", item.get("DUE_DATE")); - - System.out.println("프로젝트 업데이트 - PART_OBJID: " + item.get("PART_OBJID") + (isMachine ? ", 수량: 변경안함(Machine)" : ", 수량: " + itemQuantity)); - sqlSession.update("project.ModifyProjectByContract", updateParam); - } + Map updateParam = new HashMap(); + updateParam.putAll(paramMap); + updateParam.put("part_objid", item.get("PART_OBJID")); + if(isMachine) { + updateParam.remove("quantity"); + } else { + updateParam.put("quantity", String.valueOf(itemQuantity)); } + updateParam.put("due_date", item.get("DUE_DATE")); + sqlSession.update("project.ModifyProjectByContract", updateParam); } } else { - System.out.println("품목이 없습니다 - 기존 방식으로 프로젝트 생성"); - // 품목이 없는 경우 기존 방식대로 처리 - if(!hasProject){ - paramMap.put("OBJID", CommonUtils.createObjId()); - paramMap.put("is_temp", '1'); - if("0000170".equals(category_cd) || "0000171".equals(category_cd)){ - paramMap.put("overhaul_project_no", target_project_no); - } - cnt = sqlSession.update("project.createProject", paramMap); - cnt = sqlSession.insert("contractMgmt.insertProjectTask", paramMap); - cnt = sqlSession.insert("contractMgmt.insertProjectSetupTask", paramMap); - }else{ - sqlSession.update("project.ModifyProjectByContract", paramMap); - } + sqlSession.update("project.ModifyProjectByContract", paramMap); } } - + sqlSession.commit(); resultMap.put("result", "true"); resultMap.put("msg", "저장되었습니다."); @@ -3018,48 +2857,23 @@ private String encodeImageToBase64(String imagePath) { // 기본 수주 정보 및 합계 업데이트 int cnt = sqlSession.update("contractMgmt.updateOrderInfo", paramMap); - - //영업 수주 완료시 자동 프로젝트 등록 로직 - String result_cd= CommonUtils.checkNull(paramMap.get("contract_result")); - - String category_cd= CommonUtils.checkNull(paramMap.get("category_cd")); - String target_project_no= CommonUtils.checkNull(paramMap.get("target_project_no_direct")); - //int overhaul_order = Integer.parseInt(CommonUtils.checkNull(paramMap.get("overhaul_order"),"1")); - //int project_cnt= Integer.parseInt(CommonUtils.checkNull(paramMap.get("facility_qty"), "1")); - //long contract_price_currency= Long.parseLong(CommonUtils.checkNull(paramMap.get("contract_price_currency"), "0")); - //long contract_price= Long.parseLong(CommonUtils.checkNull(paramMap.get("contract_price"), "0")); - - //수주가와 금액은 대수로 나누어서 등록 - //paramMap.put("contract_price_currency", contract_price_currency/project_cnt + ""); - //paramMap.put("contract_price", contract_price/project_cnt + ""); - - if("0000964".equals(result_cd) || "0000968".equals(result_cd)){ - // CONTRACT_OBJID로 프로젝트 존재 여부 확인 (한 번만 체크) - resultList = sqlSession.selectOne("contractMgmt.getProjectListBycontractObjid", paramMap); - boolean hasProject = (resultList != null); - - // 제품구분 확인 (DB에서 조회) + + // 프로젝트 생성은 수주확정(updateOrderStatus)에서 처리 + // 프로젝트가 이미 존재하는 경우에는 수량/납기 등 업데이트 반영 + resultList = sqlSession.selectOne("contractMgmt.getProjectListBycontractObjid", paramMap); + if(resultList != null) { + // 제품구분 확인 Map contractInfo = (Map) sqlSession.selectOne("contractMgmt.getContractBasicInfo", paramMap); - // MyBatis resultType="map"은 소문자로 반환되므로 대문자로 변환 contractInfo = CommonUtils.toUpperCaseMapKey(contractInfo); - String product_cd = contractInfo != null ? CommonUtils.checkNull(contractInfo.get("PRODUCT")) : ""; boolean isMachine = "0000928".equals(product_cd); - - if(isMachine) { - System.out.println("제품구분: Machine(0000928) - 품목별 수량만큼 프로젝트 생성"); - } - - // 품목별로 프로젝트 생성 또는 업데이트 (같은 트랜잭션의 sqlSession 사용) + paramMap.put("contractObjId", contract_objid); List contractItemsRaw2 = sqlSession.selectList("contractMgmt.getContractItems", paramMap); List> contractItems = CommonUtils.toUpperCaseMapKey(contractItemsRaw2); - + if(contractItems != null && !contractItems.isEmpty()) { - System.out.println("품목 개수: " + contractItems.size() + "개 - 프로젝트 " + (hasProject ? "업데이트" : "생성") + " 시작" + (isMachine ? " (Machine - 수량별 생성)" : "")); - for(Map item : contractItems) { - // 수량 가져오기 (소수점 형태 "2.00"도 처리) Object quantityObj = item.get("ORDER_QUANTITY") != null ? item.get("ORDER_QUANTITY") : item.get("QUANTITY"); int itemQuantity = 1; try { @@ -3067,120 +2881,22 @@ private String encodeImageToBase64(String imagePath) { } catch (Exception e) { itemQuantity = 1; } - - // Machine인 경우 수량만큼 반복, 아니면 1번만 실행 - int loopCount = (isMachine && !hasProject) ? itemQuantity : 1; - - for(int q = 0; q < loopCount; q++) { - if(!hasProject) { - // 프로젝트가 없으면 모든 품목에 대해 생성 - Map projectParam = new HashMap(); - projectParam.putAll(paramMap); // 기본 정보 복사 - - // 품목별 정보 설정 - projectParam.put("OBJID", CommonUtils.createObjId()); - projectParam.put("is_temp", '1'); - projectParam.put("contract_item_objid", String.valueOf(item.get("OBJID"))); - System.out.println("CONTRACT_ITEM_OBJID 매핑: " + item.get("OBJID") + " (type: " + (item.get("OBJID") != null ? item.get("OBJID").getClass().getName() : "null") + ")"); - projectParam.put("part_objid", item.get("PART_OBJID")); - projectParam.put("part_no", item.get("PART_NO")); - projectParam.put("part_name", item.get("PART_NAME")); - // Machine인 경우 각 프로젝트의 수량은 1, 아니면 원래 수량 - projectParam.put("quantity", isMachine ? "1" : String.valueOf(itemQuantity)); - projectParam.put("due_date", item.get("DUE_DATE")); - - if("0000170".equals(category_cd) || "0000171".equals(category_cd)){ - projectParam.put("overhaul_project_no", target_project_no); - } - - if(isMachine) { - System.out.println("프로젝트 생성 [" + (q+1) + "/" + loopCount + "] - PART_OBJID: " + item.get("PART_OBJID") + ", 품번: " + item.get("PART_NO") + ", 품명: " + item.get("PART_NAME") + ", 수량: 1"); - } else { - System.out.println("프로젝트 생성 - PART_OBJID: " + item.get("PART_OBJID") + ", 품번: " + item.get("PART_NO") + ", 품명: " + item.get("PART_NAME") + ", 수량: " + itemQuantity); - } - - // 프로젝트 등록 - cnt = sqlSession.update("project.createProject", projectParam); - // WBS 자동생성 주석처리 - // // 프로젝트 TASK 등록 - // cnt = sqlSession.insert("contractMgmt.insertProjectTask", projectParam); - // // 프로젝트 SETUP_TASK 등록 - // cnt = sqlSession.insert("contractMgmt.insertProjectSetupTask", projectParam); - - // 동일 품번 M-BOM 자동 복사 제거 - 수주 시점에는 총생산수량 미확정이므로 - // M-BOM관리에서 생산계획 입력 후 수동 복사하도록 변경 - // if(!isMachine) { - // copyMbomIfSamePartNoExists(sqlSession, - // (String)projectParam.get("OBJID"), - // CommonUtils.checkNull(item.get("PART_NO")), - // CommonUtils.checkNull(item.get("PART_NAME")), - // person.getUserId(), - // itemQuantity); - // } - - // WBS 폴더 생성 주석처리 - // // project_no - unit 폴더 생성 - // Map projectInfo = (Map)sqlSession.selectOne("project.getProjectMngInfo", projectParam); - // - // projectParam.put("contract_objid", contract_objid); - // projectParam.put("customer_product", projectParam.get("mechanical_type")); - // List> taskUnitList = (ArrayList)sqlSession.selectList("project.getWbsTaskListByProject", projectParam); - // if(CommonUtils.isNotEmpty(taskUnitList) && !taskUnitList.isEmpty()){ - // String projectNo = (String)projectInfo.get("project_no"); - // String filepath = Constants.FILE_STORAGE+"\\PART_DATA\\"; - // for (Map map : taskUnitList) { - // File file = new File(filepath+File.separator+projectNo+File.separator+CommonUtils.checkNull((String)map.get("unit_no"))+"-"+CommonUtils.checkNull((String)map.get("task_name"))); - // if(!file.exists()){ - // file.mkdirs(); - // } - // } - // } - } else { - // 프로젝트가 있으면 모든 품목 업데이트 (수량, 금액 등만) - Map updateParam = new HashMap(); - updateParam.putAll(paramMap); - updateParam.put("part_objid", item.get("PART_OBJID")); - // Machine인 경우 수량은 변경하지 않음 (프로젝트별 1로 유지) - if(isMachine) { - updateParam.remove("quantity"); - } else { - updateParam.put("quantity", String.valueOf(itemQuantity)); - } - updateParam.put("due_date", item.get("DUE_DATE")); - - System.out.println("프로젝트 업데이트 - PART_OBJID: " + item.get("PART_OBJID") + (isMachine ? ", 수량: 변경안함(Machine)" : ", 수량: " + itemQuantity)); - sqlSession.update("project.ModifyProjectByContract", updateParam); - } + Map updateParam = new HashMap(); + updateParam.putAll(paramMap); + updateParam.put("part_objid", item.get("PART_OBJID")); + if(isMachine) { + updateParam.remove("quantity"); + } else { + updateParam.put("quantity", String.valueOf(itemQuantity)); } + updateParam.put("due_date", item.get("DUE_DATE")); + sqlSession.update("project.ModifyProjectByContract", updateParam); } - } else { - System.out.println("품목이 없습니다 - 기존 방식으로 프로젝트 생성"); - // 품목이 없는 경우 기존 방식대로 처리 - if(!hasProject){ - paramMap.put("OBJID", CommonUtils.createObjId()); - paramMap.put("is_temp", '1'); - if("0000170".equals(category_cd) || "0000171".equals(category_cd)){ - paramMap.put("overhaul_project_no", target_project_no); - } - cnt = sqlSession.update("project.createProject", paramMap); - cnt = sqlSession.insert("contractMgmt.insertProjectTask", paramMap); - cnt = sqlSession.insert("contractMgmt.insertProjectSetupTask", paramMap); - }else{ + } else { sqlSession.update("project.ModifyProjectByContract", paramMap); } } - } -// if(cnt > 0){ - //계약완료 일시 메일 -// if("0000964".equals(CommonUtils.checkNull(paramMap.get("contract_result")))){ -// commonService.SendMail(paramMap,"CONTRACT_COMP",CommonUtils.checkNull(paramMap.get("pm_user_id"))); - //그냥 등록일때 메일 -// }else{ -// if("regist".equals(CommonUtils.checkNull(paramMap.get("actionType")))){ -// commonService.SendMail(paramMap,"CONTRACT_REG",CommonUtils.checkNull(paramMap.get(""))); -// } -// } -// } + resultMap.put("result", true); resultMap.put("msg", Message.SAVE_SUCCESS); sqlSession.commit(); @@ -3205,21 +2921,122 @@ private String encodeImageToBase64(String imagePath) { SqlSession sqlSession = null; try { sqlSession = SqlMapConfig.getInstance().getSqlSession(); - + // 수주상태 업데이트 sqlSession.update("contractMgmt.updateOrderStatusOnly", paramMap); - - // 수주(0000964) 또는 수주(FCST)(0000968)인 경우 프로젝트 생성 로직 실행 + String contract_result = CommonUtils.checkNull(paramMap.get("contract_result")); - if("0000964".equals(contract_result)) { - // 프로젝트가 없으면 생성 + String objId = CommonUtils.checkNull(paramMap.get("objId")); + + // 수주(0000964) 또는 수주(FCST)(0000968)인 경우 프로젝트 생성 로직 실행 + if("0000964".equals(contract_result) || "0000968".equals(contract_result)){ + + // CONTRACT_OBJID로 프로젝트 존재 여부 확인 Map resultList = sqlSession.selectOne("contractMgmt.getProjectListBycontractObjid", paramMap); - if(resultList == null) { - // 프로젝트 생성 로직은 기존 saveOrderInfo와 동일하게 처리 - System.out.println("수주 확정으로 변경 - 프로젝트 생성 필요시 saveOrderInfo 호출 권장"); + boolean hasProject = (resultList != null); + + // 계약 기본 정보 조회 (제품구분, 주문유형 등) + Map contractInfo = (Map) sqlSession.selectOne("contractMgmt.getContractBasicInfo", paramMap); + contractInfo = CommonUtils.toUpperCaseMapKey(contractInfo); + + String product_cd = contractInfo != null ? CommonUtils.checkNull(contractInfo.get("PRODUCT")) : ""; + String category_cd = contractInfo != null ? CommonUtils.checkNull(contractInfo.get("CATEGORY_CD")) : ""; + boolean isMachine = "0000928".equals(product_cd); + + if(isMachine) { + System.out.println("[수주확정] 제품구분: Machine(0000928) - 품목별 수량만큼 프로젝트 생성"); + } + + // 품목별로 프로젝트 생성 또는 업데이트 + paramMap.put("contractObjId", objId); + List contractItemsRaw = sqlSession.selectList("contractMgmt.getContractItems", paramMap); + List> contractItems = CommonUtils.toUpperCaseMapKey(contractItemsRaw); + + if(contractItems != null && !contractItems.isEmpty()) { + System.out.println("[수주확정] 품목 개수: " + contractItems.size() + "개 - 프로젝트 " + (hasProject ? "업데이트" : "생성") + " 시작" + (isMachine ? " (Machine - 수량별 생성)" : "")); + + for(Map item : contractItems) { + // 수량 가져오기 (소수점 형태 "2.00"도 처리) + Object quantityObj = item.get("ORDER_QUANTITY") != null ? item.get("ORDER_QUANTITY") : item.get("QUANTITY"); + int itemQuantity = 1; + try { + itemQuantity = (int) Double.parseDouble(String.valueOf(quantityObj).replaceAll("[^0-9.]", "")); + } catch (Exception e) { + itemQuantity = 1; + } + + // Machine인 경우 수량만큼 반복, 아니면 1번만 실행 + int loopCount = (isMachine && !hasProject) ? itemQuantity : 1; + + for(int q = 0; q < loopCount; q++) { + if(!hasProject) { + // 프로젝트가 없으면 모든 품목에 대해 생성 + Map projectParam = new HashMap(); + projectParam.putAll(paramMap); + + // 품목별 정보 설정 + projectParam.put("OBJID", CommonUtils.createObjId()); + projectParam.put("is_temp", '1'); + projectParam.put("contract_item_objid", String.valueOf(item.get("OBJID"))); + projectParam.put("part_objid", item.get("PART_OBJID")); + projectParam.put("part_no", item.get("PART_NO")); + projectParam.put("part_name", item.get("PART_NAME")); + // Machine인 경우 각 프로젝트의 수량은 1, 아니면 원래 수량 + projectParam.put("quantity", isMachine ? "1" : String.valueOf(itemQuantity)); + projectParam.put("due_date", item.get("DUE_DATE")); + + // 오버홀/개조인 경우 + String target_project_no = CommonUtils.checkNull(paramMap.get("target_project_no_direct")); + if("0000170".equals(category_cd) || "0000171".equals(category_cd)){ + projectParam.put("overhaul_project_no", target_project_no); + } + + if(isMachine) { + System.out.println("[수주확정] 프로젝트 생성 [" + (q+1) + "/" + loopCount + "] - PART_OBJID: " + item.get("PART_OBJID") + ", 품번: " + item.get("PART_NO") + ", 품명: " + item.get("PART_NAME") + ", 수량: 1"); + } else { + System.out.println("[수주확정] 프로젝트 생성 - PART_OBJID: " + item.get("PART_OBJID") + ", 품번: " + item.get("PART_NO") + ", 품명: " + item.get("PART_NAME") + ", 수량: " + itemQuantity); + } + + // 프로젝트 등록 + sqlSession.update("project.createProject", projectParam); + + } else { + // 프로젝트가 있으면 모든 품목 업데이트 (수량, 금액 등만) + Map updateParam = new HashMap(); + updateParam.putAll(paramMap); + updateParam.put("part_objid", item.get("PART_OBJID")); + // Machine인 경우 수량은 변경하지 않음 (프로젝트별 1로 유지) + if(isMachine) { + updateParam.remove("quantity"); + } else { + updateParam.put("quantity", String.valueOf(itemQuantity)); + } + updateParam.put("due_date", item.get("DUE_DATE")); + + System.out.println("[수주확정] 프로젝트 업데이트 - PART_OBJID: " + item.get("PART_OBJID") + (isMachine ? ", 수량: 변경안함(Machine)" : ", 수량: " + itemQuantity)); + sqlSession.update("project.ModifyProjectByContract", updateParam); + } + } + } + } else { + System.out.println("[수주확정] 품목이 없습니다 - 기존 방식으로 프로젝트 생성"); + // 품목이 없는 경우 기존 방식대로 처리 + if(!hasProject){ + paramMap.put("OBJID", CommonUtils.createObjId()); + paramMap.put("is_temp", '1'); + String target_project_no = CommonUtils.checkNull(paramMap.get("target_project_no_direct")); + if("0000170".equals(category_cd) || "0000171".equals(category_cd)){ + paramMap.put("overhaul_project_no", target_project_no); + } + sqlSession.update("project.createProject", paramMap); + sqlSession.insert("contractMgmt.insertProjectTask", paramMap); + sqlSession.insert("contractMgmt.insertProjectSetupTask", paramMap); + } else { + sqlSession.update("project.ModifyProjectByContract", paramMap); + } } } - + sqlSession.commit(); } catch(Exception e) { if(sqlSession != null) sqlSession.rollback(); From 2d9c732516ec6fedbef4f34299f4f3e58ce2b4a0 Mon Sep 17 00:00:00 2001 From: hjjeong Date: Wed, 25 Mar 2026 11:53:32 +0900 Subject: [PATCH 11/11] =?UTF-8?q?=EA=B2=80=EC=83=89=EC=98=81=EC=97=AD=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../view/purchaseOrder/projectPurchaseDeliveryStatus.jsp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/WebContent/WEB-INF/view/purchaseOrder/projectPurchaseDeliveryStatus.jsp b/WebContent/WEB-INF/view/purchaseOrder/projectPurchaseDeliveryStatus.jsp index 545dbc1..08ad2c2 100644 --- a/WebContent/WEB-INF/view/purchaseOrder/projectPurchaseDeliveryStatus.jsp +++ b/WebContent/WEB-INF/view/purchaseOrder/projectPurchaseDeliveryStatus.jsp @@ -187,8 +187,8 @@ function _fnc_datepick(){ ${code_map.project_no} - - + +