Merge branch 'feature/screen-management' of http://39.117.244.52:3000/kjs/ERP-node into feature/screen-management
This commit is contained in:
@@ -187,6 +187,16 @@ export const deleteCategoryValue = async (req: AuthenticatedRequest, res: Respon
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error(`카테고리 값 삭제 실패: ${error.message}`);
|
||||
|
||||
// 사용 중인 경우 상세 에러 메시지 반환 (400)
|
||||
if (error.message.includes("삭제할 수 없습니다")) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
|
||||
// 기타 에러 (500)
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: error.message || "카테고리 값 삭제 중 오류가 발생했습니다",
|
||||
|
||||
@@ -445,7 +445,129 @@ class TableCategoryValueService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 카테고리 값 삭제 (비활성화)
|
||||
* 카테고리 값 사용 여부 확인
|
||||
* 실제 데이터 테이블에서 해당 카테고리 값이 사용되고 있는지 확인
|
||||
*/
|
||||
async checkCategoryValueUsage(
|
||||
valueId: number,
|
||||
companyCode: string
|
||||
): Promise<{ isUsed: boolean; usedInTables: any[]; totalCount: number }> {
|
||||
const pool = getPool();
|
||||
|
||||
try {
|
||||
logger.info("카테고리 값 사용 여부 확인", { valueId, companyCode });
|
||||
|
||||
// 1. 카테고리 값 정보 조회
|
||||
let valueQuery: string;
|
||||
let valueParams: any[];
|
||||
|
||||
if (companyCode === "*") {
|
||||
valueQuery = `
|
||||
SELECT table_name, column_name, value_code
|
||||
FROM table_column_category_values
|
||||
WHERE value_id = $1
|
||||
`;
|
||||
valueParams = [valueId];
|
||||
} else {
|
||||
valueQuery = `
|
||||
SELECT table_name, column_name, value_code
|
||||
FROM table_column_category_values
|
||||
WHERE value_id = $1
|
||||
AND company_code = $2
|
||||
`;
|
||||
valueParams = [valueId, companyCode];
|
||||
}
|
||||
|
||||
const valueResult = await pool.query(valueQuery, valueParams);
|
||||
|
||||
if (valueResult.rowCount === 0) {
|
||||
throw new Error("카테고리 값을 찾을 수 없습니다");
|
||||
}
|
||||
|
||||
const { table_name, column_name, value_code } = valueResult.rows[0];
|
||||
|
||||
// 2. 실제 데이터 테이블에서 사용 여부 확인
|
||||
// 테이블이 존재하는지 먼저 확인
|
||||
const tableExistsQuery = `
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = $1
|
||||
) as exists
|
||||
`;
|
||||
|
||||
const tableExistsResult = await pool.query(tableExistsQuery, [table_name]);
|
||||
|
||||
if (!tableExistsResult.rows[0].exists) {
|
||||
logger.info("테이블이 존재하지 않음", { table_name });
|
||||
return { isUsed: false, usedInTables: [], totalCount: 0 };
|
||||
}
|
||||
|
||||
// 3. 해당 테이블에서 value_code를 사용하는 데이터 개수 확인
|
||||
let dataCountQuery: string;
|
||||
let dataCountParams: any[];
|
||||
|
||||
if (companyCode === "*") {
|
||||
dataCountQuery = `
|
||||
SELECT COUNT(*) as count
|
||||
FROM ${table_name}
|
||||
WHERE ${column_name} = $1
|
||||
`;
|
||||
dataCountParams = [value_code];
|
||||
} else {
|
||||
dataCountQuery = `
|
||||
SELECT COUNT(*) as count
|
||||
FROM ${table_name}
|
||||
WHERE ${column_name} = $1
|
||||
AND company_code = $2
|
||||
`;
|
||||
dataCountParams = [value_code, companyCode];
|
||||
}
|
||||
|
||||
const dataCountResult = await pool.query(dataCountQuery, dataCountParams);
|
||||
const totalCount = parseInt(dataCountResult.rows[0].count);
|
||||
const isUsed = totalCount > 0;
|
||||
|
||||
// 4. 사용 중인 메뉴 목록 조회 (해당 테이블을 사용하는 화면/메뉴)
|
||||
const menuQuery = `
|
||||
SELECT DISTINCT
|
||||
mi.objid as menu_objid,
|
||||
mi.menu_name_kor as menu_name,
|
||||
mi.menu_url
|
||||
FROM menu_info mi
|
||||
INNER JOIN screen_menu_assignments sma ON sma.menu_objid = mi.objid
|
||||
INNER JOIN screen_definitions sd ON sd.screen_id = sma.screen_id
|
||||
WHERE sd.table_name = $1
|
||||
AND mi.company_code = $2
|
||||
ORDER BY mi.menu_name_kor
|
||||
`;
|
||||
|
||||
const menuResult = await pool.query(menuQuery, [table_name, companyCode]);
|
||||
|
||||
const usedInTables = menuResult.rows.map((row) => ({
|
||||
menuObjid: row.menu_objid,
|
||||
menuName: row.menu_name,
|
||||
menuUrl: row.menu_url,
|
||||
tableName: table_name,
|
||||
columnName: column_name,
|
||||
}));
|
||||
|
||||
logger.info("카테고리 값 사용 여부 확인 완료", {
|
||||
valueId,
|
||||
isUsed,
|
||||
totalCount,
|
||||
usedInMenusCount: usedInTables.length,
|
||||
});
|
||||
|
||||
return { isUsed, usedInTables, totalCount };
|
||||
} catch (error: any) {
|
||||
logger.error(`카테고리 값 사용 여부 확인 실패: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 카테고리 값 삭제 (물리적 삭제)
|
||||
*/
|
||||
async deleteCategoryValue(
|
||||
valueId: number,
|
||||
@@ -455,7 +577,24 @@ class TableCategoryValueService {
|
||||
const pool = getPool();
|
||||
|
||||
try {
|
||||
// 하위 값 체크 (멀티테넌시 적용)
|
||||
// 1. 사용 여부 확인
|
||||
const usage = await this.checkCategoryValueUsage(valueId, companyCode);
|
||||
|
||||
if (usage.isUsed) {
|
||||
let errorMessage = "이 카테고리 값을 삭제할 수 없습니다.\n";
|
||||
errorMessage += `\n현재 ${usage.totalCount}개의 데이터에서 사용 중입니다.`;
|
||||
|
||||
if (usage.usedInTables.length > 0) {
|
||||
const menuNames = usage.usedInTables.map((t) => t.menuName).join(", ");
|
||||
errorMessage += `\n\n다음 메뉴에서 사용 중입니다:\n${menuNames}`;
|
||||
}
|
||||
|
||||
errorMessage += "\n\n메뉴에서 사용하는 카테고리 항목을 수정한 후 다시 삭제해주세요.";
|
||||
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
// 2. 하위 값 체크 (멀티테넌시 적용)
|
||||
let checkQuery: string;
|
||||
let checkParams: any[];
|
||||
|
||||
@@ -465,7 +604,6 @@ class TableCategoryValueService {
|
||||
SELECT COUNT(*) as count
|
||||
FROM table_column_category_values
|
||||
WHERE parent_value_id = $1
|
||||
AND is_active = true
|
||||
`;
|
||||
checkParams = [valueId];
|
||||
} else {
|
||||
@@ -475,7 +613,6 @@ class TableCategoryValueService {
|
||||
FROM table_column_category_values
|
||||
WHERE parent_value_id = $1
|
||||
AND company_code = $2
|
||||
AND is_active = true
|
||||
`;
|
||||
checkParams = [valueId, companyCode];
|
||||
}
|
||||
@@ -486,27 +623,25 @@ class TableCategoryValueService {
|
||||
throw new Error("하위 카테고리 값이 있어 삭제할 수 없습니다");
|
||||
}
|
||||
|
||||
// 비활성화 (멀티테넌시 적용)
|
||||
// 3. 물리적 삭제 (멀티테넌시 적용)
|
||||
let deleteQuery: string;
|
||||
let deleteParams: any[];
|
||||
|
||||
if (companyCode === "*") {
|
||||
// 최고 관리자: 모든 카테고리 값 삭제 가능
|
||||
deleteQuery = `
|
||||
UPDATE table_column_category_values
|
||||
SET is_active = false, updated_at = NOW(), updated_by = $2
|
||||
DELETE FROM table_column_category_values
|
||||
WHERE value_id = $1
|
||||
`;
|
||||
deleteParams = [valueId, userId];
|
||||
deleteParams = [valueId];
|
||||
} else {
|
||||
// 일반 회사: 자신의 카테고리 값만 삭제 가능
|
||||
deleteQuery = `
|
||||
UPDATE table_column_category_values
|
||||
SET is_active = false, updated_at = NOW(), updated_by = $3
|
||||
DELETE FROM table_column_category_values
|
||||
WHERE value_id = $1
|
||||
AND company_code = $2
|
||||
`;
|
||||
deleteParams = [valueId, companyCode, userId];
|
||||
deleteParams = [valueId, companyCode];
|
||||
}
|
||||
|
||||
const result = await pool.query(deleteQuery, deleteParams);
|
||||
@@ -515,7 +650,7 @@ class TableCategoryValueService {
|
||||
throw new Error("카테고리 값을 찾을 수 없거나 권한이 없습니다");
|
||||
}
|
||||
|
||||
logger.info("카테고리 값 삭제(비활성화) 완료", {
|
||||
logger.info("카테고리 값 삭제 완료", {
|
||||
valueId,
|
||||
companyCode,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user