집계함수 제어 수정
This commit is contained in:
@@ -506,6 +506,24 @@ export class DynamicFormService {
|
||||
// 헤더 + 품목을 병합
|
||||
const rawMergedData = { ...dataToInsert, ...item };
|
||||
|
||||
// 🆕 새 레코드 저장 시 id 제거하여 새 UUID 생성되도록 함
|
||||
// _existingRecord가 명시적으로 true인 경우에만 기존 레코드로 처리 (UPDATE)
|
||||
// 그 외의 경우는 모두 새 레코드로 처리 (INSERT)
|
||||
const isExistingRecord = rawMergedData._existingRecord === true;
|
||||
|
||||
if (!isExistingRecord) {
|
||||
// 새 레코드: id 제거하여 새 UUID 자동 생성
|
||||
const oldId = rawMergedData.id;
|
||||
delete rawMergedData.id;
|
||||
console.log(`🆕 새 레코드로 처리 (id 제거됨: ${oldId})`);
|
||||
} else {
|
||||
console.log(`📝 기존 레코드 수정 (id 유지: ${rawMergedData.id})`);
|
||||
}
|
||||
|
||||
// 메타 플래그 제거
|
||||
delete rawMergedData._isNewItem;
|
||||
delete rawMergedData._existingRecord;
|
||||
|
||||
// 🆕 실제 테이블 컬럼만 필터링 (조인/계산 컬럼 제외)
|
||||
const validColumnNames = columnInfo.map((col) => col.column_name);
|
||||
const mergedData: Record<string, any> = {};
|
||||
|
||||
@@ -833,11 +833,18 @@ export class NodeFlowExecutionService {
|
||||
|
||||
const sql = `SELECT * FROM ${schemaPrefix}${tableName} ${whereResult.clause}`;
|
||||
|
||||
logger.info(`📊 테이블 전체 데이터 조회 SQL: ${sql}`);
|
||||
|
||||
const result = await query(sql, whereResult.values);
|
||||
|
||||
logger.info(
|
||||
`📊 테이블 전체 데이터 조회: ${tableName}, ${result.length}건`
|
||||
);
|
||||
|
||||
// 디버깅: 조회된 데이터 샘플 출력
|
||||
if (result.length > 0) {
|
||||
logger.info(`📊 조회된 데이터 샘플: ${JSON.stringify(result[0])?.substring(0, 300)}`);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1358,57 +1365,64 @@ export class NodeFlowExecutionService {
|
||||
let updatedCount = 0;
|
||||
const updatedDataArray: any[] = [];
|
||||
|
||||
// 🆕 table-all 모드: 단일 SQL로 일괄 업데이트
|
||||
// 🆕 table-all 모드: 각 그룹별로 UPDATE 실행 (집계 결과 반영)
|
||||
if (context.currentNodeDataSourceType === "table-all") {
|
||||
console.log("🚀 table-all 모드: 단일 SQL로 일괄 업데이트 시작");
|
||||
console.log("🚀 table-all 모드: 그룹별 업데이트 시작 (총 " + dataArray.length + "개 그룹)");
|
||||
|
||||
// 첫 번째 데이터를 참조하여 SET 절 생성
|
||||
const firstData = dataArray[0];
|
||||
const setClauses: string[] = [];
|
||||
const values: any[] = [];
|
||||
let paramIndex = 1;
|
||||
// 🔥 각 그룹(데이터)별로 UPDATE 실행
|
||||
for (let i = 0; i < dataArray.length; i++) {
|
||||
const data = dataArray[i];
|
||||
const setClauses: string[] = [];
|
||||
const values: any[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
console.log("🗺️ 필드 매핑 처리 중...");
|
||||
fieldMappings.forEach((mapping: any) => {
|
||||
const value =
|
||||
mapping.staticValue !== undefined
|
||||
? mapping.staticValue
|
||||
: firstData[mapping.sourceField];
|
||||
console.log(`\n📦 그룹 ${i + 1}/${dataArray.length} 처리 중...`);
|
||||
console.log("🗺️ 필드 매핑 처리 중...");
|
||||
|
||||
fieldMappings.forEach((mapping: any) => {
|
||||
const value =
|
||||
mapping.staticValue !== undefined
|
||||
? mapping.staticValue
|
||||
: data[mapping.sourceField];
|
||||
|
||||
console.log(
|
||||
` ${mapping.sourceField} → ${mapping.targetField}: ${value === undefined ? "❌ undefined" : "✅ " + value}`
|
||||
console.log(
|
||||
` ${mapping.sourceField} → ${mapping.targetField}: ${value === undefined ? "❌ undefined" : "✅ " + value}`
|
||||
);
|
||||
|
||||
if (mapping.targetField) {
|
||||
setClauses.push(`${mapping.targetField} = $${paramIndex}`);
|
||||
values.push(value);
|
||||
paramIndex++;
|
||||
}
|
||||
});
|
||||
|
||||
// WHERE 조건 (사용자 정의 조건만 사용, PK 자동 추가 안 함)
|
||||
const whereResult = this.buildWhereClause(
|
||||
whereConditions,
|
||||
data,
|
||||
paramIndex
|
||||
);
|
||||
|
||||
if (mapping.targetField) {
|
||||
setClauses.push(`${mapping.targetField} = $${paramIndex}`);
|
||||
values.push(value);
|
||||
paramIndex++;
|
||||
}
|
||||
});
|
||||
values.push(...whereResult.values);
|
||||
|
||||
// WHERE 조건 (사용자 정의 조건만 사용, PK 자동 추가 안 함)
|
||||
const whereResult = this.buildWhereClause(
|
||||
whereConditions,
|
||||
firstData,
|
||||
paramIndex
|
||||
);
|
||||
const sql = `
|
||||
UPDATE ${targetTable}
|
||||
SET ${setClauses.join(", ")}
|
||||
${whereResult.clause}
|
||||
`;
|
||||
|
||||
values.push(...whereResult.values);
|
||||
console.log("📝 실행할 SQL:", sql);
|
||||
console.log("📊 바인딩 값:", values);
|
||||
|
||||
const sql = `
|
||||
UPDATE ${targetTable}
|
||||
SET ${setClauses.join(", ")}
|
||||
${whereResult.clause}
|
||||
`;
|
||||
|
||||
console.log("📝 실행할 SQL (일괄 처리):", sql);
|
||||
console.log("📊 바인딩 값:", values);
|
||||
|
||||
const result = await txClient.query(sql, values);
|
||||
updatedCount = result.rowCount || 0;
|
||||
const result = await txClient.query(sql, values);
|
||||
const rowCount = result.rowCount || 0;
|
||||
updatedCount += rowCount;
|
||||
|
||||
console.log(`✅ 그룹 ${i + 1} UPDATE 완료: ${rowCount}건`);
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`✅ UPDATE 완료 (내부 DB, 일괄 처리): ${targetTable}, ${updatedCount}건`
|
||||
`✅ UPDATE 완료 (내부 DB, 그룹별 처리): ${targetTable}, 총 ${updatedCount}건`
|
||||
);
|
||||
|
||||
// 업데이트된 데이터는 원본 배열 반환 (실제 DB에서 다시 조회하지 않음)
|
||||
@@ -3216,10 +3230,12 @@ export class NodeFlowExecutionService {
|
||||
// 입력 데이터가 없으면 빈 배열 반환
|
||||
if (!inputData || !Array.isArray(inputData) || inputData.length === 0) {
|
||||
logger.warn("⚠️ 집계할 입력 데이터가 없습니다.");
|
||||
logger.warn(`⚠️ inputData 타입: ${typeof inputData}, 값: ${JSON.stringify(inputData)?.substring(0, 200)}`);
|
||||
return [];
|
||||
}
|
||||
|
||||
logger.info(`📥 입력 데이터: ${inputData.length}건`);
|
||||
logger.info(`📥 입력 데이터 샘플: ${JSON.stringify(inputData[0])?.substring(0, 300)}`);
|
||||
logger.info(`📊 그룹 기준: ${groupByFields.length > 0 ? groupByFields.map((f: any) => f.field).join(", ") : "전체"}`);
|
||||
logger.info(`📊 집계 연산: ${aggregations.length}개`);
|
||||
|
||||
@@ -3239,6 +3255,11 @@ export class NodeFlowExecutionService {
|
||||
}
|
||||
|
||||
logger.info(`📊 그룹 수: ${groups.size}개`);
|
||||
|
||||
// 디버깅: 각 그룹의 데이터 출력
|
||||
for (const [groupKey, groupRows] of groups) {
|
||||
logger.info(`📊 그룹 [${groupKey}]: ${groupRows.length}건, inbound_qty 합계: ${groupRows.reduce((sum, row) => sum + parseFloat(row.inbound_qty || 0), 0)}`);
|
||||
}
|
||||
|
||||
// 각 그룹에 대해 집계 수행
|
||||
const results: any[] = [];
|
||||
|
||||
@@ -91,6 +91,8 @@ export const RepeaterInput: React.FC<RepeaterInputProps> = ({
|
||||
fields.forEach((field) => {
|
||||
item[field.name] = "";
|
||||
});
|
||||
// 🆕 새 항목임을 표시하는 플래그 추가 (백엔드에서 새 레코드로 처리)
|
||||
item._isNewItem = true;
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -113,6 +115,11 @@ export const RepeaterInput: React.FC<RepeaterInputProps> = ({
|
||||
}
|
||||
});
|
||||
|
||||
// 🆕 기존 레코드임을 표시 (id가 있는 경우)
|
||||
if (updatedItem.id) {
|
||||
updatedItem._existingRecord = true;
|
||||
}
|
||||
|
||||
return hasChange ? updatedItem : item;
|
||||
});
|
||||
|
||||
@@ -125,7 +132,12 @@ export const RepeaterInput: React.FC<RepeaterInputProps> = ({
|
||||
: updatedValue;
|
||||
onChange?.(dataWithMeta);
|
||||
} else {
|
||||
setItems(value);
|
||||
// 🆕 기존 레코드 플래그 추가
|
||||
const valueWithFlag = value.map(item => ({
|
||||
...item,
|
||||
_existingRecord: !!item.id,
|
||||
}));
|
||||
setItems(valueWithFlag);
|
||||
}
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
@@ -135,6 +135,7 @@ const RepeaterFieldGroupComponent: React.FC<ComponentRendererProps> = (props) =>
|
||||
...item,
|
||||
_targetTable: targetTable,
|
||||
_originalItemIds: itemIds, // 🆕 원본 ID 목록도 함께 전달
|
||||
_existingRecord: !!item.id, // 🆕 기존 레코드 플래그 (id가 있으면 기존 레코드)
|
||||
}));
|
||||
onChange(dataWithMeta);
|
||||
}
|
||||
@@ -228,17 +229,23 @@ const RepeaterFieldGroupComponent: React.FC<ComponentRendererProps> = (props) =>
|
||||
// 반복 필드 그룹에 정의된 필드 + 시스템 필드만 유지
|
||||
const definedFields = configRef.current.fields || [];
|
||||
const definedFieldNames = new Set(definedFields.map((f: any) => f.name));
|
||||
// 시스템 필드 및 필수 필드 추가
|
||||
const systemFields = new Set(['id', '_targetTable', 'created_date', 'updated_date', 'writer', 'company_code']);
|
||||
// 시스템 필드 및 필수 필드 추가 (id는 제외 - 새 레코드로 처리하기 위해)
|
||||
const systemFields = new Set(['_targetTable', '_isNewItem', 'created_date', 'updated_date', 'writer', 'company_code']);
|
||||
|
||||
const filteredData = normalizedData.map((item: any) => {
|
||||
const filteredItem: Record<string, any> = {};
|
||||
Object.keys(item).forEach(key => {
|
||||
// 🆕 id 필드는 제외 (새 레코드로 저장되도록)
|
||||
if (key === 'id') {
|
||||
return; // id 필드 제외
|
||||
}
|
||||
// 정의된 필드이거나 시스템 필드인 경우만 포함
|
||||
if (definedFieldNames.has(key) || systemFields.has(key)) {
|
||||
filteredItem[key] = item[key];
|
||||
}
|
||||
});
|
||||
// 🆕 새 항목임을 표시하는 플래그 추가
|
||||
filteredItem._isNewItem = true;
|
||||
return filteredItem;
|
||||
});
|
||||
|
||||
@@ -259,16 +266,16 @@ const RepeaterFieldGroupComponent: React.FC<ComponentRendererProps> = (props) =>
|
||||
newItems = filteredData;
|
||||
addedCount = filteredData.length;
|
||||
} else {
|
||||
// 🆕 중복 체크: id 또는 고유 식별자를 기준으로 이미 존재하는 항목 제외
|
||||
const existingIds = new Set(
|
||||
// 🆕 중복 체크: item_code를 기준으로 이미 존재하는 항목 제외 (id는 사용하지 않음)
|
||||
const existingItemCodes = new Set(
|
||||
currentValue
|
||||
.map((item: any) => item.id || item.po_item_id || item.item_id)
|
||||
.map((item: any) => item.item_code)
|
||||
.filter(Boolean)
|
||||
);
|
||||
|
||||
const uniqueNewItems = filteredData.filter((item: any) => {
|
||||
const itemId = item.id || item.po_item_id || item.item_id;
|
||||
if (itemId && existingIds.has(itemId)) {
|
||||
const itemCode = item.item_code;
|
||||
if (itemCode && existingItemCodes.has(itemCode)) {
|
||||
duplicateCount++;
|
||||
return false; // 중복 항목 제외
|
||||
}
|
||||
@@ -291,11 +298,12 @@ const RepeaterFieldGroupComponent: React.FC<ComponentRendererProps> = (props) =>
|
||||
setGroupedData(newItems);
|
||||
|
||||
// 🆕 SplitPanelContext에 추가된 항목 ID 등록 (좌측 테이블 필터링용)
|
||||
// item_code를 기준으로 등록 (id는 새 레코드라 없을 수 있음)
|
||||
if (splitPanelContext?.addItemIds && addedCount > 0) {
|
||||
const newItemIds = newItems
|
||||
.map((item: any) => String(item.id || item.po_item_id || item.item_id))
|
||||
const newItemCodes = newItems
|
||||
.map((item: any) => String(item.item_code))
|
||||
.filter(Boolean);
|
||||
splitPanelContext.addItemIds(newItemIds);
|
||||
splitPanelContext.addItemIds(newItemCodes);
|
||||
}
|
||||
|
||||
// JSON 문자열로 변환하여 저장
|
||||
|
||||
Reference in New Issue
Block a user