diff --git a/backend-node/src/services/numberingRuleService.ts b/backend-node/src/services/numberingRuleService.ts
index 7ba5c47e..4a9b53a4 100644
--- a/backend-node/src/services/numberingRuleService.ts
+++ b/backend-node/src/services/numberingRuleService.ts
@@ -898,9 +898,10 @@ class NumberingRuleService {
switch (part.partType) {
case "sequence": {
- // 순번 (현재 순번으로 미리보기, 증가 안 함)
+ // 순번 (다음 할당될 순번으로 미리보기, 실제 증가는 allocate 시)
const length = autoConfig.sequenceLength || 3;
- return String(rule.currentSequence || 1).padStart(length, "0");
+ const nextSequence = (rule.currentSequence || 0) + 1;
+ return String(nextSequence).padStart(length, "0");
}
case "number": {
diff --git a/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalConfigPanel.tsx b/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalConfigPanel.tsx
index 6bca0181..0c2edc4e 100644
--- a/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalConfigPanel.tsx
+++ b/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalConfigPanel.tsx
@@ -2517,7 +2517,7 @@ function LayoutRowConfigModal({
{/* 외부 데이터 소스 설정 */}
-
+
{row.tableDataSource?.enabled && (
-
+ <>
-
+ />
-
+
+ {/* 조인 조건 설정 */}
+
+
+
+
+ 두 테이블을 연결하는 키를 설정합니다
+
+
+ {(row.tableDataSource?.joinConditions || []).map((condition, conditionIndex) => (
+
+
+ 조인 {conditionIndex + 1}
+
+
+
+
+
+
+
{
+ const newConditions = [...(row.tableDataSource?.joinConditions || [])];
+ newConditions[conditionIndex] = { ...condition, sourceKey: value };
+ onUpdateRow({
+ tableDataSource: { ...row.tableDataSource!, joinConditions: newConditions }
+ });
+ }}
+ placeholder="예: sales_order_id"
+ />
+
+ 외부 테이블의 컬럼
+
+
+
+
+
+
{
+ const newConditions = [...(row.tableDataSource?.joinConditions || [])];
+ newConditions[conditionIndex] = { ...condition, referenceKey: value };
+ onUpdateRow({
+ tableDataSource: { ...row.tableDataSource!, joinConditions: newConditions }
+ });
+ }}
+ placeholder="예: id"
+ />
+
+ 메인 테이블의 컬럼
+
+
+
+
+
+
+ {row.tableDataSource?.sourceTable}.{condition.sourceKey} = {dataSourceTable}.{condition.referenceKey}
+
+
+ 외부 테이블에서 메인 테이블의 값과 일치하는 데이터를 가져옵니다
+
+
+
+ ))}
+
+
+
+
+ {/* 필터 설정 */}
+
+
+
+
+
+ 특정 조건으로 데이터를 제외합니다
+
+
+
{
+ onUpdateRow({
+ tableDataSource: {
+ ...row.tableDataSource!,
+ filterConfig: {
+ enabled: checked,
+ filterField: "",
+ filterType: "notEquals",
+ referenceField: "",
+ referenceSource: "representativeData",
+ },
+ },
+ });
+ }}
+ className="scale-75"
+ />
+
+
+ {row.tableDataSource?.filterConfig?.enabled && (
+
+
+
+
+
{
+ onUpdateRow({
+ tableDataSource: {
+ ...row.tableDataSource!,
+ filterConfig: {
+ ...row.tableDataSource!.filterConfig!,
+ filterField: value,
+ },
+ },
+ });
+ }}
+ placeholder="예: order_no"
+ />
+
+ 외부 테이블에서 비교할 컬럼
+
+
+
+
+
+
{
+ onUpdateRow({
+ tableDataSource: {
+ ...row.tableDataSource!,
+ filterConfig: {
+ ...row.tableDataSource!.filterConfig!,
+ referenceField: value,
+ },
+ },
+ });
+ }}
+ placeholder="예: order_no"
+ />
+
+ 현재 선택한 행의 컬럼
+
+
+
+
+
+
+
+
+
+
+
+ {row.tableDataSource?.sourceTable}.{row.tableDataSource?.filterConfig?.filterField} != 현재행.{row.tableDataSource?.filterConfig?.referenceField}
+
+
+ {row.tableDataSource?.filterConfig?.filterType === "notEquals"
+ ? "현재 선택한 행과 다른 데이터만 표시합니다"
+ : "현재 선택한 행과 같은 데이터만 표시합니다"}
+
+
+
+ )}
+
+ >
)}
diff --git a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx
index 89838c67..1cb8439a 100644
--- a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx
+++ b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx
@@ -386,10 +386,14 @@ export function UniversalFormModalComponent({
!updatedData[field.columnName]
) {
try {
+ // generateOnOpen: 미리보기만 표시 (실제 순번 할당은 저장 시)
const response = await generateNumberingCode(field.numberingRule.ruleId);
if (response.success && response.data?.generatedCode) {
+ // 임시 플래그 추가하여 저장 시 실제 순번으로 교체할 것을 표시
updatedData[field.columnName] = response.data.generatedCode;
+ updatedData[`_${field.columnName}_needsAllocation`] = true; // 저장 시 재할당 필요
hasChanges = true;
+ console.log(`[채번 미리보기] ${field.columnName} = ${response.data.generatedCode} (저장 시 실제 순번으로 교체)`);
}
} catch (error) {
console.error(`채번규칙 생성 실패 (${field.columnName}):`, error);
@@ -633,18 +637,35 @@ export function UniversalFormModalComponent({
for (const section of config.sections) {
for (const field of section.fields) {
if (field.numberingRule?.enabled && field.numberingRule?.ruleId) {
- // generateOnSave: 저장 시 새로 생성
- // generateOnOpen: 열 때 미리보기로 표시했지만, 저장 시 실제 순번 할당 필요
- if (field.numberingRule.generateOnSave && !dataToSave[field.columnName]) {
- const response = await allocateNumberingCode(field.numberingRule.ruleId);
- if (response.success && response.data?.generatedCode) {
- dataToSave[field.columnName] = response.data.generatedCode;
- }
- } else if (field.numberingRule.generateOnOpen && dataToSave[field.columnName]) {
- // generateOnOpen인 경우, 미리보기 값이 있더라도 실제 순번 할당
+ console.log(`[채번 체크] ${field.columnName}:`, {
+ enabled: field.numberingRule.enabled,
+ ruleId: field.numberingRule.ruleId,
+ generateOnSave: field.numberingRule.generateOnSave,
+ generateOnOpen: field.numberingRule.generateOnOpen,
+ currentValue: dataToSave[field.columnName],
+ needsAllocation: dataToSave[`_${field.columnName}_needsAllocation`]
+ });
+
+ // generateOnSave: 항상 새로 생성
+ // generateOnOpen: 미리보기 값이 있으면 실제 순번으로 교체
+ const shouldAllocate =
+ field.numberingRule.generateOnSave ||
+ (field.numberingRule.generateOnOpen && dataToSave[`_${field.columnName}_needsAllocation`]);
+
+ console.log(`[채번] shouldAllocate = ${shouldAllocate}`);
+
+ if (shouldAllocate) {
+ console.log(`[채번] allocateNumberingCode 호출 시작: ${field.columnName}, ruleId: ${field.numberingRule.ruleId}`);
const response = await allocateNumberingCode(field.numberingRule.ruleId);
if (response.success && response.data?.generatedCode) {
+ const oldValue = dataToSave[field.columnName];
dataToSave[field.columnName] = response.data.generatedCode;
+ console.log(`[채번 성공] ${field.columnName}: ${oldValue} → ${response.data.generatedCode}`);
+
+ // 임시 플래그 제거
+ delete dataToSave[`_${field.columnName}_needsAllocation`];
+ } else {
+ console.error(`[채번 실패] ${field.columnName}:`, response.error);
}
}
}