Merge branch 'feature/v2-renewal' of http://39.117.244.52:3000/kjs/ERP-node into feature/v2-unified-renewal

This commit is contained in:
kjs
2026-02-03 09:34:33 +09:00
20 changed files with 534 additions and 479 deletions

View File

@@ -635,7 +635,76 @@ export class ScreenManagementService {
// 트랜잭션으로 화면 삭제와 메뉴 할당 정리를 함께 처리 (Raw Query)
await transaction(async (client) => {
// 소프트 삭제 (휴지통으로 이동)
// 1. 화면에서 사용하는 flowId 수집 (V2 레이아웃)
const layoutResult = await client.query<{ layout_data: any }>(
`SELECT layout_data FROM screen_layouts_v2
WHERE screen_id = $1 AND (company_code = $2 OR company_code = '*')
ORDER BY CASE WHEN company_code = $2 THEN 0 ELSE 1 END
LIMIT 1`,
[screenId, userCompanyCode],
);
const layoutData = layoutResult.rows[0]?.layout_data;
const flowIds = this.collectFlowIdsFromLayoutData(layoutData);
// 2. 각 flowId가 다른 화면에서도 사용되는지 체크 후 삭제
if (flowIds.size > 0) {
for (const flowId of flowIds) {
// 다른 화면에서 사용 중인지 확인 (같은 회사 내, 삭제되지 않은 화면 기준)
const companyFilterForCheck = userCompanyCode === "*" ? "" : " AND sd.company_code = $3";
const checkParams = userCompanyCode === "*"
? [screenId, flowId]
: [screenId, flowId, userCompanyCode];
const otherUsageResult = await client.query<{ count: string }>(
`SELECT COUNT(*) as count FROM screen_layouts_v2 slv
JOIN screen_definitions sd ON slv.screen_id = sd.screen_id
WHERE slv.screen_id != $1
AND sd.is_active != 'D'
${companyFilterForCheck}
AND (
slv.layout_data::text LIKE '%"flowId":' || $2 || '%'
OR slv.layout_data::text LIKE '%"flowId":"' || $2 || '"%'
)`,
checkParams,
);
const otherUsageCount = parseInt(otherUsageResult.rows[0]?.count || "0");
// 다른 화면에서 사용하지 않는 경우에만 플로우 삭제
if (otherUsageCount === 0) {
// 해당 회사의 플로우만 삭제 (멀티테넌시)
const companyFilter = userCompanyCode === "*" ? "" : " AND company_code = $2";
const flowParams = userCompanyCode === "*" ? [flowId] : [flowId, userCompanyCode];
// 1. flow_definition 관련 데이터 먼저 삭제 (외래키 순서)
await client.query(
`DELETE FROM flow_step_connection WHERE flow_definition_id = $1`,
[flowId],
);
await client.query(
`DELETE FROM flow_step WHERE flow_definition_id = $1`,
[flowId],
);
await client.query(
`DELETE FROM flow_definition WHERE id = $1${companyFilter}`,
flowParams,
);
// 2. node_flows 테이블에서도 삭제 (제어플로우)
await client.query(
`DELETE FROM node_flows WHERE flow_id = $1${companyFilter}`,
flowParams,
);
logger.info("화면 삭제 시 플로우 삭제 (flow_definition + node_flows)", { screenId, flowId, companyCode: userCompanyCode });
} else {
logger.debug("플로우가 다른 화면에서 사용 중 - 삭제 스킵", { screenId, flowId, otherUsageCount });
}
}
}
// 3. 소프트 삭제 (휴지통으로 이동)
await client.query(
`UPDATE screen_definitions
SET is_active = 'D',
@@ -655,7 +724,7 @@ export class ScreenManagementService {
],
);
// 메뉴 할당도 비활성화
// 4. 메뉴 할당도 비활성화
await client.query(
`UPDATE screen_menu_assignments
SET is_active = 'N'
@@ -2946,7 +3015,7 @@ export class ScreenManagementService {
* - current_sequence는 0으로 초기화
*/
/**
* 채번 규칙 복제 (numbering_rules_test 테이블 사용)
* 채번 규칙 복제 (numbering_rules 테이블 사용)
* - menu_objid 의존성 제거됨
* - table_name + column_name + company_code 기반
*/
@@ -2964,10 +3033,10 @@ export class ScreenManagementService {
console.log(`🔄 채번 규칙 복사 시작: ${ruleIds.size}개 규칙`);
// 1. 원본 채번 규칙 조회 (numbering_rules_test 테이블)
// 1. 원본 채번 규칙 조회 (numbering_rules 테이블)
const ruleIdArray = Array.from(ruleIds);
const sourceRulesResult = await client.query(
`SELECT * FROM numbering_rules_test WHERE rule_id = ANY($1)`,
`SELECT * FROM numbering_rules WHERE rule_id = ANY($1)`,
[ruleIdArray],
);
@@ -2980,7 +3049,7 @@ export class ScreenManagementService {
// 2. 대상 회사의 기존 채번 규칙 조회 (이름 기준)
const existingRulesResult = await client.query(
`SELECT rule_id, rule_name FROM numbering_rules_test WHERE company_code = $1`,
`SELECT rule_id, rule_name FROM numbering_rules WHERE company_code = $1`,
[targetCompanyCode],
);
const existingRulesByName = new Map<string, string>(
@@ -3001,9 +3070,9 @@ export class ScreenManagementService {
// 새로 복사 - 새 rule_id 생성
const newRuleId = `rule-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
// numbering_rules_test 복사 (current_sequence = 0으로 초기화)
// numbering_rules 복사 (current_sequence = 0으로 초기화)
await client.query(
`INSERT INTO numbering_rules_test (
`INSERT INTO numbering_rules (
rule_id, rule_name, description, separator, reset_period,
current_sequence, table_name, column_name, company_code,
created_at, updated_at, created_by, last_generated_date,
@@ -3028,15 +3097,15 @@ export class ScreenManagementService {
],
);
// numbering_rule_parts_test 복사
// numbering_rule_parts 복사
const partsResult = await client.query(
`SELECT * FROM numbering_rule_parts_test WHERE rule_id = $1 ORDER BY part_order`,
`SELECT * FROM numbering_rule_parts WHERE rule_id = $1 ORDER BY part_order`,
[rule.rule_id],
);
for (const part of partsResult.rows) {
await client.query(
`INSERT INTO numbering_rule_parts_test (
`INSERT INTO numbering_rule_parts (
rule_id, part_order, part_type, generation_method,
auto_config, manual_config, company_code, created_at
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
@@ -4542,7 +4611,8 @@ export class ScreenManagementService {
);
if (menuInfo.rows.length > 0) {
const isAdminMenu = menuInfo.rows[0].menu_type === "1";
// menu_type: "0" = 관리자 메뉴, "1" = 사용자 메뉴
const isAdminMenu = menuInfo.rows[0].menu_type === "0";
const newMenuUrl = isAdminMenu
? `/screens/${newScreenId}?mode=admin`
: `/screens/${newScreenId}`;
@@ -4707,7 +4777,7 @@ export class ScreenManagementService {
}
/**
* 카테고리 값 복제 (category_values_test 테이블 사용)
* 카테고리 값 복제 (category_values 테이블 사용)
* - menu_objid 의존성 제거됨
* - table_name + column_name + company_code 기반
*/
@@ -4741,13 +4811,13 @@ export class ScreenManagementService {
// 1. 기존 대상 회사 데이터 삭제 (다른 회사로 복제 시에만)
await client.query(
`DELETE FROM category_values_test WHERE company_code = $1`,
`DELETE FROM category_values WHERE company_code = $1`,
[targetCompanyCode],
);
// 2. category_values_test 복제
// 2. category_values 복제
const values = await client.query(
`SELECT * FROM category_values_test WHERE company_code = $1`,
`SELECT * FROM category_values WHERE company_code = $1`,
[sourceCompanyCode],
);
@@ -4756,7 +4826,7 @@ export class ScreenManagementService {
for (const v of values.rows) {
const insertResult = await client.query(
`INSERT INTO category_values_test
`INSERT INTO category_values
(table_name, column_name, value_code, value_label, value_order,
parent_value_id, depth, path, description, color, icon,
is_active, is_default, company_code, created_by)
@@ -4791,7 +4861,7 @@ export class ScreenManagementService {
const newValueId = valueIdMap.get(v.value_id);
if (newParentId && newValueId) {
await client.query(
`UPDATE category_values_test SET parent_value_id = $1 WHERE value_id = $2`,
`UPDATE category_values SET parent_value_id = $1 WHERE value_id = $2`,
[newParentId, newValueId],
);
}