diff --git a/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp b/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp
index ff709ef..bbcd5bf 100644
--- a/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp
+++ b/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp
@@ -1431,21 +1431,26 @@ function fn_buildHeaderFieldMap() {
}
// Select2 코드 컬럼: 엑셀의 코드명(text) → 코드값(id) 역매핑
-// Select2 코드 컬럼: 엑셀의 코드명(text) → 코드값(id) 역매핑
-// 반환: { value: 변환값(매칭 시 id, 미매칭/비코드 시 원본), isCodeColumn: 코드 컬럼 여부, matched: 기준정보 매칭 여부 }
-// isCodeColumn=true && matched=false 이면 기준정보에 없는 값 (호출부에서 invalid 처리)
+// Select2/정적 코드 컬럼: 엑셀의 코드명(text) → 코드값(id) 역매핑
+// 반환: { value, isCodeColumn, matched }
+// isCodeColumn=true && matched=false 이면 기준정보에 없는 값 (호출부에서 invalid 처리)
+// 화이트리스트에 있는 필드는 list가 비어 있어도 isCodeColumn=true로 간주(검증 누락 방지)
function fn_reverseSelectValue(field, value) {
if(value === null || value === undefined || value === '') {
return { value: value, isCodeColumn: false, matched: true };
}
var list = null;
- if(field === 'VENDOR_PM' || field === 'PROCESSING_VENDOR') list = processingVendorList;
- else if(field === 'CURRENCY') list = currencyList;
- if(!list || list.length === 0) {
+ var isCode = false;
+ if(field === 'VENDOR_PM' || field === 'PROCESSING_VENDOR') { list = processingVendorList; isCode = true; }
+ else if(field === 'CURRENCY') { list = currencyList; isCode = true; }
+ else if(field === 'USE_YN') { list = [{id:'사용', text:'사용'}, {id:'미사용', text:'미사용'}]; isCode = true; }
+ if(!isCode) {
return { value: value, isCodeColumn: false, matched: true };
}
+ if(!list) list = [];
+ var v = String(value).trim();
for(var i = 0; i < list.length; i++) {
- if(list[i].text === value || String(list[i].id) === String(value)) {
+ if(String(list[i].text).trim() === v || String(list[i].id).trim() === v) {
return { value: list[i].id, isCodeColumn: true, matched: true };
}
}
@@ -1457,9 +1462,16 @@ function fn_getFieldLabel(field) {
if(field === 'VENDOR_PM') return '공급업체';
if(field === 'PROCESSING_VENDOR') return '가공업체';
if(field === 'CURRENCY') return '환종';
+ if(field === 'USE_YN') return '사용여부';
+ if(field === 'UNIT_PRICE') return '소재단가';
+ if(field === 'PO_QTY') return '발주수량';
+ if(field === 'PROCESSING_UNIT_PRICE') return '가공단가';
return field;
}
+// 숫자만 허용 컬럼 (엑셀 업로드 시 형식 검증). 천단위 콤마는 허용.
+var NUMERIC_FIELDS = { UNIT_PRICE: true, PO_QTY: true, PROCESSING_UNIT_PRICE: true };
+
// 저장 버튼 표시/숨김 (엑셀 업로드에서 기준정보 미매칭 발생 시 저장 차단)
function fn_toggleSaveBtn(show) {
var $btn = $('#btnSave');
@@ -1468,6 +1480,12 @@ function fn_toggleSaveBtn(show) {
else $btn.hide();
}
+// SweetAlert html 모드에서 사용자 입력값이 HTML로 해석되지 않도록 이스케이프
+function fn_escapeHtml(s) {
+ if(s === null || s === undefined) return '';
+ return String(s).replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"');
+}
+
// 엑셀 업로드: 파싱 → 역매핑 → OBJID 무결성 검증 → PART_NO 우선 매칭(중복 시 OBJID) → 그리드 주입
function fn_excelUpload(file) {
if(!file) return;
@@ -1507,9 +1525,9 @@ function fn_excelUpload(file) {
if(oid) existingObjidSet[oid] = true;
}
- // 1) 헤더 → field 변환 + Select2 역매핑 + 기준정보 미매칭 수집 (편집 가능 컬럼만)
+ // 1) 헤더 → field 변환 + Select2 역매핑 + 기준정보 미매칭/숫자 형식 오류 수집 (편집 가능 컬럼만)
var converted = [];
- var invalidSelects = [];
+ var invalidCells = []; // { row, field, label, value, reason }
for(var i = 0; i < rows.length; i++) {
var src = rows[i];
var out = {};
@@ -1517,14 +1535,34 @@ function fn_excelUpload(file) {
if(!src.hasOwnProperty(header)) continue;
var field = headerFieldMap[header];
if(!field) continue;
- var r = fn_reverseSelectValue(field, src[header]);
+ var rawVal = src[header];
+ // 숫자 컬럼: 빈 값 통과, 변환 실패 시 invalid
+ if(NUMERIC_FIELDS[field] && editableFields[field]) {
+ if(rawVal !== null && rawVal !== undefined && String(rawVal).trim() !== '') {
+ var numVal = Number(String(rawVal).replace(/,/g, ''));
+ if(isNaN(numVal)) {
+ invalidCells.push({
+ row: i + 2, field: field, label: fn_getFieldLabel(field),
+ value: rawVal, reason: '숫자만 입력 가능합니다'
+ });
+ out[field] = rawVal;
+ } else {
+ out[field] = numVal;
+ }
+ } else {
+ out[field] = rawVal;
+ }
+ continue;
+ }
+ // 코드 컬럼: 역매핑 + 기준정보 매칭 검증
+ // (editable 체크 없이 검증 — 코드 컬럼은 모두 사용자 입력 대상이므로
+ // editableFields 판정 누락 시에도 안전하게 잡기 위함)
+ var r = fn_reverseSelectValue(field, rawVal);
out[field] = r.value;
- if(r.isCodeColumn && !r.matched && editableFields[field]) {
- invalidSelects.push({
- row: i + 2,
- field: field,
- label: fn_getFieldLabel(field),
- value: src[header]
+ if(r.isCodeColumn && !r.matched) {
+ invalidCells.push({
+ row: i + 2, field: field, label: fn_getFieldLabel(field),
+ value: rawVal, reason: '기준정보에 없습니다'
});
}
}
@@ -1629,17 +1667,23 @@ function fn_excelUpload(file) {
return;
}
- // 5) 기준정보 미매칭 분기: 데이터는 그리드에 반영하되 저장 버튼 숨김
- if(invalidSelects.length > 0) {
+ // 5) 잘못된 셀(기준정보 미매칭/숫자 형식 오류) 분기: 데이터는 그리드에 반영하되 저장 버튼 숨김
+ if(invalidCells.length > 0) {
_tabulGrid.replaceData(merged).then(function() {
fn_toggleSaveBtn(false);
- var msg = '기준정보에 없는 값이 포함되어 저장이 차단됩니다.\n정상 엑셀로 다시 업로드하세요.\n\n';
- for(var i = 0; i < Math.min(invalidSelects.length, 10); i++) {
- var it = invalidSelects[i];
- msg += ' - ' + it.row + '행: ' + it.label + " '" + it.value + "' 기준정보에 없습니다\n";
+ invalidCells.sort(function(a, b) { return (a.row || 0) - (b.row || 0); });
+ var lines = '';
+ for(var i = 0; i < Math.min(invalidCells.length, 20); i++) {
+ var it = invalidCells[i];
+ lines += ' - ' + it.row + '행: ' + fn_escapeHtml(it.label)
+ + " '" + fn_escapeHtml(it.value) + "' "
+ + fn_escapeHtml(it.reason) + '
';
}
- if(invalidSelects.length > 10) msg += ' ... (총 ' + invalidSelects.length + '건)';
- Swal.fire('오류', msg, 'error');
+ if(invalidCells.length > 20) lines += ' ... (총 ' + invalidCells.length + '건)
';
+ var html = '잘못된 값이 포함되어 저장이 차단됩니다.
'
+ + '정상 엑셀로 다시 업로드하세요.
'
+ + '