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:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 규칙 조회
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user