- Enhanced the `createMoldSerial` function to automatically generate serial numbers based on defined numbering rules when the serial number is not provided. - Integrated error handling for the automatic numbering process, ensuring robust logging for success and failure cases. - Updated the `NumberingRuleService` to support reference column handling, allowing for dynamic prefix generation based on related data. - Modified the frontend components to accommodate new reference configurations, improving user experience in managing numbering rules. These changes significantly enhance the mold management functionality by automating serial number generation and improving the flexibility of numbering rules.
498 lines
18 KiB
TypeScript
498 lines
18 KiB
TypeScript
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;
|
|
|
|
let finalSerialNumber = serial_number;
|
|
|
|
// 일련번호가 비어있으면 채번 규칙으로 자동 생성
|
|
if (!finalSerialNumber) {
|
|
try {
|
|
const { numberingRuleService } = await import("../services/numberingRuleService");
|
|
const rule = await numberingRuleService.getNumberingRuleByColumn(
|
|
companyCode,
|
|
"mold_serial",
|
|
"serial_number"
|
|
);
|
|
|
|
if (rule) {
|
|
// formData에 mold_code를 포함 (reference 파트에서 참조)
|
|
const formData = { mold_code: moldCode, ...req.body };
|
|
finalSerialNumber = await numberingRuleService.allocateCode(
|
|
rule.ruleId,
|
|
companyCode,
|
|
formData
|
|
);
|
|
logger.info("일련번호 자동 채번 완료", { serialNumber: finalSerialNumber, ruleId: rule.ruleId });
|
|
}
|
|
} catch (numError: any) {
|
|
logger.error("일련번호 자동 채번 실패", { error: numError.message });
|
|
}
|
|
}
|
|
|
|
if (!finalSerialNumber) {
|
|
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, finalSerialNumber, 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 });
|
|
}
|
|
}
|