From 02415fcd1cb4c819c6c3930239dab06806e46ac3 Mon Sep 17 00:00:00 2001 From: hjjeong Date: Fri, 24 Apr 2026 18:47:47 +0900 Subject: [PATCH] =?UTF-8?q?M-BOM,=20=EA=B5=AC=EB=A7=A4=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=97=91=EC=85=80=20=EC=97=85=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?=EB=A8=B8=EC=A7=80=20=EB=A1=9C=EC=A7=81=20=EC=A0=95=EB=B0=80?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 노란색(편집 가능) 컬럼만 머지 대상, 그 외 컬럼은 base 값 보존 → 비-노란 컬럼(가공품의서일 등) 빈 값 덮어쓰기 위험 차단 - highlighted 판정 통합: title HTML 배경색 / titleFormatter editable-header / downloadHighlighted 옵션 / 필드 화이트리스트 - M-BOM 소재품번(RAW_MATERIAL_NO)을 필드 화이트리스트로 강제 highlighted 처리 → 다운 헤더 노란색 + 사용자 입력 머지 반영 Co-Authored-By: Claude Opus 4.7 (1M context) --- .../view/productionplanning/mBomPopupLeft.jsp | 42 ++++++++++++++++++- .../view/salesMng/purchaseListFormPopUp.jsp | 39 ++++++++++++++++- 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/WebContent/WEB-INF/view/productionplanning/mBomPopupLeft.jsp b/WebContent/WEB-INF/view/productionplanning/mBomPopupLeft.jsp index 95389ee..6a790ae 100644 --- a/WebContent/WEB-INF/view/productionplanning/mBomPopupLeft.jsp +++ b/WebContent/WEB-INF/view/productionplanning/mBomPopupLeft.jsp @@ -1481,6 +1481,17 @@ function getMbomTreeData() { return mbomData; } +// 화면에서 노란색(편집 가능) 헤더인지 판정 - 엑셀 다운로드/업로드 머지 기준 동일 +// field 화이트리스트: 그리드에선 흰색이지만 엑셀에서는 사용자가 직접 입력해야 하는 컬럼 +var FORCE_HIGHLIGHTED_FIELDS = { 'RAW_MATERIAL_NO': true }; +function fn_isHighlighted(c) { + if(c.field && FORCE_HIGHLIGHTED_FIELDS[c.field]) return true; + if(c.downloadHighlighted === true) return true; + if(c.title && /background-color/.test(String(c.title))) return true; + if(c.titleFormatter && /editable-header/.test(String(c.titleFormatter))) return true; + return false; +} + // 컬럼 정의로부터 다운로드/업로드에 사용할 헤더 텍스트 결정 (titleDownload 우선) function fn_getHeaderText(c) { if(c.titleDownload) return c.titleDownload; @@ -1509,7 +1520,7 @@ function fn_excel() { field: c.field, header: fn_getHeaderText(c), accessorDownload: c.accessorDownload || null, - highlighted: c.downloadHighlighted === true || (typeof c.editor === 'function' || c.editor === true) || (c.title && /background-color/.test(String(c.title))) + highlighted: fn_isHighlighted(c) }); } } @@ -1600,6 +1611,24 @@ function fn_excel() { }); } +// 노란색(편집 가능 + downloadHighlighted) 필드 집합 - 머지 시 이것만 base에 덮어쓰기 +function fn_buildEditableFields() { + var editable = {}; + var cols = _tabulGrid.getColumnDefinitions(); + function walk(arr) { + for(var i = 0; i < arr.length; i++) { + var c = arr[i]; + if(c.columns) walk(c.columns); + if(c.field) { + var hi = fn_isHighlighted(c); + if(hi) editable[c.field] = true; + } + } + } + walk(cols); + return editable; +} + // columns 정의에서 헤더(title) → field 매핑 자동 생성 (titleDownload 우선) function fn_buildHeaderFieldMap() { var map = {}; @@ -1707,6 +1736,9 @@ function fn_excelUpload(file) { return; } + // 편집 가능 컬럼 집합 (이것만 머지 시 덮어쓰기, 그 외는 base 보존) + var editableFields = fn_buildEditableFields(); + // 3) PART_NO 우선 매칭 (중복 시 OBJID disambiguate) var usedObjids = {}; function pickMatch(uploadRow) { @@ -1753,7 +1785,13 @@ function fn_excelUpload(file) { unmatched.push({ row: i + 2, partNo: String(out.PART_NO || '') }); continue; } - merged.push($.extend({}, base, out)); + var mergedRow = $.extend({}, base); + for(var k in out) { + if(out.hasOwnProperty(k) && editableFields[k]) { + mergedRow[k] = out[k]; + } + } + merged.push(mergedRow); } if(unmatched.length > 0) { var msg = '기존에 없는 행이 포함되어 업로드를 중단합니다.\n신규 행은 추가할 수 없습니다.\n\n'; diff --git a/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp b/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp index d213fbc..fda3a9a 100644 --- a/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp +++ b/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp @@ -1266,6 +1266,14 @@ function fn_mergeSavedData(savedList){ }); } +// 화면에서 노란색(편집 가능) 헤더인지 판정 - 엑셀 다운로드/업로드 머지 기준 동일 +function fn_isHighlighted(c) { + if(c.downloadHighlighted === true) return true; + if(c.title && /background-color/.test(String(c.title))) return true; + if(c.titleFormatter && /editable-header/.test(String(c.titleFormatter))) return true; + return false; +} + // 컬럼 정의로부터 다운로드/업로드에 사용할 헤더 텍스트 결정 (titleDownload 우선) function fn_getHeaderText(c) { if(c.titleDownload) return c.titleDownload; @@ -1294,7 +1302,7 @@ function fn_excelDownload() { field: c.field, header: fn_getHeaderText(c), accessorDownload: c.accessorDownload || null, - highlighted: c.downloadHighlighted === true || (typeof c.editor === 'function' || c.editor === true) || (c.title && /background-color/.test(String(c.title))) + highlighted: fn_isHighlighted(c) }); } } @@ -1386,6 +1394,24 @@ function fn_excelDownload() { }); } +// 노란색(편집 가능 + downloadHighlighted) 필드 집합 - 머지 시 이것만 base에 덮어쓰기 +function fn_buildEditableFields() { + var editable = {}; + var cols = _tabulGrid.getColumnDefinitions(); + function walk(arr) { + for(var i = 0; i < arr.length; i++) { + var c = arr[i]; + if(c.columns) walk(c.columns); + if(c.field) { + var hi = fn_isHighlighted(c); + if(hi) editable[c.field] = true; + } + } + } + walk(cols); + return editable; +} + // columns 정의에서 헤더(title) → field 매핑 자동 생성 (titleDownload 우선) function fn_buildHeaderFieldMap() { var map = {}; @@ -1487,6 +1513,9 @@ function fn_excelUpload(file) { return; } + // 편집 가능 컬럼 집합 (이것만 머지 시 덮어쓰기, 그 외는 base 보존) + var editableFields = fn_buildEditableFields(); + // 3) PART_NO 우선 매칭 (중복 시 OBJID disambiguate) var usedObjids = {}; function pickMatch(uploadRow) { @@ -1531,7 +1560,13 @@ function fn_excelUpload(file) { unmatched.push({ row: i + 2, partNo: String(out.PART_NO || '') }); continue; } - merged.push($.extend({}, base, out)); + var mergedRow = $.extend({}, base); + for(var k in out) { + if(out.hasOwnProperty(k) && editableFields[k]) { + mergedRow[k] = out[k]; + } + } + merged.push(mergedRow); } if(unmatched.length > 0) { var msg = '기존에 없는 행이 포함되어 업로드를 중단합니다.\n신규 행은 추가할 수 없습니다.\n\n';