fix: key 중복 에러 + 다중공정 활성화 순서 버그
1. WorkOrderList: card key를 id+status+parent로 유니크화 2. saveResult: 분할행 완료+마스터 캐스케이드를 다음공정 활성화보다 먼저 실행 - 기존: 다음공정 활성화 → 분할행 완료 (마스터가 아직 미완료) - 수정: 분할행 완료 → 마스터 완료 → 다음공정 활성화 (정확한 상태 기반)
This commit is contained in:
@@ -955,6 +955,50 @@ export const saveResult = async (
|
||||
}
|
||||
}
|
||||
|
||||
// 개별 분할 행 자동완료 (다음 공정 활성화보다 먼저 실행)
|
||||
if (currentSeq.rowCount > 0) {
|
||||
const { seq_no: csSeq, wo_id: csWoId, current_input_qty: csInputQty, instruction_qty: csInstrQty, parent_process_id: csParentId } = currentSeq.rows[0];
|
||||
const csMyInput = parseInt(csInputQty, 10) || 0;
|
||||
|
||||
if (newTotal >= csMyInput && csMyInput > 0) {
|
||||
await pool.query(
|
||||
`UPDATE work_order_process SET status = 'completed', result_status = 'confirmed',
|
||||
completed_at = NOW()::text, completed_by = $3, updated_date = NOW()
|
||||
WHERE id = $1 AND company_code = $2 AND status != 'completed'`,
|
||||
[work_order_process_id, companyCode, userId]
|
||||
);
|
||||
|
||||
// 같은 seq의 모든 분할 행 완료 체크 → 마스터도 completed
|
||||
const csSeqNum = parseInt(csSeq, 10);
|
||||
let csPrevGood = parseInt(csInstrQty, 10) || 0;
|
||||
if (csSeqNum > 1) {
|
||||
const prev = await pool.query(
|
||||
`SELECT COALESCE(SUM(good_qty::int), 0) + COALESCE(SUM(concession_qty::int), 0) as tg
|
||||
FROM work_order_process WHERE wo_id = $1 AND seq_no = $2 AND company_code = $3`,
|
||||
[csWoId, String(csSeqNum - 1), companyCode]
|
||||
);
|
||||
if (prev.rowCount > 0) csPrevGood = parseInt(prev.rows[0].tg, 10) || 0;
|
||||
}
|
||||
const sibCheck = await pool.query(
|
||||
`SELECT COALESCE(SUM(input_qty::int), 0) as ti, COUNT(*) FILTER (WHERE status != 'completed') as ic
|
||||
FROM work_order_process WHERE wo_id = $1 AND seq_no = $2 AND company_code = $3 AND parent_process_id IS NOT NULL`,
|
||||
[csWoId, csSeq, companyCode]
|
||||
);
|
||||
const csTotalInput = parseInt(sibCheck.rows[0].ti, 10) || 0;
|
||||
const csIncomplete = parseInt(sibCheck.rows[0].ic, 10) || 0;
|
||||
if (csIncomplete === 0 && csPrevGood - csTotalInput <= 0 && csParentId) {
|
||||
await pool.query(
|
||||
`UPDATE work_order_process SET status = 'completed', result_status = 'confirmed',
|
||||
completed_at = NOW()::text, completed_by = $3, updated_date = NOW()
|
||||
WHERE id = $1 AND company_code = $2 AND status != 'completed'`,
|
||||
[csParentId, companyCode, userId]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await checkAndCompleteWorkInstruction(pool, csWoId, companyCode, userId);
|
||||
}
|
||||
|
||||
// 다음 공정 활성화 (다중공정 대응)
|
||||
// is_fixed_order='Y' 그룹이면 그룹 전체 완료 후 다음 활성화
|
||||
if (addGood > 0 && currentSeq.rowCount > 0) {
|
||||
@@ -1035,86 +1079,7 @@ export const saveResult = async (
|
||||
}
|
||||
}
|
||||
|
||||
// 개별 분할 행 자동완료: 이 분할 행의 접수분 전량 생산 시 completed
|
||||
if (currentSeq.rowCount > 0) {
|
||||
const { seq_no, wo_id, current_input_qty, instruction_qty } = currentSeq.rows[0];
|
||||
const myInputQty = parseInt(current_input_qty, 10) || 0;
|
||||
|
||||
if (newTotal >= myInputQty && myInputQty > 0) {
|
||||
await pool.query(
|
||||
`UPDATE work_order_process
|
||||
SET status = 'completed',
|
||||
result_status = 'confirmed',
|
||||
completed_at = NOW()::text,
|
||||
completed_by = $3,
|
||||
updated_date = NOW()
|
||||
WHERE id = $1 AND company_code = $2
|
||||
AND status != 'completed'`,
|
||||
[work_order_process_id, companyCode, userId]
|
||||
);
|
||||
logger.info("[pop/production] 분할 행 자동 완료", {
|
||||
work_order_process_id, newTotal, myInputQty,
|
||||
});
|
||||
|
||||
// 같은 공정의 모든 분할 행이 completed인지 체크 -> 원본도 completed로
|
||||
const seqNum = parseInt(seq_no, 10);
|
||||
const instrQty = parseInt(instruction_qty, 10) || 0;
|
||||
|
||||
// 앞공정 양품 합산 (접수가능 잔여 계산용)
|
||||
let prevGoodQty = instrQty;
|
||||
if (seqNum > 1) {
|
||||
const prevSeq = String(seqNum - 1);
|
||||
const prevProcess = await pool.query(
|
||||
`SELECT COALESCE(SUM(good_qty::int), 0) + COALESCE(SUM(concession_qty::int), 0) as total_good
|
||||
FROM work_order_process
|
||||
WHERE wo_id = $1 AND seq_no = $2 AND company_code = $3`,
|
||||
[wo_id, prevSeq, companyCode]
|
||||
);
|
||||
if (prevProcess.rowCount > 0) {
|
||||
prevGoodQty = parseInt(prevProcess.rows[0].total_good, 10) || 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 같은 seq_no의 모든 분할 행 접수량 합산 + 미완료 행 카운트
|
||||
const siblingCheck = await pool.query(
|
||||
`SELECT
|
||||
COALESCE(SUM(input_qty::int), 0) as total_input,
|
||||
COUNT(*) FILTER (WHERE status != 'completed') as incomplete_count
|
||||
FROM work_order_process
|
||||
WHERE wo_id = $1 AND seq_no = $2 AND company_code = $3
|
||||
AND parent_process_id IS NOT NULL`,
|
||||
[wo_id, seq_no, companyCode]
|
||||
);
|
||||
|
||||
const totalInput = parseInt(siblingCheck.rows[0].total_input, 10) || 0;
|
||||
const incompleteCount = parseInt(siblingCheck.rows[0].incomplete_count, 10) || 0;
|
||||
const remainingAcceptable = prevGoodQty - totalInput;
|
||||
|
||||
// 모든 분할 행 완료 + 잔여 접수가능 0 -> 원본(마스터)도 completed
|
||||
if (incompleteCount === 0 && remainingAcceptable <= 0) {
|
||||
const masterId = currentSeq.rows[0].parent_process_id;
|
||||
if (masterId) {
|
||||
await pool.query(
|
||||
`UPDATE work_order_process
|
||||
SET status = 'completed',
|
||||
result_status = 'confirmed',
|
||||
completed_at = NOW()::text,
|
||||
completed_by = $3,
|
||||
updated_date = NOW()
|
||||
WHERE id = $1 AND company_code = $2
|
||||
AND status != 'completed'`,
|
||||
[masterId, companyCode, userId]
|
||||
);
|
||||
logger.info("[pop/production] 원본(마스터) 공정 자동 완료", {
|
||||
masterId, totalInput, prevGoodQty,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 작업지시 전체 완료 판정
|
||||
const { wo_id: woIdForWi } = currentSeq.rows[0];
|
||||
await checkAndCompleteWorkInstruction(pool, woIdForWi, companyCode, userId);
|
||||
// (분할행 완료 + 마스터 캐스케이드는 위에서 이미 처리됨)
|
||||
}
|
||||
|
||||
logger.info("[pop/production] save-result 완료 (누적)", {
|
||||
|
||||
@@ -1182,7 +1182,7 @@ export function WorkOrderList() {
|
||||
const additionalAvailable = Math.max(0, planQty - inputQty);
|
||||
|
||||
return (
|
||||
<div key={proc.id} className="card-item">
|
||||
<div key={`${proc.id}-${proc.status}-${proc.parent_process_id || "m"}`} className="card-item">
|
||||
<div
|
||||
className={`bg-white rounded-2xl border-l-4 ${borderLeft} border border-gray-100 shadow-sm overflow-hidden flex flex-col ${
|
||||
proc.status === "waiting" ? "opacity-75" : ""
|
||||
|
||||
Reference in New Issue
Block a user