feat(pop): implement process list and result retrieval endpoints
- Added `getProcessList` to retrieve the list of work order processes along with their results, grouped by process ID. - Implemented `getProcessResult` to fetch details of a specific work order process result by its ID. - Updated `popProductionRoutes` to enable the new endpoints for accessing process data. These additions enhance the functionality of the POP production module, allowing for better tracking and management of work order processes and their results.
This commit is contained in:
@@ -3572,3 +3572,168 @@ export const getChecklistItems = async (
|
||||
return res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 작업공정 목록 조회 (POP 공정실행 화면)
|
||||
* GET /api/pop/production/processes
|
||||
*
|
||||
* 응답: WorkOrderProcessRaw[]
|
||||
* - work_order_process (기준정보) + work_order_process_result (실적 배열)
|
||||
* - plan_qty는 work_instruction_detail.qty에서 가져옴
|
||||
*/
|
||||
export const getProcessList = async (
|
||||
req: AuthenticatedRequest,
|
||||
res: Response,
|
||||
) => {
|
||||
const pool = getPool();
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
|
||||
// 1) 작업공정 기준 목록
|
||||
const wopRes = await pool.query(
|
||||
`SELECT
|
||||
p.id, p.wo_id, p.seq_no, p.process_code, p.process_name,
|
||||
p.parent_process_id, p.routing_detail_id, p.batch_id,
|
||||
p.target_warehouse_id, p.target_location_code, p.use_default_warehouse,
|
||||
p.standard_time, p.is_required, p.is_fixed_order, p.remark,
|
||||
p.created_date,
|
||||
COALESCE(NULLIF(p.plan_qty,''), NULL)::numeric AS plan_qty_base,
|
||||
wid.qty AS wi_detail_qty
|
||||
FROM work_order_process p
|
||||
LEFT JOIN work_instruction_detail wid ON wid.id = p.wo_id
|
||||
WHERE p.company_code = $1
|
||||
ORDER BY p.created_date DESC, p.seq_no ASC`,
|
||||
[companyCode],
|
||||
);
|
||||
const wops = wopRes.rows;
|
||||
|
||||
if (wops.length === 0) {
|
||||
return res.json({ success: true, data: [] });
|
||||
}
|
||||
|
||||
// 2) 실적(wop_result) 조회 — accepted_results 배열로 그룹핑
|
||||
const wopIds = wops.map((w: any) => w.id);
|
||||
const resRes = await pool.query(
|
||||
`SELECT
|
||||
id, wop_id, seq, status, result_status,
|
||||
input_qty, good_qty, defect_qty, concession_qty, total_production_qty,
|
||||
is_rework, rework_source_id, accepted_by, accepted_at,
|
||||
started_at, completed_at, equipment_code, batch_id
|
||||
FROM work_order_process_result
|
||||
WHERE wop_id = ANY($1::text[])
|
||||
ORDER BY wop_id, seq ASC`,
|
||||
[wopIds],
|
||||
);
|
||||
|
||||
const resultsByWop = new Map<string, any[]>();
|
||||
for (const r of resRes.rows) {
|
||||
const arr = resultsByWop.get(r.wop_id) || [];
|
||||
arr.push(r);
|
||||
resultsByWop.set(r.wop_id, arr);
|
||||
}
|
||||
|
||||
// 3) 대표 상태/수량은 첫 실적 기준으로 매핑 (프론트는 accepted_results도 별도 순회)
|
||||
const data = wops.map((w: any) => {
|
||||
const results = resultsByWop.get(w.id) || [];
|
||||
const first = results[0] || {};
|
||||
return {
|
||||
id: w.id,
|
||||
wo_id: w.wo_id,
|
||||
seq_no: w.seq_no,
|
||||
process_code: w.process_code,
|
||||
process_name: w.process_name,
|
||||
status: first.status || "waiting",
|
||||
result_status: first.result_status || null,
|
||||
plan_qty: w.plan_qty_base ?? w.wi_detail_qty ?? null,
|
||||
input_qty: first.input_qty ?? null,
|
||||
good_qty: first.good_qty ?? null,
|
||||
defect_qty: first.defect_qty ?? null,
|
||||
concession_qty: first.concession_qty ?? null,
|
||||
total_production_qty: first.total_production_qty ?? null,
|
||||
parent_process_id: w.parent_process_id ?? null,
|
||||
is_rework: first.is_rework ?? null,
|
||||
rework_source_id: first.rework_source_id ?? null,
|
||||
started_at: first.started_at ?? null,
|
||||
completed_at: first.completed_at ?? null,
|
||||
accepted_by: first.accepted_by ?? null,
|
||||
accepted_at: first.accepted_at ?? null,
|
||||
created_date: w.created_date ?? null,
|
||||
batch_id: first.batch_id ?? w.batch_id ?? null,
|
||||
equipment_code: first.equipment_code ?? null,
|
||||
// Phase C 계산 필드는 별도 엔드포인트(available-qty 등)에서 제공
|
||||
available_qty: null,
|
||||
prev_good_qty: null,
|
||||
my_input_qty: null,
|
||||
rework_available_qty: null,
|
||||
split_no: null,
|
||||
split_total: null,
|
||||
batch_count: results.length,
|
||||
batch_list: null,
|
||||
batch_index: null,
|
||||
accepted_results: results.map((r: any) => ({
|
||||
id: r.id,
|
||||
seq: r.seq,
|
||||
status: r.status,
|
||||
result_status: r.result_status,
|
||||
input_qty: r.input_qty,
|
||||
good_qty: r.good_qty,
|
||||
defect_qty: r.defect_qty,
|
||||
concession_qty: r.concession_qty,
|
||||
total_production_qty: r.total_production_qty,
|
||||
is_rework: r.is_rework,
|
||||
rework_source_id: r.rework_source_id,
|
||||
accepted_by: r.accepted_by,
|
||||
accepted_at: r.accepted_at,
|
||||
started_at: r.started_at,
|
||||
completed_at: r.completed_at,
|
||||
equipment_code: r.equipment_code,
|
||||
batch_id: r.batch_id,
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
return res.json({ success: true, data });
|
||||
} catch (error: any) {
|
||||
logger.error("[pop/production] processes 조회 오류:", error);
|
||||
return res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 단일 작업공정 결과 조회
|
||||
* GET /api/pop/production/result/:id
|
||||
* :id = work_order_process_result.id (wop_result row 식별자)
|
||||
*/
|
||||
export const getProcessResult = async (
|
||||
req: AuthenticatedRequest,
|
||||
res: Response,
|
||||
) => {
|
||||
const pool = getPool();
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const { id } = req.params;
|
||||
if (!id) {
|
||||
return res.status(400).json({ success: false, message: "id는 필수입니다." });
|
||||
}
|
||||
|
||||
const r = await pool.query(
|
||||
`SELECT r.*, p.process_code, p.process_name, p.wo_id, p.seq_no
|
||||
FROM work_order_process_result r
|
||||
JOIN work_order_process p ON p.id = r.wop_id
|
||||
WHERE r.id = $1 AND r.company_code = $2
|
||||
LIMIT 1`,
|
||||
[id, companyCode],
|
||||
);
|
||||
|
||||
if (r.rowCount === 0) {
|
||||
return res
|
||||
.status(404)
|
||||
.json({ success: false, message: "결과를 찾을 수 없습니다." });
|
||||
}
|
||||
|
||||
return res.json({ success: true, data: r.rows[0] });
|
||||
} catch (error: any) {
|
||||
logger.error("[pop/production] result 조회 오류:", error);
|
||||
return res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user