|
|
@@ -15,7 +15,7 @@ import { |
|
|
import { fetchItemForPutAway } from "@/app/api/stockIn/actions"; |
|
|
import { fetchItemForPutAway } from "@/app/api/stockIn/actions"; |
|
|
import { Result } from "@/app/api/settings/item"; // 只导入类型 |
|
|
import { Result } from "@/app/api/settings/item"; // 只导入类型 |
|
|
|
|
|
|
|
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useState } from "react"; |
|
|
|
|
|
|
|
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; |
|
|
import ReactQrCodeScanner, { |
|
|
import ReactQrCodeScanner, { |
|
|
ScannerConfig, |
|
|
ScannerConfig, |
|
|
} from "../ReactQrCodeScanner/ReactQrCodeScanner"; |
|
|
} from "../ReactQrCodeScanner/ReactQrCodeScanner"; |
|
|
@@ -36,20 +36,24 @@ import LoadingComponent from "../General/LoadingComponent"; |
|
|
import StockInForm from "../StockIn/StockInForm"; |
|
|
import StockInForm from "../StockIn/StockInForm"; |
|
|
import { arrayToDateString, INPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; |
|
|
import { arrayToDateString, INPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; |
|
|
import { QrCodeScanner } from "../QrCodeScannerProvider/QrCodeScannerProvider"; |
|
|
import { QrCodeScanner } from "../QrCodeScannerProvider/QrCodeScannerProvider"; |
|
|
import { msg } from "../Swal/CustomAlerts"; |
|
|
|
|
|
|
|
|
import { msg, msgError } from "../Swal/CustomAlerts"; |
|
|
import { PutAwayRecord } from "."; |
|
|
import { PutAwayRecord } from "."; |
|
|
import FgStockInForm from "../StockIn/FgStockInForm"; |
|
|
import FgStockInForm from "../StockIn/FgStockInForm"; |
|
|
|
|
|
|
|
|
import Swal from "sweetalert2"; |
|
|
import Swal from "sweetalert2"; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
interface Props extends Omit<ModalProps, "children"> { |
|
|
|
|
|
|
|
|
interface Props extends Omit<ModalProps, "children" | "onClose"> { |
|
|
warehouse: WarehouseResult[]; |
|
|
warehouse: WarehouseResult[]; |
|
|
stockInLineId: number; |
|
|
stockInLineId: number; |
|
|
warehouseId: number; |
|
|
warehouseId: number; |
|
|
scanner: QrCodeScanner; |
|
|
scanner: QrCodeScanner; |
|
|
addPutAwayHistory: (putAwayData: PutAwayRecord) => void; |
|
|
addPutAwayHistory: (putAwayData: PutAwayRecord) => void; |
|
|
onSetDefaultWarehouseId?: (warehouseId: number) => void; // 新增回调 |
|
|
|
|
|
|
|
|
onSetDefaultWarehouseId?: (warehouseId: number) => void; |
|
|
|
|
|
/** Parent toggles lot vs warehouse scan routing. */ |
|
|
|
|
|
onScanPhaseChange?: (acceptWarehouse: boolean) => void; |
|
|
|
|
|
/** When false, parent keeps scanner stopped (e.g. until alert is dismissed). */ |
|
|
|
|
|
onClose: (resumeScan?: boolean) => void; |
|
|
} |
|
|
} |
|
|
const style = { |
|
|
const style = { |
|
|
position: "absolute", |
|
|
position: "absolute", |
|
|
@@ -80,7 +84,7 @@ const scannerStyle = { |
|
|
maxWidth: "600px", |
|
|
maxWidth: "600px", |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId, warehouseId, scanner, addPutAwayHistory, onSetDefaultWarehouseId }) => { |
|
|
|
|
|
|
|
|
const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId, warehouseId, scanner, addPutAwayHistory, onSetDefaultWarehouseId, onScanPhaseChange }) => { |
|
|
const { t } = useTranslation("putAway"); |
|
|
const { t } = useTranslation("putAway"); |
|
|
const [serverError, setServerError] = useState(""); |
|
|
const [serverError, setServerError] = useState(""); |
|
|
const params = useSearchParams(); |
|
|
const params = useSearchParams(); |
|
|
@@ -101,6 +105,40 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId |
|
|
const [putQty, setPutQty] = useState<number>(itemDetail?.acceptedQty ?? 0); |
|
|
const [putQty, setPutQty] = useState<number>(itemDetail?.acceptedQty ?? 0); |
|
|
const [verified, setVerified] = useState<boolean>(false); |
|
|
const [verified, setVerified] = useState<boolean>(false); |
|
|
const [qtyError, setQtyError] = useState<string>(""); |
|
|
const [qtyError, setQtyError] = useState<string>(""); |
|
|
|
|
|
const fetchRequestIdRef = useRef(0); |
|
|
|
|
|
const processedItemKeyRef = useRef<string | null>(null); |
|
|
|
|
|
const onCloseRef = useRef(onClose); |
|
|
|
|
|
const onScanPhaseChangeRef = useRef(onScanPhaseChange); |
|
|
|
|
|
const scannerRef = useRef(scanner); |
|
|
|
|
|
onCloseRef.current = onClose; |
|
|
|
|
|
onScanPhaseChangeRef.current = onScanPhaseChange; |
|
|
|
|
|
scannerRef.current = scanner; |
|
|
|
|
|
|
|
|
|
|
|
const setWarehouseScanPhase = useCallback((accept: boolean) => { |
|
|
|
|
|
onScanPhaseChangeRef.current?.(accept); |
|
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
|
|
const getRejectMessage = (status: string): string => { |
|
|
|
|
|
switch (status) { |
|
|
|
|
|
case "pending": return "此貨品有待品檢"; |
|
|
|
|
|
case "rejected": return "此貨品已被拒收"; |
|
|
|
|
|
case "escalated": return "此貨品已被上報"; |
|
|
|
|
|
case "partially_completed": return "此貨品已部分上架"; |
|
|
|
|
|
case "completed": return "此貨品已上架"; |
|
|
|
|
|
default: return "此貨品暫時無法上架"; |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const resetModalState = useCallback(() => { |
|
|
|
|
|
fetchRequestIdRef.current += 1; |
|
|
|
|
|
setVerified(false); |
|
|
|
|
|
setItemDetail(undefined); |
|
|
|
|
|
setTotalPutAwayQty(0); |
|
|
|
|
|
setItemDefaultWarehouseId(null); |
|
|
|
|
|
setFirstWarehouseId(null); |
|
|
|
|
|
setFirstWarehouseInfo(null); |
|
|
|
|
|
setWarehouseMismatchError(""); |
|
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
const defaultNewValue = useMemo(() => { |
|
|
const defaultNewValue = useMemo(() => { |
|
|
// console.log("%c ItemDetail", "color:purple", itemDetail); |
|
|
// console.log("%c ItemDetail", "color:purple", itemDetail); |
|
|
@@ -128,54 +166,48 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId |
|
|
...defaultNewValue, |
|
|
...defaultNewValue, |
|
|
}, |
|
|
}, |
|
|
}); |
|
|
}); |
|
|
const { isSubmitting } = formProps.formState; |
|
|
|
|
|
|
|
|
const { reset: resetForm, formState: { isSubmitting } } = formProps; |
|
|
const errors = formProps.formState.errors; |
|
|
const errors = formProps.formState.errors; |
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
useEffect(() => { |
|
|
if (itemDetail) { |
|
|
|
|
|
startScanner(); |
|
|
|
|
|
|
|
|
if (itemDetail?.status === "received") { |
|
|
|
|
|
scannerRef.current.startScan(); |
|
|
|
|
|
console.log("%c Scanning started ", "color:cyan"); |
|
|
} |
|
|
} |
|
|
}, [itemDetail]) |
|
|
|
|
|
|
|
|
}, [itemDetail?.id, itemDetail?.status]); |
|
|
|
|
|
|
|
|
const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>( |
|
|
const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>( |
|
|
(...args) => { |
|
|
(...args) => { |
|
|
setVerified(false); |
|
|
|
|
|
setItemDetail(undefined); |
|
|
|
|
|
setTotalPutAwayQty(0); |
|
|
|
|
|
setItemDefaultWarehouseId(null); |
|
|
|
|
|
setFirstWarehouseId(null); |
|
|
|
|
|
setFirstWarehouseInfo(null); |
|
|
|
|
|
onClose?.(...args); |
|
|
|
|
|
// reset(); |
|
|
|
|
|
|
|
|
setWarehouseScanPhase(false); |
|
|
|
|
|
onCloseRef.current(true); |
|
|
|
|
|
resetModalState(); |
|
|
}, |
|
|
}, |
|
|
[onClose], |
|
|
|
|
|
|
|
|
[resetModalState, setWarehouseScanPhase], |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
const scannerCloseHandler = useCallback<NonNullable<ModalProps["onClose"]>>( |
|
|
const scannerCloseHandler = useCallback<NonNullable<ModalProps["onClose"]>>( |
|
|
(...args) => { |
|
|
(...args) => { |
|
|
setIsOpenScanner(false); |
|
|
setIsOpenScanner(false); |
|
|
scanner.stopScan(); |
|
|
|
|
|
|
|
|
scannerRef.current.stopScan(); |
|
|
console.log("%c Scanning stopped ", "color:cyan"); |
|
|
console.log("%c Scanning stopped ", "color:cyan"); |
|
|
}, |
|
|
}, |
|
|
[], |
|
|
[], |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
const startScanner = () => { |
|
|
const startScanner = () => { |
|
|
// setIsOpenScanner(true); |
|
|
|
|
|
scanner.startScan(); |
|
|
|
|
|
|
|
|
scannerRef.current.startScan(); |
|
|
console.log("%c Scanning started ", "color:cyan"); |
|
|
console.log("%c Scanning started ", "color:cyan"); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
const openScanner = () => { |
|
|
const openScanner = () => { |
|
|
setIsOpenScanner(true); |
|
|
setIsOpenScanner(true); |
|
|
scanner.startScan(); |
|
|
|
|
|
|
|
|
scannerRef.current.startScan(); |
|
|
console.log("%c Scanning started ", "color:cyan"); |
|
|
console.log("%c Scanning started ", "color:cyan"); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// 根据 item 的 locationCode 设置默认 warehouseId |
|
|
|
|
|
|
|
|
// 根据 item 的 locationCode 设置默认 warehouseId(仅 received 状态) |
|
|
useEffect(() => { |
|
|
useEffect(() => { |
|
|
// 直接使用 fetchStockInLineInfo 返回的 locationCode |
|
|
|
|
|
// 只在第一次上架时(firstWarehouseId === null)设置默认值 |
|
|
|
|
|
|
|
|
if (itemDetail?.status !== "received") return; |
|
|
if (itemDetail?.locationCode && warehouse.length > 0 && firstWarehouseId === null) { |
|
|
if (itemDetail?.locationCode && warehouse.length > 0 && firstWarehouseId === null) { |
|
|
const locationCode = itemDetail.locationCode; |
|
|
const locationCode = itemDetail.locationCode; |
|
|
|
|
|
|
|
|
@@ -198,9 +230,11 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}, [itemDetail?.locationCode, warehouse, firstWarehouseId]); |
|
|
|
|
|
|
|
|
}, [itemDetail?.status, itemDetail?.locationCode, warehouse, firstWarehouseId]); |
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
useEffect(() => { |
|
|
|
|
|
if (itemDetail?.status !== "received") return; |
|
|
|
|
|
|
|
|
// 只使用实际扫描的 warehouseId,不使用默认值进行验证 |
|
|
// 只使用实际扫描的 warehouseId,不使用默认值进行验证 |
|
|
if (warehouseId > 0 && firstWarehouseId !== null) { |
|
|
if (warehouseId > 0 && firstWarehouseId !== null) { |
|
|
// 第二次及后续上架:必须使用第一次的仓库 |
|
|
// 第二次及后续上架:必须使用第一次的仓库 |
|
|
@@ -215,7 +249,7 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId |
|
|
setIsOpenScanner(false); |
|
|
setIsOpenScanner(false); |
|
|
setVerified(true); |
|
|
setVerified(true); |
|
|
msg("貨倉掃瞄成功!"); |
|
|
msg("貨倉掃瞄成功!"); |
|
|
scanner.resetScan(); |
|
|
|
|
|
|
|
|
scannerRef.current.resetScan(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} else if (warehouseId > 0 && firstWarehouseId === null) { |
|
|
} else if (warehouseId > 0 && firstWarehouseId === null) { |
|
|
@@ -224,10 +258,10 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId |
|
|
setIsOpenScanner(false); |
|
|
setIsOpenScanner(false); |
|
|
setVerified(true); |
|
|
setVerified(true); |
|
|
msg("貨倉掃瞄成功!"); |
|
|
msg("貨倉掃瞄成功!"); |
|
|
scanner.resetScan(); |
|
|
|
|
|
|
|
|
scannerRef.current.resetScan(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}, [warehouseId, firstWarehouseId, scanner.isScanning]); |
|
|
|
|
|
|
|
|
}, [warehouseId, firstWarehouseId, scanner.isScanning, itemDetail?.status, warehouse]); |
|
|
|
|
|
|
|
|
const warehouseDisplay = useMemo(() => { |
|
|
const warehouseDisplay = useMemo(() => { |
|
|
// 优先使用扫描的 warehouseId,如果没有扫描则显示默认值作为建议 |
|
|
// 优先使用扫描的 warehouseId,如果没有扫描则显示默认值作为建议 |
|
|
@@ -247,69 +281,86 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId |
|
|
// }, [isOpenScanner]) |
|
|
// }, [isOpenScanner]) |
|
|
|
|
|
|
|
|
useLayoutEffect(() => { |
|
|
useLayoutEffect(() => { |
|
|
if (itemDetail !== undefined) { |
|
|
|
|
|
if (itemDetail.status == "received") { |
|
|
|
|
|
formProps.reset({ |
|
|
|
|
|
...defaultNewValue |
|
|
|
|
|
}) |
|
|
|
|
|
const total = itemDetail.putAwayLines?.reduce((sum, p) => sum + (p.stockQty ?? p.qty ?? 0), 0) ?? 0; |
|
|
|
|
|
setPutQty(itemDetail?.acceptedQty - total); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ Get first warehouse from existing put away lines |
|
|
|
|
|
const firstPutAwayLine = itemDetail.putAwayLines?.[0]; |
|
|
|
|
|
if (firstPutAwayLine?.warehouseId) { |
|
|
|
|
|
setFirstWarehouseId(firstPutAwayLine.warehouseId); |
|
|
|
|
|
// ✅ Store first warehouse info for display |
|
|
|
|
|
const firstWh = warehouse.find((w) => w.id == firstPutAwayLine.warehouseId); |
|
|
|
|
|
if (firstWh) { |
|
|
|
|
|
setFirstWarehouseInfo({ |
|
|
|
|
|
name: firstWh.name || "", |
|
|
|
|
|
code: firstWh.code || "" |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
setFirstWarehouseId(null); |
|
|
|
|
|
setFirstWarehouseInfo(null); |
|
|
|
|
|
|
|
|
if (itemDetail === undefined) { |
|
|
|
|
|
processedItemKeyRef.current = null; |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const itemKey = `${itemDetail.id}:${itemDetail.status}`; |
|
|
|
|
|
if (processedItemKeyRef.current === itemKey) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
processedItemKeyRef.current = itemKey; |
|
|
|
|
|
|
|
|
|
|
|
if (itemDetail.status == "received") { |
|
|
|
|
|
resetForm({ |
|
|
|
|
|
...defaultNewValue, |
|
|
|
|
|
}); |
|
|
|
|
|
const total = itemDetail.putAwayLines?.reduce((sum, p) => sum + (p.stockQty ?? p.qty ?? 0), 0) ?? 0; |
|
|
|
|
|
setPutQty(itemDetail?.acceptedQty - total); |
|
|
|
|
|
|
|
|
|
|
|
const firstPutAwayLine = itemDetail.putAwayLines?.[0]; |
|
|
|
|
|
if (firstPutAwayLine?.warehouseId) { |
|
|
|
|
|
setFirstWarehouseId(firstPutAwayLine.warehouseId); |
|
|
|
|
|
const firstWh = warehouse.find((w) => w.id == firstPutAwayLine.warehouseId); |
|
|
|
|
|
if (firstWh) { |
|
|
|
|
|
setFirstWarehouseInfo({ |
|
|
|
|
|
name: firstWh.name || "", |
|
|
|
|
|
code: firstWh.code || "", |
|
|
|
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
console.log("%c Loaded data:", "color:lime", defaultNewValue); |
|
|
|
|
|
} else { |
|
|
} else { |
|
|
switch (itemDetail.status) { |
|
|
|
|
|
case "pending": alert("此貨品有待品檢"); break; |
|
|
|
|
|
case "rejected": alert("此貨品已被拒收"); break; |
|
|
|
|
|
case "escalated": alert("此貨品已被上報"); break; |
|
|
|
|
|
case "partially_completed": alert("此貨品已部分上架"); break; |
|
|
|
|
|
case "completed": alert("此貨品已上架"); break; |
|
|
|
|
|
default: alert("此貨品暫時無法上架"); break; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
closeHandler({}, "backdropClick"); |
|
|
|
|
|
|
|
|
setFirstWarehouseId(null); |
|
|
|
|
|
setFirstWarehouseInfo(null); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
}, [open, itemDetail, defaultNewValue]) |
|
|
|
|
|
|
|
|
|
|
|
const fetchStockInLine = useCallback( |
|
|
|
|
|
async (stockInLineId: number) => { |
|
|
|
|
|
setUnavailableText(undefined); |
|
|
|
|
|
try { |
|
|
|
|
|
const res = await fetchStockInLineInfo(stockInLineId); |
|
|
|
|
|
console.log("%c Fetched Stock In Line Info:", "color:gold", res); |
|
|
|
|
|
|
|
|
|
|
|
const total = res.putAwayLines?.reduce((sum, p) => sum + (p.stockQty ?? p.qty ?? 0), 0) ?? 0; |
|
|
|
|
|
setTotalPutAwayQty(total); |
|
|
|
|
|
setItemDetail(res); |
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
console.log("%c Error when fetching Stock In Line: ", "color:red", e); |
|
|
|
|
|
alert("錯誤的二維碼"); |
|
|
|
|
|
closeHandler({}, "backdropClick"); |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
[formProps, itemDetail, fetchStockInLineInfo], |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
console.log("%c Loaded data:", "color:lime", defaultNewValue); |
|
|
|
|
|
setWarehouseScanPhase(true); |
|
|
|
|
|
} else { |
|
|
|
|
|
processedItemKeyRef.current = null; |
|
|
|
|
|
setWarehouseScanPhase(false); |
|
|
|
|
|
resetModalState(); |
|
|
|
|
|
onCloseRef.current(true); |
|
|
|
|
|
msgError(getRejectMessage(itemDetail.status)); |
|
|
|
|
|
} |
|
|
|
|
|
}, [itemDetail, defaultNewValue, warehouse, resetModalState, resetForm, setWarehouseScanPhase]); |
|
|
|
|
|
|
|
|
|
|
|
const fetchStockInLine = useCallback(async (stockInLineId: number) => { |
|
|
|
|
|
const requestId = ++fetchRequestIdRef.current; |
|
|
|
|
|
processedItemKeyRef.current = null; |
|
|
|
|
|
setWarehouseScanPhase(false); |
|
|
|
|
|
setUnavailableText(undefined); |
|
|
|
|
|
setItemDetail(undefined); |
|
|
|
|
|
try { |
|
|
|
|
|
const res = await fetchStockInLineInfo(stockInLineId); |
|
|
|
|
|
if (requestId !== fetchRequestIdRef.current) return; |
|
|
|
|
|
|
|
|
|
|
|
console.log("%c Fetched Stock In Line Info:", "color:gold", res); |
|
|
|
|
|
|
|
|
|
|
|
const total = res.putAwayLines?.reduce((sum, p) => sum + (p.stockQty ?? p.qty ?? 0), 0) ?? 0; |
|
|
|
|
|
setTotalPutAwayQty(total); |
|
|
|
|
|
setItemDetail(res); |
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
if (requestId !== fetchRequestIdRef.current) return; |
|
|
|
|
|
|
|
|
|
|
|
console.log("%c Error when fetching Stock In Line: ", "color:red", e); |
|
|
|
|
|
setWarehouseScanPhase(false); |
|
|
|
|
|
resetModalState(); |
|
|
|
|
|
onCloseRef.current(true); |
|
|
|
|
|
msgError("錯誤的二維碼"); |
|
|
|
|
|
} |
|
|
|
|
|
}, [resetModalState, setWarehouseScanPhase]); |
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
useEffect(() => { |
|
|
if (stockInLineId) { fetchStockInLine(stockInLineId); } |
|
|
|
|
|
}, [stockInLineId]); |
|
|
|
|
|
|
|
|
if (stockInLineId) { |
|
|
|
|
|
fetchStockInLine(stockInLineId); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
fetchRequestIdRef.current += 1; |
|
|
|
|
|
processedItemKeyRef.current = null; |
|
|
|
|
|
setWarehouseScanPhase(false); |
|
|
|
|
|
setItemDetail(undefined); |
|
|
|
|
|
}, [stockInLineId, fetchStockInLine, setWarehouseScanPhase]); |
|
|
|
|
|
|
|
|
const validateQty = useCallback((qty : number = putQty) => { |
|
|
const validateQty = useCallback((qty : number = putQty) => { |
|
|
// if (isNaN(putQty) || putQty === undefined || putQty === null || typeof(putQty) != "number") { |
|
|
// if (isNaN(putQty) || putQty === undefined || putQty === null || typeof(putQty) != "number") { |
|
|
|