Merge branch 'main' of http://39.117.244.52:3000/kjs/ERP-node into lhj
; Please enter a commit message to explain why this merge is necessary, ; especially if it merges an updated upstream into a topic branch. ; ; Lines starting with ';' will be ignored, and an empty message aborts ; the commit.
This commit is contained in:
commit
033f5eaf7e
|
|
@ -285,11 +285,14 @@ const RepeaterFieldGroupComponent: React.FC<ComponentRendererProps> = (props) =>
|
|||
|
||||
// onChange 호출하여 부모에게 알림
|
||||
if (onChange && items.length > 0) {
|
||||
// 🆕 RepeaterFieldGroup이 관리하는 필드 목록 추출
|
||||
const repeaterFieldNames = (configRef.current.fields || []).map((f: any) => f.name);
|
||||
const dataWithMeta = items.map((item: any) => ({
|
||||
...item,
|
||||
_targetTable: targetTable,
|
||||
_originalItemIds: itemIds, // 🆕 원본 ID 목록도 함께 전달
|
||||
_existingRecord: !!item.id, // 🆕 기존 레코드 플래그 (id가 있으면 기존 레코드)
|
||||
_repeaterFields: repeaterFieldNames, // 🆕 품목 고유 필드 목록
|
||||
}));
|
||||
onChange(dataWithMeta);
|
||||
}
|
||||
|
|
@ -388,10 +391,13 @@ const RepeaterFieldGroupComponent: React.FC<ComponentRendererProps> = (props) =>
|
|||
// onChange 호출 (effectiveTargetTable 사용)
|
||||
if (onChange) {
|
||||
if (items.length > 0) {
|
||||
// 🆕 RepeaterFieldGroup이 관리하는 필드 목록 추출
|
||||
const repeaterFieldNames = (configRef.current.fields || []).map((f: any) => f.name);
|
||||
const dataWithMeta = items.map((item: any) => ({
|
||||
...item,
|
||||
_targetTable: effectiveTargetTable,
|
||||
_existingRecord: !!item.id,
|
||||
_repeaterFields: repeaterFieldNames, // 🆕 품목 고유 필드 목록
|
||||
}));
|
||||
onChange(dataWithMeta);
|
||||
} else {
|
||||
|
|
@ -673,26 +679,25 @@ const RepeaterFieldGroupComponent: React.FC<ComponentRendererProps> = (props) =>
|
|||
// 🆕 RepeaterInput에서 항목 변경 시 SplitPanelContext의 addedItemIds 동기화
|
||||
const handleRepeaterChange = useCallback(
|
||||
(newValue: any[]) => {
|
||||
// 🆕 분할 패널에서 우측인 경우, 새 항목에 FK 값과 targetTable 추가
|
||||
let valueWithMeta = newValue;
|
||||
// 🆕 RepeaterFieldGroup이 관리하는 필드 목록 추출
|
||||
const repeaterFieldNames = (configRef.current.fields || []).map((f: any) => f.name);
|
||||
|
||||
// 🆕 모든 항목에 메타데이터 추가
|
||||
let valueWithMeta = newValue.map((item: any) => ({
|
||||
...item,
|
||||
_targetTable: effectiveTargetTable || targetTable,
|
||||
_existingRecord: !!item.id,
|
||||
_repeaterFields: repeaterFieldNames, // 🆕 품목 고유 필드 목록
|
||||
}));
|
||||
|
||||
if (isRightPanel && effectiveTargetTable) {
|
||||
valueWithMeta = newValue.map((item: any) => {
|
||||
const itemWithMeta = {
|
||||
...item,
|
||||
_targetTable: effectiveTargetTable,
|
||||
};
|
||||
|
||||
// 🆕 FK 값이 있고 새 항목이면 FK 컬럼에 값 추가
|
||||
if (fkColumn && fkValue && item._isNewItem) {
|
||||
itemWithMeta[fkColumn] = fkValue;
|
||||
console.log("🔗 [RepeaterFieldGroup] 새 항목에 FK 값 추가:", {
|
||||
fkColumn,
|
||||
fkValue,
|
||||
});
|
||||
// 🆕 분할 패널에서 우측인 경우, FK 값 추가
|
||||
if (isRightPanel && fkColumn && fkValue) {
|
||||
valueWithMeta = valueWithMeta.map((item: any) => {
|
||||
if (item._isNewItem) {
|
||||
console.log("🔗 [RepeaterFieldGroup] 새 항목에 FK 값 추가:", { fkColumn, fkValue });
|
||||
return { ...item, [fkColumn]: fkValue };
|
||||
}
|
||||
|
||||
return itemWithMeta;
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -754,6 +759,7 @@ const RepeaterFieldGroupComponent: React.FC<ComponentRendererProps> = (props) =>
|
|||
screenContext?.updateFormData,
|
||||
isRightPanel,
|
||||
effectiveTargetTable,
|
||||
targetTable,
|
||||
fkColumn,
|
||||
fkValue,
|
||||
fieldName,
|
||||
|
|
|
|||
|
|
@ -690,6 +690,151 @@ export class ButtonActionExecutor {
|
|||
console.log("⚠️ [handleSave] formData 전체 내용:", context.formData);
|
||||
}
|
||||
|
||||
// 🆕 RepeaterFieldGroup JSON 문자열 파싱 및 저장 처리
|
||||
// formData에 JSON 배열 문자열이 저장된 경우 처리 (반복_필드_그룹 등)
|
||||
const repeaterJsonKeys = Object.keys(context.formData).filter((key) => {
|
||||
const value = context.formData[key];
|
||||
if (typeof value === "string" && value.startsWith("[") && value.endsWith("]")) {
|
||||
try {
|
||||
const parsed = JSON.parse(value);
|
||||
return Array.isArray(parsed) && parsed.length > 0 && parsed[0]._targetTable;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (repeaterJsonKeys.length > 0) {
|
||||
console.log("🔄 [handleSave] RepeaterFieldGroup JSON 문자열 감지:", repeaterJsonKeys);
|
||||
|
||||
// 🆕 상단 폼 데이터(마스터 정보) 추출
|
||||
// RepeaterFieldGroup JSON과 컴포넌트 키를 제외한 나머지가 마스터 정보
|
||||
const masterFields: Record<string, any> = {};
|
||||
Object.keys(context.formData).forEach((fieldKey) => {
|
||||
// 제외 조건
|
||||
if (fieldKey.startsWith("comp_")) return;
|
||||
if (fieldKey.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)) return;
|
||||
if (fieldKey.endsWith("_label") || fieldKey.endsWith("_value_label")) return;
|
||||
|
||||
const value = context.formData[fieldKey];
|
||||
|
||||
// JSON 배열 문자열 제외 (RepeaterFieldGroup 데이터)
|
||||
if (typeof value === "string" && value.startsWith("[") && value.endsWith("]")) return;
|
||||
|
||||
// 객체 타입인 경우 (범용_폼_모달 등) 내부 필드를 펼쳐서 추가
|
||||
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
||||
Object.entries(value).forEach(([innerKey, innerValue]) => {
|
||||
if (innerKey.endsWith("_label") || innerKey.endsWith("_value_label")) return;
|
||||
if (innerValue !== undefined && innerValue !== null && innerValue !== "") {
|
||||
masterFields[innerKey] = innerValue;
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 유효한 값만 포함
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
masterFields[fieldKey] = value;
|
||||
}
|
||||
});
|
||||
|
||||
console.log("📋 [handleSave] 상단 마스터 정보 (모든 품목에 적용):", masterFields);
|
||||
|
||||
for (const key of repeaterJsonKeys) {
|
||||
try {
|
||||
const parsedData = JSON.parse(context.formData[key]);
|
||||
const repeaterTargetTable = parsedData[0]?._targetTable;
|
||||
|
||||
if (!repeaterTargetTable) {
|
||||
console.warn(`⚠️ [handleSave] RepeaterFieldGroup targetTable 없음 (key: ${key})`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`📦 [handleSave] RepeaterFieldGroup 저장 시작: ${repeaterTargetTable}, ${parsedData.length}건`);
|
||||
|
||||
// 🆕 품목 고유 필드 목록 (RepeaterFieldGroup 설정에서 가져옴)
|
||||
// 첫 번째 아이템의 _repeaterFields에서 추출
|
||||
const repeaterFields: string[] = parsedData[0]?._repeaterFields || [];
|
||||
const itemOnlyFields = new Set([...repeaterFields, 'id']); // id는 항상 포함
|
||||
|
||||
console.log("📋 [handleSave] RepeaterFieldGroup 품목 필드:", repeaterFields);
|
||||
|
||||
for (const item of parsedData) {
|
||||
// 메타 필드 제거
|
||||
const { _targetTable, _isNewItem, _existingRecord, _originalItemIds, _deletedItemIds, _repeaterFields, ...itemData } = item;
|
||||
|
||||
// 🔧 품목 고유 필드만 추출 (RepeaterFieldGroup 설정 기반)
|
||||
const itemOnlyData: Record<string, any> = {};
|
||||
Object.keys(itemData).forEach((field) => {
|
||||
if (itemOnlyFields.has(field)) {
|
||||
itemOnlyData[field] = itemData[field];
|
||||
}
|
||||
});
|
||||
|
||||
// 🔧 마스터 정보 + 품목 고유 정보 병합
|
||||
// masterFields: 상단 폼에서 수정한 최신 마스터 정보
|
||||
// itemOnlyData: 품목 고유 필드만 (품번, 품명, 수량 등)
|
||||
const dataWithMeta: Record<string, unknown> = {
|
||||
...masterFields, // 상단 마스터 정보 (최신)
|
||||
...itemOnlyData, // 품목 고유 필드만
|
||||
created_by: context.userId,
|
||||
updated_by: context.userId,
|
||||
company_code: context.companyCode,
|
||||
};
|
||||
|
||||
// 불필요한 필드 제거
|
||||
Object.keys(dataWithMeta).forEach((field) => {
|
||||
if (field.endsWith("_label") || field.endsWith("_value_label") || field.endsWith("_numberingRuleId")) {
|
||||
delete dataWithMeta[field];
|
||||
}
|
||||
});
|
||||
|
||||
// 새 레코드 vs 기존 레코드 판단
|
||||
const isNewRecord = _isNewItem || !item.id || item.id === "" || item.id === undefined;
|
||||
|
||||
console.log(`📦 [handleSave] 저장할 데이터 (${isNewRecord ? 'INSERT' : 'UPDATE'}):`, {
|
||||
id: item.id,
|
||||
dataWithMeta,
|
||||
});
|
||||
|
||||
if (isNewRecord) {
|
||||
// INSERT - DynamicFormApi 사용하여 제어관리 실행
|
||||
delete dataWithMeta.id;
|
||||
|
||||
const insertResult = await DynamicFormApi.saveFormData({
|
||||
screenId: context.screenId || 0,
|
||||
tableName: repeaterTargetTable,
|
||||
data: dataWithMeta as Record<string, any>,
|
||||
});
|
||||
console.log("✅ [handleSave] RepeaterFieldGroup INSERT 완료:", insertResult.data);
|
||||
} else if (item.id && _existingRecord === true) {
|
||||
// UPDATE - 기존 레코드
|
||||
const originalData = { id: item.id };
|
||||
const updatedData = { ...dataWithMeta, id: item.id };
|
||||
|
||||
const updateResult = await apiClient.put(`/table-management/tables/${repeaterTargetTable}/edit`, {
|
||||
originalData,
|
||||
updatedData,
|
||||
});
|
||||
console.log("✅ [handleSave] RepeaterFieldGroup UPDATE 완료:", updateResult.data);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`❌ [handleSave] RepeaterFieldGroup 저장 실패 (key: ${key}):`, err);
|
||||
}
|
||||
}
|
||||
|
||||
// RepeaterFieldGroup 저장 완료 후 새로고침
|
||||
console.log("✅ [handleSave] RepeaterFieldGroup 저장 완료");
|
||||
context.onRefresh?.();
|
||||
context.onFlowRefresh?.();
|
||||
window.dispatchEvent(new CustomEvent("closeEditModal"));
|
||||
window.dispatchEvent(new CustomEvent("saveSuccessInModal"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 🆕 Universal Form Modal 테이블 섹션 병합 저장 처리
|
||||
// 범용_폼_모달 내부에 _tableSection_ 데이터가 있는 경우 공통 필드 + 개별 품목 병합 저장
|
||||
const universalFormModalResult = await this.handleUniversalFormModalTableSectionSave(config, context, formData);
|
||||
|
|
|
|||
Loading…
Reference in New Issue