Implement Order Status Integration for Outsourcing Purchase Management
- Added a new endpoint `listOrderStatus` in the `outsourcePurchaseController` to retrieve integrated order status information, including filtering options for source type, order status, and date range. - Updated the `outsourcePurchaseService` to handle the new order status retrieval logic, ensuring proper filtering and data aggregation. - Introduced a new route for accessing the order status information in `outsourcePurchaseRoutes`. - Created a detailed modal for viewing outsourcing purchase order details, enhancing the user interface for better data presentation. - Developed a registration modal for creating and editing outsourcing purchase orders, featuring a tabbed interface for improved user experience. (TASK: ERP-025, ERP-019)
This commit is contained in:
@@ -1188,3 +1188,181 @@ async function mapToOpoMaterials(
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// 외주발주현황 통합 조회 (TASK:ERP-025)
|
||||
// 발주 × 공정 × 자재 × 사급출고 × 입고 단위로 펼친 행 반환
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface OrderStatusFilter {
|
||||
keyword?: string;
|
||||
source_type?: string;
|
||||
order_status?: string;
|
||||
release_status?: string; // '출고완료' | '입고완료'
|
||||
date_from?: string;
|
||||
date_to?: string;
|
||||
}
|
||||
|
||||
export async function listOrderStatus(
|
||||
companyCode: string,
|
||||
opts: OrderStatusFilter,
|
||||
) {
|
||||
const pool = getPool();
|
||||
const params: any[] = [companyCode];
|
||||
let where = `o.company_code = $1`;
|
||||
|
||||
if (opts.source_type) {
|
||||
params.push(opts.source_type);
|
||||
where += ` AND o.source_type = $${params.length}`;
|
||||
}
|
||||
if (opts.order_status) {
|
||||
params.push(opts.order_status);
|
||||
where += ` AND o.status = $${params.length}`;
|
||||
}
|
||||
if (opts.date_from) {
|
||||
params.push(opts.date_from);
|
||||
where += ` AND o.order_date >= $${params.length}`;
|
||||
}
|
||||
if (opts.date_to) {
|
||||
params.push(opts.date_to);
|
||||
where += ` AND o.order_date <= $${params.length}`;
|
||||
}
|
||||
|
||||
// 출고: outsource_purchase_order_material.outbound_id → outbound_mng.id 정참조
|
||||
// 입고: inbound_mng.source_table='outbound_mng' AND source_id=outbound_mng.id 역참조 합산
|
||||
const sql = `
|
||||
SELECT
|
||||
o.id AS order_id,
|
||||
o.order_no,
|
||||
COALESCE(o.source_type, '') AS source_type,
|
||||
COALESCE(o.source_no, '') AS source_no,
|
||||
COALESCE(o.item_code, '') AS item_code,
|
||||
COALESCE(o.item_name, '') AS item_name,
|
||||
COALESCE(o.spec, '') AS spec,
|
||||
COALESCE(o.material, '') AS material,
|
||||
COALESCE(NULLIF(o.quantity::text, '')::numeric, 0) AS quantity,
|
||||
o.order_date,
|
||||
o.due_date,
|
||||
COALESCE(o.status, '') AS order_status,
|
||||
COALESCE(o.manager, '') AS manager,
|
||||
p.id AS process_id,
|
||||
p.seq,
|
||||
COALESCE(p.process_name, '') AS process_name,
|
||||
COALESCE(p.vendor_code, '') AS vendor_code,
|
||||
COALESCE(p.vendor_name, '') AS vendor_name,
|
||||
COALESCE(p.material_needed, false) AS material_needed,
|
||||
m.id AS material_id,
|
||||
COALESCE(m.item_code, '') AS material_item_code,
|
||||
COALESCE(m.item_name, '') AS material_item_name,
|
||||
COALESCE(NULLIF(m.qty::text, '')::numeric, 0) AS material_qty,
|
||||
COALESCE(m.unit, '') AS material_unit,
|
||||
COALESCE(m.release_status, '') AS material_release_status,
|
||||
om.id AS outbound_id,
|
||||
COALESCE(om.outbound_number, '') AS outbound_number,
|
||||
COALESCE(om.outbound_status, '') AS outbound_status,
|
||||
COALESCE(NULLIF(om.outbound_qty, '')::numeric, 0) AS released_qty,
|
||||
COALESCE(om.outbound_date, '') AS request_date,
|
||||
COALESCE(in_sum.received_qty, 0)::numeric AS received_qty
|
||||
FROM outsource_purchase_order o
|
||||
LEFT JOIN outsource_purchase_order_process p
|
||||
ON p.opo_id = o.id
|
||||
LEFT JOIN outsource_purchase_order_material m
|
||||
ON m.opo_process_id = p.id
|
||||
LEFT JOIN outbound_mng om
|
||||
ON om.id = m.outbound_id
|
||||
AND om.company_code = o.company_code
|
||||
LEFT JOIN (
|
||||
SELECT company_code, source_id, SUM(inbound_qty)::numeric AS received_qty
|
||||
FROM inbound_mng
|
||||
WHERE source_table = 'outbound_mng'
|
||||
GROUP BY company_code, source_id
|
||||
) in_sum
|
||||
ON in_sum.source_id = om.id
|
||||
AND in_sum.company_code = o.company_code
|
||||
WHERE ${where}
|
||||
ORDER BY o.order_date DESC NULLS LAST, o.order_no DESC, p.seq NULLS LAST
|
||||
`;
|
||||
|
||||
const r = await pool.query(sql, params);
|
||||
|
||||
// 행 단위 파생 필드 + 키워드/출고상태 필터
|
||||
let rows = r.rows.map((row: any) => {
|
||||
const releasedQty = Number(row.released_qty || 0);
|
||||
const receivedQty = Number(row.received_qty || 0);
|
||||
const remainQty = Math.max(0, releasedQty - receivedQty);
|
||||
const ratePct =
|
||||
releasedQty > 0 ? Math.round((receivedQty / releasedQty) * 100) : 0;
|
||||
|
||||
// 화면 표기용 통합 출고상태
|
||||
let release_status = "";
|
||||
if (releasedQty > 0) {
|
||||
release_status = receivedQty >= releasedQty ? "입고완료" : "출고완료";
|
||||
}
|
||||
|
||||
return {
|
||||
order_id: row.order_id,
|
||||
order_no: row.order_no,
|
||||
source_type: row.source_type,
|
||||
source_no: row.source_no,
|
||||
item_code: row.item_code,
|
||||
item_name: row.item_name,
|
||||
spec: row.spec,
|
||||
material: row.material,
|
||||
quantity: Number(row.quantity || 0),
|
||||
order_date: row.order_date || "",
|
||||
due_date: row.due_date || "",
|
||||
order_status: row.order_status,
|
||||
manager: row.manager,
|
||||
process_id: row.process_id || "",
|
||||
seq: row.seq != null ? Number(row.seq) : null,
|
||||
process_name: row.process_name,
|
||||
vendor_code: row.vendor_code,
|
||||
vendor_name: row.vendor_name,
|
||||
material_needed: !!row.material_needed,
|
||||
material_id: row.material_id || "",
|
||||
material_item_code: row.material_item_code,
|
||||
material_item_name: row.material_item_name,
|
||||
material_qty: Number(row.material_qty || 0),
|
||||
material_unit: row.material_unit,
|
||||
material_release_status: row.material_release_status,
|
||||
outbound_id: row.outbound_id || "",
|
||||
outbound_number: row.outbound_number,
|
||||
outbound_status: row.outbound_status,
|
||||
released_qty: releasedQty,
|
||||
request_date: row.request_date,
|
||||
received_qty: receivedQty,
|
||||
remain_qty: remainQty,
|
||||
rate_pct: ratePct,
|
||||
release_status,
|
||||
};
|
||||
});
|
||||
|
||||
if (opts.keyword) {
|
||||
const kw = opts.keyword.toLowerCase();
|
||||
rows = rows.filter((row: any) => {
|
||||
const hay = [
|
||||
row.order_no,
|
||||
row.source_type,
|
||||
row.source_no,
|
||||
row.item_code,
|
||||
row.item_name,
|
||||
row.spec,
|
||||
row.material,
|
||||
row.process_name,
|
||||
row.vendor_name,
|
||||
row.material_item_code,
|
||||
row.material_item_name,
|
||||
row.manager,
|
||||
]
|
||||
.join(" ")
|
||||
.toLowerCase();
|
||||
return hay.includes(kw);
|
||||
});
|
||||
}
|
||||
|
||||
if (opts.release_status) {
|
||||
rows = rows.filter((row: any) => row.release_status === opts.release_status);
|
||||
}
|
||||
|
||||
return { rows, total: rows.length };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user