| @@ -903,8 +903,24 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO | |||
| // 将层级数据转换为平铺格式(用于表格显示) | |||
| const flatLotData: any[] = []; | |||
| mergedPickOrder.pickOrderLines.forEach((line: any) => { | |||
| // 2/F 與後端 store_id 一致時需按 itemOrder;避免 API 未走 2F 分支時畫面仍亂序 | |||
| const doFloorKey = String(hierarchicalData.fgInfo.storeId ?? '') | |||
| .trim() | |||
| .toUpperCase() | |||
| .replace(/\//g, '') | |||
| .replace(/\s/g, ''); | |||
| const pickOrderLinesForDisplay = | |||
| doFloorKey === '2F' | |||
| ? [...(mergedPickOrder.pickOrderLines || [])].sort((a: any, b: any) => { | |||
| const ao = a.itemOrder != null ? Number(a.itemOrder) : 999999; | |||
| const bo = b.itemOrder != null ? Number(b.itemOrder) : 999999; | |||
| if (ao !== bo) return ao - bo; | |||
| return (Number(a.id) || 0) - (Number(b.id) || 0); | |||
| }) | |||
| : mergedPickOrder.pickOrderLines || []; | |||
| pickOrderLinesForDisplay.forEach((line: any) => { | |||
| // 用来记录这一行已经通过 lots 出现过的 lotId | |||
| const lotIdSet = new Set<number>(); | |||
| @@ -2769,7 +2785,7 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe | |||
| if (cumulativeQty >= lot.requiredQty) { | |||
| newStatus = 'completed'; | |||
| } else if (cumulativeQty > 0) { | |||
| newStatus = 'completed'; | |||
| newStatus = 'partially_completed'; | |||
| } else { | |||
| newStatus = 'checked'; // QR scanned but no quantity submitted yet | |||
| } | |||
| @@ -2786,18 +2802,12 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe | |||
| await updateStockOutLineStatus({ | |||
| id: lot.stockOutLineId, | |||
| status: newStatus, | |||
| qty: cumulativeQty // Use cumulative quantity | |||
| // 后端 updateStatus 的 qty 是“增量 delta”,不能传 cumulativeQty(否则会重复累加导致 out/hold 大幅偏移) | |||
| qty: submitQty | |||
| }); | |||
| applyLocalStockOutLineUpdate(Number(lot.stockOutLineId), newStatus, cumulativeQty); | |||
| if (submitQty > 0) { | |||
| await updateInventoryLotLineQuantities({ | |||
| inventoryLotLineId: lot.lotId, | |||
| qty: submitQty, | |||
| status: 'available', | |||
| operation: 'pick' | |||
| }); | |||
| } | |||
| // 注意:库存过账(hold->out)与 ledger 由后端 updateStatus 内部统一处理; | |||
| // 前端不再额外调用 updateInventoryLotLineQuantities(operation='pick'),避免 double posting。 | |||
| // Check if pick order is completed when lot status becomes 'completed' | |||
| if (newStatus === 'completed' && lot.pickOrderConsoCode) { | |||
| @@ -42,7 +42,16 @@ const LotConfirmationModal: React.FC<LotConfirmationModalProps> = ({ | |||
| const { t } = useTranslation("pickOrder"); | |||
| return ( | |||
| <Dialog open={open} onClose={onClose} maxWidth="md" fullWidth> | |||
| <Dialog | |||
| open={open} | |||
| onClose={onClose} | |||
| maxWidth="md" | |||
| fullWidth | |||
| disableScrollLock | |||
| disableAutoFocus | |||
| disableEnforceFocus | |||
| disableRestoreFocus | |||
| > | |||
| <DialogTitle> | |||
| <Typography variant="h6" component="div" color="warning.main"> | |||
| {t("Lot Number Mismatch")} | |||
| @@ -252,20 +252,16 @@ useEffect(() => { | |||
| && (formData.badItemQty == null || formData.badItemQty === 0) | |||
| && (badPackageQty === 0); | |||
| if (isNormalPick) { | |||
| if (onNormalPickSubmit) { | |||
| setLoading(true); | |||
| try { | |||
| console.log('Calling onNormalPickSubmit with:', { lot: selectedLot, submitQty: verifiedQty }); | |||
| await onNormalPickSubmit(selectedLot, verifiedQty); | |||
| onClose(); | |||
| } catch (error) { | |||
| console.error('Error submitting normal pick:', error); | |||
| } finally { | |||
| setLoading(false); | |||
| } | |||
| } else { | |||
| console.warn('onNormalPickSubmit callback not provided'); | |||
| if (isNormalPick && onNormalPickSubmit) { | |||
| setLoading(true); | |||
| try { | |||
| console.log('Calling onNormalPickSubmit with:', { lot: selectedLot, submitQty: verifiedQty }); | |||
| await onNormalPickSubmit(selectedLot, verifiedQty); | |||
| onClose(); | |||
| } catch (error) { | |||
| console.error('Error submitting normal pick:', error); | |||
| } finally { | |||
| setLoading(false); | |||
| } | |||
| return; | |||
| } | |||
| @@ -42,7 +42,16 @@ const LotConfirmationModal: React.FC<LotConfirmationModalProps> = ({ | |||
| const { t } = useTranslation("pickOrder"); | |||
| return ( | |||
| <Dialog open={open} onClose={onClose} maxWidth="md" fullWidth> | |||
| <Dialog | |||
| open={open} | |||
| onClose={onClose} | |||
| maxWidth="md" | |||
| fullWidth | |||
| disableScrollLock | |||
| disableAutoFocus | |||
| disableEnforceFocus | |||
| disableRestoreFocus | |||
| > | |||
| <DialogTitle> | |||
| <Typography variant="h6" component="div" color="warning.main"> | |||
| {t("Lot Number Mismatch")} | |||
| @@ -861,8 +861,6 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { | |||
| try { | |||
| if (!pickOrderId) { | |||
| console.warn("⚠️ No pickOrderId provided, skipping API call"); | |||
| setJobOrderData(null); | |||
| setIssuePickedQtyBySolId({}); | |||
| return; | |||
| } | |||
| @@ -1937,7 +1935,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { | |||
| if (cumulativeQty >= lot.requiredQty) { | |||
| newStatus = "completed"; | |||
| } else if (cumulativeQty > 0) { | |||
| newStatus = "completed"; | |||
| newStatus = "partially_completed"; | |||
| } else { | |||
| newStatus = "checked"; | |||
| } | |||
| @@ -1954,7 +1952,8 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { | |||
| await updateStockOutLineStatus({ | |||
| id: lot.stockOutLineId, | |||
| status: newStatus, | |||
| qty: cumulativeQty | |||
| // 后端 updateStatus 的 qty 是“增量 delta”,不能传 cumulativeQty(否则会重复累加导致 out/hold 大幅偏移) | |||
| qty: submitQty | |||
| }); | |||
| if (solId > 0) { | |||
| setIssuePickedQtyBySolId((prev) => { | |||
| @@ -1965,15 +1964,8 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { | |||
| }); | |||
| setLocalSolStatusById(prev => ({ ...prev, [solId]: newStatus })); | |||
| } | |||
| if (submitQty > 0) { | |||
| await updateInventoryLotLineQuantities({ | |||
| inventoryLotLineId: lot.lotId, | |||
| qty: submitQty, | |||
| status: 'available', | |||
| operation: 'pick' | |||
| }); | |||
| } | |||
| // 注意:库存过账(hold->out)与 ledger 由后端 updateStatus 内部统一处理; | |||
| // 前端不再额外调用 updateInventoryLotLineQuantities(operation='pick'),避免 double posting。 | |||
| // Check if pick order is completed when lot status becomes 'completed' | |||
| if (newStatus === 'completed' && lot.pickOrderConsoCode) { | |||
| @@ -2317,7 +2309,10 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { | |||
| setPickExecutionFormOpen(false); | |||
| setSelectedLotForExecutionForm(null); | |||
| const pickOrderId = filterArgs?.pickOrderId ? Number(filterArgs.pickOrderId) : undefined; | |||
| 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); | |||
| @@ -3039,16 +3034,6 @@ const sortedData = [...sourceData].sort((a, b) => { | |||
| }} | |||
| pickOrderId={selectedLotForExecutionForm.pickOrderId} | |||
| pickOrderCreateDate={new Date()} | |||
| onNormalPickSubmit={async (lot, submitQty) => { | |||
| console.log('onNormalPickSubmit called in newJobPickExecution:', { lot, submitQty }); | |||
| if (!lot) { | |||
| console.error('Lot is null or undefined'); | |||
| return; | |||
| } | |||
| const lotKey = `${lot.pickOrderLineId}-${lot.lotId}`; | |||
| handlePickQtyChange(lotKey, submitQty); | |||
| await handleSubmitPickQtyWithQty(lot, submitQty); | |||
| }} | |||
| /> | |||
| )} | |||
| </FormProvider> | |||