|
|
|
@@ -457,6 +457,8 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { |
|
|
|
const [jobOrderData, setJobOrderData] = useState<JobOrderLotsHierarchicalResponse | null>(null); |
|
|
|
const [pickQtyData, setPickQtyData] = useState<Record<string, number>>({}); |
|
|
|
const [searchQuery, setSearchQuery] = useState<Record<string, any>>({}); |
|
|
|
// issue form 里填的 actualPickQty(用于 submit/batch submit 不补拣到 required) |
|
|
|
const [issuePickedQtyBySolId, setIssuePickedQtyBySolId] = useState<Record<number, number>>({}); |
|
|
|
|
|
|
|
const [paginationController, setPaginationController] = useState({ |
|
|
|
pageNum: 0, |
|
|
|
@@ -631,8 +633,24 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { |
|
|
|
return isNaN(n) ? 0 : n; |
|
|
|
}; |
|
|
|
const combinedLotData = useMemo(() => { |
|
|
|
return getAllLotsFromHierarchical(jobOrderData); |
|
|
|
}, [jobOrderData, getAllLotsFromHierarchical]); |
|
|
|
const lots = getAllLotsFromHierarchical(jobOrderData); |
|
|
|
// 前端覆盖:issue form/submit0 不会立刻改写后端 qty 时,用本地缓存让 UI 与 batch submit 计算一致 |
|
|
|
return lots.map((lot: any) => { |
|
|
|
const solId = Number(lot.stockOutLineId) || 0; |
|
|
|
if (solId > 0 && Object.prototype.hasOwnProperty.call(issuePickedQtyBySolId, solId)) { |
|
|
|
const picked = Number(issuePickedQtyBySolId[solId] ?? 0); |
|
|
|
const status = String(lot.stockOutLineStatus || '').toLowerCase(); |
|
|
|
const isEnded = status === 'completed' || status === 'rejected'; |
|
|
|
return { |
|
|
|
...lot, |
|
|
|
actualPickQty: picked, |
|
|
|
stockOutLineQty: picked, |
|
|
|
stockOutLineStatus: isEnded ? lot.stockOutLineStatus : 'checked', |
|
|
|
}; |
|
|
|
} |
|
|
|
return lot; |
|
|
|
}); |
|
|
|
}, [jobOrderData, getAllLotsFromHierarchical, issuePickedQtyBySolId]); |
|
|
|
|
|
|
|
const originalCombinedData = useMemo(() => { |
|
|
|
return getAllLotsFromHierarchical(jobOrderData); |
|
|
|
@@ -1786,16 +1804,18 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { |
|
|
|
// Continue even if handler update fails |
|
|
|
} |
|
|
|
} |
|
|
|
// Special case: If submitQty is 0 and all values are 0, mark as completed with qty: 0 |
|
|
|
// ✅ 两步完成(与 DO 对齐): |
|
|
|
// 1) Skip/Submit0 只把 SOL 标记为 checked(不直接 completed) |
|
|
|
// 2) 之后由 batch submit 把 SOL 推到 completed(允许 0) |
|
|
|
if (submitQty === 0) { |
|
|
|
console.log(`=== SUBMITTING ALL ZEROS CASE ===`); |
|
|
|
console.log(`Lot: ${lot.lotNo}`); |
|
|
|
console.log(`Stock Out Line ID: ${lot.stockOutLineId}`); |
|
|
|
console.log(`Setting status to 'completed' with qty: 0`); |
|
|
|
console.log(`Setting status to 'checked' with qty: 0 (will complete in batch submit)`); |
|
|
|
|
|
|
|
const updateResult = await updateStockOutLineStatus({ |
|
|
|
id: lot.stockOutLineId, |
|
|
|
status: 'completed', |
|
|
|
status: 'checked', |
|
|
|
qty: 0 |
|
|
|
}); |
|
|
|
|
|
|
|
@@ -1813,34 +1833,15 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Check if pick order is completed |
|
|
|
if (lot.pickOrderConsoCode) { |
|
|
|
console.log(` Lot ${lot.lotNo} completed (all zeros), checking if pick order ${lot.pickOrderConsoCode} is complete...`); |
|
|
|
|
|
|
|
try { |
|
|
|
const completionResponse = await checkAndCompletePickOrderByConsoCode(lot.pickOrderConsoCode); |
|
|
|
console.log(` Pick order completion check result:`, completionResponse); |
|
|
|
|
|
|
|
if (completionResponse.code === "SUCCESS") { |
|
|
|
console.log(` Pick order ${lot.pickOrderConsoCode} completed successfully!`); |
|
|
|
setTimeout(() => { |
|
|
|
if (onBackToList) { |
|
|
|
onBackToList(); |
|
|
|
} |
|
|
|
}, 1500); |
|
|
|
} else if (completionResponse.message === "not completed") { |
|
|
|
console.log(`⏳ Pick order not completed yet, more lines remaining`); |
|
|
|
} else { |
|
|
|
console.error(`❌ Error checking completion: ${completionResponse.message}`); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error("Error checking pick order completion:", error); |
|
|
|
} |
|
|
|
// 记录该 SOL 的“目标实际拣货量=0”,让 batch submit 走 onlyComplete(不补拣到 required) |
|
|
|
const solId = Number(lot.stockOutLineId) || 0; |
|
|
|
if (solId > 0) { |
|
|
|
setIssuePickedQtyBySolId(prev => ({ ...prev, [solId]: 0 })); |
|
|
|
} |
|
|
|
|
|
|
|
const pickOrderId = filterArgs?.pickOrderId ? Number(filterArgs.pickOrderId) : undefined; |
|
|
|
await fetchJobOrderData(pickOrderId); |
|
|
|
console.log("All zeros submission completed successfully!"); |
|
|
|
console.log("All zeros submission marked as checked successfully (waiting for batch submit)."); |
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
|
checkAndAutoAssignNext(); |
|
|
|
@@ -1965,24 +1966,32 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { |
|
|
|
|
|
|
|
// ✅ 转换为 batchSubmitList 所需的格式 |
|
|
|
const lines: batchSubmitListLineRequest[] = scannedLots.map((lot) => { |
|
|
|
const submitQty = lot.requiredQty || lot.pickOrderLineRequiredQty || 0; |
|
|
|
const currentActualPickQty = lot.actualPickQty || 0; |
|
|
|
const cumulativeQty = currentActualPickQty + submitQty; |
|
|
|
|
|
|
|
let newStatus = 'partially_completed'; |
|
|
|
if (cumulativeQty >= (lot.requiredQty || 0)) { |
|
|
|
const requiredQty = Number(lot.requiredQty || lot.pickOrderLineRequiredQty || 0); |
|
|
|
const solId = Number(lot.stockOutLineId) || 0; |
|
|
|
const issuePicked = solId > 0 ? issuePickedQtyBySolId[solId] : undefined; |
|
|
|
const currentActualPickQty = Number(issuePicked ?? lot.actualPickQty ?? 0); |
|
|
|
const onlyComplete = lot.stockOutLineStatus === 'partially_completed' || issuePicked !== undefined; |
|
|
|
|
|
|
|
let targetActual: number; |
|
|
|
let newStatus: string; |
|
|
|
if (onlyComplete) { |
|
|
|
targetActual = currentActualPickQty; |
|
|
|
newStatus = 'completed'; |
|
|
|
} else { |
|
|
|
const remainingQty = Math.max(0, requiredQty - currentActualPickQty); |
|
|
|
targetActual = currentActualPickQty + remainingQty; |
|
|
|
newStatus = (requiredQty > 0 && targetActual >= requiredQty) ? 'completed' : 'partially_completed'; |
|
|
|
} |
|
|
|
|
|
|
|
return { |
|
|
|
stockOutLineId: Number(lot.stockOutLineId) || 0, |
|
|
|
pickOrderLineId: Number(lot.pickOrderLineId), |
|
|
|
inventoryLotLineId: lot.lotId ? Number(lot.lotId) : null, |
|
|
|
requiredQty: Number(lot.requiredQty || lot.pickOrderLineRequiredQty || 0), |
|
|
|
actualPickQty: Number(cumulativeQty), |
|
|
|
requiredQty, |
|
|
|
actualPickQty: Number(targetActual), |
|
|
|
stockOutLineStatus: newStatus, |
|
|
|
pickOrderConsoCode: String(lot.pickOrderConsoCode || ''), |
|
|
|
noLot: Boolean(false) // Job Order 通常都有 lot |
|
|
|
noLot: Boolean(lot.noLot === true) |
|
|
|
}; |
|
|
|
}); |
|
|
|
|
|
|
|
@@ -2019,9 +2028,11 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { |
|
|
|
} finally { |
|
|
|
setIsSubmittingAll(false); |
|
|
|
} |
|
|
|
}, [combinedLotData, fetchJobOrderData, checkAndAutoAssignNext, currentUserId, filterArgs?.pickOrderId, onBackToList, updateHandledBy]) |
|
|
|
}, [combinedLotData, fetchJobOrderData, checkAndAutoAssignNext, currentUserId, filterArgs?.pickOrderId, onBackToList, updateHandledBy, issuePickedQtyBySolId]) |
|
|
|
const scannedItemsCount = useMemo(() => { |
|
|
|
return combinedLotData.filter(lot => lot.stockOutLineStatus === 'checked').length; |
|
|
|
return combinedLotData.filter(lot => |
|
|
|
lot.stockOutLineStatus === 'checked' || lot.stockOutLineStatus === 'partially_completed' |
|
|
|
).length; |
|
|
|
}, [combinedLotData]); |
|
|
|
|
|
|
|
// 先定义 filteredByFloor 和 availableFloors |
|
|
|
@@ -2114,6 +2125,11 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { |
|
|
|
|
|
|
|
if (result && result.code === "SUCCESS") { |
|
|
|
console.log(" Pick execution issue recorded successfully"); |
|
|
|
const solId = Number(issueData.stockOutLineId || data?.stockOutLineId); |
|
|
|
if (solId > 0) { |
|
|
|
const picked = Number(issueData.actualPickQty || 0); |
|
|
|
setIssuePickedQtyBySolId(prev => ({ ...prev, [solId]: picked })); |
|
|
|
} |
|
|
|
} else { |
|
|
|
console.error("❌ Failed to record pick execution issue:", result); |
|
|
|
} |
|
|
|
@@ -2126,7 +2142,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { |
|
|
|
} catch (error) { |
|
|
|
console.error("Error submitting pick execution form:", error); |
|
|
|
} |
|
|
|
}, [fetchJobOrderData]); |
|
|
|
}, [fetchJobOrderData, currentUserId, selectedLotForExecutionForm, updateHandledBy, filterArgs?.pickOrderId]); |
|
|
|
|
|
|
|
// Calculate remaining required quantity |
|
|
|
const calculateRemainingRequiredQty = useCallback((lot: any) => { |
|
|
|
|