- Added production plan management routes and controller to handle various operations including order summary retrieval, stock shortage checks, and CRUD operations for production plans. - Introduced service layer for production plan management, encapsulating business logic for handling production-related data. - Created API client for production plan management, enabling frontend interaction with the new backend endpoints. - Enhanced button actions to support API calls for production scheduling and management tasks. These changes aim to improve the management of production plans, enhancing usability and functionality within the ERP system. Made-with: Cursor
191 lines
6.5 KiB
TypeScript
191 lines
6.5 KiB
TypeScript
/**
|
|
* 생산계획 컨트롤러
|
|
*/
|
|
|
|
import { Request, Response } from "express";
|
|
import * as productionService from "../services/productionPlanService";
|
|
import { logger } from "../utils/logger";
|
|
|
|
// ─── 수주 데이터 조회 (품목별 그룹핑) ───
|
|
|
|
export async function getOrderSummary(req: Request, res: Response) {
|
|
try {
|
|
const companyCode = req.user!.companyCode;
|
|
const { excludePlanned, itemCode, itemName } = req.query;
|
|
|
|
const data = await productionService.getOrderSummary(companyCode, {
|
|
excludePlanned: excludePlanned === "true",
|
|
itemCode: itemCode as string,
|
|
itemName: itemName as string,
|
|
});
|
|
|
|
return res.json({ success: true, data });
|
|
} catch (error: any) {
|
|
logger.error("수주 데이터 조회 실패", { error: error.message });
|
|
return res.status(500).json({ success: false, message: error.message });
|
|
}
|
|
}
|
|
|
|
// ─── 안전재고 부족분 조회 ───
|
|
|
|
export async function getStockShortage(req: Request, res: Response) {
|
|
try {
|
|
const companyCode = req.user!.companyCode;
|
|
const data = await productionService.getStockShortage(companyCode);
|
|
return res.json({ success: true, data });
|
|
} catch (error: any) {
|
|
logger.error("안전재고 부족분 조회 실패", { error: error.message });
|
|
return res.status(500).json({ success: false, message: error.message });
|
|
}
|
|
}
|
|
|
|
// ─── 생산계획 상세 조회 ───
|
|
|
|
export async function getPlanById(req: Request, res: Response) {
|
|
try {
|
|
const companyCode = req.user!.companyCode;
|
|
const planId = parseInt(req.params.id, 10);
|
|
const data = await productionService.getPlanById(companyCode, planId);
|
|
|
|
if (!data) {
|
|
return res.status(404).json({ success: false, message: "생산계획을 찾을 수 없습니다" });
|
|
}
|
|
return res.json({ success: true, data });
|
|
} catch (error: any) {
|
|
logger.error("생산계획 조회 실패", { error: error.message });
|
|
return res.status(500).json({ success: false, message: error.message });
|
|
}
|
|
}
|
|
|
|
// ─── 생산계획 수정 ───
|
|
|
|
export async function updatePlan(req: Request, res: Response) {
|
|
try {
|
|
const companyCode = req.user!.companyCode;
|
|
const planId = parseInt(req.params.id, 10);
|
|
const updatedBy = req.user!.userId;
|
|
|
|
const data = await productionService.updatePlan(companyCode, planId, req.body, updatedBy);
|
|
return res.json({ success: true, data });
|
|
} catch (error: any) {
|
|
logger.error("생산계획 수정 실패", { error: error.message });
|
|
return res.status(error.message.includes("찾을 수 없") ? 404 : 500).json({
|
|
success: false,
|
|
message: error.message,
|
|
});
|
|
}
|
|
}
|
|
|
|
// ─── 생산계획 삭제 ───
|
|
|
|
export async function deletePlan(req: Request, res: Response) {
|
|
try {
|
|
const companyCode = req.user!.companyCode;
|
|
const planId = parseInt(req.params.id, 10);
|
|
|
|
await productionService.deletePlan(companyCode, planId);
|
|
return res.json({ success: true, message: "삭제되었습니다" });
|
|
} catch (error: any) {
|
|
logger.error("생산계획 삭제 실패", { error: error.message });
|
|
return res.status(error.message.includes("찾을 수 없") ? 404 : 500).json({
|
|
success: false,
|
|
message: error.message,
|
|
});
|
|
}
|
|
}
|
|
|
|
// ─── 자동 스케줄 생성 ───
|
|
|
|
export async function generateSchedule(req: Request, res: Response) {
|
|
try {
|
|
const companyCode = req.user!.companyCode;
|
|
const createdBy = req.user!.userId;
|
|
const { items, options } = req.body;
|
|
|
|
if (!items || !Array.isArray(items) || items.length === 0) {
|
|
return res.status(400).json({ success: false, message: "품목 정보가 필요합니다" });
|
|
}
|
|
|
|
const data = await productionService.generateSchedule(companyCode, items, options || {}, createdBy);
|
|
return res.json({ success: true, data });
|
|
} catch (error: any) {
|
|
logger.error("자동 스케줄 생성 실패", { error: error.message });
|
|
return res.status(500).json({ success: false, message: error.message });
|
|
}
|
|
}
|
|
|
|
// ─── 스케줄 병합 ───
|
|
|
|
export async function mergeSchedules(req: Request, res: Response) {
|
|
try {
|
|
const companyCode = req.user!.companyCode;
|
|
const mergedBy = req.user!.userId;
|
|
const { schedule_ids, product_type } = req.body;
|
|
|
|
if (!schedule_ids || !Array.isArray(schedule_ids) || schedule_ids.length < 2) {
|
|
return res.status(400).json({ success: false, message: "2개 이상의 스케줄을 선택해주세요" });
|
|
}
|
|
|
|
const data = await productionService.mergeSchedules(
|
|
companyCode,
|
|
schedule_ids,
|
|
product_type || "완제품",
|
|
mergedBy
|
|
);
|
|
return res.json({ success: true, data });
|
|
} catch (error: any) {
|
|
logger.error("스케줄 병합 실패", { error: error.message });
|
|
const status = error.message.includes("동일 품목") || error.message.includes("찾을 수 없") ? 400 : 500;
|
|
return res.status(status).json({ success: false, message: error.message });
|
|
}
|
|
}
|
|
|
|
// ─── 반제품 계획 자동 생성 ───
|
|
|
|
export async function generateSemiSchedule(req: Request, res: Response) {
|
|
try {
|
|
const companyCode = req.user!.companyCode;
|
|
const createdBy = req.user!.userId;
|
|
const { plan_ids, options } = req.body;
|
|
|
|
if (!plan_ids || !Array.isArray(plan_ids) || plan_ids.length === 0) {
|
|
return res.status(400).json({ success: false, message: "완제품 계획을 선택해주세요" });
|
|
}
|
|
|
|
const data = await productionService.generateSemiSchedule(
|
|
companyCode,
|
|
plan_ids,
|
|
options || {},
|
|
createdBy
|
|
);
|
|
return res.json({ success: true, data });
|
|
} catch (error: any) {
|
|
logger.error("반제품 계획 생성 실패", { error: error.message });
|
|
return res.status(500).json({ success: false, message: error.message });
|
|
}
|
|
}
|
|
|
|
// ─── 스케줄 분할 ───
|
|
|
|
export async function splitSchedule(req: Request, res: Response) {
|
|
try {
|
|
const companyCode = req.user!.companyCode;
|
|
const splitBy = req.user!.userId;
|
|
const planId = parseInt(req.params.id, 10);
|
|
const { split_qty } = req.body;
|
|
|
|
if (!split_qty || split_qty <= 0) {
|
|
return res.status(400).json({ success: false, message: "분할 수량을 입력해주세요" });
|
|
}
|
|
|
|
const data = await productionService.splitSchedule(companyCode, planId, split_qty, splitBy);
|
|
return res.json({ success: true, data });
|
|
} catch (error: any) {
|
|
logger.error("스케줄 분할 실패", { error: error.message });
|
|
return res.status(error.message.includes("찾을 수 없") ? 404 : 400).json({
|
|
success: false,
|
|
message: error.message,
|
|
});
|
|
}
|
|
}
|