From 958624012d5a7fc5d8555ead0b77e95a9a086e01 Mon Sep 17 00:00:00 2001 From: kjs Date: Fri, 19 Dec 2025 16:01:57 +0900 Subject: [PATCH] =?UTF-8?q?=EB=B3=B5=EC=82=AC=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend-node/src/services/menuCopyService.ts | 704 +++++++++++++------ 1 file changed, 499 insertions(+), 205 deletions(-) diff --git a/backend-node/src/services/menuCopyService.ts b/backend-node/src/services/menuCopyService.ts index 5c4fde7f..b66ba165 100644 --- a/backend-node/src/services/menuCopyService.ts +++ b/backend-node/src/services/menuCopyService.ts @@ -247,7 +247,9 @@ export class MenuCopyService { typeof screenId === "number" ? screenId : parseInt(screenId); if (!isNaN(numId)) { referenced.push(numId); - logger.debug(` ๐Ÿ“‘ ํƒญ ์ปดํฌ๋„ŒํŠธ์—์„œ ํ™”๋ฉด ์ฐธ์กฐ ๋ฐœ๊ฒฌ: ${numId} (ํƒญ: ${tab.label || tab.id})`); + logger.debug( + ` ๐Ÿ“‘ ํƒญ ์ปดํฌ๋„ŒํŠธ์—์„œ ํ™”๋ฉด ์ฐธ์กฐ ๋ฐœ๊ฒฌ: ${numId} (ํƒญ: ${tab.label || tab.id})` + ); } } } @@ -257,7 +259,9 @@ export class MenuCopyService { if (props?.componentConfig?.leftScreenId) { const leftScreenId = props.componentConfig.leftScreenId; const numId = - typeof leftScreenId === "number" ? leftScreenId : parseInt(leftScreenId); + typeof leftScreenId === "number" + ? leftScreenId + : parseInt(leftScreenId); if (!isNaN(numId) && numId > 0) { referenced.push(numId); logger.debug(` ๐Ÿ“ ๋ถ„ํ•  ํŒจ๋„ ์ขŒ์ธก ํ™”๋ฉด ์ฐธ์กฐ ๋ฐœ๊ฒฌ: ${numId}`); @@ -267,7 +271,9 @@ export class MenuCopyService { if (props?.componentConfig?.rightScreenId) { const rightScreenId = props.componentConfig.rightScreenId; const numId = - typeof rightScreenId === "number" ? rightScreenId : parseInt(rightScreenId); + typeof rightScreenId === "number" + ? rightScreenId + : parseInt(rightScreenId); if (!isNaN(numId) && numId > 0) { referenced.push(numId); logger.debug(` ๐Ÿ“ ๋ถ„ํ•  ํŒจ๋„ ์šฐ์ธก ํ™”๋ฉด ์ฐธ์กฐ ๋ฐœ๊ฒฌ: ${numId}`); @@ -359,7 +365,11 @@ export class MenuCopyService { logger.info(`๐Ÿ”„ ํ”Œ๋กœ์šฐ ์ˆ˜์ง‘ ์‹œ์ž‘: ${screenIds.size}๊ฐœ ํ™”๋ฉด`); const flowIds = new Set(); - const flowDetails: Array<{ flowId: number; flowName: string; screenId: number }> = []; + const flowDetails: Array<{ + flowId: number; + flowName: string; + screenId: number; + }> = []; for (const screenId of screenIds) { const layoutsResult = await client.query( @@ -372,23 +382,38 @@ export class MenuCopyService { // webTypeConfig.dataflowConfig.flowConfig.flowId const flowId = props?.webTypeConfig?.dataflowConfig?.flowConfig?.flowId; - const flowName = props?.webTypeConfig?.dataflowConfig?.flowConfig?.flowName || "Unknown"; - + const flowName = + props?.webTypeConfig?.dataflowConfig?.flowConfig?.flowName || + "Unknown"; + if (flowId && typeof flowId === "number" && flowId > 0) { if (!flowIds.has(flowId)) { flowIds.add(flowId); flowDetails.push({ flowId, flowName, screenId }); - logger.info(` ๐Ÿ“Ž ํ™”๋ฉด ${screenId}์—์„œ ํ”Œ๋กœ์šฐ ๋ฐœ๊ฒฌ: id=${flowId}, name="${flowName}"`); + logger.info( + ` ๐Ÿ“Ž ํ™”๋ฉด ${screenId}์—์„œ ํ”Œ๋กœ์šฐ ๋ฐœ๊ฒฌ: id=${flowId}, name="${flowName}"` + ); } } // selectedDiagramId๋„ ํ™•์ธ (flowId์™€ ๋™์ผํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋‹ค๋ฅผ ์ˆ˜๋„ ์žˆ์Œ) - const selectedDiagramId = props?.webTypeConfig?.dataflowConfig?.selectedDiagramId; - if (selectedDiagramId && typeof selectedDiagramId === "number" && selectedDiagramId > 0) { + const selectedDiagramId = + props?.webTypeConfig?.dataflowConfig?.selectedDiagramId; + if ( + selectedDiagramId && + typeof selectedDiagramId === "number" && + selectedDiagramId > 0 + ) { if (!flowIds.has(selectedDiagramId)) { flowIds.add(selectedDiagramId); - flowDetails.push({ flowId: selectedDiagramId, flowName: "SelectedDiagram", screenId }); - logger.info(` ๐Ÿ“Ž ํ™”๋ฉด ${screenId}์—์„œ selectedDiagramId ๋ฐœ๊ฒฌ: id=${selectedDiagramId}`); + flowDetails.push({ + flowId: selectedDiagramId, + flowName: "SelectedDiagram", + screenId, + }); + logger.info( + ` ๐Ÿ“Ž ํ™”๋ฉด ${screenId}์—์„œ selectedDiagramId ๋ฐœ๊ฒฌ: id=${selectedDiagramId}` + ); } } } @@ -400,7 +425,7 @@ export class MenuCopyService { } else { logger.info(`๐Ÿ“ญ ์ˆ˜์ง‘๋œ ํ”Œ๋กœ์šฐ ์—†์Œ (ํ™”๋ฉด์— ํ”Œ๋กœ์šฐ ์ฐธ์กฐ๊ฐ€ ์—†์Œ)`); } - + return flowIds; } @@ -462,7 +487,13 @@ export class MenuCopyService { const updated = JSON.parse(JSON.stringify(properties)); // ์žฌ๊ท€์ ์œผ๋กœ ๊ฐ์ฒด/๋ฐฐ์—ด ํƒ์ƒ‰ - this.recursiveUpdateReferences(updated, screenIdMap, flowIdMap, "", numberingRuleIdMap); + this.recursiveUpdateReferences( + updated, + screenIdMap, + flowIdMap, + "", + numberingRuleIdMap + ); return updated; } @@ -539,7 +570,12 @@ export class MenuCopyService { } // numberingRuleId ๋งคํ•‘ (๋ฌธ์ž์—ด) - if (key === "numberingRuleId" && numberingRuleIdMap && typeof value === "string" && value) { + if ( + key === "numberingRuleId" && + numberingRuleIdMap && + typeof value === "string" && + value + ) { const newRuleId = numberingRuleIdMap.get(value); if (newRuleId) { obj[key] = newRuleId; @@ -590,11 +626,15 @@ export class MenuCopyService { } const sourceMenu = sourceMenuResult.rows[0]; - const isRootMenu = !sourceMenu.parent_obj_id || sourceMenu.parent_obj_id === 0; + const isRootMenu = + !sourceMenu.parent_obj_id || sourceMenu.parent_obj_id === 0; // 2. ๋Œ€์ƒ ํšŒ์‚ฌ์— ๊ฐ™์€ ์›๋ณธ์—์„œ ๋ณต์‚ฌ๋œ ๋ฉ”๋‰ด ์ฐพ๊ธฐ (source_menu_objid๋กœ ์ •ํ™•ํžˆ ๋งค์นญ) // ์ตœ์ƒ์œ„/ํ•˜์œ„ ๊ตฌ๋ถ„ ์—†์ด ๋ชจ๋“  ๋ณต์‚ฌ๋ณธ ๊ฒ€์ƒ‰ - const existingMenuResult = await client.query<{ objid: number; parent_obj_id: number | null }>( + const existingMenuResult = await client.query<{ + objid: number; + parent_obj_id: number | null; + }>( `SELECT objid, parent_obj_id FROM menu_info WHERE source_menu_objid = $1 @@ -608,8 +648,9 @@ export class MenuCopyService { } const existingMenuObjid = existingMenuResult.rows[0].objid; - const existingIsRoot = !existingMenuResult.rows[0].parent_obj_id || - existingMenuResult.rows[0].parent_obj_id === 0; + const existingIsRoot = + !existingMenuResult.rows[0].parent_obj_id || + existingMenuResult.rows[0].parent_obj_id === 0; logger.info( `๐Ÿ” ๊ธฐ์กด ๋ณต์‚ฌ๋ณธ ๋ฐœ๊ฒฌ: ${sourceMenu.menu_name_kor} (์›๋ณธ: ${sourceMenuObjid}, ๋ณต์‚ฌ๋ณธ: ${existingMenuObjid}, ์ตœ์ƒ์œ„: ${existingIsRoot})` @@ -649,10 +690,14 @@ export class MenuCopyService { WHERE screen_id = ANY($1) AND company_code = $2`, [screenIds, targetCompanyCode] ); - const sharedScreenIds = new Set(sharedScreensResult.rows.map(r => r.screen_id)); + const sharedScreenIds = new Set( + sharedScreensResult.rows.map((r) => r.screen_id) + ); // ๊ณต์œ ๋˜์ง€ ์•Š์€ ํ™”๋ฉด๋งŒ ์‚ญ์ œ - const screensToDelete = screenIds.filter(id => !sharedScreenIds.has(id)); + const screensToDelete = screenIds.filter( + (id) => !sharedScreenIds.has(id) + ); if (screensToDelete.length > 0) { // ๋ ˆ์ด์•„์›ƒ ์‚ญ์ œ @@ -662,8 +707,8 @@ export class MenuCopyService { ); // ํ™”๋ฉด ์ •์˜ ์‚ญ์ œ - await client.query( - `DELETE FROM screen_definitions + await client.query( + `DELETE FROM screen_definitions WHERE screen_id = ANY($1) AND company_code = $2`, [screensToDelete, targetCompanyCode] ); @@ -671,7 +716,9 @@ export class MenuCopyService { } if (sharedScreenIds.size > 0) { - logger.info(` โ™ป๏ธ ๊ณต์œ  ํ™”๋ฉด ์œ ์ง€: ${sharedScreenIds.size}๊ฐœ (๋‹ค๋ฅธ ๋ฉ”๋‰ด์—์„œ ์‚ฌ์šฉ ์ค‘)`); + logger.info( + ` โ™ป๏ธ ๊ณต์œ  ํ™”๋ฉด ์œ ์ง€: ${sharedScreenIds.size}๊ฐœ (๋‹ค๋ฅธ ๋ฉ”๋‰ด์—์„œ ์‚ฌ์šฉ ์ค‘)` + ); } } @@ -681,13 +728,65 @@ export class MenuCopyService { ]); logger.info(` โœ… ๋ฉ”๋‰ด ๊ถŒํ•œ ์‚ญ์ œ ์™„๋ฃŒ`); - // 5-4. ๋ฉ”๋‰ด ์‚ญ์ œ (์—ญ์ˆœ: ํ•˜์œ„ ๋ฉ”๋‰ด๋ถ€ํ„ฐ) - // ์ฃผ์˜: ์ฑ„๋ฒˆ ๊ทœ์น™๊ณผ ์นดํ…Œ๊ณ ๋ฆฌ ์„ค์ •์€ ํšŒ์‚ฌ๋งˆ๋‹ค ๊ณ ์œ ํ•˜๋ฏ€๋กœ ์‚ญ์ œํ•˜์ง€ ์•Š์Œ - for (let i = existingMenus.length - 1; i >= 0; i--) { - await client.query(`DELETE FROM menu_info WHERE objid = $1`, [ - existingMenus[i].objid, - ]); + // 5-4. ์ฑ„๋ฒˆ ๊ทœ์น™ ์ฒ˜๋ฆฌ (์™ธ๋ž˜ํ‚ค ์ œ์•ฝ์กฐ๊ฑด ํ•ด๊ฒฐ) + // scope_type = 'menu'์ธ ์ฑ„๋ฒˆ ๊ทœ์น™: ๋ฉ”๋‰ด ์ „์šฉ์ด๋ฏ€๋กœ ์‚ญ์ œ (ํŒŒํŠธ ํฌํ•จ) + // check_menu_scope_requires_menu_objid ์ œ์•ฝ: scope_type='menu'์ด๋ฉด menu_objid NOT NULL ํ•„์ˆ˜ + const menuScopedRulesResult = await client.query( + `SELECT rule_id FROM numbering_rules + WHERE menu_objid = ANY($1) AND company_code = $2 AND scope_type = 'menu'`, + [existingMenuIds, targetCompanyCode] + ); + if (menuScopedRulesResult.rows.length > 0) { + const menuScopedRuleIds = menuScopedRulesResult.rows.map( + (r) => r.rule_id + ); + // ์ฑ„๋ฒˆ ๊ทœ์น™ ํŒŒํŠธ ๋จผ์ € ์‚ญ์ œ + await client.query( + `DELETE FROM numbering_rule_parts WHERE rule_id = ANY($1)`, + [menuScopedRuleIds] + ); + // ์ฑ„๋ฒˆ ๊ทœ์น™ ์‚ญ์ œ + await client.query( + `DELETE FROM numbering_rules WHERE rule_id = ANY($1)`, + [menuScopedRuleIds] + ); + logger.info( + ` โœ… ๋ฉ”๋‰ด ์ „์šฉ ์ฑ„๋ฒˆ ๊ทœ์น™ ์‚ญ์ œ: ${menuScopedRuleIds.length}๊ฐœ` + ); } + + // scope_type != 'menu'์ธ ์ฑ„๋ฒˆ ๊ทœ์น™: menu_objid๋งŒ NULL๋กœ ์„ค์ • (๊ทœ์น™ ๋ณด์กด) + const tableScopedRulesResult = await client.query( + `UPDATE numbering_rules + SET menu_objid = NULL + WHERE menu_objid = ANY($1) AND company_code = $2 + AND (scope_type IS NULL OR scope_type != 'menu') + RETURNING rule_id`, + [existingMenuIds, targetCompanyCode] + ); + if (tableScopedRulesResult.rows.length > 0) { + logger.info( + ` โœ… ํ…Œ์ด๋ธ” ์Šค์ฝ”ํ”„ ์ฑ„๋ฒˆ ๊ทœ์น™ ์—ฐ๊ฒฐ ํ•ด์ œ: ${tableScopedRulesResult.rows.length}๊ฐœ (๋ฐ์ดํ„ฐ ๋ณด์กด)` + ); + } + + // 5-5. ์นดํ…Œ๊ณ ๋ฆฌ ์ปฌ๋Ÿผ ๋งคํ•‘ ์‚ญ์ œ (NOT NULL ์ œ์•ฝ์กฐ๊ฑด์œผ๋กœ ์‚ญ์ œ ํ•„์š”) + const deletedCategoryMappings = await client.query( + `DELETE FROM category_column_mapping + WHERE menu_objid = ANY($1) AND company_code = $2 + RETURNING mapping_id`, + [existingMenuIds, targetCompanyCode] + ); + if (deletedCategoryMappings.rows.length > 0) { + logger.info( + ` โœ… ์นดํ…Œ๊ณ ๋ฆฌ ๋งคํ•‘ ์‚ญ์ œ: ${deletedCategoryMappings.rows.length}๊ฐœ` + ); + } + + // 5-6. ๋ฉ”๋‰ด ์‚ญ์ œ (๋ฐฐ์น˜) + await client.query(`DELETE FROM menu_info WHERE objid = ANY($1)`, [ + existingMenuIds, + ]); logger.info(` โœ… ๋ฉ”๋‰ด ์‚ญ์ œ ์™„๋ฃŒ: ${existingMenus.length}๊ฐœ`); logger.info("โœ… ๊ธฐ์กด ๋ณต์‚ฌ๋ณธ ์‚ญ์ œ ์™„๋ฃŒ - ๋ฎ์–ด์“ฐ๊ธฐ ์ค€๋น„๋จ"); @@ -794,10 +893,10 @@ export class MenuCopyService { const ruleResult = await this.copyNumberingRulesWithMap( menuObjids, menuIdMap, // ์‹ค์ œ ์ƒ์„ฑ๋œ ๋ฉ”๋‰ด ID ์‚ฌ์šฉ - targetCompanyCode, - userId, - client - ); + targetCompanyCode, + userId, + client + ); copiedNumberingRules = ruleResult.copiedCount; numberingRuleIdMap = ruleResult.ruleIdMap; } @@ -948,17 +1047,21 @@ export class MenuCopyService { `SELECT * FROM flow_definition WHERE id = ANY($1)`, [flowIdArray] ); - const flowDefMap = new Map(allFlowDefsResult.rows.map(f => [f.id, f])); + const flowDefMap = new Map(allFlowDefsResult.rows.map((f) => [f.id, f])); // 2) ๋Œ€์ƒ ํšŒ์‚ฌ์˜ ๊ธฐ์กด ํ”Œ๋กœ์šฐ ํ•œ ๋ฒˆ์— ์กฐํšŒ (์ด๋ฆ„+ํ…Œ์ด๋ธ” ๊ธฐ์ค€) - const flowNames = allFlowDefsResult.rows.map(f => f.name); - const existingFlowsResult = await client.query<{ id: number; name: string; table_name: string }>( + const flowNames = allFlowDefsResult.rows.map((f) => f.name); + const existingFlowsResult = await client.query<{ + id: number; + name: string; + table_name: string; + }>( `SELECT id, name, table_name FROM flow_definition WHERE company_code = $1 AND name = ANY($2)`, [targetCompanyCode, flowNames] ); const existingFlowMap = new Map( - existingFlowsResult.rows.map(f => [`${f.name}|${f.table_name}`, f.id]) + existingFlowsResult.rows.map((f) => [`${f.name}|${f.table_name}`, f.id]) ); // 3) ๋ณต์‚ฌ๊ฐ€ ํ•„์š”ํ•œ ํ”Œ๋กœ์šฐ ID ๋ชฉ๋ก @@ -967,16 +1070,18 @@ export class MenuCopyService { for (const originalFlowId of flowIdArray) { const flowDef = flowDefMap.get(originalFlowId); if (!flowDef) { - logger.warn(`โš ๏ธ ํ”Œ๋กœ์šฐ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ: id=${originalFlowId}`); - continue; - } + logger.warn(`โš ๏ธ ํ”Œ๋กœ์šฐ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ: id=${originalFlowId}`); + continue; + } const key = `${flowDef.name}|${flowDef.table_name}`; const existingId = existingFlowMap.get(key); if (existingId) { flowIdMap.set(originalFlowId, existingId); - logger.info(` โ™ป๏ธ ๊ธฐ์กด ํ”Œ๋กœ์šฐ ์žฌ์‚ฌ์šฉ: ${originalFlowId} โ†’ ${existingId} (${flowDef.name})`); + logger.info( + ` โ™ป๏ธ ๊ธฐ์กด ํ”Œ๋กœ์šฐ ์žฌ์‚ฌ์šฉ: ${originalFlowId} โ†’ ${existingId} (${flowDef.name})` + ); } else { flowsToCopy.push(flowDef); } @@ -985,17 +1090,26 @@ export class MenuCopyService { // 4) ์ƒˆ ํ”Œ๋กœ์šฐ ๋ณต์‚ฌ (๋ฐฐ์น˜ ์ฒ˜๋ฆฌ) if (flowsToCopy.length > 0) { // ๋ฐฐ์น˜ INSERT๋กœ ํ”Œ๋กœ์šฐ ์ƒ์„ฑ - const flowValues = flowsToCopy.map((f, i) => - `($${i * 8 + 1}, $${i * 8 + 2}, $${i * 8 + 3}, $${i * 8 + 4}, $${i * 8 + 5}, $${i * 8 + 6}, $${i * 8 + 7}, $${i * 8 + 8})` - ).join(", "); - - const flowParams = flowsToCopy.flatMap(f => [ - f.name, f.description, f.table_name, f.is_active, - targetCompanyCode, userId, f.db_source_type, f.db_connection_id + const flowValues = flowsToCopy + .map( + (f, i) => + `($${i * 8 + 1}, $${i * 8 + 2}, $${i * 8 + 3}, $${i * 8 + 4}, $${i * 8 + 5}, $${i * 8 + 6}, $${i * 8 + 7}, $${i * 8 + 8})` + ) + .join(", "); + + const flowParams = flowsToCopy.flatMap((f) => [ + f.name, + f.description, + f.table_name, + f.is_active, + targetCompanyCode, + userId, + f.db_source_type, + f.db_connection_id, ]); const newFlowsResult = await client.query<{ id: number }>( - `INSERT INTO flow_definition ( + `INSERT INTO flow_definition ( name, description, table_name, is_active, company_code, created_by, db_source_type, db_connection_id ) VALUES ${flowValues} @@ -1007,11 +1121,13 @@ export class MenuCopyService { flowsToCopy.forEach((flowDef, index) => { const newFlowId = newFlowsResult.rows[index].id; flowIdMap.set(flowDef.id, newFlowId); - logger.info(` โœ… ํ”Œ๋กœ์šฐ ์‹ ๊ทœ ๋ณต์‚ฌ: ${flowDef.id} โ†’ ${newFlowId} (${flowDef.name})`); + logger.info( + ` โœ… ํ”Œ๋กœ์šฐ ์‹ ๊ทœ ๋ณต์‚ฌ: ${flowDef.id} โ†’ ${newFlowId} (${flowDef.name})` + ); }); // 5) ์Šคํ… ๋ฐ ์—ฐ๊ฒฐ ๋ณต์‚ฌ (๋ณต์‚ฌ๋œ ํ”Œ๋กœ์šฐ๋งŒ) - const originalFlowIdsToCopy = flowsToCopy.map(f => f.id); + const originalFlowIdsToCopy = flowsToCopy.map((f) => f.id); // ๋ชจ๋“  ์Šคํ… ํ•œ ๋ฒˆ์— ์กฐํšŒ const allStepsResult = await client.query( @@ -1030,7 +1146,7 @@ export class MenuCopyService { // ์Šคํ… ๋ณต์‚ฌ (ํ”Œ๋กœ์šฐ๋ณ„) const allStepIdMaps = new Map>(); // originalFlowId -> stepIdMap - + for (const originalFlowId of originalFlowIdsToCopy) { const newFlowId = flowIdMap.get(originalFlowId)!; const steps = stepsByFlow.get(originalFlowId) || []; @@ -1038,15 +1154,31 @@ export class MenuCopyService { if (steps.length > 0) { // ๋ฐฐ์น˜ INSERT๋กœ ์Šคํ… ์ƒ์„ฑ - const stepValues = steps.map((_, i) => - `($${i * 17 + 1}, $${i * 17 + 2}, $${i * 17 + 3}, $${i * 17 + 4}, $${i * 17 + 5}, $${i * 17 + 6}, $${i * 17 + 7}, $${i * 17 + 8}, $${i * 17 + 9}, $${i * 17 + 10}, $${i * 17 + 11}, $${i * 17 + 12}, $${i * 17 + 13}, $${i * 17 + 14}, $${i * 17 + 15}, $${i * 17 + 16}, $${i * 17 + 17})` - ).join(", "); + const stepValues = steps + .map( + (_, i) => + `($${i * 17 + 1}, $${i * 17 + 2}, $${i * 17 + 3}, $${i * 17 + 4}, $${i * 17 + 5}, $${i * 17 + 6}, $${i * 17 + 7}, $${i * 17 + 8}, $${i * 17 + 9}, $${i * 17 + 10}, $${i * 17 + 11}, $${i * 17 + 12}, $${i * 17 + 13}, $${i * 17 + 14}, $${i * 17 + 15}, $${i * 17 + 16}, $${i * 17 + 17})` + ) + .join(", "); - const stepParams = steps.flatMap(s => [ - newFlowId, s.step_name, s.step_order, s.condition_json, - s.color, s.position_x, s.position_y, s.table_name, s.move_type, - s.status_column, s.status_value, s.target_table, s.field_mappings, - s.required_fields, s.integration_type, s.integration_config, s.display_config + const stepParams = steps.flatMap((s) => [ + newFlowId, + s.step_name, + s.step_order, + s.condition_json, + s.color, + s.position_x, + s.position_y, + s.table_name, + s.move_type, + s.status_column, + s.status_value, + s.target_table, + s.field_mappings, + s.required_fields, + s.integration_type, + s.integration_config, + s.display_config, ]); const newStepsResult = await client.query<{ id: number }>( @@ -1064,7 +1196,9 @@ export class MenuCopyService { stepIdMap.set(step.id, newStepsResult.rows[index].id); }); - logger.info(` โ†ณ ํ”Œ๋กœ์šฐ ${originalFlowId}: ์Šคํ… ${steps.length}๊ฐœ ๋ณต์‚ฌ`); + logger.info( + ` โ†ณ ํ”Œ๋กœ์šฐ ${originalFlowId}: ์Šคํ… ${steps.length}๊ฐœ ๋ณต์‚ฌ` + ); } allStepIdMaps.set(originalFlowId, stepIdMap); @@ -1077,14 +1211,19 @@ export class MenuCopyService { ); // ์—ฐ๊ฒฐ ๋ณต์‚ฌ (๋ฐฐ์น˜ INSERT) - const connectionsToInsert: { newFlowId: number; newFromStepId: number; newToStepId: number; label: string }[] = []; + const connectionsToInsert: { + newFlowId: number; + newFromStepId: number; + newToStepId: number; + label: string; + }[] = []; for (const conn of allConnectionsResult.rows) { const stepIdMap = allStepIdMaps.get(conn.flow_definition_id); if (!stepIdMap) continue; - const newFromStepId = stepIdMap.get(conn.from_step_id); - const newToStepId = stepIdMap.get(conn.to_step_id); + const newFromStepId = stepIdMap.get(conn.from_step_id); + const newToStepId = stepIdMap.get(conn.to_step_id); const newFlowId = flowIdMap.get(conn.flow_definition_id); if (newFromStepId && newToStepId && newFlowId) { @@ -1092,26 +1231,32 @@ export class MenuCopyService { newFlowId, newFromStepId, newToStepId, - label: conn.label || "" + label: conn.label || "", }); } } if (connectionsToInsert.length > 0) { - const connValues = connectionsToInsert.map((_, i) => - `($${i * 4 + 1}, $${i * 4 + 2}, $${i * 4 + 3}, $${i * 4 + 4})` - ).join(", "); + const connValues = connectionsToInsert + .map( + (_, i) => + `($${i * 4 + 1}, $${i * 4 + 2}, $${i * 4 + 3}, $${i * 4 + 4})` + ) + .join(", "); - const connParams = connectionsToInsert.flatMap(c => [ - c.newFlowId, c.newFromStepId, c.newToStepId, c.label + const connParams = connectionsToInsert.flatMap((c) => [ + c.newFlowId, + c.newFromStepId, + c.newToStepId, + c.label, ]); - await client.query( - `INSERT INTO flow_step_connection ( + await client.query( + `INSERT INTO flow_step_connection ( flow_definition_id, from_step_id, to_step_id, label ) VALUES ${connValues}`, connParams - ); + ); logger.info(` โ†ณ ์—ฐ๊ฒฐ ${connectionsToInsert.length}๊ฐœ ๋ณต์‚ฌ`); } @@ -1347,10 +1492,7 @@ export class MenuCopyService { }); } } catch (error: any) { - logger.error( - `โŒ ํ™”๋ฉด ์ฒ˜๋ฆฌ ์‹คํŒจ: screen_id=${originalScreenId}`, - error - ); + logger.error(`โŒ ํ™”๋ฉด ์ฒ˜๋ฆฌ ์‹คํŒจ: screen_id=${originalScreenId}`, error); throw error; } } @@ -1588,7 +1730,7 @@ export class MenuCopyService { const parentMenu = parentMenuResult.rows[0]; // ๋Œ€์ƒ ํšŒ์‚ฌ์—์„œ ๊ฐ™์€ ์ด๋ฆ„ + ๊ฐ™์€ ์›๋ณธ ํšŒ์‚ฌ์—์„œ ๋ณต์‚ฌ๋œ ๋ฉ”๋‰ด ์ฐพ๊ธฐ - // source_menu_objid๊ฐ€ ์žˆ๋Š” ๋ฉ”๋‰ด(๋ณต์‚ฌ๋œ ๋ฉ”๋‰ด)๋งŒ ๋Œ€์ƒ์œผ๋กœ, + // source_menu_objid๊ฐ€ ์žˆ๋Š” ๋ฉ”๋‰ด(๋ณต์‚ฌ๋œ ๋ฉ”๋‰ด)๋งŒ ๋Œ€์ƒ์œผ๋กœ, // ํ•ด๋‹น source_menu_objid์˜ ์›๋ณธ ๋ฉ”๋‰ด๊ฐ€ ๊ฐ™์€ ํšŒ์‚ฌ(sourceCompanyCode)์— ์†ํ•˜๋Š”์ง€ ํ™•์ธ const sameNameResult = await client.query<{ objid: number }>( `SELECT m.objid FROM menu_info m @@ -1643,7 +1785,10 @@ export class MenuCopyService { try { // 0. ์ด๋ฏธ ๋ณต์‚ฌ๋œ ๋ฉ”๋‰ด๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ (๊ณ ์•„ ๋ฉ”๋‰ด ์žฌ์—ฐ๊ฒฐ์šฉ) // 1์ฐจ: source_menu_objid๋กœ ๊ฒ€์ƒ‰ - let existingCopyResult = await client.query<{ objid: number; parent_obj_id: number | null }>( + let existingCopyResult = await client.query<{ + objid: number; + parent_obj_id: number | null; + }>( `SELECT objid, parent_obj_id FROM menu_info WHERE source_menu_objid = $1 AND company_code = $2 LIMIT 1`, @@ -1652,7 +1797,10 @@ export class MenuCopyService { // 2์ฐจ: source_menu_objid๊ฐ€ ์—†๋Š” ๊ธฐ์กด ๋ณต์‚ฌ๋ณธ (์ด๋ฆ„ + ๋ฉ”๋‰ดํƒ€์ž…์œผ๋กœ ๊ฒ€์ƒ‰) - ํ˜ธํ™˜์„ฑ ์œ ์ง€ if (existingCopyResult.rows.length === 0 && menu.menu_name_kor) { - existingCopyResult = await client.query<{ objid: number; parent_obj_id: number | null }>( + existingCopyResult = await client.query<{ + objid: number; + parent_obj_id: number | null; + }>( `SELECT objid, parent_obj_id FROM menu_info WHERE menu_name_kor = $1 AND company_code = $2 @@ -1733,7 +1881,9 @@ export class MenuCopyService { // === ์‹ ๊ทœ ๋ฉ”๋‰ด ๋ณต์‚ฌ === // ๋ฏธ๋ฆฌ ํ• ๋‹น๋œ ID๊ฐ€ ์žˆ์œผ๋ฉด ์‚ฌ์šฉ, ์—†์œผ๋ฉด ์ƒˆ๋กœ ์ƒ์„ฑ - const newObjId = preAllocatedMenuIdMap?.get(menu.objid) ?? await this.getNextMenuObjid(client); + const newObjId = + preAllocatedMenuIdMap?.get(menu.objid) ?? + (await this.getNextMenuObjid(client)); // source_menu_objid ์ €์žฅ: ๋ชจ๋“  ๋ณต์‚ฌ๋œ ๋ฉ”๋‰ด์— ์›๋ณธ ID ์ €์žฅ (์ถ”์ ์šฉ) const sourceMenuObjid = menu.objid; @@ -1807,15 +1957,15 @@ export class MenuCopyService { // === ์ตœ์ ํ™”: ๋ฐฐ์น˜ ์กฐํšŒ === // 1. ๋ชจ๋“  ์›๋ณธ ๋ฉ”๋‰ด์˜ ํ™”๋ฉด ํ• ๋‹น ํ•œ ๋ฒˆ์— ์กฐํšŒ - const menuObjids = menus.map(m => m.objid); - const companyCodes = [...new Set(menus.map(m => m.company_code))]; + const menuObjids = menus.map((m) => m.objid); + const companyCodes = [...new Set(menus.map((m) => m.company_code))]; const allAssignmentsResult = await client.query<{ menu_objid: number; - screen_id: number; - display_order: number; - is_active: string; - }>( + screen_id: number; + display_order: number; + is_active: string; + }>( `SELECT menu_objid, screen_id, display_order, is_active FROM screen_menu_assignments WHERE menu_objid = ANY($1) AND company_code = ANY($2)`, @@ -1837,36 +1987,45 @@ export class MenuCopyService { for (const assignment of allAssignmentsResult.rows) { const newMenuObjid = menuIdMap.get(assignment.menu_objid); - const newScreenId = screenIdMap.get(assignment.screen_id); + const newScreenId = screenIdMap.get(assignment.screen_id); if (!newMenuObjid || !newScreenId) { if (!newScreenId) { - logger.warn(`โš ๏ธ ํ™”๋ฉด ID ๋งคํ•‘ ์—†์Œ: screen_id=${assignment.screen_id}`); - } - continue; + logger.warn( + `โš ๏ธ ํ™”๋ฉด ID ๋งคํ•‘ ์—†์Œ: screen_id=${assignment.screen_id}` + ); } + continue; + } validAssignments.push({ newScreenId, newMenuObjid, displayOrder: assignment.display_order, - isActive: assignment.is_active + isActive: assignment.is_active, }); } // 3. ๋ฐฐ์น˜ INSERT if (validAssignments.length > 0) { - const assignmentValues = validAssignments.map((_, i) => - `($${i * 6 + 1}, $${i * 6 + 2}, $${i * 6 + 3}, $${i * 6 + 4}, $${i * 6 + 5}, $${i * 6 + 6})` - ).join(", "); + const assignmentValues = validAssignments + .map( + (_, i) => + `($${i * 6 + 1}, $${i * 6 + 2}, $${i * 6 + 3}, $${i * 6 + 4}, $${i * 6 + 5}, $${i * 6 + 6})` + ) + .join(", "); - const assignmentParams = validAssignments.flatMap(a => [ - a.newScreenId, a.newMenuObjid, targetCompanyCode, - a.displayOrder, a.isActive, "system" + const assignmentParams = validAssignments.flatMap((a) => [ + a.newScreenId, + a.newMenuObjid, + targetCompanyCode, + a.displayOrder, + a.isActive, + "system", ]); - await client.query( - `INSERT INTO screen_menu_assignments ( + await client.query( + `INSERT INTO screen_menu_assignments ( screen_id, menu_objid, company_code, display_order, is_active, created_by ) VALUES ${assignmentValues}`, assignmentParams @@ -1906,30 +2065,42 @@ export class MenuCopyService { } // 2. ๋Œ€์ƒ ํšŒ์‚ฌ์— ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์นดํ…Œ๊ณ ๋ฆฌ ํ•œ ๋ฒˆ์— ์กฐํšŒ - const categoryCodes = allCategoriesResult.rows.map(c => c.category_code); + const categoryCodes = allCategoriesResult.rows.map((c) => c.category_code); const existingCategoriesResult = await client.query( `SELECT category_code FROM code_category WHERE category_code = ANY($1) AND company_code = $2`, [categoryCodes, targetCompanyCode] ); - const existingCategoryCodes = new Set(existingCategoriesResult.rows.map(c => c.category_code)); + const existingCategoryCodes = new Set( + existingCategoriesResult.rows.map((c) => c.category_code) + ); // 3. ๋ณต์‚ฌํ•  ์นดํ…Œ๊ณ ๋ฆฌ ํ•„ํ„ฐ๋ง const categoriesToCopy = allCategoriesResult.rows.filter( - c => !existingCategoryCodes.has(c.category_code) + (c) => !existingCategoryCodes.has(c.category_code) ); // 4. ๋ฐฐ์น˜ INSERT๋กœ ์นดํ…Œ๊ณ ๋ฆฌ ๋ณต์‚ฌ if (categoriesToCopy.length > 0) { - const categoryValues = categoriesToCopy.map((_, i) => - `($${i * 9 + 1}, $${i * 9 + 2}, $${i * 9 + 3}, $${i * 9 + 4}, $${i * 9 + 5}, $${i * 9 + 6}, NOW(), $${i * 9 + 7}, $${i * 9 + 8}, $${i * 9 + 9})` - ).join(", "); + const categoryValues = categoriesToCopy + .map( + (_, i) => + `($${i * 9 + 1}, $${i * 9 + 2}, $${i * 9 + 3}, $${i * 9 + 4}, $${i * 9 + 5}, $${i * 9 + 6}, NOW(), $${i * 9 + 7}, $${i * 9 + 8}, $${i * 9 + 9})` + ) + .join(", "); - const categoryParams = categoriesToCopy.flatMap(c => { + const categoryParams = categoriesToCopy.flatMap((c) => { const newMenuObjid = menuIdMap.get(c.menu_objid); return [ - c.category_code, c.category_name, c.category_name_eng, c.description, - c.sort_order, c.is_active, userId, targetCompanyCode, newMenuObjid + c.category_code, + c.category_name, + c.category_name_eng, + c.description, + c.sort_order, + c.is_active, + userId, + targetCompanyCode, + newMenuObjid, ]; }); @@ -1963,25 +2134,36 @@ export class MenuCopyService { [Array.from(menuIdMap.values()), targetCompanyCode] ); const existingCodeKeys = new Set( - existingCodesResult.rows.map(c => `${c.code_category}|${c.code_value}`) + existingCodesResult.rows.map((c) => `${c.code_category}|${c.code_value}`) ); // 7. ๋ณต์‚ฌํ•  ์ฝ”๋“œ ํ•„ํ„ฐ๋ง const codesToCopy = allCodesResult.rows.filter( - c => !existingCodeKeys.has(`${c.code_category}|${c.code_value}`) + (c) => !existingCodeKeys.has(`${c.code_category}|${c.code_value}`) ); // 8. ๋ฐฐ์น˜ INSERT๋กœ ์ฝ”๋“œ ๋ณต์‚ฌ if (codesToCopy.length > 0) { - const codeValues = codesToCopy.map((_, i) => - `($${i * 10 + 1}, $${i * 10 + 2}, $${i * 10 + 3}, $${i * 10 + 4}, $${i * 10 + 5}, $${i * 10 + 6}, $${i * 10 + 7}, NOW(), $${i * 10 + 8}, $${i * 10 + 9}, $${i * 10 + 10})` - ).join(", "); + const codeValues = codesToCopy + .map( + (_, i) => + `($${i * 10 + 1}, $${i * 10 + 2}, $${i * 10 + 3}, $${i * 10 + 4}, $${i * 10 + 5}, $${i * 10 + 6}, $${i * 10 + 7}, NOW(), $${i * 10 + 8}, $${i * 10 + 9}, $${i * 10 + 10})` + ) + .join(", "); - const codeParams = codesToCopy.flatMap(c => { + const codeParams = codesToCopy.flatMap((c) => { const newMenuObjid = menuIdMap.get(c.menu_objid); return [ - c.code_category, c.code_value, c.code_name, c.code_name_eng, c.description, - c.sort_order, c.is_active, userId, targetCompanyCode, newMenuObjid + c.code_category, + c.code_value, + c.code_name, + c.code_name_eng, + c.description, + c.sort_order, + c.is_active, + userId, + targetCompanyCode, + newMenuObjid, ]; }); @@ -1997,7 +2179,9 @@ export class MenuCopyService { logger.info(` โœ… ์ฝ”๋“œ ${copiedCodes}๊ฐœ ๋ณต์‚ฌ`); } - logger.info(`โœ… ์ฝ”๋“œ ์นดํ…Œ๊ณ ๋ฆฌ + ์ฝ”๋“œ ๋ณต์‚ฌ ์™„๋ฃŒ: ์นดํ…Œ๊ณ ๋ฆฌ ${copiedCategories}๊ฐœ, ์ฝ”๋“œ ${copiedCodes}๊ฐœ`); + logger.info( + `โœ… ์ฝ”๋“œ ์นดํ…Œ๊ณ ๋ฆฌ + ์ฝ”๋“œ ๋ณต์‚ฌ ์™„๋ฃŒ: ์นดํ…Œ๊ณ ๋ฆฌ ${copiedCategories}๊ฐœ, ์ฝ”๋“œ ${copiedCodes}๊ฐœ` + ); return { copiedCategories, copiedCodes }; } @@ -2031,31 +2215,34 @@ export class MenuCopyService { return { copiedCount, ruleIdMap }; } - // 2. ๋Œ€์ƒ ํšŒ์‚ฌ์— ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์ฑ„๋ฒˆ ๊ทœ์น™ ํ•œ ๋ฒˆ์— ์กฐํšŒ - const ruleIds = allRulesResult.rows.map(r => r.rule_id); + // 2. ๋Œ€์ƒ ํšŒ์‚ฌ์— ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๋ชจ๋“  ์ฑ„๋ฒˆ ๊ทœ์น™ ์กฐํšŒ (์›๋ณธ ID + ์ƒˆ๋กœ ์ƒ์„ฑ๋  ID ๋ชจ๋‘ ์ฒดํฌ) const existingRulesResult = await client.query( - `SELECT rule_id FROM numbering_rules - WHERE rule_id = ANY($1) AND company_code = $2`, - [ruleIds, targetCompanyCode] + `SELECT rule_id FROM numbering_rules WHERE company_code = $1`, + [targetCompanyCode] + ); + const existingRuleIds = new Set( + existingRulesResult.rows.map((r) => r.rule_id) ); - const existingRuleIds = new Set(existingRulesResult.rows.map(r => r.rule_id)); // 3. ๋ณต์‚ฌํ•  ๊ทœ์น™๊ณผ ์Šคํ‚ตํ•  ๊ทœ์น™ ๋ถ„๋ฅ˜ const rulesToCopy: any[] = []; const originalToNewRuleMap: Array<{ original: string; new: string }> = []; for (const rule of allRulesResult.rows) { + // ์ƒˆ rule_id ์ƒ์„ฑ + const originalSuffix = rule.rule_id.includes("_") + ? rule.rule_id.replace(/^[^_]*_/, "") + : rule.rule_id; + const newRuleId = `${targetCompanyCode}_${originalSuffix}`; + + // ์›๋ณธ ID ๋˜๋Š” ์ƒˆ๋กœ ์ƒ์„ฑ๋  ID๊ฐ€ ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ ์Šคํ‚ต if (existingRuleIds.has(rule.rule_id)) { - // ๊ธฐ์กด ๊ทœ์น™์€ ๋™์ผํ•œ ID๋กœ ๋งคํ•‘ ruleIdMap.set(rule.rule_id, rule.rule_id); - logger.info(` โ™ป๏ธ ์ฑ„๋ฒˆ๊ทœ์น™ ์ด๋ฏธ ์กด์žฌ (์Šคํ‚ต): ${rule.rule_id}`); + logger.info(` โ™ป๏ธ ์ฑ„๋ฒˆ๊ทœ์น™ ์ด๋ฏธ ์กด์žฌ (์›๋ณธ ID): ${rule.rule_id}`); + } else if (existingRuleIds.has(newRuleId)) { + ruleIdMap.set(rule.rule_id, newRuleId); + logger.info(` โ™ป๏ธ ์ฑ„๋ฒˆ๊ทœ์น™ ์ด๋ฏธ ์กด์žฌ (๋Œ€์ƒ ID): ${newRuleId}`); } else { - // ์ƒˆ rule_id ์ƒ์„ฑ - const originalSuffix = rule.rule_id.includes('_') - ? rule.rule_id.replace(/^[^_]*_/, '') - : rule.rule_id; - const newRuleId = `${targetCompanyCode}_${originalSuffix}`; - ruleIdMap.set(rule.rule_id, newRuleId); originalToNewRuleMap.push({ original: rule.rule_id, new: newRuleId }); rulesToCopy.push({ ...rule, newRuleId }); @@ -2064,16 +2251,29 @@ export class MenuCopyService { // 4. ๋ฐฐ์น˜ INSERT๋กœ ์ฑ„๋ฒˆ ๊ทœ์น™ ๋ณต์‚ฌ if (rulesToCopy.length > 0) { - const ruleValues = rulesToCopy.map((_, i) => - `($${i * 13 + 1}, $${i * 13 + 2}, $${i * 13 + 3}, $${i * 13 + 4}, $${i * 13 + 5}, $${i * 13 + 6}, $${i * 13 + 7}, $${i * 13 + 8}, $${i * 13 + 9}, NOW(), $${i * 13 + 10}, $${i * 13 + 11}, $${i * 13 + 12}, $${i * 13 + 13})` - ).join(", "); + const ruleValues = rulesToCopy + .map( + (_, i) => + `($${i * 13 + 1}, $${i * 13 + 2}, $${i * 13 + 3}, $${i * 13 + 4}, $${i * 13 + 5}, $${i * 13 + 6}, $${i * 13 + 7}, $${i * 13 + 8}, $${i * 13 + 9}, NOW(), $${i * 13 + 10}, $${i * 13 + 11}, $${i * 13 + 12}, $${i * 13 + 13})` + ) + .join(", "); - const ruleParams = rulesToCopy.flatMap(r => { + const ruleParams = rulesToCopy.flatMap((r) => { const newMenuObjid = menuIdMap.get(r.menu_objid); return [ - r.newRuleId, r.rule_name, r.description, r.separator, r.reset_period, - 0, r.table_name, r.column_name, targetCompanyCode, - userId, newMenuObjid, r.scope_type, null + r.newRuleId, + r.rule_name, + r.description, + r.separator, + r.reset_period, + 0, + r.table_name, + r.column_name, + targetCompanyCode, + userId, + newMenuObjid, + r.scope_type, + null, ]; }); @@ -2090,7 +2290,7 @@ export class MenuCopyService { logger.info(` โœ… ์ฑ„๋ฒˆ ๊ทœ์น™ ${copiedCount}๊ฐœ ๋ณต์‚ฌ`); // 5. ๋ชจ๋“  ์›๋ณธ ํŒŒํŠธ ํ•œ ๋ฒˆ์— ์กฐํšŒ - const originalRuleIds = rulesToCopy.map(r => r.rule_id); + const originalRuleIds = rulesToCopy.map((r) => r.rule_id); const allPartsResult = await client.query( `SELECT * FROM numbering_rule_parts WHERE rule_id = ANY($1) ORDER BY rule_id, part_order`, @@ -2100,15 +2300,25 @@ export class MenuCopyService { // 6. ๋ฐฐ์น˜ INSERT๋กœ ์ฑ„๋ฒˆ ๊ทœ์น™ ํŒŒํŠธ ๋ณต์‚ฌ if (allPartsResult.rows.length > 0) { // ์›๋ณธ rule_id -> ์ƒˆ rule_id ๋งคํ•‘ - const ruleMapping = new Map(originalToNewRuleMap.map(m => [m.original, m.new])); + const ruleMapping = new Map( + originalToNewRuleMap.map((m) => [m.original, m.new]) + ); - const partValues = allPartsResult.rows.map((_, i) => - `($${i * 7 + 1}, $${i * 7 + 2}, $${i * 7 + 3}, $${i * 7 + 4}, $${i * 7 + 5}, $${i * 7 + 6}, $${i * 7 + 7}, NOW())` - ).join(", "); + const partValues = allPartsResult.rows + .map( + (_, i) => + `($${i * 7 + 1}, $${i * 7 + 2}, $${i * 7 + 3}, $${i * 7 + 4}, $${i * 7 + 5}, $${i * 7 + 6}, $${i * 7 + 7}, NOW())` + ) + .join(", "); - const partParams = allPartsResult.rows.flatMap(p => [ - ruleMapping.get(p.rule_id), p.part_order, p.part_type, p.generation_method, - p.auto_config, p.manual_config, targetCompanyCode + const partParams = allPartsResult.rows.flatMap((p) => [ + ruleMapping.get(p.rule_id), + p.part_order, + p.part_type, + p.generation_method, + p.auto_config, + p.manual_config, + targetCompanyCode, ]); await client.query( @@ -2123,7 +2333,9 @@ export class MenuCopyService { } } - logger.info(`โœ… ์ฑ„๋ฒˆ ๊ทœ์น™ ๋ณต์‚ฌ ์™„๋ฃŒ: ${copiedCount}๊ฐœ, ๋งคํ•‘: ${ruleIdMap.size}๊ฐœ`); + logger.info( + `โœ… ์ฑ„๋ฒˆ ๊ทœ์น™ ๋ณต์‚ฌ ์™„๋ฃŒ: ${copiedCount}๊ฐœ, ๋งคํ•‘: ${ruleIdMap.size}๊ฐœ` + ); return { copiedCount, ruleIdMap }; } @@ -2162,28 +2374,40 @@ export class MenuCopyService { [targetCompanyCode] ); const existingMappingKeys = new Map( - existingMappingsResult.rows.map(m => [`${m.table_name}|${m.logical_column_name}`, m.mapping_id]) + existingMappingsResult.rows.map((m) => [ + `${m.table_name}|${m.logical_column_name}`, + m.mapping_id, + ]) ); // 3. ๋ณต์‚ฌํ•  ๋งคํ•‘ ํ•„ํ„ฐ๋ง ๋ฐ ๋ฐฐ์น˜ INSERT const mappingsToCopy = allMappingsResult.rows.filter( - m => !existingMappingKeys.has(`${m.table_name}|${m.logical_column_name}`) + (m) => + !existingMappingKeys.has(`${m.table_name}|${m.logical_column_name}`) ); // ์ƒˆ ๋งคํ•‘ ID -> ์›๋ณธ ๋งคํ•‘ ์ •๋ณด ์ถ”์  const mappingInsertInfo: Array<{ mapping: any; newMenuObjid: number }> = []; if (mappingsToCopy.length > 0) { - const mappingValues = mappingsToCopy.map((_, i) => - `($${i * 7 + 1}, $${i * 7 + 2}, $${i * 7 + 3}, $${i * 7 + 4}, $${i * 7 + 5}, $${i * 7 + 6}, NOW(), $${i * 7 + 7})` - ).join(", "); + const mappingValues = mappingsToCopy + .map( + (_, i) => + `($${i * 7 + 1}, $${i * 7 + 2}, $${i * 7 + 3}, $${i * 7 + 4}, $${i * 7 + 5}, $${i * 7 + 6}, NOW(), $${i * 7 + 7})` + ) + .join(", "); - const mappingParams = mappingsToCopy.flatMap(m => { + const mappingParams = mappingsToCopy.flatMap((m) => { const newMenuObjid = menuIdMap.get(m.menu_objid) || 0; mappingInsertInfo.push({ mapping: m, newMenuObjid }); return [ - m.table_name, m.logical_column_name, m.physical_column_name, - newMenuObjid, targetCompanyCode, m.description, userId + m.table_name, + m.logical_column_name, + m.physical_column_name, + newMenuObjid, + targetCompanyCode, + m.description, + userId, ]; }); @@ -2199,7 +2423,10 @@ export class MenuCopyService { // ์ƒˆ๋กœ ์ƒ์„ฑ๋œ ๋งคํ•‘ ID๋ฅผ ๊ธฐ์กด ๋งคํ•‘ ๋งต์— ์ถ”๊ฐ€ insertResult.rows.forEach((row, index) => { const m = mappingsToCopy[index]; - existingMappingKeys.set(`${m.table_name}|${m.logical_column_name}`, row.mapping_id); + existingMappingKeys.set( + `${m.table_name}|${m.logical_column_name}`, + row.mapping_id + ); }); copiedCount = mappingsToCopy.length; @@ -2226,7 +2453,10 @@ export class MenuCopyService { [targetCompanyCode] ); const existingValueKeys = new Map( - existingValuesResult.rows.map(v => [`${v.table_name}|${v.column_name}|${v.value_code}`, v.value_id]) + existingValuesResult.rows.map((v) => [ + `${v.table_name}|${v.column_name}|${v.value_code}`, + v.value_id, + ]) ); // 6. ๊ฐ’ ๋ณต์‚ฌ (๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„ ์œ ์ง€๋ฅผ ์œ„ํ•ด depth ์ˆœ์„œ๋กœ ์ฒ˜๋ฆฌ) @@ -2262,17 +2492,34 @@ export class MenuCopyService { const values = valuesByDepth.get(depth)!; if (values.length === 0) continue; - const valueStrings = values.map((_, i) => - `($${i * 15 + 1}, $${i * 15 + 2}, $${i * 15 + 3}, $${i * 15 + 4}, $${i * 15 + 5}, $${i * 15 + 6}, $${i * 15 + 7}, $${i * 15 + 8}, $${i * 15 + 9}, $${i * 15 + 10}, $${i * 15 + 11}, $${i * 15 + 12}, NOW(), $${i * 15 + 13}, $${i * 15 + 14}, $${i * 15 + 15})` - ).join(", "); + const valueStrings = values + .map( + (_, i) => + `($${i * 15 + 1}, $${i * 15 + 2}, $${i * 15 + 3}, $${i * 15 + 4}, $${i * 15 + 5}, $${i * 15 + 6}, $${i * 15 + 7}, $${i * 15 + 8}, $${i * 15 + 9}, $${i * 15 + 10}, $${i * 15 + 11}, $${i * 15 + 12}, NOW(), $${i * 15 + 13}, $${i * 15 + 14}, $${i * 15 + 15})` + ) + .join(", "); - const valueParams = values.flatMap(v => { + const valueParams = values.flatMap((v) => { const newMenuObjid = menuIdMap.get(v.menu_objid); - const newParentId = v.parent_value_id ? valueIdMap.get(v.parent_value_id) || null : null; + const newParentId = v.parent_value_id + ? valueIdMap.get(v.parent_value_id) || null + : null; return [ - v.table_name, v.column_name, v.value_code, v.value_label, v.value_order, - newParentId, v.depth, v.description, v.color, v.icon, - v.is_active, v.is_default, userId, targetCompanyCode, newMenuObjid + v.table_name, + v.column_name, + v.value_code, + v.value_label, + v.value_order, + newParentId, + v.depth, + v.description, + v.color, + v.icon, + v.is_active, + v.is_default, + userId, + targetCompanyCode, + newMenuObjid, ]; }); @@ -2353,25 +2600,35 @@ export class MenuCopyService { [tableNames, targetCompanyCode] ); const existingKeys = new Set( - existingSettingsResult.rows.map(s => `${s.table_name}|${s.column_name}`) + existingSettingsResult.rows.map((s) => `${s.table_name}|${s.column_name}`) ); // 4. ๋ณต์‚ฌํ•  ์„ค์ • ํ•„ํ„ฐ๋ง const settingsToCopy = sourceSettingsResult.rows.filter( - s => !existingKeys.has(`${s.table_name}|${s.column_name}`) + (s) => !existingKeys.has(`${s.table_name}|${s.column_name}`) ); - logger.info(` ์›๋ณธ ์„ค์ •: ${sourceSettingsResult.rows.length}๊ฐœ, ๋ณต์‚ฌ ๋Œ€์ƒ: ${settingsToCopy.length}๊ฐœ`); + logger.info( + ` ์›๋ณธ ์„ค์ •: ${sourceSettingsResult.rows.length}๊ฐœ, ๋ณต์‚ฌ ๋Œ€์ƒ: ${settingsToCopy.length}๊ฐœ` + ); // 5. ๋ฐฐ์น˜ INSERT if (settingsToCopy.length > 0) { - const settingValues = settingsToCopy.map((_, i) => - `($${i * 7 + 1}, $${i * 7 + 2}, $${i * 7 + 3}, $${i * 7 + 4}, $${i * 7 + 5}, $${i * 7 + 6}, NOW(), NOW(), $${i * 7 + 7})` - ).join(", "); + const settingValues = settingsToCopy + .map( + (_, i) => + `($${i * 7 + 1}, $${i * 7 + 2}, $${i * 7 + 3}, $${i * 7 + 4}, $${i * 7 + 5}, $${i * 7 + 6}, NOW(), NOW(), $${i * 7 + 7})` + ) + .join(", "); - const settingParams = settingsToCopy.flatMap(s => [ - s.table_name, s.column_name, s.input_type, s.detail_settings, - s.is_nullable, s.display_order, targetCompanyCode + const settingParams = settingsToCopy.flatMap((s) => [ + s.table_name, + s.column_name, + s.input_type, + s.detail_settings, + s.is_nullable, + s.display_order, + targetCompanyCode, ]); await client.query( @@ -2421,7 +2678,7 @@ export class MenuCopyService { [targetCompanyCode] ); const existingGroupsByCode = new Map( - existingGroupsResult.rows.map(g => [g.relation_code, g.group_id]) + existingGroupsResult.rows.map((g) => [g.relation_code, g.group_id]) ); // group_id ๋งคํ•‘ @@ -2437,7 +2694,9 @@ export class MenuCopyService { } } - logger.info(` ๊ธฐ์กด: ${groupsResult.rows.length - groupsToCopy.length}๊ฐœ, ์‹ ๊ทœ: ${groupsToCopy.length}๊ฐœ`); + logger.info( + ` ๊ธฐ์กด: ${groupsResult.rows.length - groupsToCopy.length}๊ฐœ, ์‹ ๊ทœ: ${groupsToCopy.length}๊ฐœ` + ); // ๊ทธ๋ฃน๋ณ„๋กœ ์‚ฝ์ž…ํ•˜๊ณ  ๋งคํ•‘ ์ €์žฅ (RETURNING์ด ํ•„์š”ํ•ด์„œ ๋ฐฐ์น˜ ๋ถˆ๊ฐ€) for (const group of groupsToCopy) { @@ -2459,12 +2718,22 @@ export class MenuCopyService { ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, NOW()) RETURNING group_id`, [ - group.relation_code, group.relation_name, group.description, - group.parent_table_name, group.parent_column_name, newParentMenuObjid, - group.child_table_name, group.child_column_name, newChildMenuObjid, - group.clear_on_parent_change, group.show_group_label, - group.empty_parent_message, group.no_options_message, - targetCompanyCode, "Y", userId + group.relation_code, + group.relation_name, + group.description, + group.parent_table_name, + group.parent_column_name, + newParentMenuObjid, + group.child_table_name, + group.child_column_name, + newChildMenuObjid, + group.clear_on_parent_change, + group.show_group_label, + group.empty_parent_message, + group.no_options_message, + targetCompanyCode, + "Y", + userId, ] ); @@ -2474,7 +2743,7 @@ export class MenuCopyService { } // ๋ชจ๋“  ๋งคํ•‘ ํ•œ ๋ฒˆ์— ์กฐํšŒ (๋ณต์‚ฌํ•  ๊ทธ๋ฃน๋งŒ) - const groupIdsToCopy = groupsToCopy.map(g => g.group_id); + const groupIdsToCopy = groupsToCopy.map((g) => g.group_id); if (groupIdsToCopy.length > 0) { const allMappingsResult = await client.query( `SELECT * FROM category_value_cascading_mapping @@ -2485,16 +2754,24 @@ export class MenuCopyService { // ๋ฐฐ์น˜ INSERT if (allMappingsResult.rows.length > 0) { - const mappingValues = allMappingsResult.rows.map((_, i) => - `($${i * 8 + 1}, $${i * 8 + 2}, $${i * 8 + 3}, $${i * 8 + 4}, $${i * 8 + 5}, $${i * 8 + 6}, $${i * 8 + 7}, $${i * 8 + 8}, NOW())` - ).join(", "); + const mappingValues = allMappingsResult.rows + .map( + (_, i) => + `($${i * 8 + 1}, $${i * 8 + 2}, $${i * 8 + 3}, $${i * 8 + 4}, $${i * 8 + 5}, $${i * 8 + 6}, $${i * 8 + 7}, $${i * 8 + 8}, NOW())` + ) + .join(", "); - const mappingParams = allMappingsResult.rows.flatMap(m => { + const mappingParams = allMappingsResult.rows.flatMap((m) => { const newGroupId = groupIdMap.get(m.group_id); return [ - newGroupId, m.parent_value_code, m.parent_value_label, - m.child_value_code, m.child_value_label, m.display_order, - targetCompanyCode, "Y" + newGroupId, + m.parent_value_code, + m.parent_value_label, + m.child_value_code, + m.child_value_label, + m.display_order, + targetCompanyCode, + "Y", ]; }); @@ -2531,29 +2808,47 @@ export class MenuCopyService { [targetCompanyCode] ); const existingRelationCodes = new Set( - existingRelationsResult.rows.map(r => r.relation_code) + existingRelationsResult.rows.map((r) => r.relation_code) ); // ๋ณต์‚ฌํ•  ๊ด€๊ณ„ ํ•„ํ„ฐ๋ง const relationsToCopy = relationsResult.rows.filter( - r => !existingRelationCodes.has(r.relation_code) + (r) => !existingRelationCodes.has(r.relation_code) ); - logger.info(` ๊ธฐ์กด: ${relationsResult.rows.length - relationsToCopy.length}๊ฐœ, ์‹ ๊ทœ: ${relationsToCopy.length}๊ฐœ`); + logger.info( + ` ๊ธฐ์กด: ${relationsResult.rows.length - relationsToCopy.length}๊ฐœ, ์‹ ๊ทœ: ${relationsToCopy.length}๊ฐœ` + ); // ๋ฐฐ์น˜ INSERT if (relationsToCopy.length > 0) { - const relationValues = relationsToCopy.map((_, i) => - `($${i * 19 + 1}, $${i * 19 + 2}, $${i * 19 + 3}, $${i * 19 + 4}, $${i * 19 + 5}, $${i * 19 + 6}, $${i * 19 + 7}, $${i * 19 + 8}, $${i * 19 + 9}, $${i * 19 + 10}, $${i * 19 + 11}, $${i * 19 + 12}, $${i * 19 + 13}, $${i * 19 + 14}, $${i * 19 + 15}, $${i * 19 + 16}, $${i * 19 + 17}, $${i * 19 + 18}, $${i * 19 + 19}, NOW())` - ).join(", "); + const relationValues = relationsToCopy + .map( + (_, i) => + `($${i * 19 + 1}, $${i * 19 + 2}, $${i * 19 + 3}, $${i * 19 + 4}, $${i * 19 + 5}, $${i * 19 + 6}, $${i * 19 + 7}, $${i * 19 + 8}, $${i * 19 + 9}, $${i * 19 + 10}, $${i * 19 + 11}, $${i * 19 + 12}, $${i * 19 + 13}, $${i * 19 + 14}, $${i * 19 + 15}, $${i * 19 + 16}, $${i * 19 + 17}, $${i * 19 + 18}, $${i * 19 + 19}, NOW())` + ) + .join(", "); - const relationParams = relationsToCopy.flatMap(r => [ - r.relation_code, r.relation_name, r.description, - r.parent_table, r.parent_value_column, r.parent_label_column, - r.child_table, r.child_filter_column, r.child_value_column, r.child_label_column, - r.child_order_column, r.child_order_direction, - r.empty_parent_message, r.no_options_message, r.loading_message, - r.clear_on_parent_change, targetCompanyCode, "Y", userId + const relationParams = relationsToCopy.flatMap((r) => [ + r.relation_code, + r.relation_name, + r.description, + r.parent_table, + r.parent_value_column, + r.parent_label_column, + r.child_table, + r.child_filter_column, + r.child_value_column, + r.child_label_column, + r.child_order_column, + r.child_order_direction, + r.empty_parent_message, + r.no_options_message, + r.loading_message, + r.clear_on_parent_change, + targetCompanyCode, + "Y", + userId, ]); await client.query( @@ -2575,5 +2870,4 @@ export class MenuCopyService { logger.info(`โœ… ์—ฐ์‡„๊ด€๊ณ„ ๋ณต์‚ฌ ์™„๋ฃŒ: ${copiedCount}๊ฐœ`); return copiedCount; } - }