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

This commit is contained in:
kjs
2026-02-26 20:55:03 +09:00
9 changed files with 414 additions and 88 deletions

View File

@@ -92,9 +92,9 @@ export async function createBomVersion(req: Request, res: Response) {
const { bomId } = req.params;
const companyCode = (req as any).user?.companyCode || "*";
const createdBy = (req as any).user?.userName || (req as any).user?.userId || "";
const { tableName, detailTable } = req.body || {};
const { tableName, detailTable, versionName } = req.body || {};
const result = await bomService.createBomVersion(bomId, companyCode, createdBy, tableName, detailTable);
const result = await bomService.createBomVersion(bomId, companyCode, createdBy, tableName, detailTable, versionName);
res.json({ success: true, data: result });
} catch (error: any) {
logger.error("BOM 버전 생성 실패", { error: error.message });
@@ -129,6 +129,20 @@ export async function activateBomVersion(req: Request, res: Response) {
}
}
export async function initializeBomVersion(req: Request, res: Response) {
try {
const { bomId } = req.params;
const companyCode = (req as any).user?.companyCode || "*";
const createdBy = (req as any).user?.userName || (req as any).user?.userId || "";
const result = await bomService.initializeBomVersion(bomId, companyCode, createdBy);
res.json({ success: true, data: result });
} catch (error: any) {
logger.error("BOM 초기 버전 생성 실패", { error: error.message });
res.status(500).json({ success: false, message: error.message });
}
}
export async function deleteBomVersion(req: Request, res: Response) {
try {
const { bomId, versionId } = req.params;

View File

@@ -20,6 +20,7 @@ router.post("/:bomId/history", bomController.addBomHistory);
// 버전
router.get("/:bomId/versions", bomController.getBomVersions);
router.post("/:bomId/versions", bomController.createBomVersion);
router.post("/:bomId/initialize-version", bomController.initializeBomVersion);
router.post("/:bomId/versions/:versionId/load", bomController.loadBomVersion);
router.post("/:bomId/versions/:versionId/activate", bomController.activateBomVersion);
router.delete("/:bomId/versions/:versionId", bomController.deleteBomVersion);

View File

@@ -98,6 +98,7 @@ export async function getBomVersions(bomId: string, companyCode: string, tableNa
export async function createBomVersion(
bomId: string, companyCode: string, createdBy: string,
versionTableName?: string, detailTableName?: string,
inputVersionName?: string,
) {
const vTable = safeTableName(versionTableName || "", "bom_version");
const dTable = safeTableName(detailTableName || "", "bom_detail");
@@ -107,17 +108,24 @@ export async function createBomVersion(
if (bomRow.rows.length === 0) throw new Error("BOM을 찾을 수 없습니다");
const bomData = bomRow.rows[0];
// 다음 버전 번호 결정
const lastVersion = await client.query(
`SELECT version_name FROM ${vTable} WHERE bom_id = $1 ORDER BY created_date DESC LIMIT 1`,
[bomId],
);
let nextVersionNum = 1;
if (lastVersion.rows.length > 0) {
const parsed = parseFloat(lastVersion.rows[0].version_name);
if (!isNaN(parsed)) nextVersionNum = Math.floor(parsed) + 1;
// 버전명: 사용자 입력 > 순번 자동 생성
let versionName = inputVersionName?.trim();
if (!versionName) {
const countResult = await client.query(
`SELECT COUNT(*)::int as cnt FROM ${vTable} WHERE bom_id = $1`,
[bomId],
);
versionName = `${(countResult.rows[0].cnt || 0) + 1}.0`;
}
// 중복 체크
const dupCheck = await client.query(
`SELECT id FROM ${vTable} WHERE bom_id = $1 AND version_name = $2`,
[bomId, versionName],
);
if (dupCheck.rows.length > 0) {
throw new Error(`이미 존재하는 버전명입니다: ${versionName}`);
}
const versionName = `${nextVersionNum}.0`;
// 새 버전 레코드 생성 (snapshot_data 없이)
const insertSql = `
@@ -249,6 +257,68 @@ export async function activateBomVersion(bomId: string, versionId: string, table
});
}
/**
* 신규 BOM 초기화: 첫 번째 버전 자동 생성 + version_id null인 디테일 보정
* BOM 헤더의 version 필드를 그대로 버전명으로 사용 (사용자 입력값 존중)
*/
export async function initializeBomVersion(
bomId: string, companyCode: string, createdBy: string,
) {
return transaction(async (client) => {
const bomRow = await client.query(`SELECT * FROM bom WHERE id = $1`, [bomId]);
if (bomRow.rows.length === 0) throw new Error("BOM을 찾을 수 없습니다");
const bomData = bomRow.rows[0];
if (bomData.current_version_id) {
await client.query(
`UPDATE bom_detail SET version_id = $1 WHERE bom_id = $2 AND version_id IS NULL`,
[bomData.current_version_id, bomId],
);
return { versionId: bomData.current_version_id, created: false };
}
// 이미 버전 레코드가 존재하는지 확인 (동시 호출 방지)
const existingVersion = await client.query(
`SELECT id, version_name FROM bom_version WHERE bom_id = $1 ORDER BY created_date ASC LIMIT 1`,
[bomId],
);
if (existingVersion.rows.length > 0) {
const existId = existingVersion.rows[0].id;
await client.query(
`UPDATE bom_detail SET version_id = $1 WHERE bom_id = $2 AND version_id IS NULL`,
[existId, bomId],
);
await client.query(
`UPDATE bom SET current_version_id = $1 WHERE id = $2 AND current_version_id IS NULL`,
[existId, bomId],
);
return { versionId: existId, created: false };
}
const versionName = bomData.version || "1.0";
const versionResult = await client.query(
`INSERT INTO bom_version (bom_id, version_name, revision, status, created_by, company_code)
VALUES ($1, $2, 0, 'active', $3, $4) RETURNING id`,
[bomId, versionName, createdBy, companyCode],
);
const versionId = versionResult.rows[0].id;
const updated = await client.query(
`UPDATE bom_detail SET version_id = $1 WHERE bom_id = $2 AND version_id IS NULL`,
[versionId, bomId],
);
await client.query(
`UPDATE bom SET current_version_id = $1 WHERE id = $2`,
[versionId, bomId],
);
logger.info("BOM 초기 버전 생성", { bomId, versionId, versionName, updatedDetails: updated.rowCount });
return { versionId, versionName, created: true };
});
}
/**
* 버전 삭제: 해당 version_id의 bom_detail 행도 함께 삭제
*/