From 038c5a0973b30bbea93993a6ee8caf1e01765c1a Mon Sep 17 00:00:00 2001 From: SeongHyun Kim Date: Thu, 11 Dec 2025 18:26:33 +0900 Subject: [PATCH] =?UTF-8?q?fix(numbering-rule):=20=EC=B1=84=EB=B2=88=20?= =?UTF-8?q?=EB=AF=B8=EB=A6=AC=EB=B3=B4=EA=B8=B0=20=EC=88=9C=EB=B2=88=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=A0=80=EC=9E=A5=20=EC=8B=9C?= =?UTF-8?q?=20=EC=9E=AC=ED=95=A0=EB=8B=B9=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 미리보기 시 currentSequence + 1로 다음 순번 표시 - UniversalFormModal에서 미리보기/실제할당 분리 - _needsAllocation 플래그로 저장 시 재할당 여부 판단 - RepeatScreenModal 외부 데이터 소스 조인/필터 설정 UI 추가 --- .../src/services/numberingRuleService.ts | 5 +- .../RepeatScreenModalConfigPanel.tsx | 240 ++++++++++++++++-- .../UniversalFormModalComponent.tsx | 39 ++- 3 files changed, 256 insertions(+), 28 deletions(-) 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); } } }