diff --git a/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp b/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp index 8dbd86a..333ca26 100644 --- a/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp +++ b/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp @@ -68,6 +68,36 @@ body, html { padding: 20px; overflow: auto; } +.vendor-context-menu { + position: fixed; + background: #fff; + border: 1px solid #ccc; + box-shadow: 2px 2px 8px rgba(0,0,0,0.2); + z-index: 10000; + border-radius: 4px; + padding: 4px 0; + min-width: 180px; +} +.vendor-context-menu .ctx-item { + padding: 8px 16px; + cursor: pointer; + font-size: 13px; + white-space: nowrap; +} +.vendor-context-menu .ctx-item:hover { + background: #e3f2fd; +} +.vendor-context-menu .ctx-item.disabled { + color: #aaa; + cursor: default; +} +.vendor-context-menu .ctx-item.disabled:hover { + background: transparent; +} +.vendor-context-menu .ctx-divider { + border-top: 1px solid #eee; + margin: 4px 0; +} @@ -137,6 +167,7 @@ var bomReportObjid = "${resolvedBomReportObjid}"; var mbomHeaderObjid = "${resolvedMbomHeaderObjid}"; // MBOM_HEADER.OBJID (M-BOM 관리 화면에서 사용하는 ID) var vendorList = []; // 공급업체 목록 var processingVendorList = []; // 가공업체 목록 (Select2용 배열) +var copiedVendorData = { field: null, value: null, displayName: '' }; // 복사된 업체 정보 // 디버그: resultMap 내용 확인 (주석처리) // console.log("=== JSP resultMap 디버그 ==="); @@ -875,6 +906,149 @@ function fn_initGrid() { row.reformat(); } }); + + // 공급업체/가공업체 셀 우클릭 → 복사/붙여넣기 컨텍스트 메뉴 + _tabulGrid.on("cellContext", function(e, cell) { + var field = cell.getField(); + if(field !== 'VENDOR_PM' && field !== 'PROCESSING_VENDOR') return; + e.preventDefault(); + fn_showVendorContextMenu(e, cell); + }); + + // 다른 곳 클릭 시 컨텍스트 메뉴 닫기 + $(document).on('click', function() { + $('.vendor-context-menu').remove(); + }); +} + +// 업체 복사/붙여넣기 컨텍스트 메뉴 표시 +function fn_showVendorContextMenu(e, cell) { + $('.vendor-context-menu').remove(); + + var field = cell.getField(); + var cellValue = cell.getValue(); + var fieldLabel = (field === 'VENDOR_PM') ? '공급업체' : '가공업체'; + var displayName = fn_getVendorDisplayName(cellValue); + + var menu = $('
'); + + // 복사 + var copyLabel = cellValue ? '복사 : ' + displayName : '복사 (값 없음)'; + var $copyItem = $('
' + copyLabel + '
'); + if(cellValue) { + $copyItem.on('click', function() { + copiedVendorData = { field: field, value: cellValue, displayName: displayName }; + menu.remove(); + Swal.fire({ toast: true, position: 'top-end', icon: 'success', title: fieldLabel + ' 복사: ' + displayName, showConfirmButton: false, timer: 1200 }); + }); + } else { + $copyItem.addClass('disabled'); + } + menu.append($copyItem); + + menu.append('
'); + + // 선택행에 붙여넣기 + var hasCopied = copiedVendorData.value && copiedVendorData.field === field; + var pasteLabel = hasCopied + ? '선택행에 붙여넣기 : ' + copiedVendorData.displayName + : '선택행에 붙여넣기 (복사된 ' + fieldLabel + ' 없음)'; + var $pasteItem = $('
' + pasteLabel + '
'); + if(hasCopied) { + $pasteItem.on('click', function() { + menu.remove(); + fn_pasteVendorToCheckedRows(copiedVendorData.field, copiedVendorData.value); + }); + } else { + $pasteItem.addClass('disabled'); + } + menu.append($pasteItem); + + menu.append('
'); + + // 전체행에 붙여넣기 + var pasteAllLabel = hasCopied + ? '전체행에 붙여넣기 : ' + copiedVendorData.displayName + : '전체행에 붙여넣기 (복사된 ' + fieldLabel + ' 없음)'; + var $pasteAllItem = $('
' + pasteAllLabel + '
'); + if(hasCopied) { + $pasteAllItem.on('click', function() { + menu.remove(); + fn_pasteVendorToAllRows(copiedVendorData.field, copiedVendorData.value); + }); + } else { + $pasteAllItem.addClass('disabled'); + } + menu.append($pasteAllItem); + + // 화면 밖으로 나가지 않도록 위치 보정 + var menuX = e.pageX; + var menuY = e.pageY; + $('body').append(menu); + var menuWidth = menu.outerWidth(); + var menuHeight = menu.outerHeight(); + if(menuX + menuWidth > $(window).width()) menuX = $(window).width() - menuWidth - 5; + if(menuY + menuHeight > $(window).height()) menuY = $(window).height() - menuHeight - 5; + menu.css({ left: menuX + 'px', top: menuY + 'px' }); +} + +// 업체 OBJID → 업체명 변환 +function fn_getVendorDisplayName(value) { + if(!value) return ''; + for(var i = 0; i < processingVendorList.length; i++) { + if(processingVendorList[i].id == value) return processingVendorList[i].text; + } + return value; +} + +// 체크된 행에 업체값 붙여넣기 +function fn_pasteVendorToCheckedRows(field, value) { + var count = 0; + $('.rowCheck:checked').each(function() { + var row = $(this).closest('.tabulator-row'); + if(row.length > 0 && _tabulGrid) { + var rowComponent = _tabulGrid.getRow(row[0]); + if(rowComponent) { + var updateData = {}; + updateData[field] = value; + rowComponent.update(updateData); + count++; + } + } + }); + var fieldLabel = (field === 'VENDOR_PM') ? '공급업체' : '가공업체'; + if(count > 0) { + Swal.fire({ toast: true, position: 'top-end', icon: 'success', title: fieldLabel + ' ' + count + '건 적용 완료', showConfirmButton: false, timer: 1500 }); + } else { + Swal.fire({ title: '알림', text: '체크된 행이 없습니다.\n먼저 적용할 행을 체크해주세요.', icon: 'info' }); + } +} + +// 전체 행에 업체값 붙여넣기 +function fn_pasteVendorToAllRows(field, value) { + var fieldLabel = (field === 'VENDOR_PM') ? '공급업체' : '가공업체'; + var displayName = fn_getVendorDisplayName(value); + + Swal.fire({ + title: '전체 적용', + text: '모든 행의 ' + fieldLabel + '을(를) "' + displayName + '"(으)로 변경하시겠습니까?', + icon: 'question', + showCancelButton: true, + confirmButtonText: '적용', + cancelButtonText: '취소' + }).then(function(result) { + if(result.isConfirmed) { + var rows = _tabulGrid.getRows(); + var count = 0; + rows.forEach(function(row) { + var updateData = {}; + updateData[field] = value; + row.update(updateData); + count++; + }); + Swal.fire({ toast: true, position: 'top-end', icon: 'success', title: fieldLabel + ' 전체 ' + count + '건 적용 완료', showConfirmButton: false, timer: 1500 }); + } + }); } // 기존 구매리스트 조회 @@ -944,11 +1118,7 @@ function fn_loadFromMBom(callback) { var list = (data && data.list) ? data.list : []; if(list.length > 0) { - // console.log("M-BOM 데이터 " + list.length + "건 로드됨"); _tabulGrid.setData(list); - } else { - // console.log("M-BOM 데이터 없음!"); - // 알림 제거 - 머지 모드에서는 알림 안 띄움 } if(typeof callback === "function"){ callback(list); diff --git a/src/com/pms/mapper/salesMng.xml b/src/com/pms/mapper/salesMng.xml index 9980caf..45bf26c 100644 --- a/src/com/pms/mapper/salesMng.xml +++ b/src/com/pms/mapper/salesMng.xml @@ -3305,7 +3305,7 @@ WITH RECURSIVE VIEW_BOM( A.PART_NO, A.PART_NAME, A.QTY, - A.QTY, + A.ITEM_QTY, A.QTY, A.REGDATE, A.SEQ, @@ -3368,7 +3368,7 @@ WITH RECURSIVE VIEW_BOM( B.PART_NO, B.PART_NAME, B.QTY, - B.QTY, + B.ITEM_QTY, B.QTY, B.REGDATE, B.SEQ, @@ -3445,6 +3445,7 @@ GROUPED_BOM AS ( MIN(V.CHILD_OBJID) AS CHILD_OBJID, MIN(V.PART_NAME) AS PART_NAME, SUM(COALESCE(NULLIF(V.QTY::TEXT, '')::NUMERIC, 0)) AS QTY, + SUM(COALESCE(NULLIF(V.ITEM_QTY::TEXT, '')::NUMERIC, 0)) AS ITEM_QTY, MIN(V.SEQ) AS SEQ, MIN(V.STATUS) AS STATUS, MIN(V.LEV) AS LEV, @@ -3476,6 +3477,7 @@ GROUPED_BOM AS ( COUNT(*) AS GROUPED_COUNT FROM VIEW_BOM V WHERE COALESCE(V.PRODUCTION_QTY, 0) > 0 + AND V.LEV > 1 GROUP BY V.PART_NO, V.PART_OBJID, COALESCE(V.SUPPLY_TYPE, ''), COALESCE(V.RAW_MATERIAL, ''), @@ -3494,7 +3496,7 @@ SELECT G.PART_NO, G.PART_NAME, G.QTY, - G.QTY AS ITEM_QTY, + G.ITEM_QTY, G.QTY AS QTY_TEMP, G.LEV AS LEVEL, 0 AS SUB_PART_CNT, @@ -3507,7 +3509,7 @@ SELECT G.RAW_MATERIAL_SPEC, G.RAW_MATERIAL, G.RAW_MATERIAL_SIZE AS SIZE, - CASE WHEN G.PROCESSING_VENDOR IS NULL THEN '0000008377' ELSE G.PROCESSING_VENDOR END AS PROCESSING_VENDOR, + G.PROCESSING_VENDOR, G.PROCESSING_DEADLINE, G.GRINDING_DEADLINE, G.REQUIRED_QTY,