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 });
|
|
}
|
|
}
|