feat: add shipping order and design management features
- Introduced new routes and controllers for managing shipping orders, including listing, saving, and previewing next order numbers. - Added design management routes and controller for handling design requests, projects, tasks, and work logs. - Implemented company code filtering for multi-tenancy support in both shipping order and design request functionalities. - Enhanced the shipping plan routes to include listing and updating plans, improving overall shipping management capabilities. These changes aim to provide comprehensive management features for shipping orders and design processes, facilitating better organization and tracking within the application.
This commit is contained in:
@@ -144,6 +144,218 @@ async function getNormalizedOrders(
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 출하계획 목록 조회 (관리 화면용) ───
|
||||
|
||||
export async function getList(req: AuthenticatedRequest, res: Response) {
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const { dateFrom, dateTo, status, customer, keyword } = req.query;
|
||||
|
||||
const conditions: string[] = [];
|
||||
const params: any[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
// 멀티테넌시
|
||||
if (companyCode === "*") {
|
||||
// 최고 관리자: 전체 조회
|
||||
} else {
|
||||
conditions.push(`sp.company_code = $${paramIndex}`);
|
||||
params.push(companyCode);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
if (dateFrom) {
|
||||
conditions.push(`sp.plan_date >= $${paramIndex}::date`);
|
||||
params.push(dateFrom);
|
||||
paramIndex++;
|
||||
}
|
||||
if (dateTo) {
|
||||
conditions.push(`sp.plan_date <= $${paramIndex}::date`);
|
||||
params.push(dateTo);
|
||||
paramIndex++;
|
||||
}
|
||||
if (status) {
|
||||
conditions.push(`sp.status = $${paramIndex}`);
|
||||
params.push(status);
|
||||
paramIndex++;
|
||||
}
|
||||
if (customer) {
|
||||
conditions.push(`(c.customer_name ILIKE $${paramIndex} OR COALESCE(m.partner_id, d.delivery_partner_code, '') ILIKE $${paramIndex})`);
|
||||
params.push(`%${customer}%`);
|
||||
paramIndex++;
|
||||
}
|
||||
if (keyword) {
|
||||
conditions.push(`(
|
||||
COALESCE(m.order_no, d.order_no, '') ILIKE $${paramIndex}
|
||||
OR COALESCE(d.part_code, m.part_code, '') ILIKE $${paramIndex}
|
||||
OR COALESCE(i.item_name, d.part_name, m.part_name, '') ILIKE $${paramIndex}
|
||||
OR sp.shipment_plan_no ILIKE $${paramIndex}
|
||||
)`);
|
||||
params.push(`%${keyword}%`);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
||||
|
||||
const query = `
|
||||
SELECT
|
||||
sp.id,
|
||||
sp.plan_date,
|
||||
sp.plan_qty,
|
||||
sp.status,
|
||||
sp.memo,
|
||||
sp.shipment_plan_no,
|
||||
sp.created_date,
|
||||
sp.created_by,
|
||||
sp.detail_id,
|
||||
sp.sales_order_id,
|
||||
sp.remain_qty,
|
||||
COALESCE(m.order_no, d.order_no, '') AS order_no,
|
||||
COALESCE(d.part_code, m.part_code, '') AS part_code,
|
||||
COALESCE(i.item_name, d.part_name, m.part_name, COALESCE(d.part_code, m.part_code, '')) AS part_name,
|
||||
COALESCE(d.spec, m.spec, '') AS spec,
|
||||
COALESCE(m.material, '') AS material,
|
||||
COALESCE(c.customer_name, m.partner_id, d.delivery_partner_code, '') AS customer_name,
|
||||
COALESCE(m.partner_id, d.delivery_partner_code, '') AS partner_code,
|
||||
COALESCE(d.due_date, m.due_date::text, '') AS due_date,
|
||||
COALESCE(NULLIF(d.qty,'')::numeric, m.order_qty, 0) AS order_qty,
|
||||
COALESCE(NULLIF(d.ship_qty,'')::numeric, m.ship_qty, 0) AS shipped_qty
|
||||
FROM shipment_plan sp
|
||||
LEFT JOIN sales_order_detail d
|
||||
ON sp.detail_id = d.id AND sp.company_code = d.company_code
|
||||
LEFT JOIN sales_order_mng m
|
||||
ON sp.sales_order_id = m.id AND sp.company_code = m.company_code
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT item_name FROM item_info
|
||||
WHERE item_number = COALESCE(d.part_code, m.part_code)
|
||||
AND company_code = sp.company_code
|
||||
LIMIT 1
|
||||
) i ON true
|
||||
LEFT JOIN customer_mng c
|
||||
ON COALESCE(m.partner_id, d.delivery_partner_code) = c.customer_code
|
||||
AND sp.company_code = c.company_code
|
||||
${whereClause}
|
||||
ORDER BY sp.created_date DESC
|
||||
`;
|
||||
|
||||
const pool = getPool();
|
||||
const result = await pool.query(query, params);
|
||||
|
||||
logger.info("출하계획 목록 조회", {
|
||||
companyCode,
|
||||
rowCount: result.rowCount,
|
||||
});
|
||||
|
||||
return res.json({ success: true, data: result.rows });
|
||||
} catch (error: any) {
|
||||
logger.error("출하계획 목록 조회 실패", {
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
return res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 출하계획 단건 수정 ───
|
||||
|
||||
export async function updatePlan(req: AuthenticatedRequest, res: Response) {
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const userId = req.user!.userId;
|
||||
const { id } = req.params;
|
||||
const { planQty, planDate, memo } = req.body;
|
||||
|
||||
const pool = getPool();
|
||||
|
||||
const check = await pool.query(
|
||||
`SELECT id, status FROM shipment_plan WHERE id = $1 AND company_code = $2`,
|
||||
[id, companyCode]
|
||||
);
|
||||
|
||||
if (check.rowCount === 0) {
|
||||
return res.status(404).json({ success: false, message: "출하계획을 찾을 수 없습니다" });
|
||||
}
|
||||
|
||||
const setClauses: string[] = [];
|
||||
const updateParams: any[] = [];
|
||||
let idx = 1;
|
||||
|
||||
if (planQty !== undefined) {
|
||||
setClauses.push(`plan_qty = $${idx}`);
|
||||
updateParams.push(planQty);
|
||||
idx++;
|
||||
}
|
||||
if (planDate !== undefined) {
|
||||
setClauses.push(`plan_date = $${idx}::date`);
|
||||
updateParams.push(planDate);
|
||||
idx++;
|
||||
}
|
||||
if (memo !== undefined) {
|
||||
setClauses.push(`memo = $${idx}`);
|
||||
updateParams.push(memo);
|
||||
idx++;
|
||||
}
|
||||
|
||||
setClauses.push(`updated_date = NOW()`);
|
||||
setClauses.push(`updated_by = $${idx}`);
|
||||
updateParams.push(userId);
|
||||
idx++;
|
||||
|
||||
updateParams.push(id);
|
||||
updateParams.push(companyCode);
|
||||
|
||||
const updateQuery = `
|
||||
UPDATE shipment_plan
|
||||
SET ${setClauses.join(", ")}
|
||||
WHERE id = $${idx - 1} AND company_code = $${idx}
|
||||
RETURNING *
|
||||
`;
|
||||
|
||||
// 파라미터 인덱스 수정
|
||||
const finalParams: any[] = [];
|
||||
let pIdx = 1;
|
||||
const setClausesFinal: string[] = [];
|
||||
|
||||
if (planQty !== undefined) {
|
||||
setClausesFinal.push(`plan_qty = $${pIdx}`);
|
||||
finalParams.push(planQty);
|
||||
pIdx++;
|
||||
}
|
||||
if (planDate !== undefined) {
|
||||
setClausesFinal.push(`plan_date = $${pIdx}::date`);
|
||||
finalParams.push(planDate);
|
||||
pIdx++;
|
||||
}
|
||||
if (memo !== undefined) {
|
||||
setClausesFinal.push(`memo = $${pIdx}`);
|
||||
finalParams.push(memo);
|
||||
pIdx++;
|
||||
}
|
||||
setClausesFinal.push(`updated_date = NOW()`);
|
||||
setClausesFinal.push(`updated_by = $${pIdx}`);
|
||||
finalParams.push(userId);
|
||||
pIdx++;
|
||||
|
||||
finalParams.push(id);
|
||||
finalParams.push(companyCode);
|
||||
|
||||
const result = await pool.query(
|
||||
`UPDATE shipment_plan
|
||||
SET ${setClausesFinal.join(", ")}
|
||||
WHERE id = $${pIdx} AND company_code = $${pIdx + 1}
|
||||
RETURNING *`,
|
||||
finalParams
|
||||
);
|
||||
|
||||
logger.info("출하계획 수정", { companyCode, planId: id, userId });
|
||||
|
||||
return res.json({ success: true, data: result.rows[0] });
|
||||
} catch (error: any) {
|
||||
logger.error("출하계획 수정 실패", { error: error.message });
|
||||
return res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 품목별 집계 + 기존 출하계획 조회 ───
|
||||
|
||||
export async function getAggregate(req: AuthenticatedRequest, res: Response) {
|
||||
|
||||
Reference in New Issue
Block a user