엔티티 조인컬럼 표시문제 수정

This commit is contained in:
kjs 2025-12-11 12:01:00 +09:00
parent 2327dbe35c
commit fc5ffb03b2
2 changed files with 107 additions and 74 deletions

View File

@ -134,8 +134,8 @@ export class EntityJoinService {
`🔧 기존 display_column 사용: ${column.column_name}${displayColumn}`
);
} else {
// display_column이 "none"이거나 없는 경우 참조 테이블의 모든 컬럼 가져오기
logger.info(`🔍 ${referenceTable}모든 컬럼 조회 중...`);
// display_column이 "none"이거나 없는 경우 참조 테이블의 표시용 컬럼 자동 감지
logger.info(`🔍 ${referenceTable}표시 컬럼 자동 감지 중...`);
// 참조 테이블의 모든 컬럼 이름 가져오기
const tableColumnsResult = await query<{ column_name: string }>(
@ -148,10 +148,34 @@ export class EntityJoinService {
);
if (tableColumnsResult.length > 0) {
displayColumns = tableColumnsResult.map((col) => col.column_name);
const allColumns = tableColumnsResult.map((col) => col.column_name);
// 🆕 표시용 컬럼 자동 감지 (우선순위 순서)
// 1. *_name 컬럼 (item_name, customer_name 등)
// 2. name 컬럼
// 3. label 컬럼
// 4. title 컬럼
// 5. 참조 컬럼 (referenceColumn)
const nameColumn = allColumns.find(
(col) => col.endsWith("_name") && col !== "company_name"
);
const simpleNameColumn = allColumns.find((col) => col === "name");
const labelColumn = allColumns.find(
(col) => col === "label" || col.endsWith("_label")
);
const titleColumn = allColumns.find((col) => col === "title");
// 우선순위에 따라 표시 컬럼 선택
const displayColumn =
nameColumn ||
simpleNameColumn ||
labelColumn ||
titleColumn ||
referenceColumn;
displayColumns = [displayColumn];
logger.info(
`${referenceTable}의 모든 컬럼 자동 포함 (${displayColumns.length}개):`,
displayColumns.join(", ")
`${referenceTable}의 표시 컬럼 자동 감지: ${displayColumn} (전체 ${allColumns.length}개 중)`
);
} else {
// 테이블 컬럼을 못 찾으면 기본값 사용

View File

@ -73,6 +73,67 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
return true;
};
// 🆕 엔티티 조인 컬럼명 변환 헬퍼
// "테이블명.컬럼명" 형식을 "원본컬럼_조인컬럼명" 형식으로 변환하여 데이터 접근
const getEntityJoinValue = useCallback(
(item: any, columnName: string, entityColumnMap?: Record<string, string>): any => {
// 직접 매칭 시도
if (item[columnName] !== undefined) {
return item[columnName];
}
// "테이블명.컬럼명" 형식인 경우 (예: item_info.item_name)
if (columnName.includes(".")) {
const [tableName, fieldName] = columnName.split(".");
// 🔍 디버깅: 첫 번째 아이템에서 키 목록 출력
if (item && typeof item === "object") {
const keys = Object.keys(item);
const matchingKeys = keys.filter((k) => k.includes(fieldName));
console.log(`🔍 getEntityJoinValue: columnName=${columnName}, fieldName=${fieldName}`);
console.log(" 전체 키 목록:", keys);
console.log(" 매칭 가능한 키들:", matchingKeys);
}
// entityColumnMap에서 매핑 찾기 (예: item_info → item_code)
if (entityColumnMap && entityColumnMap[tableName]) {
const sourceColumn = entityColumnMap[tableName];
const joinedColumnName = `${sourceColumn}_${fieldName}`;
if (item[joinedColumnName] !== undefined) {
return item[joinedColumnName];
}
}
// 모든 키에서 _fieldName으로 끝나는 것 찾기
for (const key of Object.keys(item)) {
if (key.endsWith(`_${fieldName}`)) {
console.log(` ✅ 매칭됨: ${key}${item[key]}`);
return item[key];
}
}
// 🆕 엔티티 조인 기본 패턴: 테이블명.컬럼명 → 소스컬럼_name
// 예: item_info.item_name → item_code_name
// tableName에서 소스 컬럼 추론 (item_info → item_code, customer_mng → customer_id 등)
const inferredSourceColumn = tableName.replace("_info", "_code").replace("_mng", "_id");
const defaultAliasKey = `${inferredSourceColumn}_name`;
if (item[defaultAliasKey] !== undefined) {
console.log(` ✅ 기본 별칭 매칭: ${defaultAliasKey}${item[defaultAliasKey]}`);
return item[defaultAliasKey];
}
// 테이블명_컬럼명 형식으로 시도
const underscoreKey = `${tableName}_${fieldName}`;
if (item[underscoreKey] !== undefined) {
return item[underscoreKey];
}
}
return undefined;
},
[],
);
// TableOptions Context
const { registerTable, unregisterTable } = useTableOptions();
const [leftFilters, setLeftFilters] = useState<TableFilter[]>([]);
@ -1737,7 +1798,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
>
{formatCellValue(
col.name,
item[col.name],
getEntityJoinValue(item, col.name),
leftCategoryMappings,
col.format,
)}
@ -1796,7 +1857,12 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
className="px-3 py-2 text-sm whitespace-nowrap text-gray-900"
style={{ textAlign: col.align || "left" }}
>
{formatCellValue(col.name, item[col.name], leftCategoryMappings, col.format)}
{formatCellValue(
col.name,
getEntityJoinValue(item, col.name),
leftCategoryMappings,
col.format,
)}
</td>
))}
</tr>
@ -2172,7 +2238,12 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
className="px-3 py-2 text-sm whitespace-nowrap text-gray-900"
style={{ textAlign: col.align || "left" }}
>
{formatCellValue(col.name, item[col.name], rightCategoryMappings, col.format)}
{formatCellValue(
col.name,
getEntityJoinValue(item, col.name),
rightCategoryMappings,
col.format,
)}
</td>
))}
{/* 수정 또는 삭제 버튼이 하나라도 활성화되어 있을 때만 작업 셀 표시 */}
@ -2246,78 +2317,16 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
firstValues = rightColumns
.slice(0, summaryCount)
.map((col) => {
// 🆕 엔티티 조인 컬럼 처리 (예: item_info.item_number → item_number 또는 item_id_name)
let value = item[col.name];
if (value === undefined && col.name.includes(".")) {
const columnName = col.name.split(".").pop();
// 1차: 컬럼명 그대로 (예: item_number)
value = item[columnName || ""];
// 2차: item_info.item_number → item_id_name 또는 item_id_item_number 형식 확인
if (value === undefined) {
const parts = col.name.split(".");
if (parts.length === 2) {
const refTable = parts[0]; // item_info
const refColumn = parts[1]; // item_number 또는 item_name
// FK 컬럼명 추론: item_info → item_id
const fkColumn = refTable.replace("_info", "").replace("_mng", "") + "_id";
// 백엔드에서 반환하는 별칭 패턴:
// 1) item_id_name (기본 referenceColumn)
// 2) item_id_item_name (추가 컬럼)
if (
refColumn === refTable.replace("_info", "").replace("_mng", "") + "_number" ||
refColumn === refTable.replace("_info", "").replace("_mng", "") + "_code"
) {
// 기본 참조 컬럼 (item_number, customer_code 등)
const aliasKey = fkColumn + "_name";
value = item[aliasKey];
} else {
// 추가 컬럼 (item_name, customer_name 등)
const aliasKey = `${fkColumn}_${refColumn}`;
value = item[aliasKey];
}
}
}
}
// 🆕 엔티티 조인 컬럼 처리 (getEntityJoinValue 사용)
const value = getEntityJoinValue(item, col.name);
return [col.name, value, col.label] as [string, any, string];
})
.filter(([_, value]) => value !== null && value !== undefined && value !== "");
allValues = rightColumns
.map((col) => {
// 🆕 엔티티 조인 컬럼 처리
let value = item[col.name];
if (value === undefined && col.name.includes(".")) {
const columnName = col.name.split(".").pop();
// 1차: 컬럼명 그대로
value = item[columnName || ""];
// 2차: {fk_column}_name 또는 {fk_column}_{ref_column} 형식 확인
if (value === undefined) {
const parts = col.name.split(".");
if (parts.length === 2) {
const refTable = parts[0]; // item_info
const refColumn = parts[1]; // item_number 또는 item_name
// FK 컬럼명 추론: item_info → item_id
const fkColumn = refTable.replace("_info", "").replace("_mng", "") + "_id";
// 백엔드에서 반환하는 별칭 패턴:
// 1) item_id_name (기본 referenceColumn)
// 2) item_id_item_name (추가 컬럼)
if (
refColumn === refTable.replace("_info", "").replace("_mng", "") + "_number" ||
refColumn === refTable.replace("_info", "").replace("_mng", "") + "_code"
) {
// 기본 참조 컬럼
const aliasKey = fkColumn + "_name";
value = item[aliasKey];
} else {
// 추가 컬럼
const aliasKey = `${fkColumn}_${refColumn}`;
value = item[aliasKey];
}
}
}
}
// 🆕 엔티티 조인 컬럼 처리 (getEntityJoinValue 사용)
const value = getEntityJoinValue(item, col.name);
return [col.name, value, col.label] as [string, any, string];
})
.filter(([_, value]) => value !== null && value !== undefined && value !== "");