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,