Files
vexplor_dev/backend-node/src/controllers/qualityMonitoringController.ts
kjs 6ddc84f285 Update date handling in inventory and sales order pages
- Refactored date handling in the InventoryStatusPage to use `toLocaleString` for transaction dates and last in dates, ensuring correct timezone formatting.
- Introduced FormDatePicker in SalesOrderPage for date inputs, enhancing user experience with automatic formatting and improved date handling.
- Added a checkbox for filtering items by customer in SalesOrderPage, allowing users to view only items registered for the selected customer.

This update improves date accuracy and user interaction in the inventory and sales order modules.
2026-04-29 18:20:01 +09:00

117 lines
4.1 KiB
TypeScript

/**
* 품질 모니터링 데이터 조회 (서버 페이징 + 통계 합산)
* work_order_process(공정 메타) + work_order_process_result(실적) JOIN
* - 페이지: 화면 표 표시용
* - summary: KPI 카드용 (전체 합산)
*/
import { Response } from "express";
import { AuthenticatedRequest } from "../types/auth";
import { getPool } from "../database/db";
import { logger } from "../utils/logger";
export async function getQualityMonitoringData(req: AuthenticatedRequest, res: Response) {
try {
const companyCode = req.user!.companyCode;
const from = (req.query.from as string | undefined)?.trim() || "";
const to = (req.query.to as string | undefined)?.trim() || "";
const page = Math.max(1, parseInt(String(req.query.page ?? "1"), 10) || 1);
const size = Math.max(1, Math.min(500, parseInt(String(req.query.size ?? "50"), 10) || 50));
const offset = (page - 1) * size;
const params: any[] = [companyCode];
const conds: string[] = ["wopr.company_code = $1"];
if (from) {
params.push(`${from} 00:00:00`);
conds.push(`wopr.created_date >= $${params.length}::timestamp`);
}
if (to) {
params.push(`${to} 23:59:59`);
conds.push(`wopr.created_date <= $${params.length}::timestamp`);
}
const whereClause = `WHERE ${conds.join(" AND ")}`;
const pool = getPool();
// 1) total + summary (KPI 카드)
const summaryQuery = `
SELECT
COUNT(*)::int AS total,
SUM(CASE WHEN wopr.status = 'completed' AND COALESCE(CAST(NULLIF(wopr.defect_qty, '') AS numeric), 0) = 0 THEN 1 ELSE 0 END)::int AS passed,
SUM(CASE WHEN wopr.status = 'completed' AND COALESCE(CAST(NULLIF(wopr.defect_qty, '') AS numeric), 0) > 0 THEN 1 ELSE 0 END)::int AS failed,
SUM(CASE WHEN wopr.status <> 'completed' OR wopr.status IS NULL THEN 1 ELSE 0 END)::int AS pending
FROM work_order_process_result wopr
${whereClause}
`;
const summaryRes = await pool.query(summaryQuery, params);
const summaryRow = summaryRes.rows[0] || { total: 0, passed: 0, failed: 0, pending: 0 };
const total = summaryRow.total || 0;
const passRate = total > 0 ? Math.round((summaryRow.passed / total) * 1000) / 10 : 0;
// 2) 페이지 데이터
const pageParams = [...params, size, offset];
const dataQuery = `
SELECT
wopr.id,
wopr.wop_id,
wopr.status,
wopr.input_qty,
wopr.good_qty,
wopr.defect_qty,
wopr.started_at,
wopr.completed_at,
wopr.completed_by,
wopr.accepted_by,
wop.wo_id,
wop.process_code,
wop.process_name,
wop.plan_qty
FROM work_order_process_result wopr
LEFT JOIN work_order_process wop
ON wop.id = wopr.wop_id AND wop.company_code = wopr.company_code
${whereClause}
ORDER BY wopr.created_date DESC
LIMIT $${params.length + 1} OFFSET $${params.length + 2}
`;
const dataRes = await pool.query(dataQuery, pageParams);
const rows = dataRes.rows.map((r: any) => ({
id: r.id,
wo_id: r.wo_id,
process_code: r.process_code || "",
process_name: r.process_name || "",
status: r.status || "",
plan_qty: Number(r.plan_qty) || 0,
input_qty: Number(r.input_qty) || 0,
good_qty: Number(r.good_qty) || 0,
defect_qty: Number(r.defect_qty) || 0,
started_at: r.started_at || null,
completed_at: r.completed_at || null,
worker_name: r.completed_by || r.accepted_by || "",
}));
logger.info("품질 모니터링 조회", {
companyCode, from, to, page, size, total,
});
return res.json({
success: true,
rows,
total,
page,
size,
totalPages: Math.max(1, Math.ceil(total / size)),
summary: {
total,
passed: summaryRow.passed || 0,
failed: summaryRow.failed || 0,
pending: summaryRow.pending || 0,
passRate,
},
});
} catch (error: any) {
logger.error("품질 모니터링 조회 실패", { error: error.message });
return res.status(500).json({ success: false, message: error.message });
}
}