Merge branch 'jskim-node' of http://39.117.244.52:3000/kjs/ERP-node into gbpark-node
This commit is contained in:
parent
4e12f93da4
commit
0512a3214c
|
|
@ -2830,12 +2830,12 @@ export class NodeFlowExecutionService {
|
||||||
inputData: any,
|
inputData: any,
|
||||||
context: ExecutionContext
|
context: ExecutionContext
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const { conditions, logic } = node.data;
|
const { conditions, logic, targetLookup } = node.data;
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
`🔍 조건 노드 실행 - inputData 타입: ${typeof inputData}, 배열 여부: ${Array.isArray(inputData)}, 길이: ${Array.isArray(inputData) ? inputData.length : "N/A"}`
|
`🔍 조건 노드 실행 - inputData 타입: ${typeof inputData}, 배열 여부: ${Array.isArray(inputData)}, 길이: ${Array.isArray(inputData) ? inputData.length : "N/A"}`
|
||||||
);
|
);
|
||||||
logger.info(`🔍 조건 개수: ${conditions?.length || 0}, 로직: ${logic}`);
|
logger.info(`🔍 조건 개수: ${conditions?.length || 0}, 로직: ${logic}, 타겟조회: ${targetLookup ? targetLookup.tableName : "없음"}`);
|
||||||
|
|
||||||
if (inputData) {
|
if (inputData) {
|
||||||
console.log(
|
console.log(
|
||||||
|
|
@ -2865,6 +2865,9 @@ export class NodeFlowExecutionService {
|
||||||
|
|
||||||
// 배열의 각 항목에 대해 조건 평가 (EXISTS 조건은 비동기)
|
// 배열의 각 항목에 대해 조건 평가 (EXISTS 조건은 비동기)
|
||||||
for (const item of inputData) {
|
for (const item of inputData) {
|
||||||
|
// 타겟 테이블 조회 (DB 기존값 비교용)
|
||||||
|
const targetRow = await this.lookupTargetRow(targetLookup, item, context);
|
||||||
|
|
||||||
const results: boolean[] = [];
|
const results: boolean[] = [];
|
||||||
|
|
||||||
for (const condition of conditions) {
|
for (const condition of conditions) {
|
||||||
|
|
@ -2887,9 +2890,14 @@ export class NodeFlowExecutionService {
|
||||||
`🔍 EXISTS 조건: ${condition.field} (${fieldValue}) ${condition.operator} ${condition.lookupTable}.${condition.lookupField} => ${existsResult}`
|
`🔍 EXISTS 조건: ${condition.field} (${fieldValue}) ${condition.operator} ${condition.lookupTable}.${condition.lookupField} => ${existsResult}`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// 일반 연산자 처리
|
// 비교값 결정: static(고정값) / field(같은 데이터 내 필드) / target(DB 기존값)
|
||||||
let compareValue = condition.value;
|
let compareValue = condition.value;
|
||||||
if (condition.valueType === "field") {
|
if (condition.valueType === "target" && targetRow) {
|
||||||
|
compareValue = targetRow[condition.value];
|
||||||
|
logger.info(
|
||||||
|
`🎯 타겟(DB) 비교: ${condition.field} (${fieldValue}) vs DB.${condition.value} (${compareValue})`
|
||||||
|
);
|
||||||
|
} else if (condition.valueType === "field") {
|
||||||
compareValue = item[condition.value];
|
compareValue = item[condition.value];
|
||||||
logger.info(
|
logger.info(
|
||||||
`🔄 필드 참조 비교: ${condition.field} (${fieldValue}) vs ${condition.value} (${compareValue})`
|
`🔄 필드 참조 비교: ${condition.field} (${fieldValue}) vs ${condition.value} (${compareValue})`
|
||||||
|
|
@ -2931,6 +2939,9 @@ export class NodeFlowExecutionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 단일 객체인 경우
|
// 단일 객체인 경우
|
||||||
|
// 타겟 테이블 조회 (DB 기존값 비교용)
|
||||||
|
const targetRow = await this.lookupTargetRow(targetLookup, inputData, context);
|
||||||
|
|
||||||
const results: boolean[] = [];
|
const results: boolean[] = [];
|
||||||
|
|
||||||
for (const condition of conditions) {
|
for (const condition of conditions) {
|
||||||
|
|
@ -2953,9 +2964,14 @@ export class NodeFlowExecutionService {
|
||||||
`🔍 EXISTS 조건: ${condition.field} (${fieldValue}) ${condition.operator} ${condition.lookupTable}.${condition.lookupField} => ${existsResult}`
|
`🔍 EXISTS 조건: ${condition.field} (${fieldValue}) ${condition.operator} ${condition.lookupTable}.${condition.lookupField} => ${existsResult}`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// 일반 연산자 처리
|
// 비교값 결정: static(고정값) / field(같은 데이터 내 필드) / target(DB 기존값)
|
||||||
let compareValue = condition.value;
|
let compareValue = condition.value;
|
||||||
if (condition.valueType === "field") {
|
if (condition.valueType === "target" && targetRow) {
|
||||||
|
compareValue = targetRow[condition.value];
|
||||||
|
logger.info(
|
||||||
|
`🎯 타겟(DB) 비교: ${condition.field} (${fieldValue}) vs DB.${condition.value} (${compareValue})`
|
||||||
|
);
|
||||||
|
} else if (condition.valueType === "field") {
|
||||||
compareValue = inputData[condition.value];
|
compareValue = inputData[condition.value];
|
||||||
logger.info(
|
logger.info(
|
||||||
`🔄 필드 참조 비교: ${condition.field} (${fieldValue}) vs ${condition.value} (${compareValue})`
|
`🔄 필드 참조 비교: ${condition.field} (${fieldValue}) vs ${condition.value} (${compareValue})`
|
||||||
|
|
@ -2990,6 +3006,63 @@ export class NodeFlowExecutionService {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 조건 노드의 타겟 테이블 조회 (DB 기존값 비교용)
|
||||||
|
* targetLookup 설정이 있을 때, 소스 데이터의 키값으로 DB에서 기존 레코드를 조회
|
||||||
|
*/
|
||||||
|
private static async lookupTargetRow(
|
||||||
|
targetLookup: any,
|
||||||
|
sourceRow: any,
|
||||||
|
context: ExecutionContext
|
||||||
|
): Promise<any | null> {
|
||||||
|
if (!targetLookup?.tableName || !targetLookup?.lookupKeys?.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const whereConditions = targetLookup.lookupKeys
|
||||||
|
.map((key: any, idx: number) => `"${key.targetField}" = $${idx + 1}`)
|
||||||
|
.join(" AND ");
|
||||||
|
|
||||||
|
const lookupValues = targetLookup.lookupKeys.map(
|
||||||
|
(key: any) => sourceRow[key.sourceField]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 키값이 비어있으면 조회 불필요
|
||||||
|
if (lookupValues.some((v: any) => v === null || v === undefined || v === "")) {
|
||||||
|
logger.info(`⚠️ 조건 노드 타겟 조회: 키값이 비어있어 스킵`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// company_code 필터링 (멀티테넌시)
|
||||||
|
const companyCode = context.buttonContext?.companyCode || sourceRow.company_code;
|
||||||
|
let sql = `SELECT * FROM "${targetLookup.tableName}" WHERE ${whereConditions}`;
|
||||||
|
const params = [...lookupValues];
|
||||||
|
|
||||||
|
if (companyCode && companyCode !== "*") {
|
||||||
|
sql += ` AND company_code = $${params.length + 1}`;
|
||||||
|
params.push(companyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
sql += " LIMIT 1";
|
||||||
|
|
||||||
|
logger.info(`🎯 조건 노드 타겟 조회: ${targetLookup.tableName}, 조건: ${whereConditions}, 값: ${JSON.stringify(lookupValues)}`);
|
||||||
|
|
||||||
|
const targetRow = await queryOne(sql, params);
|
||||||
|
|
||||||
|
if (targetRow) {
|
||||||
|
logger.info(`🎯 타겟 데이터 조회 성공`);
|
||||||
|
} else {
|
||||||
|
logger.info(`🎯 타겟 데이터 없음 (신규 레코드)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return targetRow;
|
||||||
|
} catch (error: any) {
|
||||||
|
logger.warn(`⚠️ 조건 노드 타겟 조회 실패: ${error.message}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EXISTS_IN / NOT_EXISTS_IN 조건 평가
|
* EXISTS_IN / NOT_EXISTS_IN 조건 평가
|
||||||
* 다른 테이블에 값이 존재하는지 확인
|
* 다른 테이블에 값이 존재하는지 확인
|
||||||
|
|
|
||||||
|
|
@ -251,6 +251,14 @@ export function ConditionProperties({ nodeId, data }: ConditionPropertiesProps)
|
||||||
const [logic, setLogic] = useState<"AND" | "OR">(data.logic || "AND");
|
const [logic, setLogic] = useState<"AND" | "OR">(data.logic || "AND");
|
||||||
const [availableFields, setAvailableFields] = useState<FieldDefinition[]>([]);
|
const [availableFields, setAvailableFields] = useState<FieldDefinition[]>([]);
|
||||||
|
|
||||||
|
// 타겟 조회 설정 (DB 기존값 비교용)
|
||||||
|
const [targetLookup, setTargetLookup] = useState<{
|
||||||
|
tableName: string;
|
||||||
|
tableLabel?: string;
|
||||||
|
lookupKeys: Array<{ sourceField: string; targetField: string; sourceFieldLabel?: string }>;
|
||||||
|
} | undefined>(data.targetLookup);
|
||||||
|
const [targetLookupColumns, setTargetLookupColumns] = useState<ColumnInfo[]>([]);
|
||||||
|
|
||||||
// EXISTS 연산자용 상태
|
// EXISTS 연산자용 상태
|
||||||
const [allTables, setAllTables] = useState<TableInfo[]>([]);
|
const [allTables, setAllTables] = useState<TableInfo[]>([]);
|
||||||
const [tableColumnsCache, setTableColumnsCache] = useState<Record<string, ColumnInfo[]>>({});
|
const [tableColumnsCache, setTableColumnsCache] = useState<Record<string, ColumnInfo[]>>({});
|
||||||
|
|
@ -262,8 +270,20 @@ export function ConditionProperties({ nodeId, data }: ConditionPropertiesProps)
|
||||||
setDisplayName(data.displayName || "조건 분기");
|
setDisplayName(data.displayName || "조건 분기");
|
||||||
setConditions(data.conditions || []);
|
setConditions(data.conditions || []);
|
||||||
setLogic(data.logic || "AND");
|
setLogic(data.logic || "AND");
|
||||||
|
setTargetLookup(data.targetLookup);
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
|
// targetLookup 테이블 변경 시 컬럼 목록 로드
|
||||||
|
useEffect(() => {
|
||||||
|
if (targetLookup?.tableName) {
|
||||||
|
loadTableColumns(targetLookup.tableName).then((cols) => {
|
||||||
|
setTargetLookupColumns(cols);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setTargetLookupColumns([]);
|
||||||
|
}
|
||||||
|
}, [targetLookup?.tableName]);
|
||||||
|
|
||||||
// 전체 테이블 목록 로드 (EXISTS 연산자용)
|
// 전체 테이블 목록 로드 (EXISTS 연산자용)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadAllTables = async () => {
|
const loadAllTables = async () => {
|
||||||
|
|
@ -559,6 +579,47 @@ export function ConditionProperties({ nodeId, data }: ConditionPropertiesProps)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 타겟 조회 테이블 변경
|
||||||
|
const handleTargetLookupTableChange = async (tableName: string) => {
|
||||||
|
await ensureTablesLoaded();
|
||||||
|
const tableInfo = allTables.find((t) => t.tableName === tableName);
|
||||||
|
const newLookup = {
|
||||||
|
tableName,
|
||||||
|
tableLabel: tableInfo?.tableLabel || tableName,
|
||||||
|
lookupKeys: targetLookup?.lookupKeys || [],
|
||||||
|
};
|
||||||
|
setTargetLookup(newLookup);
|
||||||
|
updateNode(nodeId, { targetLookup: newLookup });
|
||||||
|
|
||||||
|
// 컬럼 로드
|
||||||
|
const cols = await loadTableColumns(tableName);
|
||||||
|
setTargetLookupColumns(cols);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 타겟 조회 키 필드 변경
|
||||||
|
const handleTargetLookupKeyChange = (sourceField: string, targetField: string) => {
|
||||||
|
if (!targetLookup) return;
|
||||||
|
const sourceFieldInfo = availableFields.find((f) => f.name === sourceField);
|
||||||
|
const newLookup = {
|
||||||
|
...targetLookup,
|
||||||
|
lookupKeys: [{ sourceField, targetField, sourceFieldLabel: sourceFieldInfo?.label || sourceField }],
|
||||||
|
};
|
||||||
|
setTargetLookup(newLookup);
|
||||||
|
updateNode(nodeId, { targetLookup: newLookup });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 타겟 조회 제거
|
||||||
|
const handleRemoveTargetLookup = () => {
|
||||||
|
setTargetLookup(undefined);
|
||||||
|
updateNode(nodeId, { targetLookup: undefined });
|
||||||
|
// target 타입 조건들을 field로 변경
|
||||||
|
const newConditions = conditions.map((c) =>
|
||||||
|
(c as any).valueType === "target" ? { ...c, valueType: "field" } : c
|
||||||
|
);
|
||||||
|
setConditions(newConditions);
|
||||||
|
updateNode(nodeId, { conditions: newConditions });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="space-y-4 p-4 pb-8">
|
<div className="space-y-4 p-4 pb-8">
|
||||||
|
|
@ -597,6 +658,119 @@ export function ConditionProperties({ nodeId, data }: ConditionPropertiesProps)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 타겟 조회 (DB 기존값 비교) */}
|
||||||
|
<div>
|
||||||
|
<div className="mb-2 flex items-center justify-between">
|
||||||
|
<h3 className="text-sm font-semibold">
|
||||||
|
<Database className="mr-1 inline h-3.5 w-3.5" />
|
||||||
|
타겟 조회 (DB 기존값)
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!targetLookup ? (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="rounded border border-dashed p-3 text-center text-xs text-gray-400">
|
||||||
|
DB의 기존값과 비교하려면 타겟 테이블을 설정하세요.
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
className="h-7 w-full text-xs"
|
||||||
|
onClick={async () => {
|
||||||
|
await ensureTablesLoaded();
|
||||||
|
setTargetLookup({ tableName: "", lookupKeys: [] });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Database className="mr-1 h-3 w-3" />
|
||||||
|
타겟 조회 설정
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-2 rounded border bg-orange-50 p-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-xs font-medium text-orange-700">타겟 테이블</span>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={handleRemoveTargetLookup}
|
||||||
|
className="h-5 px-1 text-xs text-orange-500 hover:text-orange-700"
|
||||||
|
>
|
||||||
|
제거
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 테이블 선택 */}
|
||||||
|
{allTables.length > 0 ? (
|
||||||
|
<TableCombobox
|
||||||
|
tables={allTables}
|
||||||
|
value={targetLookup.tableName}
|
||||||
|
onSelect={handleTargetLookupTableChange}
|
||||||
|
placeholder="비교할 테이블 검색..."
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="rounded border border-dashed bg-gray-50 p-2 text-center text-xs text-gray-400">
|
||||||
|
테이블 로딩 중...
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 키 필드 매핑 */}
|
||||||
|
{targetLookup.tableName && (
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<Label className="text-xs text-orange-600">조회 키 (소스 → 타겟)</Label>
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
|
<Select
|
||||||
|
value={targetLookup.lookupKeys?.[0]?.sourceField || ""}
|
||||||
|
onValueChange={(val) => {
|
||||||
|
const targetField = targetLookup.lookupKeys?.[0]?.targetField || "";
|
||||||
|
handleTargetLookupKeyChange(val, targetField);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="h-7 flex-1 text-xs">
|
||||||
|
<SelectValue placeholder="소스 필드" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{availableFields.map((f) => (
|
||||||
|
<SelectItem key={f.name} value={f.name} className="text-xs">
|
||||||
|
{f.label || f.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<span className="text-xs text-gray-400">=</span>
|
||||||
|
{targetLookupColumns.length > 0 ? (
|
||||||
|
<Select
|
||||||
|
value={targetLookup.lookupKeys?.[0]?.targetField || ""}
|
||||||
|
onValueChange={(val) => {
|
||||||
|
const sourceField = targetLookup.lookupKeys?.[0]?.sourceField || "";
|
||||||
|
handleTargetLookupKeyChange(sourceField, val);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="h-7 flex-1 text-xs">
|
||||||
|
<SelectValue placeholder="타겟 필드" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{targetLookupColumns.map((c) => (
|
||||||
|
<SelectItem key={c.columnName} value={c.columnName} className="text-xs">
|
||||||
|
{c.columnLabel || c.columnName}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
) : (
|
||||||
|
<div className="flex-1 rounded border border-dashed bg-gray-50 p-1 text-center text-[10px] text-gray-400">
|
||||||
|
컬럼 로딩 중...
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="rounded bg-orange-100 p-1.5 text-[10px] text-orange-600">
|
||||||
|
비교 값 타입에서 "타겟 필드 (DB 기존값)"을 선택하면 이 테이블의 기존값과 비교합니다.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 조건식 */}
|
{/* 조건식 */}
|
||||||
<div>
|
<div>
|
||||||
<div className="mb-2 flex items-center justify-between">
|
<div className="mb-2 flex items-center justify-between">
|
||||||
|
|
@ -738,15 +912,46 @@ export function ConditionProperties({ nodeId, data }: ConditionPropertiesProps)
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="static">고정값</SelectItem>
|
<SelectItem value="static">고정값</SelectItem>
|
||||||
<SelectItem value="field">필드 참조</SelectItem>
|
<SelectItem value="field">필드 참조</SelectItem>
|
||||||
|
{targetLookup?.tableName && (
|
||||||
|
<SelectItem value="target">타겟 필드 (DB 기존값)</SelectItem>
|
||||||
|
)}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Label className="text-xs text-gray-600">
|
<Label className="text-xs text-gray-600">
|
||||||
{(condition as any).valueType === "field" ? "비교 필드" : "비교 값"}
|
{(condition as any).valueType === "target"
|
||||||
|
? "타겟 필드 (DB 기존값)"
|
||||||
|
: (condition as any).valueType === "field"
|
||||||
|
? "비교 필드"
|
||||||
|
: "비교 값"}
|
||||||
</Label>
|
</Label>
|
||||||
{(condition as any).valueType === "field" ? (
|
{(condition as any).valueType === "target" ? (
|
||||||
|
// 타겟 필드 (DB 기존값): 타겟 테이블 컬럼에서 선택
|
||||||
|
targetLookupColumns.length > 0 ? (
|
||||||
|
<Select
|
||||||
|
value={condition.value as string}
|
||||||
|
onValueChange={(value) => handleConditionChange(index, "value", value)}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="mt-1 h-8 text-xs">
|
||||||
|
<SelectValue placeholder="DB 필드 선택" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{targetLookupColumns.map((col) => (
|
||||||
|
<SelectItem key={col.columnName} value={col.columnName}>
|
||||||
|
{col.columnLabel || col.columnName}
|
||||||
|
<span className="ml-2 text-xs text-gray-400">({col.dataType})</span>
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
) : (
|
||||||
|
<div className="mt-1 rounded border border-dashed bg-gray-50 p-2 text-center text-xs text-gray-400">
|
||||||
|
타겟 조회를 먼저 설정하세요
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
) : (condition as any).valueType === "field" ? (
|
||||||
// 필드 참조: 드롭다운으로 선택
|
// 필드 참조: 드롭다운으로 선택
|
||||||
availableFields.length > 0 ? (
|
availableFields.length > 0 ? (
|
||||||
<Select
|
<Select
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ export interface ConditionNodeData {
|
||||||
field: string;
|
field: string;
|
||||||
operator: ConditionOperator;
|
operator: ConditionOperator;
|
||||||
value: any;
|
value: any;
|
||||||
valueType?: "static" | "field"; // 비교 값 타입
|
valueType?: "static" | "field" | "target"; // 비교 값 타입 (target: DB 기존값 비교)
|
||||||
// EXISTS_IN / NOT_EXISTS_IN 전용 필드
|
// EXISTS_IN / NOT_EXISTS_IN 전용 필드
|
||||||
lookupTable?: string; // 조회할 테이블명
|
lookupTable?: string; // 조회할 테이블명
|
||||||
lookupTableLabel?: string; // 조회할 테이블 라벨
|
lookupTableLabel?: string; // 조회할 테이블 라벨
|
||||||
|
|
@ -127,6 +127,16 @@ export interface ConditionNodeData {
|
||||||
}>;
|
}>;
|
||||||
logic: "AND" | "OR";
|
logic: "AND" | "OR";
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
|
// 타겟 테이블 조회 (DB 기존값과 비교할 때 사용)
|
||||||
|
targetLookup?: {
|
||||||
|
tableName: string;
|
||||||
|
tableLabel?: string;
|
||||||
|
lookupKeys: Array<{
|
||||||
|
sourceField: string; // 소스(폼) 데이터의 키 필드
|
||||||
|
targetField: string; // 타겟(DB) 테이블의 키 필드
|
||||||
|
sourceFieldLabel?: string;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 필드 매핑 노드
|
// 필드 매핑 노드
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue