feat: 채번 규칙 테이블 기반 자동 필터링 구현

- 채번 규칙 scope_type을 table로 단순화
- 화면의 테이블명을 자동으로 감지하여 채번 규칙 필터링
- TextInputConfigPanel에 screenTableName prop 추가
- getAvailableNumberingRulesForScreen API로 테이블 기반 조회
- NumberingRuleDesigner에서 자동으로 테이블명 설정
- webTypeConfigConverter 유틸리티 추가 (기존 화면 호환성)
- AutoGenerationConfig 타입 개선 (enabled, options.numberingRuleId)
- 채번 규칙 선택 UI에서 ID 제거, 설명 추가
- 불필요한 console.log 제거

Backend:
- numberingRuleService: 테이블 기반 필터링 로직 구현
- numberingRuleController: available-for-screen 엔드포인트 수정

Frontend:
- TextInputConfigPanel: 테이블명 기반 채번 규칙 로드
- NumberingRuleDesigner: 적용 범위 UI 제거, 테이블명 자동 설정
- ScreenDesigner: webTypeConfig → autoGeneration 변환 로직 통합
- DetailSettingsPanel: autoGeneration 속성 매핑 개선
This commit is contained in:
kjs
2025-11-07 14:27:07 +09:00
parent 5b79bfb19d
commit 4294fbf608
23 changed files with 2941 additions and 135 deletions

View File

@@ -39,6 +39,44 @@ router.get("/available/:menuObjid?", authenticateToken, async (req: Authenticate
}
});
// 화면용 채번 규칙 조회 (테이블 기반 필터링 - 간소화)
router.get("/available-for-screen", authenticateToken, async (req: AuthenticatedRequest, res: Response) => {
const companyCode = req.user!.companyCode;
const { tableName } = req.query;
try {
// tableName 필수 검증
if (!tableName || typeof tableName !== "string") {
return res.status(400).json({
success: false,
error: "tableName is required",
});
}
const rules = await numberingRuleService.getAvailableRulesForScreen(
companyCode,
tableName
);
logger.info("화면용 채번 규칙 조회 성공", {
companyCode,
tableName,
count: rules.length,
});
return res.json({ success: true, data: rules });
} catch (error: any) {
logger.error("화면용 채번 규칙 조회 실패", {
error: error.message,
tableName,
});
return res.status(500).json({
success: false,
error: error.message,
});
}
});
// 특정 규칙 조회
router.get("/:ruleId", authenticateToken, async (req: AuthenticatedRequest, res: Response) => {
const companyCode = req.user!.companyCode;

View File

@@ -62,10 +62,10 @@ export async function getColumnList(
try {
const { tableName } = req.params;
const { page = 1, size = 50 } = req.query;
// 🔥 회사 코드 추출 (JWT에서 또는 DB에서 조회)
let companyCode = req.user?.companyCode;
if (!companyCode && req.user?.userId) {
// JWT에 없으면 DB에서 조회
const { query } = require("../database/db");
@@ -74,7 +74,9 @@ export async function getColumnList(
[req.user.userId]
);
companyCode = userResult[0]?.company_code;
logger.info(`DB에서 회사 코드 조회 (컬럼 목록): ${req.user.userId}${companyCode}`);
logger.info(
`DB에서 회사 코드 조회 (컬럼 목록): ${req.user.userId}${companyCode}`
);
}
logger.info(
@@ -139,10 +141,10 @@ export async function updateColumnSettings(
try {
const { tableName, columnName } = req.params;
const settings: ColumnSettings = req.body;
// 🔥 회사 코드 추출 (JWT에서 또는 DB에서 조회)
let companyCode = req.user?.companyCode;
if (!companyCode && req.user?.userId) {
// JWT에 없으면 DB에서 조회
const { query } = require("../database/db");
@@ -154,7 +156,9 @@ export async function updateColumnSettings(
logger.info(`DB에서 회사 코드 조회: ${req.user.userId}${companyCode}`);
}
logger.info(`=== 컬럼 설정 업데이트 시작: ${tableName}.${columnName}, company: ${companyCode} ===`);
logger.info(
`=== 컬럼 설정 업데이트 시작: ${tableName}.${columnName}, company: ${companyCode} ===`
);
if (!tableName || !columnName) {
const response: ApiResponse<null> = {
@@ -194,7 +198,8 @@ export async function updateColumnSettings(
message: "회사 코드를 찾을 수 없습니다.",
error: {
code: "MISSING_COMPANY_CODE",
details: "사용자 정보에서 회사 코드를 찾을 수 없습니다. 관리자에게 문의하세요.",
details:
"사용자 정보에서 회사 코드를 찾을 수 없습니다. 관리자에게 문의하세요.",
},
};
res.status(400).json(response);
@@ -209,7 +214,9 @@ export async function updateColumnSettings(
companyCode // 🔥 회사 코드 전달
);
logger.info(`컬럼 설정 업데이트 완료: ${tableName}.${columnName}, company: ${companyCode}`);
logger.info(
`컬럼 설정 업데이트 완료: ${tableName}.${columnName}, company: ${companyCode}`
);
const response: ApiResponse<null> = {
success: true,
@@ -243,10 +250,10 @@ export async function updateAllColumnSettings(
try {
const { tableName } = req.params;
const columnSettings: ColumnSettings[] = req.body;
// 🔥 회사 코드 추출 (JWT에서 또는 DB에서 조회)
let companyCode = req.user?.companyCode;
if (!companyCode && req.user?.userId) {
// JWT에 없으면 DB에서 조회
const { query } = require("../database/db");
@@ -264,7 +271,9 @@ export async function updateAllColumnSettings(
logger.info(`[DEBUG] req.user?.userId: ${req.user?.userId}`);
logger.info(`[DEBUG] companyCode 최종값: ${companyCode}`);
logger.info(`=== 전체 컬럼 설정 일괄 업데이트 시작: ${tableName}, company: ${companyCode} ===`);
logger.info(
`=== 전체 컬럼 설정 일괄 업데이트 시작: ${tableName}, company: ${companyCode} ===`
);
if (!tableName) {
const response: ApiResponse<null> = {
@@ -305,7 +314,8 @@ export async function updateAllColumnSettings(
message: "회사 코드를 찾을 수 없습니다.",
error: {
code: "MISSING_COMPANY_CODE",
details: "사용자 정보에서 회사 코드를 찾을 수 없습니다. 관리자에게 문의하세요.",
details:
"사용자 정보에서 회사 코드를 찾을 수 없습니다. 관리자에게 문의하세요.",
},
};
res.status(400).json(response);
@@ -543,10 +553,10 @@ export async function updateColumnInputType(
try {
const { tableName, columnName } = req.params;
const { inputType, detailSettings } = req.body;
// 🔥 회사 코드 추출 (JWT에서 또는 DB에서 조회)
let companyCode = req.user?.companyCode;
if (!companyCode && req.user?.userId) {
// JWT에 없으면 DB에서 조회
const { query } = require("../database/db");
@@ -588,7 +598,8 @@ export async function updateColumnInputType(
message: "회사 코드를 찾을 수 없습니다.",
error: {
code: "MISSING_COMPANY_CODE",
details: "사용자 정보에서 회사 코드를 찾을 수 없습니다. 관리자에게 문의하세요.",
details:
"사용자 정보에서 회사 코드를 찾을 수 없습니다. 관리자에게 문의하세요.",
},
};
res.status(400).json(response);
@@ -1085,10 +1096,10 @@ export async function getColumnWebTypes(
): Promise<void> {
try {
const { tableName } = req.params;
// 🔥 회사 코드 추출 (JWT에서 또는 DB에서 조회)
let companyCode = req.user?.companyCode;
if (!companyCode && req.user?.userId) {
// JWT에 없으면 DB에서 조회
const { query } = require("../database/db");
@@ -1097,7 +1108,9 @@ export async function getColumnWebTypes(
[req.user.userId]
);
companyCode = userResult[0]?.company_code;
logger.info(`DB에서 회사 코드 조회 (조회): ${req.user.userId}${companyCode}`);
logger.info(
`DB에서 회사 코드 조회 (조회): ${req.user.userId}${companyCode}`
);
}
logger.info(
@@ -1129,7 +1142,8 @@ export async function getColumnWebTypes(
message: "회사 코드를 찾을 수 없습니다.",
error: {
code: "MISSING_COMPANY_CODE",
details: "사용자 정보에서 회사 코드를 찾을 수 없습니다. 관리자에게 문의하세요.",
details:
"사용자 정보에서 회사 코드를 찾을 수 없습니다. 관리자에게 문의하세요.",
},
};
res.status(400).json(response);

View File

@@ -401,6 +401,117 @@ class NumberingRuleService {
}
}
/**
* 화면용 채번 규칙 조회 (테이블 기반 필터링 - 간소화)
* @param companyCode 회사 코드
* @param tableName 화면의 테이블명
* @returns 해당 테이블의 채번 규칙 목록
*/
async getAvailableRulesForScreen(
companyCode: string,
tableName: string
): Promise<NumberingRuleConfig[]> {
try {
logger.info("화면용 채번 규칙 조회", {
companyCode,
tableName,
});
const pool = getPool();
// 멀티테넌시: 최고 관리자 vs 일반 회사
let query: string;
let params: any[];
if (companyCode === "*") {
// 최고 관리자: 모든 회사의 규칙 조회 가능 (최고 관리자 전용 규칙 제외)
query = `
SELECT
rule_id AS "ruleId",
rule_name AS "ruleName",
description,
separator,
reset_period AS "resetPeriod",
current_sequence AS "currentSequence",
table_name AS "tableName",
column_name AS "columnName",
company_code AS "companyCode",
menu_objid AS "menuObjid",
scope_type AS "scopeType",
created_at AS "createdAt",
updated_at AS "updatedAt",
created_by AS "createdBy"
FROM numbering_rules
WHERE company_code != '*'
AND table_name = $1
ORDER BY created_at DESC
`;
params = [tableName];
logger.info("최고 관리자: 일반 회사 채번 규칙 조회");
} else {
// 일반 회사: 자신의 규칙만 조회
query = `
SELECT
rule_id AS "ruleId",
rule_name AS "ruleName",
description,
separator,
reset_period AS "resetPeriod",
current_sequence AS "currentSequence",
table_name AS "tableName",
column_name AS "columnName",
company_code AS "companyCode",
menu_objid AS "menuObjid",
scope_type AS "scopeType",
created_at AS "createdAt",
updated_at AS "updatedAt",
created_by AS "createdBy"
FROM numbering_rules
WHERE company_code = $1
AND table_name = $2
ORDER BY created_at DESC
`;
params = [companyCode, tableName];
}
const result = await pool.query(query, params);
// 각 규칙의 파트 정보 로드
for (const rule of result.rows) {
const partsQuery = `
SELECT
id,
part_order AS "order",
part_type AS "partType",
generation_method AS "generationMethod",
auto_config AS "autoConfig",
manual_config AS "manualConfig"
FROM numbering_rule_parts
WHERE rule_id = $1
AND company_code = $2
ORDER BY part_order
`;
const partsResult = await pool.query(partsQuery, [
rule.ruleId,
companyCode === "*" ? rule.companyCode : companyCode,
]);
rule.parts = partsResult.rows;
}
logger.info(`화면용 채번 규칙 조회 완료: ${result.rows.length}`, {
companyCode,
tableName,
});
return result.rows;
} catch (error: any) {
logger.error("화면용 채번 규칙 조회 실패", error);
throw error;
}
}
/**
* 특정 규칙 조회
*/