refactor: Optimize order summary query and implement pagination in production plan management

- Consolidated the order summary query to integrate detailed, stock, and planning information into a single CTE for improved performance and clarity.
- Removed redundant checks for lead time in item_info, simplifying the query structure.
- Introduced pagination functionality in the production plan management page, allowing users to navigate through order items more efficiently.
- Enhanced the user interface to reflect the paginated data, improving overall usability and data handling across multiple company implementations.
This commit is contained in:
kjs
2026-04-14 11:51:53 +09:00
parent 3f108f87a2
commit 0622a5c1b6
37 changed files with 2164 additions and 827 deletions

View File

@@ -35,46 +35,15 @@ export async function getOrderSummary(
const whereClause = conditions.join(" AND ");
// item_info에 lead_time 컬럼이 존재하는지 확인
const leadTimeColCheck = await pool.query(`
SELECT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'item_info' AND column_name = 'lead_time'
) AS has_lead_time
`);
const hasLeadTime = leadTimeColCheck.rows[0]?.has_lead_time === true;
const itemLeadTimeCte = hasLeadTime
? `item_lead_time AS (
SELECT DISTINCT ON (item_number)
item_number,
id AS item_id,
COALESCE(lead_time::int, 0) AS lead_time
FROM item_info
WHERE company_code = $1
ORDER BY item_number, created_date DESC
),`
: `item_lead_time AS (
SELECT DISTINCT ON (item_number)
item_number,
id AS item_id,
0 AS lead_time
FROM item_info
WHERE company_code = $1
ORDER BY item_number, created_date DESC
),`;
// 단일 쿼리로 요약 + 상세 + 재고 + 계획 통합 조회
const query = `
WITH all_orders AS (
-- 레거시: sales_order_mng에 part_code가 직접 있는 경우
SELECT
so.part_code,
so.part_name,
so.company_code,
so.id::text, so.order_no, so.part_code, so.part_name, so.company_code,
COALESCE(so.order_qty::numeric, 0) AS order_qty,
COALESCE(so.ship_qty::numeric, 0) AS ship_qty,
COALESCE(so.balance_qty::numeric, 0) AS balance_qty,
so.due_date
so.due_date, so.status, so.partner_id, so.manager_name
FROM sales_order_mng so
WHERE ${whereClause}
AND so.part_code IS NOT NULL AND so.part_code != ''
@@ -85,52 +54,47 @@ export async function getOrderSummary(
UNION ALL
-- 마스터-디테일: sales_order_detail에 품목이 있는 경우
SELECT
sd.part_code,
sd.part_name,
sd.company_code,
sd.id::text, sd.order_no, sd.part_code, sd.part_name, sd.company_code,
COALESCE(sd.qty::numeric, 0) AS order_qty,
COALESCE(sd.ship_qty::numeric, 0) AS ship_qty,
COALESCE(sd.balance_qty::numeric, sd.qty::numeric - COALESCE(sd.ship_qty::numeric, 0), 0) AS balance_qty,
sd.due_date::date
sd.due_date::date, so.status, so.partner_id, so.manager_name
FROM sales_order_detail sd
INNER JOIN sales_order_mng so ON sd.order_no = so.order_no AND sd.company_code = so.company_code
WHERE sd.company_code = $1
AND sd.part_code IS NOT NULL AND sd.part_code != ''
),
distinct_item AS (
SELECT DISTINCT ON (item_number, company_code)
item_number, item_name, company_code
item_info_dedup AS (
SELECT DISTINCT ON (item_number)
item_number, item_name, id AS item_id,
COALESCE(lead_time::int, 0) AS lead_time
FROM item_info
ORDER BY item_number, company_code, created_date DESC
WHERE company_code = $1
ORDER BY item_number, created_date DESC
),
order_summary AS (
SELECT
ao.part_code AS item_code,
COALESCE(NULLIF(ao.part_name, ''), ii.item_name, ao.part_code) AS item_name,
COALESCE(NULLIF(MAX(ao.part_name), ''), MAX(ii.item_name), ao.part_code) AS item_name,
SUM(ao.order_qty) AS total_order_qty,
SUM(ao.ship_qty) AS total_ship_qty,
SUM(ao.balance_qty) AS total_balance_qty,
COUNT(*) AS order_count,
MIN(ao.due_date) AS earliest_due_date
FROM all_orders ao
LEFT JOIN distinct_item ii ON ao.part_code = ii.item_number AND ao.company_code = ii.company_code
GROUP BY ao.part_code, COALESCE(NULLIF(ao.part_name, ''), ii.item_name, ao.part_code)
LEFT JOIN item_info_dedup ii ON ao.part_code = ii.item_number
GROUP BY ao.part_code
),
${itemLeadTimeCte}
stock_info AS (
SELECT
item_code,
SELECT item_code,
SUM(COALESCE(current_qty::numeric, 0)) AS current_stock,
MAX(COALESCE(safety_qty::numeric, 0)) AS safety_stock
FROM inventory_stock
WHERE company_code = $1
FROM inventory_stock WHERE company_code = $1
GROUP BY item_code
),
plan_info AS (
SELECT
item_code,
SELECT item_code,
SUM(CASE WHEN status = 'planned' THEN COALESCE(plan_qty, 0) ELSE 0 END) AS existing_plan_qty,
SUM(CASE WHEN status = 'in_progress' THEN COALESCE(plan_qty, 0) ELSE 0 END) AS in_progress_qty
FROM production_plan_mng
@@ -140,13 +104,9 @@ export async function getOrderSummary(
GROUP BY item_code
)
SELECT
os.item_code,
os.item_name,
os.total_order_qty,
os.total_ship_qty,
os.total_balance_qty,
os.order_count,
os.earliest_due_date,
os.item_code, os.item_name,
os.total_order_qty, os.total_ship_qty, os.total_balance_qty,
os.order_count, os.earliest_due_date,
COALESCE(si.current_stock, 0) AS current_stock,
COALESCE(si.safety_stock, 0) AS safety_stock,
COALESCE(pi.existing_plan_qty, 0) AS existing_plan_qty,
@@ -160,15 +120,14 @@ export async function getOrderSummary(
FROM order_summary os
LEFT JOIN stock_info si ON os.item_code = si.item_code
LEFT JOIN plan_info pi ON os.item_code = pi.item_code
LEFT JOIN item_lead_time ilt ON (os.item_code = ilt.item_number OR os.item_code = ilt.item_id)
LEFT JOIN item_info_dedup ilt ON os.item_code = ilt.item_number
${options?.excludePlanned ? "WHERE COALESCE(pi.existing_plan_qty, 0) = 0" : ""}
ORDER BY os.item_code;
`;
const result = await pool.query(query, params);
// 그룹별 상세 수주 데이터도 함께 조회 (레거시 + 디테일 UNION)
const detailWhere = conditions.map(c => c.replace(/so\./g, "")).join(" AND ");
// 상세 데이터: all_orders CTE와 동일 로직 (쿼리 재사용 위해 별도 실행)
const detailQuery = `
SELECT id::text, order_no, part_code, part_name,
COALESCE(order_qty::numeric, 0) AS order_qty,
@@ -176,15 +135,13 @@ export async function getOrderSummary(
COALESCE(balance_qty::numeric, 0) AS balance_qty,
due_date, status, partner_id, manager_name
FROM sales_order_mng
WHERE ${detailWhere}
WHERE ${conditions.map(c => c.replace(/so\./g, "")).join(" AND ")}
AND part_code IS NOT NULL AND part_code != ''
AND NOT EXISTS (
SELECT 1 FROM sales_order_detail sd
WHERE sd.order_no = sales_order_mng.order_no AND sd.company_code = sales_order_mng.company_code
)
UNION ALL
SELECT sd.id::text, sd.order_no, sd.part_code, sd.part_name,
COALESCE(sd.qty::numeric, 0) AS order_qty,
COALESCE(sd.ship_qty::numeric, 0) AS ship_qty,
@@ -194,7 +151,6 @@ export async function getOrderSummary(
INNER JOIN sales_order_mng so ON sd.order_no = so.order_no AND sd.company_code = so.company_code
WHERE sd.company_code = $1
AND sd.part_code IS NOT NULL AND sd.part_code != ''
ORDER BY part_code, due_date;
`;
const detailResult = await pool.query(detailQuery, params);