구매리스트 가공업체, 공급업체 복사, 붙여넣기 기능 추가

This commit is contained in:
2026-03-05 15:50:36 +09:00
parent f5cb14ae48
commit c7c465e0cf
2 changed files with 180 additions and 8 deletions

View File

@@ -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;
}
</style>
</head>
<body>
@@ -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 = $('<div class="vendor-context-menu"></div>');
// 복사
var copyLabel = cellValue ? '<b>복사</b> : ' + displayName : '<b>복사</b> (값 없음)';
var $copyItem = $('<div class="ctx-item">' + copyLabel + '</div>');
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('<div class="ctx-divider"></div>');
// 선택행에 붙여넣기
var hasCopied = copiedVendorData.value && copiedVendorData.field === field;
var pasteLabel = hasCopied
? '<b>선택행에 붙여넣기</b> : ' + copiedVendorData.displayName
: '<b>선택행에 붙여넣기</b> (복사된 ' + fieldLabel + ' 없음)';
var $pasteItem = $('<div class="ctx-item">' + pasteLabel + '</div>');
if(hasCopied) {
$pasteItem.on('click', function() {
menu.remove();
fn_pasteVendorToCheckedRows(copiedVendorData.field, copiedVendorData.value);
});
} else {
$pasteItem.addClass('disabled');
}
menu.append($pasteItem);
menu.append('<div class="ctx-divider"></div>');
// 전체행에 붙여넣기
var pasteAllLabel = hasCopied
? '<b>전체행에 붙여넣기</b> : ' + copiedVendorData.displayName
: '<b>전체행에 붙여넣기</b> (복사된 ' + fieldLabel + ' 없음)';
var $pasteAllItem = $('<div class="ctx-item">' + pasteAllLabel + '</div>');
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);

View File

@@ -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,