feat: Implement orphan record deletion logic based on edit mode
- Updated the DataService to conditionally delete orphan records only when in EDIT mode, controlled by the deleteOrphans flag. - Enhanced the SelectedItemsDetailInputComponent to determine the mode (EDIT or CREATE) based on the presence of existing database IDs, ensuring that orphan records are only deleted when necessary. - Improved data integrity by preventing unintended deletions during the CREATE process.
This commit is contained in:
parent
d7f900d8ae
commit
7118a723f3
|
|
@ -1505,14 +1505,17 @@ class DataService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 고아 레코드 삭제: 기존 레코드 중 이번에 처리되지 않은 것 삭제
|
// 3. 고아 레코드 삭제: deleteOrphans=true일 때만 (EDIT 모드)
|
||||||
for (const existingRow of existingRecords.rows) {
|
// CREATE 모드에서는 기존 레코드를 건드리지 않음
|
||||||
const existId = existingRow[pkColumn];
|
if (deleteOrphans) {
|
||||||
if (!processedIds.has(existId)) {
|
for (const existingRow of existingRecords.rows) {
|
||||||
const deleteQuery = `DELETE FROM "${tableName}" WHERE "${pkColumn}" = $1`;
|
const existId = existingRow[pkColumn];
|
||||||
await pool.query(deleteQuery, [existId]);
|
if (!processedIds.has(existId)) {
|
||||||
deleted++;
|
const deleteQuery = `DELETE FROM "${tableName}" WHERE "${pkColumn}" = $1`;
|
||||||
console.log(`🗑️ DELETE orphan: ${pkColumn} = ${existId}`);
|
await pool.query(deleteQuery, [existId]);
|
||||||
|
deleted++;
|
||||||
|
console.log(`🗑️ DELETE orphan: ${pkColumn} = ${existId}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -449,6 +449,15 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 프리뷰 모드: 사이드바/헤더 없이 화면만 표시 (인증 대기 없이 즉시 렌더링)
|
||||||
|
if (isPreviewMode) {
|
||||||
|
return (
|
||||||
|
<div className="h-screen w-full overflow-auto bg-white p-4">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// 사용자 정보가 없으면 로딩 표시
|
// 사용자 정보가 없으면 로딩 표시
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -461,15 +470,6 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 프리뷰 모드: 사이드바/헤더 없이 화면만 표시
|
|
||||||
if (isPreviewMode) {
|
|
||||||
return (
|
|
||||||
<div className="h-screen w-full overflow-auto bg-white p-4">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// UI 변환된 메뉴 데이터
|
// UI 변환된 메뉴 데이터
|
||||||
const uiMenus = convertMenuToUI(currentMenus, user as ExtendedUserInfo);
|
const uiMenus = convertMenuToUI(currentMenus, user as ExtendedUserInfo);
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -686,11 +686,15 @@ export const SelectedItemsDetailInputComponent: React.FC<SelectedItemsDetailInpu
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 레코드에 id(기존 DB PK)가 있으면 EDIT 모드 → 고아 삭제
|
||||||
|
// id 없으면 CREATE 모드 → 기존 레코드 건드리지 않음
|
||||||
|
const mappingHasDbIds = mappingRecords.some((r) => !!r.id);
|
||||||
try {
|
try {
|
||||||
const mappingResult = await dataApi.upsertGroupedRecords(
|
const mappingResult = await dataApi.upsertGroupedRecords(
|
||||||
mainTable,
|
mainTable,
|
||||||
itemParentKeys,
|
itemParentKeys,
|
||||||
mappingRecords,
|
mappingRecords,
|
||||||
|
{ deleteOrphans: mappingHasDbIds },
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`❌ ${mainTable} 저장 실패:`, err);
|
console.error(`❌ ${mainTable} 저장 실패:`, err);
|
||||||
|
|
@ -751,11 +755,13 @@ export const SelectedItemsDetailInputComponent: React.FC<SelectedItemsDetailInpu
|
||||||
priceRecords.push(emptyRecord);
|
priceRecords.push(emptyRecord);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const priceHasDbIds = priceRecords.some((r) => !!r.id);
|
||||||
try {
|
try {
|
||||||
const detailResult = await dataApi.upsertGroupedRecords(
|
const detailResult = await dataApi.upsertGroupedRecords(
|
||||||
detailTable,
|
detailTable,
|
||||||
itemParentKeys,
|
itemParentKeys,
|
||||||
priceRecords,
|
priceRecords,
|
||||||
|
{ deleteOrphans: priceHasDbIds },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!detailResult.success) {
|
if (!detailResult.success) {
|
||||||
|
|
@ -767,12 +773,14 @@ export const SelectedItemsDetailInputComponent: React.FC<SelectedItemsDetailInpu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 저장 성공 이벤트
|
// 저장 성공 이벤트 + 테이블 새로고침 (모든 아이템 저장 완료 후)
|
||||||
window.dispatchEvent(
|
window.dispatchEvent(
|
||||||
new CustomEvent("formSaveSuccess", {
|
new CustomEvent("formSaveSuccess", {
|
||||||
detail: { message: "데이터가 저장되었습니다." },
|
detail: { message: "데이터가 저장되었습니다." },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
// 분할 패널 우측 데이터 새로고침
|
||||||
|
window.dispatchEvent(new CustomEvent("refreshTable"));
|
||||||
} else {
|
} else {
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// 단일 테이블 저장 (기존 로직 - detailTable 없는 경우)
|
// 단일 테이블 저장 (기존 로직 - detailTable 없는 경우)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue