|
|
|
@@ -186,7 +186,7 @@ const QrCodeModal: React.FC<{ |
|
|
|
const { t } = useTranslation("jo"); |
|
|
|
const { values: qrValues, isScanning, startScan, stopScan, resetScan } = useQrCodeScannerContext(); |
|
|
|
const [manualInput, setManualInput] = useState<string>(''); |
|
|
|
// 楼层筛选状态 |
|
|
|
|
|
|
|
const [selectedFloor, setSelectedFloor] = useState<string | null>(null); |
|
|
|
const [manualInputSubmitted, setManualInputSubmitted] = useState<boolean>(false); |
|
|
|
const [manualInputError, setManualInputError] = useState<boolean>(false); |
|
|
|
@@ -390,10 +390,15 @@ const QrCodeModal: React.FC<{ |
|
|
|
: '' |
|
|
|
} |
|
|
|
/> |
|
|
|
|
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
onClick={handleManualSubmit} |
|
|
|
disabled={!manualInput.trim()} |
|
|
|
disabled={ |
|
|
|
!manualInput.trim() || |
|
|
|
lot?.noLot === true || |
|
|
|
!lot?.lotId |
|
|
|
} |
|
|
|
size="small" |
|
|
|
color="primary" |
|
|
|
> |
|
|
|
@@ -824,8 +829,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { |
|
|
|
|
|
|
|
// 使用辅助函数获取所有 lots(不再扁平化) |
|
|
|
const allLots = getAllLotsFromHierarchical(jobOrderData); |
|
|
|
|
|
|
|
// ... 其他逻辑保持不变 ... |
|
|
|
|
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
console.error("❌ Error fetching job order data:", error); |
|
|
|
@@ -1935,10 +1939,27 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { |
|
|
|
} |
|
|
|
}, [handleSubmitPickQtyWithQty]); |
|
|
|
const handleSubmitAllScanned = useCallback(async () => { |
|
|
|
const scannedLots = combinedLotData.filter(lot => |
|
|
|
lot.stockOutLineStatus === 'checked' || lot.stockOutLineStatus === 'partially_completed' |
|
|
|
); |
|
|
|
|
|
|
|
const scannedLots = combinedLotData.filter(lot => { |
|
|
|
const status = lot.stockOutLineStatus; |
|
|
|
console.log("lot.noLot:", lot.noLot); |
|
|
|
console.log("lot.status:", lot.stockOutLineStatus); |
|
|
|
// ✅ no-lot:允許 pending / checked / partially_completed / PARTIALLY_COMPLETE |
|
|
|
if (lot.noLot === true || !lot.lotId) { |
|
|
|
return ( |
|
|
|
status === 'checked' || |
|
|
|
status === 'pending' || |
|
|
|
status === 'partially_completed' || |
|
|
|
status === 'PARTIALLY_COMPLETE' |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
// ✅ 有 lot:維持原本規則 |
|
|
|
return ( |
|
|
|
status === 'checked' || |
|
|
|
status === 'partially_completed' || |
|
|
|
status === 'PARTIALLY_COMPLETE' |
|
|
|
); |
|
|
|
}); |
|
|
|
if (scannedLots.length === 0) { |
|
|
|
console.log("No scanned items to submit"); |
|
|
|
return; |
|
|
|
@@ -1967,21 +1988,31 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { |
|
|
|
// ✅ 转换为 batchSubmitList 所需的格式 |
|
|
|
const lines: batchSubmitListLineRequest[] = scannedLots.map((lot) => { |
|
|
|
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'; |
|
|
|
} |
|
|
|
const solId = Number(lot.stockOutLineId) || 0; |
|
|
|
const issuePicked = solId > 0 ? issuePickedQtyBySolId[solId] : undefined; |
|
|
|
const currentActualPickQty = Number(issuePicked ?? lot.actualPickQty ?? 0); |
|
|
|
const isNoLot = lot.noLot === true || !lot.lotId; |
|
|
|
|
|
|
|
// ✅ 只改狀態模式:有 issuePicked 或 noLot |
|
|
|
const onlyComplete = |
|
|
|
lot.stockOutLineStatus === 'partially_completed' || |
|
|
|
issuePicked !== undefined || |
|
|
|
isNoLot; |
|
|
|
|
|
|
|
let targetActual: number; |
|
|
|
let newStatus: string; |
|
|
|
|
|
|
|
if (onlyComplete) { |
|
|
|
targetActual = currentActualPickQty; // no‑lot = 0,一律只改狀態 |
|
|
|
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, |
|
|
|
@@ -2056,9 +2087,27 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => { |
|
|
|
} |
|
|
|
}, [combinedLotData, fetchJobOrderData, checkAndAutoAssignNext, currentUserId, filterArgs?.pickOrderId, onBackToList, updateHandledBy, issuePickedQtyBySolId]) |
|
|
|
const scannedItemsCount = useMemo(() => { |
|
|
|
return combinedLotData.filter(lot => |
|
|
|
lot.stockOutLineStatus === 'checked' || lot.stockOutLineStatus === 'partially_completed' |
|
|
|
).length; |
|
|
|
return combinedLotData.filter(lot => { |
|
|
|
const status = lot.stockOutLineStatus; |
|
|
|
const isNoLot = lot.noLot === true || !lot.lotId; |
|
|
|
|
|
|
|
if (isNoLot) { |
|
|
|
// no-lot:pending / checked / partially_completed 都算「已掃描」 |
|
|
|
return ( |
|
|
|
status === 'pending' || |
|
|
|
status === 'checked' || |
|
|
|
status === 'partially_completed' || |
|
|
|
status === 'PARTIALLY_COMPLETE' |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
// 有 lot:維持原規則 |
|
|
|
return ( |
|
|
|
status === 'checked' || |
|
|
|
status === 'partially_completed' || |
|
|
|
status === 'PARTIALLY_COMPLETE' |
|
|
|
); |
|
|
|
}).length; |
|
|
|
}, [combinedLotData]); |
|
|
|
|
|
|
|
// 先定义 filteredByFloor 和 availableFloors |
|
|
|
@@ -2495,17 +2544,10 @@ const sortedData = [...sourceData].sort((a, b) => { |
|
|
|
<TableCell>{lot.itemCode}</TableCell> |
|
|
|
<TableCell>{lot.itemName+'('+lot.uomDesc+')'}</TableCell> |
|
|
|
<TableCell> |
|
|
|
<Box> |
|
|
|
<Typography |
|
|
|
sx={{ |
|
|
|
// color: lot.lotAvailability === 'rejected' ? 'text.disabled' : 'inherit', |
|
|
|
//opacity: lot.lotAvailability === 'rejected' ? 0.6 : 1 |
|
|
|
}} |
|
|
|
> |
|
|
|
{lot.lotNo} |
|
|
|
</Typography> |
|
|
|
</Box> |
|
|
|
</TableCell> |
|
|
|
{lot.noLot === true || !lot.lotId |
|
|
|
? t("Please check around have QR code or not, may be have just now stock in or transfer in or transfer out.") // i18n key,下一步加文案 |
|
|
|
: (lot.lotNo || '-')} |
|
|
|
</TableCell> |
|
|
|
<TableCell align="right"> |
|
|
|
{(() => { |
|
|
|
const requiredQty = lot.requiredQty || 0; |
|
|
|
@@ -2589,6 +2631,7 @@ const sortedData = [...sourceData].sort((a, b) => { |
|
|
|
|
|
|
|
// 正常 lot:显示按钮 |
|
|
|
return ( |
|
|
|
|
|
|
|
<Stack direction="row" spacing={1} alignItems="center"> |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
@@ -2621,7 +2664,7 @@ const sortedData = [...sourceData].sort((a, b) => { |
|
|
|
size="small" |
|
|
|
onClick={() => handlePickExecutionForm(lot)} |
|
|
|
disabled={ |
|
|
|
lot.stockOutLineStatus === 'completed' |
|
|
|
lot.stockOutLineStatus === 'completed' || lot.noLot === true || !lot.lotId |
|
|
|
} |
|
|
|
sx={{ |
|
|
|
fontSize: '0.7rem', |
|
|
|
@@ -2649,7 +2692,7 @@ const sortedData = [...sourceData].sort((a, b) => { |
|
|
|
} |
|
|
|
await handleSubmitPickQtyWithQty(lot, lot.requiredQty || lot.pickOrderLineRequiredQty || 0); |
|
|
|
}} |
|
|
|
disabled={lot.stockOutLineStatus === 'completed'} |
|
|
|
disabled={lot.stockOutLineStatus === 'completed' || lot.noLot === true || !lot.lotId} |
|
|
|
sx={{ fontSize: '0.7rem', py: 0.5, minHeight: '28px', minWidth: '90px' }} |
|
|
|
> |
|
|
|
{t("Just Complete")} |
|
|
|
|