fix: SelectedItemsDetailInput 저장 시 NULL 레코드 생성 버그 수정
This commit is contained in:
@@ -815,6 +815,9 @@ export class ButtonActionExecutor {
|
||||
console.log("🎯 채번 규칙 할당 시작 (allocateCode 호출)");
|
||||
const { allocateNumberingCode } = await import("@/lib/api/numberingRule");
|
||||
|
||||
let hasAllocationFailure = false;
|
||||
const failedFields: string[] = [];
|
||||
|
||||
for (const [fieldName, ruleId] of Object.entries(fieldsWithNumbering)) {
|
||||
try {
|
||||
console.log(`🔄 ${fieldName} 필드에 대해 allocateCode 호출: ${ruleId}`);
|
||||
@@ -825,13 +828,31 @@ export class ButtonActionExecutor {
|
||||
console.log(`✅ ${fieldName} 새 코드 할당: ${formData[fieldName]} → ${newCode}`);
|
||||
formData[fieldName] = newCode;
|
||||
} else {
|
||||
console.warn(`⚠️ ${fieldName} 코드 할당 실패, 기존 값 유지:`, allocateResult.error);
|
||||
console.warn(`⚠️ ${fieldName} 코드 할당 실패:`, allocateResult.error);
|
||||
// 🆕 기존 값이 빈 문자열이면 실패로 표시
|
||||
if (!formData[fieldName] || formData[fieldName] === "") {
|
||||
hasAllocationFailure = true;
|
||||
failedFields.push(fieldName);
|
||||
}
|
||||
}
|
||||
} catch (allocateError) {
|
||||
console.error(`❌ ${fieldName} 코드 할당 오류:`, allocateError);
|
||||
// 오류 시 기존 값 유지
|
||||
// 🆕 기존 값이 빈 문자열이면 실패로 표시
|
||||
if (!formData[fieldName] || formData[fieldName] === "") {
|
||||
hasAllocationFailure = true;
|
||||
failedFields.push(fieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 🆕 채번 규칙 할당 실패 시 저장 중단
|
||||
if (hasAllocationFailure) {
|
||||
const fieldNames = failedFields.join(", ");
|
||||
toast.error(`채번 규칙 할당에 실패했습니다 (${fieldNames}). 화면 설정에서 채번 규칙을 확인해주세요.`);
|
||||
console.error(`❌ 채번 규칙 할당 실패로 저장 중단. 실패 필드: ${fieldNames}`);
|
||||
console.error("💡 해결 방법: 화면관리에서 해당 필드의 채번 규칙 설정을 확인하세요.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("✅ 채번 규칙 할당 완료");
|
||||
@@ -3062,6 +3083,7 @@ export class ButtonActionExecutor {
|
||||
config: ButtonActionConfig,
|
||||
rowData: any,
|
||||
context: ButtonActionContext,
|
||||
isCreateMode: boolean = false, // 🆕 복사 모드에서 true로 전달
|
||||
): Promise<void> {
|
||||
const { groupByColumns = [] } = config;
|
||||
|
||||
@@ -3135,10 +3157,11 @@ export class ButtonActionExecutor {
|
||||
const modalEvent = new CustomEvent("openEditModal", {
|
||||
detail: {
|
||||
screenId: config.targetScreenId,
|
||||
title: config.editModalTitle || "데이터 수정",
|
||||
title: isCreateMode ? (config.editModalTitle || "데이터 복사") : (config.editModalTitle || "데이터 수정"),
|
||||
description: description,
|
||||
modalSize: config.modalSize || "lg",
|
||||
editData: rowData,
|
||||
isCreateMode: isCreateMode, // 🆕 복사 모드에서 INSERT로 처리되도록
|
||||
groupByColumns: groupByColumns.length > 0 ? groupByColumns : undefined, // 🆕 그룹핑 컬럼 전달
|
||||
tableName: context.tableName, // 🆕 테이블명 전달
|
||||
buttonConfig: config, // 🆕 버튼 설정 전달 (제어로직 실행용)
|
||||
@@ -3253,23 +3276,61 @@ export class ButtonActionExecutor {
|
||||
"code",
|
||||
];
|
||||
|
||||
// 🆕 화면 설정에서 채번 규칙 가져오기
|
||||
let screenNumberingRules: Record<string, string> = {};
|
||||
if (config.targetScreenId) {
|
||||
try {
|
||||
const { screenApi } = await import("@/lib/api/screen");
|
||||
const layout = await screenApi.getLayout(config.targetScreenId);
|
||||
|
||||
// 레이아웃에서 채번 규칙이 설정된 컴포넌트 찾기
|
||||
const findNumberingRules = (components: any[]): void => {
|
||||
for (const comp of components) {
|
||||
const compConfig = comp.componentConfig || {};
|
||||
// text-input 컴포넌트의 채번 규칙 확인
|
||||
if (compConfig.autoGeneration?.type === "numbering_rule" && compConfig.autoGeneration?.options?.numberingRuleId) {
|
||||
const columnName = compConfig.columnName || comp.columnName;
|
||||
if (columnName) {
|
||||
screenNumberingRules[columnName] = compConfig.autoGeneration.options.numberingRuleId;
|
||||
console.log(`📋 화면 설정에서 채번 규칙 발견: ${columnName} → ${compConfig.autoGeneration.options.numberingRuleId}`);
|
||||
}
|
||||
}
|
||||
// 중첩된 컴포넌트 확인
|
||||
if (comp.children && Array.isArray(comp.children)) {
|
||||
findNumberingRules(comp.children);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (layout?.components) {
|
||||
findNumberingRules(layout.components);
|
||||
}
|
||||
console.log("📋 화면 설정에서 찾은 채번 규칙:", screenNumberingRules);
|
||||
} catch (error) {
|
||||
console.warn("⚠️ 화면 레이아웃 조회 실패:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// 품목코드 필드를 찾아서 무조건 공백으로 초기화
|
||||
let resetFieldName = "";
|
||||
for (const field of itemCodeFields) {
|
||||
if (copiedData[field] !== undefined) {
|
||||
const originalValue = copiedData[field];
|
||||
const ruleIdKey = `${field}_numberingRuleId`;
|
||||
const hasNumberingRule =
|
||||
rowData[ruleIdKey] !== undefined && rowData[ruleIdKey] !== null && rowData[ruleIdKey] !== "";
|
||||
|
||||
// 1순위: 원본 데이터에서 채번 규칙 ID 확인
|
||||
// 2순위: 화면 설정에서 채번 규칙 ID 확인
|
||||
const numberingRuleId = rowData[ruleIdKey] || screenNumberingRules[field];
|
||||
const hasNumberingRule = numberingRuleId !== undefined && numberingRuleId !== null && numberingRuleId !== "";
|
||||
|
||||
// 품목코드를 무조건 공백으로 초기화
|
||||
copiedData[field] = "";
|
||||
|
||||
// 채번 규칙 ID가 있으면 복사 (저장 시 자동 생성)
|
||||
if (hasNumberingRule) {
|
||||
copiedData[ruleIdKey] = rowData[ruleIdKey];
|
||||
copiedData[ruleIdKey] = numberingRuleId;
|
||||
console.log(`✅ 품목코드 초기화 (채번 규칙 있음): ${field} (기존값: ${originalValue})`);
|
||||
console.log(`📋 채번 규칙 ID 복사: ${ruleIdKey} = ${rowData[ruleIdKey]}`);
|
||||
console.log(`📋 채번 규칙 ID 설정: ${ruleIdKey} = ${numberingRuleId}`);
|
||||
} else {
|
||||
console.log(`✅ 품목코드 초기화 (수동 입력 필요): ${field} (기존값: ${originalValue})`);
|
||||
}
|
||||
@@ -3326,9 +3387,9 @@ export class ButtonActionExecutor {
|
||||
|
||||
switch (editMode) {
|
||||
case "modal":
|
||||
// 모달로 복사 폼 열기 (편집 모달 재사용)
|
||||
console.log("📋 모달로 복사 폼 열기");
|
||||
await this.openEditModal(config, rowData, context);
|
||||
// 모달로 복사 폼 열기 (편집 모달 재사용, INSERT 모드로)
|
||||
console.log("📋 모달로 복사 폼 열기 (INSERT 모드)");
|
||||
await this.openEditModal(config, rowData, context, true); // 🆕 isCreateMode: true
|
||||
break;
|
||||
|
||||
case "navigate":
|
||||
@@ -3339,8 +3400,8 @@ export class ButtonActionExecutor {
|
||||
|
||||
default:
|
||||
// 기본값: 모달
|
||||
console.log("📋 기본 모달로 복사 폼 열기");
|
||||
this.openEditModal(config, rowData, context);
|
||||
console.log("📋 기본 모달로 복사 폼 열기 (INSERT 모드)");
|
||||
this.openEditModal(config, rowData, context, true); // 🆕 isCreateMode: true
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("❌ openCopyForm 실행 중 오류:", error);
|
||||
@@ -4943,26 +5004,35 @@ export class ButtonActionExecutor {
|
||||
|
||||
const { oldValue, newValue } = confirmed;
|
||||
|
||||
// 미리보기 표시 (옵션)
|
||||
// 미리보기 표시 (값 기반 검색 - 모든 테이블의 모든 컬럼에서 검색)
|
||||
if (config.mergeShowPreview !== false) {
|
||||
const { apiClient } = await import("@/lib/api/client");
|
||||
|
||||
const previewResponse = await apiClient.post("/code-merge/preview", {
|
||||
columnName,
|
||||
toast.loading("영향받는 데이터 검색 중...", { duration: Infinity });
|
||||
|
||||
const previewResponse = await apiClient.post("/code-merge/preview-by-value", {
|
||||
oldValue,
|
||||
});
|
||||
|
||||
toast.dismiss();
|
||||
|
||||
if (previewResponse.data.success) {
|
||||
const preview = previewResponse.data.data;
|
||||
const totalRows = preview.totalAffectedRows;
|
||||
|
||||
// 상세 정보 생성
|
||||
const detailList = preview.preview
|
||||
.map((p: any) => ` - ${p.tableName}.${p.columnName}: ${p.affectedRows}건`)
|
||||
.join("\n");
|
||||
|
||||
const confirmMerge = confirm(
|
||||
"⚠️ 코드 병합 확인\n\n" +
|
||||
"코드 병합 확인\n\n" +
|
||||
`${oldValue} → ${newValue}\n\n` +
|
||||
"영향받는 데이터:\n" +
|
||||
`- 테이블 수: ${preview.preview.length}개\n` +
|
||||
`- 테이블/컬럼 수: ${preview.preview.length}개\n` +
|
||||
`- 총 행 수: ${totalRows}개\n\n` +
|
||||
`데이터는 삭제되지 않고, "${columnName}" 컬럼 값만 변경됩니다.\n\n` +
|
||||
(preview.preview.length <= 10 ? `상세:\n${detailList}\n\n` : "") +
|
||||
"모든 테이블에서 해당 값이 변경됩니다.\n\n" +
|
||||
"계속하시겠습니까?",
|
||||
);
|
||||
|
||||
@@ -4972,13 +5042,12 @@ export class ButtonActionExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
// 병합 실행
|
||||
// 병합 실행 (값 기반 - 모든 테이블의 모든 컬럼)
|
||||
toast.loading("코드 병합 중...", { duration: Infinity });
|
||||
|
||||
const { apiClient } = await import("@/lib/api/client");
|
||||
|
||||
const response = await apiClient.post("/code-merge/merge-all-tables", {
|
||||
columnName,
|
||||
const response = await apiClient.post("/code-merge/merge-by-value", {
|
||||
oldValue,
|
||||
newValue,
|
||||
});
|
||||
@@ -4987,9 +5056,17 @@ export class ButtonActionExecutor {
|
||||
|
||||
if (response.data.success) {
|
||||
const data = response.data.data;
|
||||
|
||||
// 변경된 테이블/컬럼 목록 생성
|
||||
const changedList = data.affectedData
|
||||
.map((d: any) => `${d.tableName}.${d.columnName}: ${d.rowsUpdated}건`)
|
||||
.join(", ");
|
||||
|
||||
toast.success(
|
||||
"코드 병합 완료!\n" + `${data.affectedTables.length}개 테이블, ${data.totalRowsUpdated}개 행 업데이트`,
|
||||
`코드 병합 완료! ${data.affectedData.length}개 테이블/컬럼, ${data.totalRowsUpdated}개 행 업데이트`,
|
||||
);
|
||||
|
||||
console.log("코드 병합 결과:", data.affectedData);
|
||||
|
||||
// 화면 새로고침
|
||||
context.onRefresh?.();
|
||||
|
||||
Reference in New Issue
Block a user