chore: 미사용 수주 등록 모듈(orderController) 삭제
- 백엔드: orderController.ts, orderRoutes.ts 삭제 - 프론트엔드: components/order/, order-registration-modal/ 삭제 - app.ts, index.ts, getComponentConfigPanel.tsx에서 참조 제거 - 현재 sales_order_mng 기반 수주 시스템 사용으로 구 모듈 불필요
This commit is contained in:
@@ -1,276 +0,0 @@
|
||||
import { Response } from "express";
|
||||
import { AuthenticatedRequest } from "../types/auth";
|
||||
import { getPool } from "../database/db";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
/**
|
||||
* 수주 번호 생성 함수
|
||||
* 형식: ORD + YYMMDD + 4자리 시퀀스
|
||||
* 예: ORD250114001
|
||||
*/
|
||||
async function generateOrderNumber(companyCode: string): Promise<string> {
|
||||
const pool = getPool();
|
||||
const today = new Date();
|
||||
const year = today.getFullYear().toString().slice(2); // 25
|
||||
const month = String(today.getMonth() + 1).padStart(2, "0"); // 01
|
||||
const day = String(today.getDate()).padStart(2, "0"); // 14
|
||||
const dateStr = `${year}${month}${day}`; // 250114
|
||||
|
||||
// 당일 수주 카운트 조회
|
||||
const countQuery = `
|
||||
SELECT COUNT(*) as count
|
||||
FROM order_mng_master
|
||||
WHERE objid LIKE $1
|
||||
AND writer LIKE $2
|
||||
`;
|
||||
|
||||
const pattern = `ORD${dateStr}%`;
|
||||
const result = await pool.query(countQuery, [pattern, `%${companyCode}%`]);
|
||||
const count = parseInt(result.rows[0]?.count || "0");
|
||||
const seq = count + 1;
|
||||
|
||||
return `ORD${dateStr}${String(seq).padStart(4, "0")}`; // ORD250114001
|
||||
}
|
||||
|
||||
/**
|
||||
* 수주 등록 API
|
||||
* POST /api/orders
|
||||
*/
|
||||
export async function createOrder(req: AuthenticatedRequest, res: Response) {
|
||||
const pool = getPool();
|
||||
|
||||
try {
|
||||
const {
|
||||
inputMode, // 입력 방식
|
||||
customerCode, // 거래처 코드
|
||||
deliveryDate, // 납품일
|
||||
items, // 품목 목록
|
||||
memo, // 메모
|
||||
} = req.body;
|
||||
|
||||
// 멀티테넌시
|
||||
const companyCode = req.user!.companyCode;
|
||||
const userId = req.user!.userId;
|
||||
|
||||
// 유효성 검사
|
||||
if (!customerCode) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "거래처 코드는 필수입니다",
|
||||
});
|
||||
}
|
||||
|
||||
if (!items || items.length === 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "품목은 최소 1개 이상 필요합니다",
|
||||
});
|
||||
}
|
||||
|
||||
// 수주 번호 생성
|
||||
const orderNo = await generateOrderNumber(companyCode);
|
||||
|
||||
// 전체 금액 계산
|
||||
const totalAmount = items.reduce(
|
||||
(sum: number, item: any) => sum + (item.amount || 0),
|
||||
0
|
||||
);
|
||||
|
||||
// 수주 마스터 생성
|
||||
const masterQuery = `
|
||||
INSERT INTO order_mng_master (
|
||||
objid,
|
||||
partner_objid,
|
||||
final_delivery_date,
|
||||
reason,
|
||||
status,
|
||||
reg_date,
|
||||
writer
|
||||
) VALUES ($1, $2, $3, $4, $5, NOW(), $6)
|
||||
RETURNING *
|
||||
`;
|
||||
|
||||
const masterResult = await pool.query(masterQuery, [
|
||||
orderNo,
|
||||
customerCode,
|
||||
deliveryDate || null,
|
||||
memo || null,
|
||||
"진행중",
|
||||
`${userId}|${companyCode}`,
|
||||
]);
|
||||
|
||||
const masterObjid = masterResult.rows[0].objid;
|
||||
|
||||
// 수주 상세 (품목) 생성
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
const subObjid = `${orderNo}_${i + 1}`;
|
||||
|
||||
const subQuery = `
|
||||
INSERT INTO order_mng_sub (
|
||||
objid,
|
||||
order_mng_master_objid,
|
||||
part_objid,
|
||||
partner_objid,
|
||||
partner_price,
|
||||
partner_qty,
|
||||
delivery_date,
|
||||
status,
|
||||
regdate,
|
||||
writer
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW(), $9)
|
||||
`;
|
||||
|
||||
await pool.query(subQuery, [
|
||||
subObjid,
|
||||
masterObjid,
|
||||
item.item_code || item.id, // 품목 코드
|
||||
customerCode,
|
||||
item.unit_price || 0,
|
||||
item.quantity || 0,
|
||||
item.delivery_date || deliveryDate || null,
|
||||
"진행중",
|
||||
`${userId}|${companyCode}`,
|
||||
]);
|
||||
}
|
||||
|
||||
logger.info("수주 등록 성공", {
|
||||
companyCode,
|
||||
orderNo,
|
||||
masterObjid,
|
||||
itemCount: items.length,
|
||||
totalAmount,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
orderNo,
|
||||
masterObjid,
|
||||
itemCount: items.length,
|
||||
totalAmount,
|
||||
},
|
||||
message: "수주가 등록되었습니다",
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error("수주 등록 오류", {
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message || "수주 등록 중 오류가 발생했습니다",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 수주 목록 조회 API (마스터 + 품목 JOIN)
|
||||
* GET /api/orders
|
||||
*/
|
||||
export async function getOrders(req: AuthenticatedRequest, res: Response) {
|
||||
const pool = getPool();
|
||||
|
||||
try {
|
||||
const { page = "1", limit = "20", searchText = "" } = req.query;
|
||||
const companyCode = req.user!.companyCode;
|
||||
|
||||
const offset = (parseInt(page as string) - 1) * parseInt(limit as string);
|
||||
|
||||
// WHERE 조건
|
||||
const whereConditions: string[] = [];
|
||||
const params: any[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
// 멀티테넌시 (writer 필드에 company_code 포함)
|
||||
if (companyCode !== "*") {
|
||||
whereConditions.push(`m.writer LIKE $${paramIndex}`);
|
||||
params.push(`%${companyCode}%`);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
// 검색
|
||||
if (searchText) {
|
||||
whereConditions.push(`m.objid LIKE $${paramIndex}`);
|
||||
params.push(`%${searchText}%`);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
const whereClause =
|
||||
whereConditions.length > 0
|
||||
? `WHERE ${whereConditions.join(" AND ")}`
|
||||
: "";
|
||||
|
||||
// 카운트 쿼리 (고유한 수주 개수)
|
||||
const countQuery = `
|
||||
SELECT COUNT(DISTINCT m.objid) as count
|
||||
FROM order_mng_master m
|
||||
${whereClause}
|
||||
`;
|
||||
const countResult = await pool.query(countQuery, params);
|
||||
const total = parseInt(countResult.rows[0]?.count || "0");
|
||||
|
||||
// 데이터 쿼리 (마스터 + 품목 JOIN)
|
||||
const dataQuery = `
|
||||
SELECT
|
||||
m.objid as order_no,
|
||||
m.partner_objid,
|
||||
m.final_delivery_date,
|
||||
m.reason,
|
||||
m.status,
|
||||
m.reg_date,
|
||||
m.writer,
|
||||
COALESCE(
|
||||
json_agg(
|
||||
CASE WHEN s.objid IS NOT NULL THEN
|
||||
json_build_object(
|
||||
'sub_objid', s.objid,
|
||||
'part_objid', s.part_objid,
|
||||
'partner_price', s.partner_price,
|
||||
'partner_qty', s.partner_qty,
|
||||
'delivery_date', s.delivery_date,
|
||||
'status', s.status,
|
||||
'regdate', s.regdate
|
||||
)
|
||||
END
|
||||
ORDER BY s.regdate
|
||||
) FILTER (WHERE s.objid IS NOT NULL),
|
||||
'[]'::json
|
||||
) as items
|
||||
FROM order_mng_master m
|
||||
LEFT JOIN order_mng_sub s ON m.objid = s.order_mng_master_objid
|
||||
${whereClause}
|
||||
GROUP BY m.objid, m.partner_objid, m.final_delivery_date, m.reason, m.status, m.reg_date, m.writer
|
||||
ORDER BY m.reg_date DESC
|
||||
LIMIT $${paramIndex} OFFSET $${paramIndex + 1}
|
||||
`;
|
||||
|
||||
params.push(parseInt(limit as string));
|
||||
params.push(offset);
|
||||
|
||||
const dataResult = await pool.query(dataQuery, params);
|
||||
|
||||
logger.info("수주 목록 조회 성공", {
|
||||
companyCode,
|
||||
total,
|
||||
page: parseInt(page as string),
|
||||
itemCount: dataResult.rows.length,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: dataResult.rows,
|
||||
pagination: {
|
||||
total,
|
||||
page: parseInt(page as string),
|
||||
limit: parseInt(limit as string),
|
||||
},
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error("수주 목록 조회 오류", { error: error.message });
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user