feat: Implement Mold Management API and Frontend Integration

- Added new API endpoints for mold management, including CRUD operations for molds, mold serials, inspections, and parts.
- Created the `moldRoutes` to handle requests related to mold management.
- Developed the `moldController` to manage the business logic for mold operations, ensuring proper company code filtering for data access.
- Integrated frontend API calls for mold management, allowing users to interact with the mold data seamlessly.
- Introduced a new component for displaying status counts, enhancing the user interface for monitoring mold statuses.

These additions improve the overall functionality and user experience in managing molds within the application.
This commit is contained in:
kjs
2026-03-09 13:15:41 +09:00
parent 27558787b0
commit 13506912d9
10 changed files with 1312 additions and 0 deletions

View File

@@ -0,0 +1,470 @@
import { Response } from "express";
import { AuthenticatedRequest } from "../types/auth";
import { query } from "../database/db";
import { logger } from "../utils/logger";
// ============================================
// 금형 마스터 CRUD
// ============================================
export async function getMoldList(req: AuthenticatedRequest, res: Response): Promise<void> {
try {
const companyCode = req.user!.companyCode;
const { mold_code, mold_name, mold_type, operation_status } = req.query;
const conditions: string[] = [];
const params: any[] = [];
let paramIndex = 1;
if (companyCode === "*") {
// 최고 관리자: 전체 조회
} else {
conditions.push(`company_code = $${paramIndex}`);
params.push(companyCode);
paramIndex++;
}
if (mold_code) {
conditions.push(`mold_code ILIKE $${paramIndex}`);
params.push(`%${mold_code}%`);
paramIndex++;
}
if (mold_name) {
conditions.push(`mold_name ILIKE $${paramIndex}`);
params.push(`%${mold_name}%`);
paramIndex++;
}
if (mold_type) {
conditions.push(`mold_type = $${paramIndex}`);
params.push(mold_type);
paramIndex++;
}
if (operation_status) {
conditions.push(`operation_status = $${paramIndex}`);
params.push(operation_status);
paramIndex++;
}
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
const sql = `SELECT * FROM mold_mng ${whereClause} ORDER BY created_date DESC`;
const result = await query(sql, params);
logger.info("금형 목록 조회", { companyCode, count: result.length });
res.json({ success: true, data: result });
} catch (error: any) {
logger.error("금형 목록 조회 오류", error);
res.status(500).json({ success: false, message: error.message });
}
}
export async function getMoldDetail(req: AuthenticatedRequest, res: Response): Promise<void> {
try {
const companyCode = req.user!.companyCode;
const { moldCode } = req.params;
let sql: string;
let params: any[];
if (companyCode === "*") {
sql = `SELECT * FROM mold_mng WHERE mold_code = $1 LIMIT 1`;
params = [moldCode];
} else {
sql = `SELECT * FROM mold_mng WHERE mold_code = $1 AND company_code = $2 LIMIT 1`;
params = [moldCode, companyCode];
}
const result = await query(sql, params);
if (result.length === 0) {
res.status(404).json({ success: false, message: "금형을 찾을 수 없습니다." });
return;
}
res.json({ success: true, data: result[0] });
} catch (error: any) {
logger.error("금형 상세 조회 오류", error);
res.status(500).json({ success: false, message: error.message });
}
}
export async function createMold(req: AuthenticatedRequest, res: Response): Promise<void> {
try {
const companyCode = req.user!.companyCode;
const userId = req.user!.userId;
const {
mold_code, mold_name, mold_type, category, manufacturer,
manufacturing_number, manufacturing_date, cavity_count,
shot_count, mold_quantity, base_input_qty, operation_status,
remarks, image_path, memo,
} = req.body;
if (!mold_code || !mold_name) {
res.status(400).json({ success: false, message: "금형코드와 금형명은 필수입니다." });
return;
}
const sql = `
INSERT INTO mold_mng (
company_code, mold_code, mold_name, mold_type, category,
manufacturer, manufacturing_number, manufacturing_date,
cavity_count, shot_count, mold_quantity, base_input_qty,
operation_status, remarks, image_path, memo, writer
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17)
RETURNING *
`;
const params = [
companyCode, mold_code, mold_name, mold_type || null, category || null,
manufacturer || null, manufacturing_number || null, manufacturing_date || null,
cavity_count || 0, shot_count || 0, mold_quantity || 1, base_input_qty || 0,
operation_status || "ACTIVE", remarks || null, image_path || null, memo || null, userId,
];
const result = await query(sql, params);
logger.info("금형 생성", { companyCode, moldCode: mold_code });
res.json({ success: true, data: result[0], message: "금형이 등록되었습니다." });
} catch (error: any) {
if (error.code === "23505") {
res.status(409).json({ success: false, message: "이미 존재하는 금형코드입니다." });
return;
}
logger.error("금형 생성 오류", error);
res.status(500).json({ success: false, message: error.message });
}
}
export async function updateMold(req: AuthenticatedRequest, res: Response): Promise<void> {
try {
const companyCode = req.user!.companyCode;
const { moldCode } = req.params;
const {
mold_name, mold_type, category, manufacturer,
manufacturing_number, manufacturing_date, cavity_count,
shot_count, mold_quantity, base_input_qty, operation_status,
remarks, image_path, memo,
} = req.body;
const sql = `
UPDATE mold_mng SET
mold_name = COALESCE($1, mold_name),
mold_type = $2, category = $3, manufacturer = $4,
manufacturing_number = $5, manufacturing_date = $6,
cavity_count = COALESCE($7, cavity_count),
shot_count = COALESCE($8, shot_count),
mold_quantity = COALESCE($9, mold_quantity),
base_input_qty = COALESCE($10, base_input_qty),
operation_status = COALESCE($11, operation_status),
remarks = $12, image_path = $13, memo = $14,
updated_date = NOW()
WHERE mold_code = $15 AND company_code = $16
RETURNING *
`;
const params = [
mold_name, mold_type, category, manufacturer,
manufacturing_number, manufacturing_date,
cavity_count, shot_count, mold_quantity, base_input_qty,
operation_status, remarks, image_path, memo,
moldCode, companyCode,
];
const result = await query(sql, params);
if (result.length === 0) {
res.status(404).json({ success: false, message: "금형을 찾을 수 없습니다." });
return;
}
logger.info("금형 수정", { companyCode, moldCode });
res.json({ success: true, data: result[0], message: "금형이 수정되었습니다." });
} catch (error: any) {
logger.error("금형 수정 오류", error);
res.status(500).json({ success: false, message: error.message });
}
}
export async function deleteMold(req: AuthenticatedRequest, res: Response): Promise<void> {
try {
const companyCode = req.user!.companyCode;
const { moldCode } = req.params;
// 관련 데이터 먼저 삭제
await query(`DELETE FROM mold_serial WHERE mold_code = $1 AND company_code = $2`, [moldCode, companyCode]);
await query(`DELETE FROM mold_inspection_item WHERE mold_code = $1 AND company_code = $2`, [moldCode, companyCode]);
await query(`DELETE FROM mold_part WHERE mold_code = $1 AND company_code = $2`, [moldCode, companyCode]);
const result = await query(
`DELETE FROM mold_mng WHERE mold_code = $1 AND company_code = $2 RETURNING id`,
[moldCode, companyCode]
);
if (result.length === 0) {
res.status(404).json({ success: false, message: "금형을 찾을 수 없습니다." });
return;
}
logger.info("금형 삭제", { companyCode, moldCode });
res.json({ success: true, message: "금형이 삭제되었습니다." });
} catch (error: any) {
logger.error("금형 삭제 오류", error);
res.status(500).json({ success: false, message: error.message });
}
}
// ============================================
// 일련번호 CRUD
// ============================================
export async function getMoldSerials(req: AuthenticatedRequest, res: Response): Promise<void> {
try {
const companyCode = req.user!.companyCode;
const { moldCode } = req.params;
const sql = `SELECT * FROM mold_serial WHERE mold_code = $1 AND company_code = $2 ORDER BY serial_number`;
const result = await query(sql, [moldCode, companyCode]);
res.json({ success: true, data: result });
} catch (error: any) {
logger.error("일련번호 목록 조회 오류", error);
res.status(500).json({ success: false, message: error.message });
}
}
export async function createMoldSerial(req: AuthenticatedRequest, res: Response): Promise<void> {
try {
const companyCode = req.user!.companyCode;
const userId = req.user!.userId;
const { moldCode } = req.params;
const { serial_number, status, progress, work_description, manager, completion_date, remarks } = req.body;
if (!serial_number) {
res.status(400).json({ success: false, message: "일련번호는 필수입니다." });
return;
}
const sql = `
INSERT INTO mold_serial (company_code, mold_code, serial_number, status, progress, work_description, manager, completion_date, remarks, writer)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)
RETURNING *
`;
const params = [
companyCode, moldCode, serial_number, status || "STORED",
progress || 0, work_description || null, manager || null,
completion_date || null, remarks || null, userId,
];
const result = await query(sql, params);
res.json({ success: true, data: result[0], message: "일련번호가 등록되었습니다." });
} catch (error: any) {
if (error.code === "23505") {
res.status(409).json({ success: false, message: "이미 존재하는 일련번호입니다." });
return;
}
logger.error("일련번호 생성 오류", error);
res.status(500).json({ success: false, message: error.message });
}
}
export async function deleteMoldSerial(req: AuthenticatedRequest, res: Response): Promise<void> {
try {
const companyCode = req.user!.companyCode;
const { id } = req.params;
const result = await query(
`DELETE FROM mold_serial WHERE id = $1 AND company_code = $2 RETURNING id`,
[id, companyCode]
);
if (result.length === 0) {
res.status(404).json({ success: false, message: "일련번호를 찾을 수 없습니다." });
return;
}
res.json({ success: true, message: "일련번호가 삭제되었습니다." });
} catch (error: any) {
logger.error("일련번호 삭제 오류", error);
res.status(500).json({ success: false, message: error.message });
}
}
// ============================================
// 점검항목 CRUD
// ============================================
export async function getMoldInspections(req: AuthenticatedRequest, res: Response): Promise<void> {
try {
const companyCode = req.user!.companyCode;
const { moldCode } = req.params;
const sql = `SELECT * FROM mold_inspection_item WHERE mold_code = $1 AND company_code = $2 ORDER BY created_date`;
const result = await query(sql, [moldCode, companyCode]);
res.json({ success: true, data: result });
} catch (error: any) {
logger.error("점검항목 조회 오류", error);
res.status(500).json({ success: false, message: error.message });
}
}
export async function createMoldInspection(req: AuthenticatedRequest, res: Response): Promise<void> {
try {
const companyCode = req.user!.companyCode;
const userId = req.user!.userId;
const { moldCode } = req.params;
const {
inspection_item, inspection_cycle, inspection_method,
inspection_content, lower_limit, upper_limit, unit,
is_active, checklist, remarks,
} = req.body;
if (!inspection_item) {
res.status(400).json({ success: false, message: "점검항목명은 필수입니다." });
return;
}
const sql = `
INSERT INTO mold_inspection_item (
company_code, mold_code, inspection_item, inspection_cycle,
inspection_method, inspection_content, lower_limit, upper_limit,
unit, is_active, checklist, remarks, writer
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13)
RETURNING *
`;
const params = [
companyCode, moldCode, inspection_item, inspection_cycle || null,
inspection_method || null, inspection_content || null,
lower_limit || null, upper_limit || null, unit || null,
is_active || "Y", checklist || null, remarks || null, userId,
];
const result = await query(sql, params);
res.json({ success: true, data: result[0], message: "점검항목이 등록되었습니다." });
} catch (error: any) {
logger.error("점검항목 생성 오류", error);
res.status(500).json({ success: false, message: error.message });
}
}
export async function deleteMoldInspection(req: AuthenticatedRequest, res: Response): Promise<void> {
try {
const companyCode = req.user!.companyCode;
const { id } = req.params;
const result = await query(
`DELETE FROM mold_inspection_item WHERE id = $1 AND company_code = $2 RETURNING id`,
[id, companyCode]
);
if (result.length === 0) {
res.status(404).json({ success: false, message: "점검항목을 찾을 수 없습니다." });
return;
}
res.json({ success: true, message: "점검항목이 삭제되었습니다." });
} catch (error: any) {
logger.error("점검항목 삭제 오류", error);
res.status(500).json({ success: false, message: error.message });
}
}
// ============================================
// 부품 CRUD
// ============================================
export async function getMoldParts(req: AuthenticatedRequest, res: Response): Promise<void> {
try {
const companyCode = req.user!.companyCode;
const { moldCode } = req.params;
const sql = `SELECT * FROM mold_part WHERE mold_code = $1 AND company_code = $2 ORDER BY created_date`;
const result = await query(sql, [moldCode, companyCode]);
res.json({ success: true, data: result });
} catch (error: any) {
logger.error("부품 목록 조회 오류", error);
res.status(500).json({ success: false, message: error.message });
}
}
export async function createMoldPart(req: AuthenticatedRequest, res: Response): Promise<void> {
try {
const companyCode = req.user!.companyCode;
const userId = req.user!.userId;
const { moldCode } = req.params;
const {
part_name, replacement_cycle, unit, specification,
manufacturer, manufacturer_code, image_path, remarks,
} = req.body;
if (!part_name) {
res.status(400).json({ success: false, message: "부품명은 필수입니다." });
return;
}
const sql = `
INSERT INTO mold_part (
company_code, mold_code, part_name, replacement_cycle,
unit, specification, manufacturer, manufacturer_code,
image_path, remarks, writer
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)
RETURNING *
`;
const params = [
companyCode, moldCode, part_name, replacement_cycle || null,
unit || null, specification || null, manufacturer || null,
manufacturer_code || null, image_path || null, remarks || null, userId,
];
const result = await query(sql, params);
res.json({ success: true, data: result[0], message: "부품이 등록되었습니다." });
} catch (error: any) {
logger.error("부품 생성 오류", error);
res.status(500).json({ success: false, message: error.message });
}
}
export async function deleteMoldPart(req: AuthenticatedRequest, res: Response): Promise<void> {
try {
const companyCode = req.user!.companyCode;
const { id } = req.params;
const result = await query(
`DELETE FROM mold_part WHERE id = $1 AND company_code = $2 RETURNING id`,
[id, companyCode]
);
if (result.length === 0) {
res.status(404).json({ success: false, message: "부품을 찾을 수 없습니다." });
return;
}
res.json({ success: true, message: "부품이 삭제되었습니다." });
} catch (error: any) {
logger.error("부품 삭제 오류", error);
res.status(500).json({ success: false, message: error.message });
}
}
// ============================================
// 일련번호 현황 집계
// ============================================
export async function getMoldSerialSummary(req: AuthenticatedRequest, res: Response): Promise<void> {
try {
const companyCode = req.user!.companyCode;
const { moldCode } = req.params;
const sql = `
SELECT
COUNT(*) as total,
COUNT(*) FILTER (WHERE status = 'IN_USE') as in_use,
COUNT(*) FILTER (WHERE status = 'REPAIR') as repair,
COUNT(*) FILTER (WHERE status = 'STORED') as stored,
COUNT(*) FILTER (WHERE status = 'DISPOSED') as disposed
FROM mold_serial
WHERE mold_code = $1 AND company_code = $2
`;
const result = await query(sql, [moldCode, companyCode]);
res.json({ success: true, data: result[0] });
} catch (error: any) {
logger.error("일련번호 현황 조회 오류", error);
res.status(500).json({ success: false, message: error.message });
}
}