매미킴
This commit is contained in:
@@ -3586,6 +3586,11 @@ export const getProcessList = async (
|
||||
res: Response,
|
||||
) => {
|
||||
const pool = getPool();
|
||||
const __debugTag = `[POP-DEBUG][getProcessList][${req.user!.userId}@${req.user!.companyCode}][${new Date().toISOString()}]`;
|
||||
console.log(`${__debugTag} ▶ ENTER`, {
|
||||
query: req.query,
|
||||
headers_user_agent: req.headers["user-agent"],
|
||||
});
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
|
||||
@@ -3606,8 +3611,15 @@ export const getProcessList = async (
|
||||
[companyCode],
|
||||
);
|
||||
const wops = wopRes.rows;
|
||||
console.log(`${__debugTag} 1) wop SQL → rows:`, {
|
||||
total: wops.length,
|
||||
by_process: wops.reduce((m: Record<string, number>, w: any) => { m[w.process_code] = (m[w.process_code] || 0) + 1; return m; }, {}),
|
||||
master_count: wops.filter((w: any) => !w.parent_process_id).length,
|
||||
split_count: wops.filter((w: any) => w.parent_process_id).length,
|
||||
});
|
||||
|
||||
if (wops.length === 0) {
|
||||
console.log(`${__debugTag} ◀ EXIT (no wop)`);
|
||||
return res.json({ success: true, data: [] });
|
||||
}
|
||||
|
||||
@@ -3624,6 +3636,11 @@ export const getProcessList = async (
|
||||
ORDER BY wop_id, seq ASC`,
|
||||
[wopIds],
|
||||
);
|
||||
console.log(`${__debugTag} 2) wopr SQL → rows:`, {
|
||||
total: resRes.rows.length,
|
||||
by_status: resRes.rows.reduce((m: Record<string, number>, r: any) => { m[r.status] = (m[r.status] || 0) + 1; return m; }, {}),
|
||||
rework_count: resRes.rows.filter((r: any) => r.is_rework === "Y").length,
|
||||
});
|
||||
|
||||
const resultsByWop = new Map<string, any[]>();
|
||||
for (const r of resRes.rows) {
|
||||
@@ -3631,6 +3648,12 @@ export const getProcessList = async (
|
||||
arr.push(r);
|
||||
resultsByWop.set(r.wop_id, arr);
|
||||
}
|
||||
const __wopWithoutResult = wops.filter((w: any) => !resultsByWop.has(w.id));
|
||||
console.log(`${__debugTag} 2-1) wop ↔ wopr 매핑:`, {
|
||||
wop_with_result: wops.length - __wopWithoutResult.length,
|
||||
wop_WITHOUT_result: __wopWithoutResult.length,
|
||||
missing_sample: __wopWithoutResult.slice(0, 5).map((w: any) => ({ id: w.id, process_code: w.process_code, seq_no: w.seq_no })),
|
||||
});
|
||||
|
||||
// 3) 대표 상태/수량은 첫 실적 기준으로 매핑 (프론트는 accepted_results도 별도 순회)
|
||||
const data = wops.map((w: any) => {
|
||||
@@ -3692,6 +3715,45 @@ export const getProcessList = async (
|
||||
};
|
||||
});
|
||||
|
||||
// 3) 응답 데이터 분포 로그
|
||||
const masterFirstDist: Record<string, number> = {};
|
||||
const p001WoprDist: Record<string, number> = {};
|
||||
const reworkDist: Record<string, number> = {};
|
||||
let totalWopr = 0;
|
||||
const p001Cards: any[] = [];
|
||||
for (const d of data) {
|
||||
masterFirstDist[d.status] = (masterFirstDist[d.status] || 0) + 1;
|
||||
if (d.process_code === "P001") {
|
||||
p001Cards.push({
|
||||
id: d.id,
|
||||
seq_no: d.seq_no,
|
||||
parent: d.parent_process_id,
|
||||
master_status: d.status,
|
||||
wopr_count: d.accepted_results.length,
|
||||
wopr_statuses: d.accepted_results.map((r: any) => `${r.seq}:${r.status}${r.is_rework === "Y" ? "(R)" : ""}`),
|
||||
});
|
||||
}
|
||||
for (const ar of d.accepted_results) {
|
||||
totalWopr++;
|
||||
if (d.process_code === "P001") {
|
||||
p001WoprDist[ar.status] = (p001WoprDist[ar.status] || 0) + 1;
|
||||
}
|
||||
if (ar.is_rework === "Y") {
|
||||
reworkDist[`${d.process_code}:${ar.status}`] = (reworkDist[`${d.process_code}:${ar.status}`] || 0) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(`${__debugTag} 3) 응답 빌드 완료:`, {
|
||||
data_length: data.length,
|
||||
wopr_total_in_response: totalWopr,
|
||||
master_first_status_dist: masterFirstDist,
|
||||
p001_wopr_status_dist: p001WoprDist,
|
||||
rework_dist: reworkDist,
|
||||
});
|
||||
console.log(`${__debugTag} 3-1) P001 카드 상세 (${p001Cards.length}개):`);
|
||||
console.table(p001Cards);
|
||||
console.log(`${__debugTag} ◀ EXIT`);
|
||||
|
||||
return res.json({ success: true, data });
|
||||
} catch (error: any) {
|
||||
logger.error("[pop/production] processes 조회 오류:", error);
|
||||
|
||||
@@ -831,7 +831,7 @@ export function WorkOrderList(props: WorkOrderListProps) {
|
||||
const masterProcesses = useMemo(() => {
|
||||
// 마스터 행 + 분할 행(진행중/완료/리워크) — 중복 제거
|
||||
const seen = new Set<string>();
|
||||
return allProcesses.filter((p) => {
|
||||
const result = allProcesses.filter((p) => {
|
||||
if (seen.has(p.id)) return false;
|
||||
const include =
|
||||
!p.parent_process_id || // 마스터 행
|
||||
@@ -841,6 +841,32 @@ export function WorkOrderList(props: WorkOrderListProps) {
|
||||
if (include) seen.add(p.id);
|
||||
return include;
|
||||
});
|
||||
const __tag = `[POP-DEBUG][WorkOrderList][${new Date().toISOString()}]`;
|
||||
const dist: Record<string, number> = {};
|
||||
for (const p of result) {
|
||||
const kind = p.parent_process_id ? "split" : "master";
|
||||
const rw = isReworkProcess(p) ? "(R)" : "";
|
||||
const k = `${p.process_code}:${kind}${rw}:${p.status}`;
|
||||
dist[k] = (dist[k] || 0) + 1;
|
||||
}
|
||||
console.log(`${__tag} 3) masterProcesses 생성:`, {
|
||||
input_allProcesses: allProcesses.length,
|
||||
output_masterProcesses: result.length,
|
||||
excluded: allProcesses.length - result.length,
|
||||
distribution: dist,
|
||||
});
|
||||
console.log(`${__tag} 3-1) masterProcesses — P001 + 모든 리워크 상세:`);
|
||||
console.table(result
|
||||
.filter((p) => p.process_code === "P001" || isReworkProcess(p))
|
||||
.map((p) => ({
|
||||
id: p.id,
|
||||
wo_id: p.wo_id,
|
||||
process_code: p.process_code,
|
||||
kind: p.parent_process_id ? "split" : "master",
|
||||
status: p.status,
|
||||
is_rework: p.is_rework,
|
||||
})));
|
||||
return result;
|
||||
}, [allProcesses]);
|
||||
|
||||
const equipmentMap = useMemo(() => {
|
||||
@@ -857,36 +883,56 @@ export function WorkOrderList(props: WorkOrderListProps) {
|
||||
|
||||
/* ---- Filtered processes ---- */
|
||||
const filteredProcesses = useMemo(() => {
|
||||
if (selectedProcess === "__all__") return []; // 공정 미선택 시 빈 목록
|
||||
return masterProcesses.filter((proc) => {
|
||||
const __tag = `[POP-DEBUG][WorkOrderList][${new Date().toISOString()}]`;
|
||||
if (selectedProcess === "__all__") {
|
||||
console.log(`${__tag} 4) filteredProcesses: 공정 미선택 — 빈 배열`);
|
||||
return [];
|
||||
}
|
||||
const __excluded: Array<{ id: string; reason: string; status: string; process_code: string }> = [];
|
||||
const result = masterProcesses.filter((proc) => {
|
||||
const isRework = isReworkProcess(proc);
|
||||
const isMaster = !proc.parent_process_id;
|
||||
// 완료/진행중 탭에서는 SPLIT만 표시 (마스터 제외)
|
||||
if (
|
||||
isMaster &&
|
||||
!isRework &&
|
||||
(activeTab === "completed" || activeTab === "in_progress")
|
||||
)
|
||||
if (isMaster && !isRework && (activeTab === "completed" || activeTab === "in_progress")) {
|
||||
__excluded.push({ id: proc.id, reason: "master_in_completed_or_in_progress_tab", status: proc.status, process_code: proc.process_code });
|
||||
return false;
|
||||
// 리워크 마스터가 in_progress/completed면 SPLIT이 생성된 것 → 리워크 마스터 숨김 (SPLIT은 표시)
|
||||
if (
|
||||
isRework &&
|
||||
!proc.parent_process_id &&
|
||||
(proc.status === "in_progress" || proc.status === "completed")
|
||||
)
|
||||
}
|
||||
if (isRework && !proc.parent_process_id && (proc.status === "in_progress" || proc.status === "completed")) {
|
||||
__excluded.push({ id: proc.id, reason: "rework_master_with_split", status: proc.status, process_code: proc.process_code });
|
||||
return false;
|
||||
// 재작업 카드는 공정 필터 무시 (모든 공정에서 표시)
|
||||
if (!isRework && proc.process_code !== selectedProcess) return false;
|
||||
}
|
||||
if (!isRework && proc.process_code !== selectedProcess) {
|
||||
__excluded.push({ id: proc.id, reason: `process_code_mismatch(${proc.process_code} vs ${selectedProcess})`, status: proc.status, process_code: proc.process_code });
|
||||
return false;
|
||||
}
|
||||
if (selectedEquipment !== "__all__") {
|
||||
const wi = instructionMap[proc.wo_id];
|
||||
if (!wi) return false;
|
||||
if (!wi) {
|
||||
__excluded.push({ id: proc.id, reason: "no_wi_for_equipment_filter", status: proc.status, process_code: proc.process_code });
|
||||
return false;
|
||||
}
|
||||
const eqId = wi.equipment_id;
|
||||
const eq = equipmentMap[eqId];
|
||||
if (!eq || eq.equipment_code !== selectedEquipment) return false;
|
||||
if (!eq || eq.equipment_code !== selectedEquipment) {
|
||||
__excluded.push({ id: proc.id, reason: `equipment_mismatch(${eq?.equipment_code} vs ${selectedEquipment})`, status: proc.status, process_code: proc.process_code });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (activeTab !== "all" && proc.status !== activeTab) {
|
||||
__excluded.push({ id: proc.id, reason: `tab_mismatch(${proc.status} vs ${activeTab})`, status: proc.status, process_code: proc.process_code });
|
||||
return false;
|
||||
}
|
||||
if (activeTab !== "all" && proc.status !== activeTab) return false;
|
||||
return true;
|
||||
});
|
||||
console.log(`${__tag} 4) filteredProcesses 계산:`, {
|
||||
activeTab,
|
||||
selectedProcess,
|
||||
selectedEquipment,
|
||||
input: masterProcesses.length,
|
||||
output: result.length,
|
||||
excluded_count: __excluded.length,
|
||||
excluded_by_reason: __excluded.reduce((m: Record<string, number>, e) => { m[e.reason] = (m[e.reason] || 0) + 1; return m; }, {}),
|
||||
});
|
||||
return result;
|
||||
}, [
|
||||
masterProcesses,
|
||||
selectedProcess,
|
||||
@@ -923,23 +969,36 @@ export function WorkOrderList(props: WorkOrderListProps) {
|
||||
waiting: 0,
|
||||
completed: 0,
|
||||
};
|
||||
const __tag = `[POP-DEBUG][WorkOrderList][${new Date().toISOString()}]`;
|
||||
const breakdown: Array<{ id: string; wo_id: string; process_code: string; status: string; kind: string; is_rework: string; bucket: string }> = [];
|
||||
for (const proc of preFiltered) {
|
||||
const isMaster = !proc.parent_process_id;
|
||||
const isRw = isReworkProcess(proc);
|
||||
// 리워크 마스터가 in_progress/completed면 SPLIT이 있으므로 카운트 제외
|
||||
const kind = isMaster ? "master" : "split";
|
||||
if (
|
||||
isRw &&
|
||||
!proc.parent_process_id &&
|
||||
(proc.status === "in_progress" || proc.status === "completed")
|
||||
)
|
||||
) {
|
||||
breakdown.push({ id: proc.id, wo_id: proc.wo_id, process_code: proc.process_code, status: proc.status, kind, is_rework: proc.is_rework || "", bucket: "★SKIPPED" });
|
||||
continue;
|
||||
if (proc.status === "acceptable") counts.acceptable++;
|
||||
else if (proc.status === "in_progress" && (!isMaster || isRw))
|
||||
counts.in_progress++;
|
||||
else if (proc.status === "completed" && (!isMaster || isRw))
|
||||
counts.completed++;
|
||||
else counts.waiting++;
|
||||
}
|
||||
let bucket: string;
|
||||
if (proc.status === "acceptable") { counts.acceptable++; bucket = "acceptable"; }
|
||||
else if (proc.status === "in_progress" && (!isMaster || isRw)) { counts.in_progress++; bucket = "in_progress"; }
|
||||
else if (proc.status === "completed" && (!isMaster || isRw)) { counts.completed++; bucket = "completed"; }
|
||||
else { counts.waiting++; bucket = "waiting"; }
|
||||
breakdown.push({ id: proc.id, wo_id: proc.wo_id, process_code: proc.process_code, status: proc.status, kind, is_rework: proc.is_rework || "", bucket });
|
||||
}
|
||||
console.log(`${__tag} 5) tabCounts 계산:`, {
|
||||
selectedProcess,
|
||||
selectedEquipment,
|
||||
masterProcesses_total: masterProcesses.length,
|
||||
preFiltered_total: preFiltered.length,
|
||||
counts,
|
||||
});
|
||||
console.log(`${__tag} 5-1) 카드별 분류 breakdown (${breakdown.length}개):`);
|
||||
console.table(breakdown);
|
||||
return counts;
|
||||
}, [
|
||||
masterProcesses,
|
||||
|
||||
@@ -77,25 +77,53 @@ export function useProcessData() {
|
||||
if (inFlight.current) return inFlight.current;
|
||||
|
||||
const task = (async () => {
|
||||
const __tag = `[POP-DEBUG][useProcessData][${new Date().toISOString()}]`;
|
||||
console.log(`${__tag} ▶ fetchData 시작`, { withSync: !!opts?.withSync });
|
||||
setLoading(true);
|
||||
if (opts?.withSync) setSyncing(true);
|
||||
try {
|
||||
if (opts?.withSync) {
|
||||
try {
|
||||
await apiClient.post("/pop/production/sync-work-instructions");
|
||||
} catch {
|
||||
const __t0 = Date.now();
|
||||
const syncRes = await apiClient.post("/pop/production/sync-work-instructions");
|
||||
console.log(`${__tag} 0) POST /sync-work-instructions (${Date.now() - __t0}ms)`, syncRes.data);
|
||||
} catch (e) {
|
||||
console.warn(`${__tag} 0) sync 실패`, e);
|
||||
toast.warning(
|
||||
"동기화 실패 — 최신 작업지시가 반영되지 않을 수 있습니다",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const __t1 = Date.now();
|
||||
const [wiRes, procRes, pmRes, eqRes] = await Promise.all([
|
||||
apiClient.get("/work-instruction/list"),
|
||||
apiClient.get("/pop/production/processes"),
|
||||
dataApi.getTableData("process_mng", { size: 500 }),
|
||||
dataApi.getTableData("equipment_mng", { size: 500 }),
|
||||
]);
|
||||
console.log(`${__tag} 1) 4개 API 병렬 호출 완료 (${Date.now() - __t1}ms)`);
|
||||
const __wiArr = (Array.isArray(wiRes.data?.data) ? wiRes.data.data : wiRes.data?.data?.rows ?? []) as any[];
|
||||
console.log(`${__tag} 1-1) /work-instruction/list 응답:`, {
|
||||
raw_length: __wiArr.length,
|
||||
unique_wi_ids: new Set(__wiArr.map((r: any) => r.wi_id || r.id)).size,
|
||||
sample: __wiArr.slice(0, 3),
|
||||
});
|
||||
console.log(`${__tag} 1-2) /pop/production/processes 응답:`, {
|
||||
raw_length: procRes.data?.data?.length ?? 0,
|
||||
by_process_code: (procRes.data?.data ?? []).reduce((m: Record<string, number>, d: any) => { m[d.process_code] = (m[d.process_code] || 0) + 1; return m; }, {}),
|
||||
});
|
||||
console.log(`${__tag} 1-3) /pop/production/processes — P001 카드 RAW:`);
|
||||
console.table((procRes.data?.data ?? [])
|
||||
.filter((d: any) => d.process_code === "P001")
|
||||
.map((d: any) => ({
|
||||
id: d.id,
|
||||
seq_no: d.seq_no,
|
||||
parent: d.parent_process_id || "",
|
||||
master_status: d.status,
|
||||
wopr_count: d.accepted_results?.length ?? 0,
|
||||
wopr_statuses: (d.accepted_results || []).map((r: any) => `${r.seq}:${r.status}${r.is_rework === "Y" ? "(R)" : ""}`).join("|"),
|
||||
})));
|
||||
|
||||
// work-instruction: header+detail 조인이라 id 중복 → 첫 행만 취함
|
||||
let wiRaw: Record<string, unknown>[] = [];
|
||||
@@ -175,6 +203,55 @@ export function useProcessData() {
|
||||
setAllProcesses(flat);
|
||||
setProcessList((pmRes.data ?? []) as ProcessMng[]);
|
||||
setEquipmentList((eqRes.data ?? []) as EquipmentMng[]);
|
||||
|
||||
// 2) flat 펼친 후 분포 — useProcessData가 master + 모든 wopr를 split row로 펼친 결과
|
||||
const allDist: Record<string, number> = {};
|
||||
const p001Dist: Record<string, number> = {};
|
||||
const reworkDist: Record<string, number> = {};
|
||||
for (const p of flat) {
|
||||
const kind = p.parent_process_id ? "split" : "master";
|
||||
const key = `${kind}:${p.status}`;
|
||||
allDist[key] = (allDist[key] || 0) + 1;
|
||||
if (p.process_code === "P001") {
|
||||
p001Dist[key] = (p001Dist[key] || 0) + 1;
|
||||
}
|
||||
if (p.is_rework === "Y") {
|
||||
reworkDist[`${p.process_code}:${kind}:${p.status}`] =
|
||||
(reworkDist[`${p.process_code}:${kind}:${p.status}`] || 0) + 1;
|
||||
}
|
||||
}
|
||||
console.log(`${__tag} 2) flat 펼침 완료:`, {
|
||||
raw_wop_count: rawRows.length,
|
||||
flat_count: flat.length,
|
||||
expand_factor: flat.length / Math.max(1, rawRows.length),
|
||||
all_kind_status_dist: allDist,
|
||||
p001_kind_status_dist: p001Dist,
|
||||
rework_dist: reworkDist,
|
||||
});
|
||||
console.log(`${__tag} 2-1) flat — P001 row 상세:`);
|
||||
console.table(flat
|
||||
.filter((p) => p.process_code === "P001")
|
||||
.map((p) => ({
|
||||
id: p.id,
|
||||
wo_id: p.wo_id,
|
||||
seq_no: p.seq_no,
|
||||
kind: p.parent_process_id ? "split" : "master",
|
||||
status: p.status,
|
||||
is_rework: p.is_rework,
|
||||
split_no: p.split_no ?? "",
|
||||
})));
|
||||
console.log(`${__tag} 2-2) flat — 모든 리워크 row:`);
|
||||
console.table(flat
|
||||
.filter((p) => p.is_rework === "Y")
|
||||
.map((p) => ({
|
||||
id: p.id,
|
||||
wo_id: p.wo_id,
|
||||
process_code: p.process_code,
|
||||
seq_no: p.seq_no,
|
||||
kind: p.parent_process_id ? "split" : "master",
|
||||
status: p.status,
|
||||
})));
|
||||
console.log(`${__tag} ◀ fetchData 종료`);
|
||||
} catch {
|
||||
toast.error("데이터 조회 실패");
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user