feat: 채번규칙 editable 옵션 수동 모드 감지 기능 구현

모달 오픈 시 채번 미리보기 원본값 저장 (numberingOriginalValues)
handleFieldChange에서 원본값 비교하여 수동/자동 모드 전환
사용자 수정 시 ruleId 제거하여 저장 시 채번 스킵
원본값 복구 시 ruleId 복구하여 자동 모드 복원
handleSave에서 채번 할당 조건 분기 처리
This commit is contained in:
SeongHyun Kim 2026-01-06 13:06:28 +09:00
parent b3ee2b50e8
commit 40fd5f9055
1 changed files with 85 additions and 10 deletions

View File

@ -197,6 +197,10 @@ export function UniversalFormModalComponent({
// 로딩 상태
const [saving, setSaving] = useState(false);
// 채번규칙 원본 값 추적 (수동 모드 감지용)
// key: columnName, value: 자동 생성된 원본 값
const [numberingOriginalValues, setNumberingOriginalValues] = useState<Record<string, string>>({});
// 🆕 수정 모드: 원본 그룹 데이터 (INSERT/UPDATE/DELETE 추적용)
const [originalGroupedData, setOriginalGroupedData] = useState<any[]>([]);
const groupedDataInitializedRef = useRef(false);
@ -457,16 +461,23 @@ export function UniversalFormModalComponent({
// generateOnOpen: 미리보기만 표시 (DB 시퀀스 증가 안 함)
const response = await previewNumberingCode(field.numberingRule.ruleId);
if (response.success && response.data?.generatedCode) {
updatedData[field.columnName] = response.data.generatedCode;
const generatedCode = response.data.generatedCode;
updatedData[field.columnName] = generatedCode;
// 저장 시 실제 할당을 위해 ruleId 저장 (TextInput과 동일한 키 형식)
const ruleIdKey = `${field.columnName}_numberingRuleId`;
updatedData[ruleIdKey] = field.numberingRule.ruleId;
// 원본 채번 값 저장 (수동 모드 감지용)
setNumberingOriginalValues((prev) => ({
...prev,
[field.columnName]: generatedCode,
}));
hasChanges = true;
numberingGeneratedRef.current = true; // 생성 완료 표시
console.log(
`[채번 미리보기 완료] ${field.columnName} = ${response.data.generatedCode} (저장 시 실제 할당)`,
`[채번 미리보기 완료] ${field.columnName} = ${generatedCode} (저장 시 실제 할당)`,
);
console.log(`[채번 규칙 ID 저장] ${ruleIdKey} = ${field.numberingRule.ruleId}`);
@ -694,8 +705,46 @@ export function UniversalFormModalComponent({
// 필드 값 변경 핸들러
const handleFieldChange = useCallback(
(columnName: string, value: any) => {
// 채번규칙 필드의 수동 모드 감지
const originalNumberingValue = numberingOriginalValues[columnName];
const ruleIdKey = `${columnName}_numberingRuleId`;
// 해당 필드의 채번규칙 설정 찾기
let fieldConfig: FormFieldConfig | undefined;
for (const section of config.sections) {
if (section.type === "table" || section.repeatable) continue;
fieldConfig = section.fields?.find((f) => f.columnName === columnName);
if (fieldConfig) break;
// 옵셔널 필드 그룹에서도 찾기
for (const group of section.optionalFieldGroups || []) {
fieldConfig = group.fields?.find((f) => f.columnName === columnName);
if (fieldConfig) break;
}
if (fieldConfig) break;
}
setFormData((prev) => {
const newData = { ...prev, [columnName]: value };
// 채번규칙이 활성화된 필드이고, "사용자 수정 가능"이 ON인 경우
if (
fieldConfig?.numberingRule?.enabled &&
fieldConfig?.numberingRule?.editable &&
originalNumberingValue
) {
// 사용자가 값을 수정했으면 (원본과 다르면) ruleId 제거 → 수동 모드
if (value !== originalNumberingValue) {
delete newData[ruleIdKey];
console.log(`[채번 수동 모드] ${columnName}: 사용자가 값 수정 → ruleId 제거`);
} else {
// 원본 값으로 복구하면 ruleId 복구 → 자동 모드
if (fieldConfig.numberingRule.ruleId) {
newData[ruleIdKey] = fieldConfig.numberingRule.ruleId;
console.log(`[채번 자동 모드] ${columnName}: 원본 값 복구 → ruleId 복구`);
}
}
}
// onChange는 렌더링 외부에서 호출해야 함 (setTimeout 사용)
if (onChange) {
setTimeout(() => onChange(newData), 0);
@ -703,7 +752,7 @@ export function UniversalFormModalComponent({
return newData;
});
},
[onChange],
[onChange, numberingOriginalValues, config.sections],
);
// 반복 섹션 필드 값 변경 핸들러
@ -975,19 +1024,45 @@ export function UniversalFormModalComponent({
}
});
// 저장 시점 채번규칙 처리 (generateOnSave만 처리)
// 저장 시점 채번규칙 처리
for (const section of config.sections) {
// 테이블 타입 섹션은 건너뛰기
if (section.type === "table") continue;
for (const field of (section.fields || [])) {
if (field.numberingRule?.enabled && field.numberingRule?.generateOnSave && field.numberingRule?.ruleId) {
const response = await allocateNumberingCode(field.numberingRule.ruleId);
if (response.success && response.data?.generatedCode) {
dataToSave[field.columnName] = response.data.generatedCode;
console.log(`[채번 할당] ${field.columnName} = ${response.data.generatedCode}`);
if (field.numberingRule?.enabled && field.numberingRule?.ruleId) {
const ruleIdKey = `${field.columnName}_numberingRuleId`;
const hasRuleId = dataToSave[ruleIdKey]; // 사용자가 수정하지 않았으면 ruleId 유지됨
// 채번 규칙 할당 조건
const shouldAllocate =
// 1. generateOnSave가 ON인 경우: 항상 저장 시점에 할당
field.numberingRule.generateOnSave ||
// 2. editable이 OFF인 경우: 사용자 입력 무시하고 채번 규칙으로 덮어씌움
!field.numberingRule.editable ||
// 3. editable이 ON이고 사용자가 수정하지 않은 경우 (ruleId 유지됨): 실제 번호 할당
(field.numberingRule.editable && hasRuleId);
if (shouldAllocate) {
const response = await allocateNumberingCode(field.numberingRule.ruleId);
if (response.success && response.data?.generatedCode) {
dataToSave[field.columnName] = response.data.generatedCode;
let reason = "(알 수 없음)";
if (field.numberingRule.generateOnSave) {
reason = "(generateOnSave)";
} else if (!field.numberingRule.editable) {
reason = "(editable=OFF, 강제 덮어씌움)";
} else if (hasRuleId) {
reason = "(editable=ON, 사용자 미수정)";
}
console.log(`[채번 할당] ${field.columnName} = ${response.data.generatedCode} ${reason}`);
} else {
console.error(`[채번 실패] ${field.columnName}:`, response.error);
}
} else {
console.error(`[채번 실패] ${field.columnName}:`, response.error);
console.log(
`[채번 스킵] ${field.columnName}: 사용자가 직접 입력한 값 유지 = ${dataToSave[field.columnName]}`,
);
}
}
}