feat: Add Numbering Rule APIs and Frontend Integration

- Implemented a new API endpoint to retrieve numbering rules based on table and column names, enhancing the flexibility of numbering rule management.
- Added a new service method to handle the retrieval of numbering columns specific to a company, ensuring proper company code filtering.
- Updated the frontend to load and display numbering columns, allowing users to select and manage numbering rules effectively.
- Refactored existing logic to improve the handling of numbering rules, including fallback mechanisms for legacy data.

These changes enhance the functionality and user experience in managing numbering rules within the application.
This commit is contained in:
kjs
2026-03-09 14:10:08 +09:00
parent f6a02b5182
commit 2b4b7819c5
7 changed files with 350 additions and 503 deletions

View File

@@ -494,7 +494,7 @@ class MasterDetailExcelService {
/**
* 특정 테이블의 특정 컬럼이 채번 타입인지 확인하고, 채번 규칙 ID를 반환
* 회사별 설정을 우선 조회하고, 없으면 공통(*) 설정으로 fallback
* numbering_rules 테이블에서 table_name + column_name + company_code로 직접 조회
*/
private async detectNumberingRuleForColumn(
tableName: string,
@@ -502,32 +502,58 @@ class MasterDetailExcelService {
companyCode?: string
): Promise<{ numberingRuleId: string } | null> {
try {
// 회사별 설정 우선, 공통 설정 fallback (company_code DESC로 회사별이 먼저)
// 1. table_type_columns에서 numbering 타입인지 확인
const companyCondition = companyCode && companyCode !== "*"
? `AND company_code IN ($3, '*')`
: `AND company_code = '*'`;
const params = companyCode && companyCode !== "*"
const ttcParams = companyCode && companyCode !== "*"
? [tableName, columnName, companyCode]
: [tableName, columnName];
const result = await query<any>(
`SELECT input_type, detail_settings, company_code
FROM table_type_columns
const ttcResult = await query<any>(
`SELECT input_type FROM table_type_columns
WHERE table_name = $1 AND column_name = $2 ${companyCondition}
ORDER BY CASE WHEN company_code = '*' THEN 1 ELSE 0 END`,
params
AND input_type = 'numbering' LIMIT 1`,
ttcParams
);
// 채번 타입인 행 찾기 (회사별 우선)
for (const row of result) {
if (row.input_type === "numbering") {
const settings = typeof row.detail_settings === "string"
? JSON.parse(row.detail_settings || "{}")
: row.detail_settings;
if (settings?.numberingRuleId) {
return { numberingRuleId: settings.numberingRuleId };
}
if (ttcResult.length === 0) return null;
// 2. numbering_rules에서 table_name + column_name으로 규칙 조회
const ruleCompanyCondition = companyCode && companyCode !== "*"
? `AND company_code IN ($3, '*')`
: `AND company_code = '*'`;
const ruleParams = companyCode && companyCode !== "*"
? [tableName, columnName, companyCode]
: [tableName, columnName];
const ruleResult = await query<any>(
`SELECT rule_id FROM numbering_rules
WHERE table_name = $1 AND column_name = $2 ${ruleCompanyCondition}
ORDER BY CASE WHEN company_code = '*' THEN 1 ELSE 0 END
LIMIT 1`,
ruleParams
);
if (ruleResult.length > 0) {
return { numberingRuleId: ruleResult[0].rule_id };
}
// 3. fallback: detail_settings.numberingRuleId (하위 호환)
const fallbackResult = await query<any>(
`SELECT detail_settings FROM table_type_columns
WHERE table_name = $1 AND column_name = $2 ${companyCondition}
AND input_type = 'numbering'
ORDER BY CASE WHEN company_code = '*' THEN 1 ELSE 0 END`,
ttcParams
);
for (const row of fallbackResult) {
const settings = typeof row.detail_settings === "string"
? JSON.parse(row.detail_settings || "{}")
: row.detail_settings;
if (settings?.numberingRuleId) {
return { numberingRuleId: settings.numberingRuleId };
}
}
@@ -540,7 +566,7 @@ class MasterDetailExcelService {
/**
* 특정 테이블의 모든 채번 컬럼을 한 번에 조회
* 회사별 설정 우선, 공통(*) 설정 fallback
* numbering_rules 테이블에서 table_name + column_name으로 직접 조회
* @returns Map<columnName, numberingRuleId>
*/
private async detectAllNumberingColumns(
@@ -549,6 +575,7 @@ class MasterDetailExcelService {
): Promise<Map<string, string>> {
const numberingCols = new Map<string, string>();
try {
// 1. table_type_columns에서 numbering 타입 컬럼 목록 조회
const companyCondition = companyCode && companyCode !== "*"
? `AND company_code IN ($2, '*')`
: `AND company_code = '*'`;
@@ -556,22 +583,26 @@ class MasterDetailExcelService {
? [tableName, companyCode]
: [tableName];
const result = await query<any>(
`SELECT column_name, detail_settings, company_code
FROM table_type_columns
WHERE table_name = $1 AND input_type = 'numbering' ${companyCondition}
ORDER BY column_name, CASE WHEN company_code = '*' THEN 1 ELSE 0 END`,
const ttcResult = await query<any>(
`SELECT DISTINCT column_name FROM table_type_columns
WHERE table_name = $1 AND input_type = 'numbering' ${companyCondition}`,
params
);
// 컬럼별로 회사 설정 우선 적용
for (const row of result) {
if (numberingCols.has(row.column_name)) continue; // 이미 회사별 설정이 있으면 스킵
const settings = typeof row.detail_settings === "string"
? JSON.parse(row.detail_settings || "{}")
: row.detail_settings;
if (settings?.numberingRuleId) {
numberingCols.set(row.column_name, settings.numberingRuleId);
// 2. 각 컬럼에 대해 numbering_rules에서 규칙 조회
for (const row of ttcResult) {
const ruleResult = await query<any>(
`SELECT rule_id FROM numbering_rules
WHERE table_name = $1 AND column_name = $2 ${companyCondition}
ORDER BY CASE WHEN company_code = '*' THEN 1 ELSE 0 END
LIMIT 1`,
companyCode && companyCode !== "*"
? [tableName, row.column_name, companyCode]
: [tableName, row.column_name]
);
if (ruleResult.length > 0) {
numberingCols.set(row.column_name, ruleResult[0].rule_id);
}
}