Merge remote-tracking branch 'upstream/main'
All checks were successful
Build and Push Images / build-and-push (push) Successful in 11m28s
All checks were successful
Build and Push Images / build-and-push (push) Successful in 11m28s
This commit is contained in:
478
backend-node/src/controllers/packagingController.ts
Normal file
478
backend-node/src/controllers/packagingController.ts
Normal file
@@ -0,0 +1,478 @@
|
||||
import { Response } from "express";
|
||||
import { AuthenticatedRequest } from "../types/auth";
|
||||
import { logger } from "../utils/logger";
|
||||
import { getPool } from "../database/db";
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// 포장단위 (pkg_unit) CRUD
|
||||
// ──────────────────────────────────────────────
|
||||
|
||||
export async function getPkgUnits(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const pool = getPool();
|
||||
|
||||
let sql: string;
|
||||
let params: any[];
|
||||
|
||||
if (companyCode === "*") {
|
||||
sql = `SELECT * FROM pkg_unit ORDER BY company_code, created_date DESC`;
|
||||
params = [];
|
||||
} else {
|
||||
sql = `SELECT * FROM pkg_unit WHERE company_code = $1 ORDER BY created_date DESC`;
|
||||
params = [companyCode];
|
||||
}
|
||||
|
||||
const result = await pool.query(sql, params);
|
||||
logger.info("포장단위 목록 조회", { companyCode, count: result.rowCount });
|
||||
res.json({ success: true, data: result.rows });
|
||||
} catch (error: any) {
|
||||
logger.error("포장단위 목록 조회 실패", { error: error.message });
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
export async function createPkgUnit(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const pool = getPool();
|
||||
const {
|
||||
pkg_code, pkg_name, pkg_type, status,
|
||||
width_mm, length_mm, height_mm,
|
||||
self_weight_kg, max_load_kg, volume_l, remarks,
|
||||
} = req.body;
|
||||
|
||||
if (!pkg_code || !pkg_name) {
|
||||
res.status(400).json({ success: false, message: "포장코드와 포장명은 필수입니다." });
|
||||
return;
|
||||
}
|
||||
|
||||
const dup = await pool.query(
|
||||
`SELECT id FROM pkg_unit WHERE pkg_code = $1 AND company_code = $2`,
|
||||
[pkg_code, companyCode]
|
||||
);
|
||||
if (dup.rowCount && dup.rowCount > 0) {
|
||||
res.status(409).json({ success: false, message: "이미 존재하는 포장코드입니다." });
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await pool.query(
|
||||
`INSERT INTO pkg_unit
|
||||
(company_code, pkg_code, pkg_name, pkg_type, status,
|
||||
width_mm, length_mm, height_mm, self_weight_kg, max_load_kg, volume_l, remarks, writer)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13)
|
||||
RETURNING *`,
|
||||
[companyCode, pkg_code, pkg_name, pkg_type, status || "ACTIVE",
|
||||
width_mm, length_mm, height_mm, self_weight_kg, max_load_kg, volume_l, remarks,
|
||||
req.user!.userId]
|
||||
);
|
||||
|
||||
logger.info("포장단위 등록", { companyCode, pkg_code });
|
||||
res.json({ success: true, data: result.rows[0] });
|
||||
} catch (error: any) {
|
||||
logger.error("포장단위 등록 실패", { error: error.message });
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
export async function updatePkgUnit(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const { id } = req.params;
|
||||
const pool = getPool();
|
||||
const {
|
||||
pkg_name, pkg_type, status,
|
||||
width_mm, length_mm, height_mm,
|
||||
self_weight_kg, max_load_kg, volume_l, remarks,
|
||||
} = req.body;
|
||||
|
||||
const result = await pool.query(
|
||||
`UPDATE pkg_unit SET
|
||||
pkg_name=$1, pkg_type=$2, status=$3,
|
||||
width_mm=$4, length_mm=$5, height_mm=$6,
|
||||
self_weight_kg=$7, max_load_kg=$8, volume_l=$9, remarks=$10,
|
||||
updated_date=NOW(), writer=$11
|
||||
WHERE id=$12 AND company_code=$13
|
||||
RETURNING *`,
|
||||
[pkg_name, pkg_type, status,
|
||||
width_mm, length_mm, height_mm,
|
||||
self_weight_kg, max_load_kg, volume_l, remarks,
|
||||
req.user!.userId, id, companyCode]
|
||||
);
|
||||
|
||||
if (result.rowCount === 0) {
|
||||
res.status(404).json({ success: false, message: "데이터를 찾을 수 없습니다." });
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("포장단위 수정", { companyCode, id });
|
||||
res.json({ success: true, data: result.rows[0] });
|
||||
} catch (error: any) {
|
||||
logger.error("포장단위 수정 실패", { error: error.message });
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
export async function deletePkgUnit(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
const pool = getPool();
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const { id } = req.params;
|
||||
|
||||
await client.query("BEGIN");
|
||||
await client.query(
|
||||
`DELETE FROM pkg_unit_item WHERE pkg_code = (SELECT pkg_code FROM pkg_unit WHERE id=$1 AND company_code=$2) AND company_code=$2`,
|
||||
[id, companyCode]
|
||||
);
|
||||
const result = await client.query(
|
||||
`DELETE FROM pkg_unit WHERE id=$1 AND company_code=$2 RETURNING id`,
|
||||
[id, companyCode]
|
||||
);
|
||||
await client.query("COMMIT");
|
||||
|
||||
if (result.rowCount === 0) {
|
||||
res.status(404).json({ success: false, message: "데이터를 찾을 수 없습니다." });
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("포장단위 삭제", { companyCode, id });
|
||||
res.json({ success: true });
|
||||
} catch (error: any) {
|
||||
await client.query("ROLLBACK");
|
||||
logger.error("포장단위 삭제 실패", { error: error.message });
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// 포장단위 매칭품목 (pkg_unit_item) CRUD
|
||||
// ──────────────────────────────────────────────
|
||||
|
||||
export async function getPkgUnitItems(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const { pkgCode } = req.params;
|
||||
const pool = getPool();
|
||||
|
||||
const result = await pool.query(
|
||||
`SELECT * FROM pkg_unit_item WHERE pkg_code=$1 AND company_code=$2 ORDER BY created_date DESC`,
|
||||
[pkgCode, companyCode]
|
||||
);
|
||||
|
||||
res.json({ success: true, data: result.rows });
|
||||
} catch (error: any) {
|
||||
logger.error("매칭품목 조회 실패", { error: error.message });
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
export async function createPkgUnitItem(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const pool = getPool();
|
||||
const { pkg_code, item_number, pkg_qty } = req.body;
|
||||
|
||||
if (!pkg_code || !item_number) {
|
||||
res.status(400).json({ success: false, message: "포장코드와 품번은 필수입니다." });
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await pool.query(
|
||||
`INSERT INTO pkg_unit_item (company_code, pkg_code, item_number, pkg_qty, writer)
|
||||
VALUES ($1,$2,$3,$4,$5)
|
||||
RETURNING *`,
|
||||
[companyCode, pkg_code, item_number, pkg_qty, req.user!.userId]
|
||||
);
|
||||
|
||||
logger.info("매칭품목 추가", { companyCode, pkg_code, item_number });
|
||||
res.json({ success: true, data: result.rows[0] });
|
||||
} catch (error: any) {
|
||||
logger.error("매칭품목 추가 실패", { error: error.message });
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
export async function deletePkgUnitItem(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const { id } = req.params;
|
||||
const pool = getPool();
|
||||
|
||||
const result = await pool.query(
|
||||
`DELETE FROM pkg_unit_item WHERE id=$1 AND company_code=$2 RETURNING id`,
|
||||
[id, companyCode]
|
||||
);
|
||||
|
||||
if (result.rowCount === 0) {
|
||||
res.status(404).json({ success: false, message: "데이터를 찾을 수 없습니다." });
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("매칭품목 삭제", { companyCode, id });
|
||||
res.json({ success: true });
|
||||
} catch (error: any) {
|
||||
logger.error("매칭품목 삭제 실패", { error: error.message });
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// 적재함 (loading_unit) CRUD
|
||||
// ──────────────────────────────────────────────
|
||||
|
||||
export async function getLoadingUnits(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const pool = getPool();
|
||||
|
||||
let sql: string;
|
||||
let params: any[];
|
||||
|
||||
if (companyCode === "*") {
|
||||
sql = `SELECT * FROM loading_unit ORDER BY company_code, created_date DESC`;
|
||||
params = [];
|
||||
} else {
|
||||
sql = `SELECT * FROM loading_unit WHERE company_code = $1 ORDER BY created_date DESC`;
|
||||
params = [companyCode];
|
||||
}
|
||||
|
||||
const result = await pool.query(sql, params);
|
||||
logger.info("적재함 목록 조회", { companyCode, count: result.rowCount });
|
||||
res.json({ success: true, data: result.rows });
|
||||
} catch (error: any) {
|
||||
logger.error("적재함 목록 조회 실패", { error: error.message });
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
export async function createLoadingUnit(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const pool = getPool();
|
||||
const {
|
||||
loading_code, loading_name, loading_type, status,
|
||||
width_mm, length_mm, height_mm,
|
||||
self_weight_kg, max_load_kg, max_stack, remarks,
|
||||
} = req.body;
|
||||
|
||||
if (!loading_code || !loading_name) {
|
||||
res.status(400).json({ success: false, message: "적재함코드와 적재함명은 필수입니다." });
|
||||
return;
|
||||
}
|
||||
|
||||
const dup = await pool.query(
|
||||
`SELECT id FROM loading_unit WHERE loading_code=$1 AND company_code=$2`,
|
||||
[loading_code, companyCode]
|
||||
);
|
||||
if (dup.rowCount && dup.rowCount > 0) {
|
||||
res.status(409).json({ success: false, message: "이미 존재하는 적재함코드입니다." });
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await pool.query(
|
||||
`INSERT INTO loading_unit
|
||||
(company_code, loading_code, loading_name, loading_type, status,
|
||||
width_mm, length_mm, height_mm, self_weight_kg, max_load_kg, max_stack, remarks, writer)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13)
|
||||
RETURNING *`,
|
||||
[companyCode, loading_code, loading_name, loading_type, status || "ACTIVE",
|
||||
width_mm, length_mm, height_mm, self_weight_kg, max_load_kg, max_stack, remarks,
|
||||
req.user!.userId]
|
||||
);
|
||||
|
||||
logger.info("적재함 등록", { companyCode, loading_code });
|
||||
res.json({ success: true, data: result.rows[0] });
|
||||
} catch (error: any) {
|
||||
logger.error("적재함 등록 실패", { error: error.message });
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateLoadingUnit(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const { id } = req.params;
|
||||
const pool = getPool();
|
||||
const {
|
||||
loading_name, loading_type, status,
|
||||
width_mm, length_mm, height_mm,
|
||||
self_weight_kg, max_load_kg, max_stack, remarks,
|
||||
} = req.body;
|
||||
|
||||
const result = await pool.query(
|
||||
`UPDATE loading_unit SET
|
||||
loading_name=$1, loading_type=$2, status=$3,
|
||||
width_mm=$4, length_mm=$5, height_mm=$6,
|
||||
self_weight_kg=$7, max_load_kg=$8, max_stack=$9, remarks=$10,
|
||||
updated_date=NOW(), writer=$11
|
||||
WHERE id=$12 AND company_code=$13
|
||||
RETURNING *`,
|
||||
[loading_name, loading_type, status,
|
||||
width_mm, length_mm, height_mm,
|
||||
self_weight_kg, max_load_kg, max_stack, remarks,
|
||||
req.user!.userId, id, companyCode]
|
||||
);
|
||||
|
||||
if (result.rowCount === 0) {
|
||||
res.status(404).json({ success: false, message: "데이터를 찾을 수 없습니다." });
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("적재함 수정", { companyCode, id });
|
||||
res.json({ success: true, data: result.rows[0] });
|
||||
} catch (error: any) {
|
||||
logger.error("적재함 수정 실패", { error: error.message });
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteLoadingUnit(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
const pool = getPool();
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const { id } = req.params;
|
||||
|
||||
await client.query("BEGIN");
|
||||
await client.query(
|
||||
`DELETE FROM loading_unit_pkg WHERE loading_code = (SELECT loading_code FROM loading_unit WHERE id=$1 AND company_code=$2) AND company_code=$2`,
|
||||
[id, companyCode]
|
||||
);
|
||||
const result = await client.query(
|
||||
`DELETE FROM loading_unit WHERE id=$1 AND company_code=$2 RETURNING id`,
|
||||
[id, companyCode]
|
||||
);
|
||||
await client.query("COMMIT");
|
||||
|
||||
if (result.rowCount === 0) {
|
||||
res.status(404).json({ success: false, message: "데이터를 찾을 수 없습니다." });
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("적재함 삭제", { companyCode, id });
|
||||
res.json({ success: true });
|
||||
} catch (error: any) {
|
||||
await client.query("ROLLBACK");
|
||||
logger.error("적재함 삭제 실패", { error: error.message });
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// 적재함 포장구성 (loading_unit_pkg) CRUD
|
||||
// ──────────────────────────────────────────────
|
||||
|
||||
export async function getLoadingUnitPkgs(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const { loadingCode } = req.params;
|
||||
const pool = getPool();
|
||||
|
||||
const result = await pool.query(
|
||||
`SELECT * FROM loading_unit_pkg WHERE loading_code=$1 AND company_code=$2 ORDER BY created_date DESC`,
|
||||
[loadingCode, companyCode]
|
||||
);
|
||||
|
||||
res.json({ success: true, data: result.rows });
|
||||
} catch (error: any) {
|
||||
logger.error("적재구성 조회 실패", { error: error.message });
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
export async function createLoadingUnitPkg(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const pool = getPool();
|
||||
const { loading_code, pkg_code, max_load_qty, load_method } = req.body;
|
||||
|
||||
if (!loading_code || !pkg_code) {
|
||||
res.status(400).json({ success: false, message: "적재함코드와 포장코드는 필수입니다." });
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await pool.query(
|
||||
`INSERT INTO loading_unit_pkg (company_code, loading_code, pkg_code, max_load_qty, load_method, writer)
|
||||
VALUES ($1,$2,$3,$4,$5,$6)
|
||||
RETURNING *`,
|
||||
[companyCode, loading_code, pkg_code, max_load_qty, load_method, req.user!.userId]
|
||||
);
|
||||
|
||||
logger.info("적재구성 추가", { companyCode, loading_code, pkg_code });
|
||||
res.json({ success: true, data: result.rows[0] });
|
||||
} catch (error: any) {
|
||||
logger.error("적재구성 추가 실패", { error: error.message });
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteLoadingUnitPkg(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const { id } = req.params;
|
||||
const pool = getPool();
|
||||
|
||||
const result = await pool.query(
|
||||
`DELETE FROM loading_unit_pkg WHERE id=$1 AND company_code=$2 RETURNING id`,
|
||||
[id, companyCode]
|
||||
);
|
||||
|
||||
if (result.rowCount === 0) {
|
||||
res.status(404).json({ success: false, message: "데이터를 찾을 수 없습니다." });
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("적재구성 삭제", { companyCode, id });
|
||||
res.json({ success: true });
|
||||
} catch (error: any) {
|
||||
logger.error("적재구성 삭제 실패", { error: error.message });
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import { logger } from "../../utils/logger";
|
||||
import { NodeFlowExecutionService } from "../../services/nodeFlowExecutionService";
|
||||
import { AuthenticatedRequest } from "../../types/auth";
|
||||
import { authenticateToken } from "../../middleware/authMiddleware";
|
||||
import { auditLogService, getClientIp } from "../../services/auditLogService";
|
||||
|
||||
const router = Router();
|
||||
|
||||
@@ -124,6 +125,21 @@ router.post("/", async (req: AuthenticatedRequest, res: Response) => {
|
||||
`플로우 저장 성공: ${result.flowId} (회사: ${userCompanyCode})`
|
||||
);
|
||||
|
||||
auditLogService.log({
|
||||
companyCode: userCompanyCode,
|
||||
userId: req.user?.userId || "",
|
||||
userName: req.user?.userName,
|
||||
action: "CREATE",
|
||||
resourceType: "NODE_FLOW",
|
||||
resourceId: String(result.flowId),
|
||||
resourceName: flowName,
|
||||
tableName: "node_flows",
|
||||
summary: `노드 플로우 "${flowName}" 생성`,
|
||||
changes: { after: { flowName, flowDescription } },
|
||||
ipAddress: getClientIp(req as any),
|
||||
requestPath: req.originalUrl,
|
||||
});
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: "플로우가 저장되었습니다.",
|
||||
@@ -143,7 +159,7 @@ router.post("/", async (req: AuthenticatedRequest, res: Response) => {
|
||||
/**
|
||||
* 플로우 수정
|
||||
*/
|
||||
router.put("/", async (req: Request, res: Response) => {
|
||||
router.put("/", async (req: AuthenticatedRequest, res: Response) => {
|
||||
try {
|
||||
const { flowId, flowName, flowDescription, flowData } = req.body;
|
||||
|
||||
@@ -154,6 +170,11 @@ router.put("/", async (req: Request, res: Response) => {
|
||||
});
|
||||
}
|
||||
|
||||
const oldFlow = await queryOne(
|
||||
`SELECT flow_name, flow_description FROM node_flows WHERE flow_id = $1`,
|
||||
[flowId]
|
||||
);
|
||||
|
||||
await query(
|
||||
`
|
||||
UPDATE node_flows
|
||||
@@ -168,6 +189,25 @@ router.put("/", async (req: Request, res: Response) => {
|
||||
|
||||
logger.info(`플로우 수정 성공: ${flowId}`);
|
||||
|
||||
const userCompanyCode = req.user?.companyCode || "*";
|
||||
auditLogService.log({
|
||||
companyCode: userCompanyCode,
|
||||
userId: req.user?.userId || "",
|
||||
userName: req.user?.userName,
|
||||
action: "UPDATE",
|
||||
resourceType: "NODE_FLOW",
|
||||
resourceId: String(flowId),
|
||||
resourceName: flowName,
|
||||
tableName: "node_flows",
|
||||
summary: `노드 플로우 "${flowName}" 수정`,
|
||||
changes: {
|
||||
before: oldFlow ? { flowName: (oldFlow as any).flow_name, flowDescription: (oldFlow as any).flow_description } : undefined,
|
||||
after: { flowName, flowDescription },
|
||||
},
|
||||
ipAddress: getClientIp(req as any),
|
||||
requestPath: req.originalUrl,
|
||||
});
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: "플로우가 수정되었습니다.",
|
||||
@@ -187,10 +227,15 @@ router.put("/", async (req: Request, res: Response) => {
|
||||
/**
|
||||
* 플로우 삭제
|
||||
*/
|
||||
router.delete("/:flowId", async (req: Request, res: Response) => {
|
||||
router.delete("/:flowId", async (req: AuthenticatedRequest, res: Response) => {
|
||||
try {
|
||||
const { flowId } = req.params;
|
||||
|
||||
const oldFlow = await queryOne(
|
||||
`SELECT flow_name, flow_description, company_code FROM node_flows WHERE flow_id = $1`,
|
||||
[flowId]
|
||||
);
|
||||
|
||||
await query(
|
||||
`
|
||||
DELETE FROM node_flows
|
||||
@@ -201,6 +246,25 @@ router.delete("/:flowId", async (req: Request, res: Response) => {
|
||||
|
||||
logger.info(`플로우 삭제 성공: ${flowId}`);
|
||||
|
||||
const userCompanyCode = req.user?.companyCode || "*";
|
||||
const flowName = (oldFlow as any)?.flow_name || `ID:${flowId}`;
|
||||
auditLogService.log({
|
||||
companyCode: userCompanyCode,
|
||||
userId: req.user?.userId || "",
|
||||
userName: req.user?.userName,
|
||||
action: "DELETE",
|
||||
resourceType: "NODE_FLOW",
|
||||
resourceId: String(flowId),
|
||||
resourceName: flowName,
|
||||
tableName: "node_flows",
|
||||
summary: `노드 플로우 "${flowName}" 삭제`,
|
||||
changes: {
|
||||
before: oldFlow ? { flowName: (oldFlow as any).flow_name, flowDescription: (oldFlow as any).flow_description } : undefined,
|
||||
},
|
||||
ipAddress: getClientIp(req as any),
|
||||
requestPath: req.originalUrl,
|
||||
});
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: "플로우가 삭제되었습니다.",
|
||||
|
||||
@@ -1,10 +1,36 @@
|
||||
import { Router } from "express";
|
||||
import { authenticateToken } from "../middleware/authMiddleware";
|
||||
import {
|
||||
getPkgUnits, createPkgUnit, updatePkgUnit, deletePkgUnit,
|
||||
getPkgUnitItems, createPkgUnitItem, deletePkgUnitItem,
|
||||
getLoadingUnits, createLoadingUnit, updateLoadingUnit, deleteLoadingUnit,
|
||||
getLoadingUnitPkgs, createLoadingUnitPkg, deleteLoadingUnitPkg,
|
||||
} from "../controllers/packagingController";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.use(authenticateToken);
|
||||
|
||||
// TODO: 포장/적재정보 관리 API 구현 예정
|
||||
// 포장단위
|
||||
router.get("/pkg-units", getPkgUnits);
|
||||
router.post("/pkg-units", createPkgUnit);
|
||||
router.put("/pkg-units/:id", updatePkgUnit);
|
||||
router.delete("/pkg-units/:id", deletePkgUnit);
|
||||
|
||||
// 포장단위 매칭품목
|
||||
router.get("/pkg-unit-items/:pkgCode", getPkgUnitItems);
|
||||
router.post("/pkg-unit-items", createPkgUnitItem);
|
||||
router.delete("/pkg-unit-items/:id", deletePkgUnitItem);
|
||||
|
||||
// 적재함
|
||||
router.get("/loading-units", getLoadingUnits);
|
||||
router.post("/loading-units", createLoadingUnit);
|
||||
router.put("/loading-units/:id", updateLoadingUnit);
|
||||
router.delete("/loading-units/:id", deleteLoadingUnit);
|
||||
|
||||
// 적재함 포장구성
|
||||
router.get("/loading-unit-pkgs/:loadingCode", getLoadingUnitPkgs);
|
||||
router.post("/loading-unit-pkgs", createLoadingUnitPkg);
|
||||
router.delete("/loading-unit-pkgs/:id", deleteLoadingUnitPkg);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -41,7 +41,8 @@ export type AuditResourceType =
|
||||
| "DATA"
|
||||
| "TABLE"
|
||||
| "NUMBERING_RULE"
|
||||
| "BATCH";
|
||||
| "BATCH"
|
||||
| "NODE_FLOW";
|
||||
|
||||
export interface AuditLogParams {
|
||||
companyCode: string;
|
||||
|
||||
@@ -2346,19 +2346,24 @@ export class ScreenManagementService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 메뉴별 화면 목록 조회 (✅ Raw Query 전환 완료)
|
||||
* 메뉴별 화면 목록 조회
|
||||
* company_code 매칭: 본인 회사 할당 + SUPER_ADMIN 글로벌 할당('*') 모두 조회
|
||||
* 본인 회사 할당이 우선, 없으면 글로벌 할당 사용
|
||||
*/
|
||||
async getScreensByMenu(
|
||||
menuObjid: number,
|
||||
companyCode: string,
|
||||
): Promise<ScreenDefinition[]> {
|
||||
const screens = await query<any>(
|
||||
`SELECT sd.* FROM screen_menu_assignments sma
|
||||
`SELECT sd.*
|
||||
FROM screen_menu_assignments sma
|
||||
INNER JOIN screen_definitions sd ON sma.screen_id = sd.screen_id
|
||||
WHERE sma.menu_objid = $1
|
||||
AND sma.company_code = $2
|
||||
AND (sma.company_code = $2 OR sma.company_code = '*')
|
||||
AND sma.is_active = 'Y'
|
||||
ORDER BY sma.display_order ASC`,
|
||||
ORDER BY
|
||||
CASE WHEN sma.company_code = $2 THEN 0 ELSE 1 END,
|
||||
sma.display_order ASC`,
|
||||
[menuObjid, companyCode],
|
||||
);
|
||||
|
||||
|
||||
@@ -3367,22 +3367,26 @@ export class TableManagementService {
|
||||
`${safeColumn} != '${String(value).replace(/'/g, "''")}'`
|
||||
);
|
||||
break;
|
||||
case "in":
|
||||
if (Array.isArray(value) && value.length > 0) {
|
||||
const values = value
|
||||
case "in": {
|
||||
const inArr = Array.isArray(value) ? value : value != null && value !== "" ? [String(value)] : [];
|
||||
if (inArr.length > 0) {
|
||||
const values = inArr
|
||||
.map((v) => `'${String(v).replace(/'/g, "''")}'`)
|
||||
.join(", ");
|
||||
filterConditions.push(`${safeColumn} IN (${values})`);
|
||||
}
|
||||
break;
|
||||
case "not_in":
|
||||
if (Array.isArray(value) && value.length > 0) {
|
||||
const values = value
|
||||
}
|
||||
case "not_in": {
|
||||
const notInArr = Array.isArray(value) ? value : value != null && value !== "" ? [String(value)] : [];
|
||||
if (notInArr.length > 0) {
|
||||
const values = notInArr
|
||||
.map((v) => `'${String(v).replace(/'/g, "''")}'`)
|
||||
.join(", ");
|
||||
filterConditions.push(`${safeColumn} NOT IN (${values})`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "contains":
|
||||
filterConditions.push(
|
||||
`${safeColumn} LIKE '%${String(value).replace(/'/g, "''")}%'`
|
||||
|
||||
@@ -98,23 +98,27 @@ export function buildDataFilterWhereClause(
|
||||
paramIndex++;
|
||||
break;
|
||||
|
||||
case "in":
|
||||
if (Array.isArray(value) && value.length > 0) {
|
||||
const placeholders = value.map((_, idx) => `$${paramIndex + idx}`).join(", ");
|
||||
case "in": {
|
||||
const inArr = Array.isArray(value) ? value : value != null && value !== "" ? [String(value)] : [];
|
||||
if (inArr.length > 0) {
|
||||
const placeholders = inArr.map((_, idx) => `$${paramIndex + idx}`).join(", ");
|
||||
conditions.push(`${columnRef} IN (${placeholders})`);
|
||||
params.push(...value);
|
||||
paramIndex += value.length;
|
||||
params.push(...inArr);
|
||||
paramIndex += inArr.length;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "not_in":
|
||||
if (Array.isArray(value) && value.length > 0) {
|
||||
const placeholders = value.map((_, idx) => `$${paramIndex + idx}`).join(", ");
|
||||
case "not_in": {
|
||||
const notInArr = Array.isArray(value) ? value : value != null && value !== "" ? [String(value)] : [];
|
||||
if (notInArr.length > 0) {
|
||||
const placeholders = notInArr.map((_, idx) => `$${paramIndex + idx}`).join(", ");
|
||||
conditions.push(`${columnRef} NOT IN (${placeholders})`);
|
||||
params.push(...value);
|
||||
paramIndex += value.length;
|
||||
params.push(...notInArr);
|
||||
paramIndex += notInArr.length;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "contains":
|
||||
conditions.push(`${columnRef} LIKE $${paramIndex}`);
|
||||
|
||||
Reference in New Issue
Block a user