feat: Implement entity join functionality in V2Repeater and configuration panel

- Added support for entity joins in the V2Repeater component, allowing for automatic resolution of foreign key references to display data from related tables.
- Introduced a new `resolveEntityJoins` function to handle the fetching and mapping of reference data based on configured entity joins.
- Enhanced the V2RepeaterConfigPanel to manage entity join configurations, including loading available columns and toggling join settings.
- Updated the data handling logic to incorporate mapping rules for incoming data, ensuring that only necessary fields are retained during processing.
- Improved user experience by providing clear logging and feedback during entity join resolution and data mapping operations.
This commit is contained in:
kjs
2026-03-04 21:08:45 +09:00
parent f97edad1ea
commit ac2da7a1d7
10 changed files with 813 additions and 31 deletions

View File

@@ -2339,9 +2339,12 @@ export class ButtonActionExecutor {
}
// 🎯 채번 규칙 할당 처리 (저장 시점에 실제 순번 증가)
// 🔧 수정 모드 체크: formData.id 또는 originalGroupedData가 있으면 UPDATE 모드
// 🔧 수정 모드 체크: _mainRecordId, formData.id 또는 originalGroupedData가 있으면 UPDATE 모드
const parentMainRecordId = modalData._mainRecordId || formData._mainRecordId;
const isEditModeUniversal =
(formData.id !== undefined && formData.id !== null && formData.id !== "") || originalGroupedData.length > 0;
(parentMainRecordId !== undefined && parentMainRecordId !== null && parentMainRecordId !== "") ||
(formData.id !== undefined && formData.id !== null && formData.id !== "") ||
originalGroupedData.length > 0;
const fieldsWithNumbering: Record<string, string> = {};
@@ -2417,6 +2420,13 @@ export class ButtonActionExecutor {
);
if (hasSeparateTargetTable && Object.keys(commonFieldsData).length > 0) {
// _mainRecordId: 분할패널에서 전달한 실제 메인 레코드 ID (formData.id는 디테일 레코드 ID일 수 있음)
const mainRecordIdFromParent =
modalData._mainRecordId || formData._mainRecordId || commonFieldsData._mainRecordId;
// 메인 테이블 ID 결정: _mainRecordId > formData.id 순서로 탐색
const existingMainId = mainRecordIdFromParent || formData.id;
const isMainUpdate = existingMainId !== undefined && existingMainId !== null && existingMainId !== "";
const mainRowToSave = { ...commonFieldsData, ...userInfo };
// 메타데이터 제거
@@ -2426,24 +2436,15 @@ export class ButtonActionExecutor {
}
});
// 🆕 메인 테이블 UPDATE/INSERT 판단
// - formData.id가 있으면 편집 모드 → UPDATE
// - formData.id가 없으면 신규 등록 → INSERT
const existingMainId = formData.id;
const isMainUpdate = existingMainId !== undefined && existingMainId !== null && existingMainId !== "";
let mainSaveResult: { success: boolean; data?: any; message?: string };
if (isMainUpdate) {
// 🔄 편집 모드: UPDATE 실행
mainSaveResult = await DynamicFormApi.updateFormData(existingMainId, {
tableName: tableName!,
data: mainRowToSave,
});
mainRecordId = existingMainId;
} else {
// 신규 등록: INSERT 실행
console.log(" [handleUniversalFormModalTableSectionSave] 메인 테이블 INSERT 실행");
mainSaveResult = await DynamicFormApi.saveFormData({
screenId: screenId!,
@@ -2471,8 +2472,32 @@ export class ButtonActionExecutor {
// 1⃣ 신규 품목 INSERT (id가 없는 항목)
const newItems = currentItems.filter((item) => !item.id);
const existingItemsForRef = currentItems.filter((item) => item.id);
// 기존 DB 항목에서 공통 필드 추출 (수정 모달에서 commonFieldsData에 누락된 필드 보완)
// 예: item_code, item_name 등이 commonFieldsData에 없으면 기존 항목에서 가져옴
const sharedFieldsFromExisting: Record<string, any> = {};
if (existingItemsForRef.length > 0 && newItems.length > 0) {
const refItem = existingItemsForRef[0];
for (const [key, val] of Object.entries(refItem)) {
if (
key !== "id" &&
!key.startsWith("_") &&
val !== undefined &&
val !== null &&
val !== "" &&
commonFieldsData[key] === undefined
) {
sharedFieldsFromExisting[key] = val;
}
}
if (Object.keys(sharedFieldsFromExisting).length > 0) {
console.log("📋 [INSERT] 기존 항목에서 공통 필드 보완:", Object.keys(sharedFieldsFromExisting));
}
}
for (const item of newItems) {
const rowToSave = { ...commonFieldsData, ...item, ...userInfo };
const rowToSave = { ...sharedFieldsFromExisting, ...commonFieldsData, ...item, ...userInfo };
Object.keys(rowToSave).forEach((key) => {
if (key.startsWith("_")) {
@@ -6484,6 +6509,12 @@ export class ButtonActionExecutor {
return true;
} else {
// 기본: 분할 패널 데이터 전달 이벤트
console.log("📤 [transferData] splitPanelDataTransfer 발송:", {
rowCount: selectedRows.length,
mappingRules,
sampleRow: selectedRows[0],
hasItemId: selectedRows[0]?.item_id,
});
const transferEvent = new CustomEvent("splitPanelDataTransfer", {
detail: {