|
|
|
@@ -27,6 +27,7 @@ import { |
|
|
|
updateStockOutLineStatus, |
|
|
|
createStockOutLine, |
|
|
|
recordPickExecutionIssue, |
|
|
|
//applyPickExecutionHoldAndChecked, |
|
|
|
fetchFGPickOrders, |
|
|
|
FGPickOrderResponse, |
|
|
|
autoAssignAndReleasePickOrder, |
|
|
|
@@ -46,6 +47,7 @@ import { |
|
|
|
fetchJobOrderLotsHierarchicalByPickOrderId, |
|
|
|
updateJoPickOrderHandledBy, |
|
|
|
JobOrderLotsHierarchicalResponse, |
|
|
|
applyPickExecutionHoldAndChecked |
|
|
|
} from "@/app/api/jo/actions"; |
|
|
|
import { fetchNameList, NameList } from "@/app/api/user/actions"; |
|
|
|
import { |
|
|
|
@@ -2403,32 +2405,55 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { |
|
|
|
console.log("Pick execution form opened for lot ID:", lot.lotId); |
|
|
|
}, []); |
|
|
|
|
|
|
|
const handlePickExecutionFormSubmit = useCallback(async (data: any) => { |
|
|
|
try { |
|
|
|
if (currentUserId && selectedLotForExecutionForm?.pickOrderId && selectedLotForExecutionForm?.itemId) { |
|
|
|
try { |
|
|
|
await updateHandledBy(selectedLotForExecutionForm.pickOrderId, selectedLotForExecutionForm.itemId); |
|
|
|
console.log(`✅ [ISSUE FORM] Handler updated for itemId ${selectedLotForExecutionForm.itemId}`); |
|
|
|
} catch (error) { |
|
|
|
console.error(`❌ [ISSUE FORM] Error updating handler (non-critical):`, error); |
|
|
|
const handlePickExecutionFormSubmit = useCallback( |
|
|
|
async (data: any) => { |
|
|
|
const lotSnap = selectedLotForExecutionForm; |
|
|
|
const pickOrderIdEarly = |
|
|
|
filterArgs?.pickOrderId |
|
|
|
? Number(filterArgs.pickOrderId) |
|
|
|
: Number(lotSnap?.pickOrderId || 0) || undefined; |
|
|
|
|
|
|
|
try { |
|
|
|
if (currentUserId && lotSnap?.pickOrderId && lotSnap?.itemId) { |
|
|
|
try { |
|
|
|
await updateHandledBy(lotSnap.pickOrderId, lotSnap.itemId); |
|
|
|
console.log(`✅ [ISSUE FORM] Handler updated for itemId ${lotSnap.itemId}`); |
|
|
|
} catch (error) { |
|
|
|
console.error(`❌ [ISSUE FORM] Error updating handler (non-critical):`, error); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
console.log("Pick execution form submitted:", data); |
|
|
|
const issueData = { |
|
|
|
...data, |
|
|
|
type: "Jo", // Delivery Order Record 类型 |
|
|
|
pickerName: session?.user?.name || undefined, |
|
|
|
handledBy: currentUserId || undefined, |
|
|
|
}; |
|
|
|
|
|
|
|
const result = await recordPickExecutionIssue(issueData); |
|
|
|
console.log("Pick execution issue recorded:", result); |
|
|
|
|
|
|
|
if (result && result.code === "SUCCESS") { |
|
|
|
console.log(" Pick execution issue recorded successfully"); |
|
|
|
|
|
|
|
console.log("Pick execution form submitted:", data); |
|
|
|
const issueData = { |
|
|
|
...data, |
|
|
|
type: "Jo", |
|
|
|
pickerName: session?.user?.name || undefined, |
|
|
|
handledBy: currentUserId || undefined, |
|
|
|
}; |
|
|
|
|
|
|
|
const missN = Number(issueData.missQty ?? 0) || 0; |
|
|
|
const badN = Number(issueData.badItemQty ?? 0) || 0; |
|
|
|
const badPkgN = Number(issueData.badPackageQty ?? 0) || 0; |
|
|
|
const useHoldOnlyApi = missN === 0 && badN === 0 && badPkgN === 0; |
|
|
|
|
|
|
|
const result = useHoldOnlyApi |
|
|
|
? await applyPickExecutionHoldAndChecked(issueData) |
|
|
|
: await recordPickExecutionIssue(issueData); |
|
|
|
|
|
|
|
console.log( |
|
|
|
useHoldOnlyApi ? "Pick hold/checked applied:" : "Pick execution issue recorded:", |
|
|
|
result, |
|
|
|
); |
|
|
|
|
|
|
|
if (!result || result.code !== "SUCCESS") { |
|
|
|
console.error("❌ Pick execution submit failed:", result); |
|
|
|
throw new Error(result?.message || "Submit failed"); |
|
|
|
} |
|
|
|
|
|
|
|
const solId = Number(issueData.stockOutLineId || data?.stockOutLineId); |
|
|
|
const picked = Number(issueData.actualPickQty ?? 0); |
|
|
|
|
|
|
|
if (solId > 0) { |
|
|
|
const picked = Number(issueData.actualPickQty || 0); |
|
|
|
setIssuePickedQtyBySolId((prev) => { |
|
|
|
const next = { ...prev, [solId]: picked }; |
|
|
|
const pid = filterArgs?.pickOrderId ? Number(filterArgs.pickOrderId) : undefined; |
|
|
|
@@ -2436,23 +2461,101 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { |
|
|
|
return next; |
|
|
|
}); |
|
|
|
} |
|
|
|
} else { |
|
|
|
console.error("❌ Failed to record pick execution issue:", result); |
|
|
|
|
|
|
|
// Hold-only:與整批相同規則,只送一筆 batch,把實揈/完成狀態寫回 DB |
|
|
|
if (useHoldOnlyApi && pickOrderIdEarly && solId > 0) { |
|
|
|
const freshData = await fetchJobOrderLotsHierarchicalByPickOrderId(pickOrderIdEarly); |
|
|
|
const flatLots = getAllLotsFromHierarchical(freshData); |
|
|
|
const lotRow = flatLots.find((l: any) => Number(l.stockOutLineId) === solId); |
|
|
|
if (!lotRow) { |
|
|
|
throw new Error("Could not find lot row after refresh for batch submit"); |
|
|
|
} |
|
|
|
|
|
|
|
const requiredQty = Number(lotRow.requiredQty || lotRow.pickOrderLineRequiredQty || 0); |
|
|
|
const issuePickedVal = picked; |
|
|
|
const currentActualPickQty = Number(issuePickedVal ?? lotRow.actualPickQty ?? 0); |
|
|
|
const onlyComplete = |
|
|
|
lotRow.stockOutLineStatus === "partially_completed" || |
|
|
|
lotRow.stockOutLineStatus === "PARTIALLY_COMPLETE" || |
|
|
|
issuePickedVal !== undefined; |
|
|
|
const expired = isLotAvailabilityExpired(lotRow); |
|
|
|
const unavailable = isInventoryLotLineUnavailable(lotRow); |
|
|
|
|
|
|
|
let targetActual: number; |
|
|
|
let newStatus: string; |
|
|
|
|
|
|
|
if (unavailable) { |
|
|
|
targetActual = currentActualPickQty; |
|
|
|
newStatus = "completed"; |
|
|
|
} else if (expired && issuePickedVal === undefined) { |
|
|
|
targetActual = 0; |
|
|
|
newStatus = "completed"; |
|
|
|
} else if (onlyComplete) { |
|
|
|
targetActual = currentActualPickQty; |
|
|
|
newStatus = "completed"; |
|
|
|
} else { |
|
|
|
const remainingQty = Math.max(0, requiredQty - currentActualPickQty); |
|
|
|
const cumulativeQty = currentActualPickQty + remainingQty; |
|
|
|
targetActual = cumulativeQty; |
|
|
|
newStatus = "partially_completed"; |
|
|
|
if (requiredQty > 0 && cumulativeQty >= requiredQty) { |
|
|
|
newStatus = "completed"; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const line: batchSubmitListLineRequest = { |
|
|
|
stockOutLineId: solId, |
|
|
|
pickOrderLineId: Number(lotRow.pickOrderLineId), |
|
|
|
inventoryLotLineId: lotRow.lotId ? Number(lotRow.lotId) : null, |
|
|
|
requiredQty, |
|
|
|
actualPickQty: targetActual, |
|
|
|
stockOutLineStatus: newStatus, |
|
|
|
pickOrderConsoCode: String(lotRow.pickOrderConsoCode || ""), |
|
|
|
noLot: Boolean(lotRow.noLot === true), |
|
|
|
}; |
|
|
|
|
|
|
|
const batchResult = await batchSubmitList({ |
|
|
|
userId: currentUserId || 0, |
|
|
|
lines: [line], |
|
|
|
}); |
|
|
|
|
|
|
|
if (!batchResult || batchResult.code !== "SUCCESS") { |
|
|
|
throw new Error(batchResult?.message || "Batch submit failed after hold adjustment"); |
|
|
|
} |
|
|
|
|
|
|
|
const conso = String(lotRow.pickOrderConsoCode || "").trim(); |
|
|
|
if (conso) { |
|
|
|
try { |
|
|
|
await checkAndCompletePickOrderByConsoCode(conso); |
|
|
|
} catch (e) { |
|
|
|
console.error("❌ completion check after single batch:", e); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
setPickExecutionFormOpen(false); |
|
|
|
setSelectedLotForExecutionForm(null); |
|
|
|
|
|
|
|
await fetchJobOrderData(pickOrderIdEarly); |
|
|
|
} catch (error) { |
|
|
|
console.error("Error submitting pick execution form:", error); |
|
|
|
throw error; |
|
|
|
} |
|
|
|
|
|
|
|
setPickExecutionFormOpen(false); |
|
|
|
setSelectedLotForExecutionForm(null); |
|
|
|
|
|
|
|
const pickOrderId = |
|
|
|
filterArgs?.pickOrderId |
|
|
|
? Number(filterArgs.pickOrderId) |
|
|
|
: Number(selectedLotForExecutionForm?.pickOrderId || 0) || undefined; |
|
|
|
await fetchJobOrderData(pickOrderId); |
|
|
|
} catch (error) { |
|
|
|
console.error("Error submitting pick execution form:", error); |
|
|
|
} |
|
|
|
}, [fetchJobOrderData, currentUserId, selectedLotForExecutionForm, updateHandledBy, filterArgs?.pickOrderId, filterArgs]); |
|
|
|
|
|
|
|
}, |
|
|
|
[ |
|
|
|
fetchJobOrderData, |
|
|
|
getAllLotsFromHierarchical, |
|
|
|
currentUserId, |
|
|
|
selectedLotForExecutionForm, |
|
|
|
updateHandledBy, |
|
|
|
filterArgs, |
|
|
|
session?.user?.name, |
|
|
|
batchSubmitList, |
|
|
|
checkAndCompletePickOrderByConsoCode, |
|
|
|
isLotAvailabilityExpired, |
|
|
|
isInventoryLotLineUnavailable, |
|
|
|
], |
|
|
|
); |
|
|
|
// Calculate remaining required quantity |
|
|
|
const calculateRemainingRequiredQty = useCallback((lot: any) => { |
|
|
|
const requiredQty = lot.requiredQty || 0; |
|
|
|
|