diff --git a/backend-node/src/controllers/numberingRuleController.ts b/backend-node/src/controllers/numberingRuleController.ts
index 3764c3bc..2cd27c14 100644
--- a/backend-node/src/controllers/numberingRuleController.ts
+++ b/backend-node/src/controllers/numberingRuleController.ts
@@ -323,6 +323,9 @@ router.post(
formData,
manualInputValue
);
+ // TODO: 디버그용 임시 응답 (나중에 제거)
+ const { getPool } = require("../database/db");
+ const dbPool = getPool();
return res.json({ success: true, data: { generatedCode: previewCode } });
} catch (error: any) {
logger.error("코드 미리보기 실패", { error: error.message });
diff --git a/backend-node/src/services/numberingRuleService.ts b/backend-node/src/services/numberingRuleService.ts
index e682d5e7..1179edbe 100644
--- a/backend-node/src/services/numberingRuleService.ts
+++ b/backend-node/src/services/numberingRuleService.ts
@@ -305,26 +305,26 @@ class NumberingRuleService {
if (hasCompanyCode && companyCode !== "*") {
sql = `
SELECT MAX(
- CAST(SUBSTRING("${columnName}" FROM $1 FOR $2) AS INTEGER)
+ CAST(SUBSTRING("${columnName}" FROM ${seqStart} FOR ${seqLength}) AS INTEGER)
) as max_seq
FROM "${tableName}"
- WHERE "${columnName}" LIKE $3
- AND company_code = $4
- AND LENGTH("${columnName}") = $5
- AND SUBSTRING("${columnName}" FROM $1 FOR $2) ~ '^[0-9]+$'
+ WHERE "${columnName}" LIKE $1
+ AND company_code = $2
+ AND LENGTH("${columnName}") = $3
+ AND SUBSTRING("${columnName}" FROM ${seqStart} FOR ${seqLength}) ~ '^[0-9]+$'
`;
- params = [seqStart, seqLength, likePattern, companyCode, prefixLen + seqLength + codeSuffix.length];
+ params = [likePattern, companyCode, prefixLen + seqLength + codeSuffix.length];
} else {
sql = `
SELECT MAX(
- CAST(SUBSTRING("${columnName}" FROM $1 FOR $2) AS INTEGER)
+ CAST(SUBSTRING("${columnName}" FROM ${seqStart} FOR ${seqLength}) AS INTEGER)
) as max_seq
FROM "${tableName}"
- WHERE "${columnName}" LIKE $3
- AND LENGTH("${columnName}") = $4
- AND SUBSTRING("${columnName}" FROM $1 FOR $2) ~ '^[0-9]+$'
+ WHERE "${columnName}" LIKE $1
+ AND LENGTH("${columnName}") = $2
+ AND SUBSTRING("${columnName}" FROM ${seqStart} FOR ${seqLength}) ~ '^[0-9]+$'
`;
- params = [seqStart, seqLength, likePattern, prefixLen + seqLength + codeSuffix.length];
+ params = [likePattern, prefixLen + seqLength + codeSuffix.length];
}
const result = await client.query(sql, params);
@@ -1436,6 +1436,7 @@ class NumberingRuleService {
psInfo.prefix, psInfo.suffix, psInfo.seqLength, companyCode
);
+
if (maxFromTable > baseSeq) {
logger.info("미리보기: 테이블 내 최대값이 카운터보다 높음", {
ruleId, companyCode, currentSeq, maxFromTable,
diff --git a/frontend/app/(main)/COMPANY_10/master-data/item-info/page.tsx b/frontend/app/(main)/COMPANY_10/master-data/item-info/page.tsx
index 19ef9dd9..ad55d2b2 100644
--- a/frontend/app/(main)/COMPANY_10/master-data/item-info/page.tsx
+++ b/frontend/app/(main)/COMPANY_10/master-data/item-info/page.tsx
@@ -556,8 +556,8 @@ export default function ItemInfoPage() {
if (numberingRuleIdRef.current) {
try {
const hasManual = numberingParts.some(p => p.isManual);
- const userInputCode = hasManual
- ? buildCodeFromParts(numberingParts, manualInputValue)
+ const userInputCode = hasManual && manualInputValue
+ ? manualInputValue
: undefined;
const allocRes = await apiClient.post(
diff --git a/frontend/app/(main)/COMPANY_10/production/process-info/ItemRoutingTab.tsx b/frontend/app/(main)/COMPANY_10/production/process-info/ItemRoutingTab.tsx
index a5b136c2..e50e27d5 100644
--- a/frontend/app/(main)/COMPANY_10/production/process-info/ItemRoutingTab.tsx
+++ b/frontend/app/(main)/COMPANY_10/production/process-info/ItemRoutingTab.tsx
@@ -39,6 +39,7 @@ import {
type RoutingVersion,
} from "@/lib/api/processInfo";
import { cn } from "@/lib/utils";
+import { apiClient } from "@/lib/api/client";
function normalizeDefaultFlag(v: RoutingVersion): boolean {
const raw = v.is_default as unknown;
@@ -210,6 +211,7 @@ export function ItemRoutingTab() {
return;
}
const list = res.data.map((v) => ({ ...v, is_default: normalizeDefaultFlag(v) }));
+ list.sort((a, b) => (a.is_default === b.is_default ? 0 : a.is_default ? -1 : 1));
setVersions(list);
const preferred = preferVersionId ? list.find((v) => v.id === preferVersionId) : undefined;
const def = list.find((v) => v.is_default);
@@ -399,6 +401,38 @@ export function ItemRoutingTab() {
}
};
+ // 선택 버전이 기본인지
+ const selectedVersionIsDefault = useMemo(() => {
+ if (!selectedVersionId) return false;
+ const v = versions.find((v) => v.id === selectedVersionId);
+ return v ? normalizeDefaultFlag(v) : false;
+ }, [selectedVersionId, versions]);
+
+ // 기본 라우팅으로 설정
+ const handleSetDefaultVersion = async () => {
+ if (!selectedVersionId || !selectedItem) return;
+ try {
+ // 기존 기본 해제
+ for (const v of versions) {
+ if (normalizeDefaultFlag(v) && v.id !== selectedVersionId) {
+ await apiClient.put(`/table-management/tables/item_routing_version/edit`, {
+ originalData: { id: v.id },
+ updatedData: { is_default: false },
+ });
+ }
+ }
+ // 선택 버전 기본 설정
+ await apiClient.put(`/table-management/tables/item_routing_version/edit`, {
+ originalData: { id: selectedVersionId },
+ updatedData: { is_default: true },
+ });
+ toast.success("기본 라우팅으로 설정했어요");
+ await loadVersions(selectedItem, selectedVersionId);
+ } catch {
+ toast.error("기본 설정에 실패했어요");
+ }
+ };
+
const submitNewVersion = async () => {
if (!selectedItem) return;
const name = versionName.trim();
@@ -639,6 +673,12 @@ export function ItemRoutingTab() {
+ {selectedVersionId && !selectedVersionIsDefault && (
+
+ )}
+ {selectedVersionId && !selectedVersionIsDefault && (
+
+ )}
+ {selectedVersionId && !selectedVersionIsDefault && (
+
+ )}
+ {selectedVersionId && !selectedVersionIsDefault && (
+
+ )}
+ {selectedVersionId && !selectedVersionIsDefault && (
+
+ )}
+ {selectedVersionId && !selectedVersionIsDefault && (
+
+ )}
+ {selectedVersionId && !selectedVersionIsDefault && (
+
+ )}