@@ -28,7 +28,7 @@ const PutAway: React.FC = async () => { | |||||
{t("Put Away")} | {t("Put Away")} | ||||
</Typography> | </Typography> | ||||
</Stack> | </Stack> | ||||
<I18nProvider namespaces={["putAway", "common"]}> | |||||
<I18nProvider namespaces={["putAway", "purchaseOrder", "common"]}> | |||||
<Suspense fallback={<PutAwayScan.Loading />}> | <Suspense fallback={<PutAwayScan.Loading />}> | ||||
<PutAwayScan /> | <PutAwayScan /> | ||||
</Suspense> | </Suspense> | ||||
@@ -540,7 +540,7 @@ const PutAwayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled, setRowM | |||||
_formKey={"putAwayLines"} | _formKey={"putAwayLines"} | ||||
columns={columns} | columns={columns} | ||||
validateRow={validation} | validateRow={validation} | ||||
needAdd={true} | |||||
needAdd={false} | |||||
showRemoveBtn={false} | showRemoveBtn={false} | ||||
addRowDefaultValue={addRowDefaultValue} | addRowDefaultValue={addRowDefaultValue} | ||||
_setRowModesModel={setRowModesModel} | _setRowModesModel={setRowModesModel} | ||||
@@ -527,6 +527,18 @@ const QcComponent: React.FC<Props> = ({ itemDetail, disabled = false }) => { | |||||
<Grid item xs={12}> | <Grid item xs={12}> | ||||
<EscalationLogTable type="qc" items={itemDetail.escResult || []}/> | <EscalationLogTable type="qc" items={itemDetail.escResult || []}/> | ||||
<CollapsibleCard title={t("QC Record")}> | <CollapsibleCard title={t("QC Record")}> | ||||
<Box sx={{ mb: 2, p: 2, backgroundColor: '#f5f5f5', borderRadius: 1 }}> | |||||
<Typography variant="h5" component="h2" sx={{ fontWeight: 'bold', color: '#333' }}> | |||||
Group A - 急凍貨類 (QCA1-MEAT01) | |||||
</Typography> | |||||
<Typography variant="subtitle1" sx={{ color: '#666' }}> | |||||
<b>品檢類型</b>:IQC | |||||
</Typography> | |||||
<Typography variant="subtitle2" sx={{ color: '#666' }}> | |||||
記錄探測溫度的時間,請在1小時内完成卸貨盤點入庫,以保障食品安全<br/> | |||||
監察方法:目視檢查、嗅覺檢查和使用適當的食物溫度計,檢查食物溫度是否符合指標 | |||||
</Typography> | |||||
</Box> | |||||
<StyledDataGrid | <StyledDataGrid | ||||
columns={qcColumns} | columns={qcColumns} | ||||
rows={qcHistory} | rows={qcHistory} | ||||
@@ -404,10 +404,10 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
console.log("Putaway Data:", putawayData); | console.log("Putaway Data:", putawayData); | ||||
console.log("DEBUG",data.putAwayLines); | console.log("DEBUG",data.putAwayLines); | ||||
if (data.putAwayLines!!.filter((line) => line._isNew !== false).length <= 0) { | |||||
alert("請新增上架資料!"); | |||||
return; | |||||
} | |||||
// if (data.putAwayLines!!.filter((line) => line._isNew !== false).length <= 0) { | |||||
// alert("請新增上架資料!"); | |||||
// return; | |||||
// } | |||||
if (data.putAwayLines!!.filter((line) => /[^0-9]/.test(String(line.qty))).length > 0) { //TODO Improve | if (data.putAwayLines!!.filter((line) => /[^0-9]/.test(String(line.qty))).length > 0) { //TODO Improve | ||||
alert("上架數量不正確!"); | alert("上架數量不正確!"); | ||||
return; | return; | ||||
@@ -0,0 +1,419 @@ | |||||
"use client"; | |||||
import { | |||||
Box, | |||||
Button, | |||||
Grid, | |||||
Modal, | |||||
ModalProps, | |||||
Stack, | |||||
TextField, | |||||
Typography, | |||||
Paper, | |||||
} from "@mui/material"; | |||||
import { useCallback, useEffect, useLayoutEffect, useMemo, useState } from "react"; | |||||
import ReactQrCodeScanner, { | |||||
ScannerConfig, | |||||
} from "../ReactQrCodeScanner/ReactQrCodeScanner"; | |||||
import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; | |||||
import { | |||||
fetchStockInLineInfo, | |||||
ModalFormInput, | |||||
StockInLineEntry, | |||||
updateStockInLine, | |||||
} from "@/app/api/po/actions"; | |||||
import { StockInLine } from "@/app/api/po"; | |||||
import { WarehouseResult } from "@/app/api/warehouse"; | |||||
// import { QrCodeInfo } from "@/app/api/qrcde"; | |||||
import { Check, QrCode } from "@mui/icons-material"; | |||||
import { useTranslation } from "react-i18next"; | |||||
import { useSearchParams } from "next/navigation"; | |||||
import { useQrCodeScannerContext } from "../QrCodeScannerProvider/QrCodeScannerProvider"; | |||||
import LoadingComponent from "../General/LoadingComponent"; | |||||
import StockInForm from "../PoDetail/StockInForm"; | |||||
import { arrayToDateString, INPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; | |||||
import { QrCodeScanner } from "../QrCodeScannerProvider/QrCodeScannerProvider"; | |||||
import { msg } from "../Swal/CustomAlerts"; | |||||
interface Props extends Omit<ModalProps, "children"> { | |||||
warehouse: WarehouseResult[]; | |||||
stockInLineId: number; | |||||
warehouseId: number; | |||||
scanner: QrCodeScanner; | |||||
} | |||||
const style = { | |||||
position: "absolute", | |||||
top: "50%", | |||||
left: "50%", | |||||
transform: "translate(-50%, -50%)", | |||||
bgcolor: "background.paper", | |||||
pt: 5, | |||||
px: 5, | |||||
pb: 10, | |||||
// width: "auto", | |||||
width: { xs: "90%", sm: "90%", md: "90%" }, | |||||
}; | |||||
const scannerStyle = { | |||||
position: "absolute", | |||||
top: "50%", | |||||
left: "50%", | |||||
transform: "translate(-50%, -50%)", | |||||
bgcolor: "background.paper", | |||||
pt: 5, | |||||
px: 5, | |||||
pb: 10, | |||||
// width: "auto", | |||||
width: { xs: "60%", sm: "60%", md: "60%" }, | |||||
}; | |||||
const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId, warehouseId, scanner }) => { | |||||
const { t } = useTranslation("putAway"); | |||||
const [serverError, setServerError] = useState(""); | |||||
const params = useSearchParams(); | |||||
const [isOpenScanner, setIsOpenScanner] = useState<boolean>(false); | |||||
const [itemDetail, setItemDetail] = useState<StockInLine>(); | |||||
const [unavailableText, setUnavailableText] = useState<string | undefined>( | |||||
undefined, | |||||
); | |||||
const [putQty, setPutQty] = useState<number>(itemDetail?.demandQty ?? 0); | |||||
const defaultNewValue = useMemo(() => { | |||||
// console.log("%c ItemDetail", "color:purple", itemDetail); | |||||
return ( | |||||
{ | |||||
...itemDetail, | |||||
// status: itemDetail.status ?? "pending", | |||||
dnDate: arrayToDateString(itemDetail?.dnDate, "input")?? undefined, | |||||
// // putAwayLines: dummyPutAwayLine, | |||||
// // putAwayLines: itemDetail.putAwayLines.map((line) => (return {...line, printQty: 1})) ?? [], | |||||
// putAwayLines: itemDetail.putAwayLines?.map((line) => ({...line, printQty: 1, _isNew: false})) ?? [], | |||||
// // qcResult: (itemDetail.qcResult && itemDetail.qcResult?.length > 0) ? itemDetail.qcResult : [],//[...dummyQCData], | |||||
// escResult: (itemDetail.escResult && itemDetail.escResult?.length > 0) ? itemDetail.escResult : [], | |||||
productionDate: itemDetail?.productionDate ? arrayToDateString(itemDetail?.productionDate, "input") : undefined, | |||||
expiryDate: itemDetail?.expiryDate ? arrayToDateString(itemDetail?.expiryDate, "input") : undefined, | |||||
receiptDate: itemDetail?.receiptDate ? arrayToDateString(itemDetail?.receiptDate, "input") : undefined, | |||||
// acceptQty: itemDetail.demandQty?? itemDetail.acceptedQty, | |||||
defaultWarehouseId: itemDetail?.defaultWarehouseId ?? 1, | |||||
} | |||||
) | |||||
}, [itemDetail]) | |||||
const formProps = useForm<ModalFormInput>({ | |||||
defaultValues: { | |||||
...defaultNewValue, | |||||
}, | |||||
}); | |||||
const errors = formProps.formState.errors; | |||||
const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>( | |||||
(...args) => { | |||||
setVerified(false); | |||||
setItemDetail(undefined); | |||||
onClose?.(...args); | |||||
// reset(); | |||||
}, | |||||
[onClose], | |||||
); | |||||
const scannerCloseHandler = useCallback<NonNullable<ModalProps["onClose"]>>( | |||||
(...args) => { | |||||
setIsOpenScanner(false); | |||||
scanner.stopScan(); | |||||
console.log("%c Scanning stopped ", "color:cyan"); | |||||
}, | |||||
[], | |||||
); | |||||
const openScanner = () => { | |||||
setIsOpenScanner(true); | |||||
scanner.startScan(); | |||||
console.log("%c Scanning started ", "color:cyan"); | |||||
}; | |||||
useEffect(() => { | |||||
if (warehouseId > 0) { // Scanned Warehouse | |||||
if (isOpenScanner) { | |||||
setIsOpenScanner(false); | |||||
setVerified(true); | |||||
scanner.stopScan(); | |||||
console.log("%c Scanner stopped", "color:cyan"); | |||||
} | |||||
} | |||||
}, [warehouseId]) | |||||
useLayoutEffect(() => { | |||||
if (itemDetail !== undefined) { | |||||
if (itemDetail.status == "received") { | |||||
formProps.reset({ | |||||
...defaultNewValue | |||||
}) | |||||
setPutQty(itemDetail?.demandQty); | |||||
console.log("%c Loaded data:", "color:lime", defaultNewValue); | |||||
} 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"); | |||||
} | |||||
} | |||||
}, [open, itemDetail, defaultNewValue]) | |||||
const fetchStockInLine = useCallback( | |||||
async (stockInLineId: number) => { | |||||
setUnavailableText(undefined); | |||||
const res = await fetchStockInLineInfo(stockInLineId); | |||||
console.log("%c Fetched Stock In Line Info:", "color:gold", res); | |||||
setItemDetail(res); | |||||
}, | |||||
[formProps, itemDetail, fetchStockInLineInfo], | |||||
); | |||||
useEffect(() => { | |||||
if (stockInLineId) { fetchStockInLine(stockInLineId); } | |||||
}, [stockInLineId]); | |||||
const [verified, setVerified] = useState<boolean>(false); | |||||
const [qtyError, setQtyError] = useState<string>(""); | |||||
const validateQty = useCallback((qty : number = putQty) => { | |||||
// if (isNaN(putQty) || putQty === undefined || putQty === null || typeof(putQty) != "number") { | |||||
// setQtyError(t("value must be a number")); | |||||
// } else | |||||
if (!Number.isInteger(qty)) { | |||||
setQtyError(t("value must be integer")); | |||||
} | |||||
if (qty > itemDetail?.acceptedQty!!) { | |||||
setQtyError(`${t("putQty must not greater than")} ${ | |||||
itemDetail?.acceptedQty}` ); | |||||
} else | |||||
if (qty < 1) { | |||||
setQtyError(t("minimal value is 1")); | |||||
} else { | |||||
setQtyError(""); | |||||
} | |||||
console.log("%c Validated putQty:", "color:yellow", putQty); | |||||
},[setQtyError, putQty, itemDetail]) | |||||
const onSubmit = useCallback<SubmitHandler<ModalFormInput>>( | |||||
async (data, event) => { | |||||
// console.log("errors", errors); | |||||
// const lotLine = { | |||||
// warehouseId: warehouseId, | |||||
// qty: acceptQty; | |||||
// } | |||||
try { | |||||
const args = { | |||||
// ...itemDetail, | |||||
id: itemDetail?.id, | |||||
purchaseOrderId: itemDetail?.purchaseOrderId, | |||||
purchaseOrderLineId: itemDetail?.purchaseOrderLineId, | |||||
itemId: itemDetail?.itemId, | |||||
acceptedQty: itemDetail?.acceptedQty, | |||||
acceptQty: itemDetail?.demandQty, | |||||
status: "received", | |||||
// purchaseOrderId: parseInt(params.get("id")!), | |||||
// purchaseOrderLineId: itemDetail?.purchaseOrderLineId, | |||||
// itemId: itemDetail?.itemId, | |||||
// acceptedQty: data.acceptedQty, | |||||
// status: data.status, | |||||
// ...data, | |||||
// productionDate: productionDate, | |||||
// for putaway data | |||||
inventoryLotLines: [{ | |||||
warehouseId: warehouseId, | |||||
qty: putQty, | |||||
}], | |||||
// data.putAwayLines?.filter((line) => line._isNew !== false) | |||||
} as StockInLineEntry & ModalFormInput; | |||||
console.log(args); | |||||
// return | |||||
// if (formProps.formState.errors) { | |||||
// setServerError(t("An error has occurred. Please try again later.")); | |||||
// return false; | |||||
// } | |||||
if (qtyError !== "") { | |||||
return; | |||||
} | |||||
console.log("%c Submitting Data:", "color:blue", args); | |||||
const res = await updateStockInLine(args); | |||||
if (Boolean(res.id)) { | |||||
// update entries | |||||
console.log("%c Update Success:", "color:green", res); | |||||
// add loading | |||||
msg("貨品上架成功!"); | |||||
closeHandler({}, "backdropClick"); | |||||
} | |||||
console.log(res); | |||||
// if (res) | |||||
} catch (e) { | |||||
// server error | |||||
setServerError(t("An error has occurred. Please try again later.")); | |||||
console.log(e); | |||||
} | |||||
}, | |||||
[t, itemDetail, putQty, warehouseId], | |||||
); | |||||
return ( | |||||
<FormProvider {...formProps}> | |||||
<Modal open={open} onClose={closeHandler}> | |||||
<Box | |||||
sx={style} | |||||
component="form" | |||||
onSubmit={formProps.handleSubmit(onSubmit)} | |||||
> | |||||
<Grid container xs={12}> | |||||
<Grid item xs={12}> | |||||
{itemDetail != undefined ? ( | |||||
<> | |||||
<Stack direction="column" justifyContent="flex-end" gap={1}> | |||||
<Typography variant="h4"> | |||||
處理上架 | |||||
</Typography> | |||||
<Grid item xs={12}> | |||||
<StockInForm itemDetail={itemDetail} disabled={true} putawayMode={true}/> | |||||
</Grid> | |||||
<Paper sx={{ padding: 2, width: "100%" }}> | |||||
<Grid container spacing={2}> | |||||
<Grid item xs={3}> | |||||
<TextField | |||||
type="number" // TODO fix the "e" input | |||||
label={t("putQty")} | |||||
fullWidth | |||||
defaultValue={itemDetail.demandQty} | |||||
onChange={(e) => { | |||||
const value = e.target.value; | |||||
validateQty(Number(value)); | |||||
setPutQty(Number(value)); | |||||
}} | |||||
// onBlur={(e) => { | |||||
// const value = e.target.value; | |||||
// setPutQty(Number(value)); | |||||
// validateQty(Number(value)); | |||||
// }} | |||||
// disabled={true} | |||||
// {...register("acceptedQty", { | |||||
// required: "acceptedQty required!", | |||||
// })} | |||||
error={qtyError !== ""} | |||||
helperText={qtyError} | |||||
/> | |||||
</Grid> | |||||
<Grid item xs={3}> | |||||
<Button | |||||
id="scanWarehouse" | |||||
variant="contained" | |||||
startIcon={<QrCode />} | |||||
color="primary" | |||||
// sx={{ mx: 3, minWidth : "120px", height: "80px", | |||||
// padding: "12px 12px", fontSize: "24px"}} | |||||
sx={{ | |||||
flex: "0 0 auto", | |||||
padding: "8px 16px", | |||||
fontSize: { xs: "16px", sm: "20px", md: "24px" }, | |||||
whiteSpace: "nowrap", | |||||
textAlign: "center",}} | |||||
onClick={openScanner}> | |||||
{t("scan warehouse")} | |||||
</Button> | |||||
</Grid> | |||||
<Grid item xs={3}> | |||||
<TextField | |||||
key={warehouseId} | |||||
label={t("warehouse")} | |||||
fullWidth | |||||
disabled={true} | |||||
value={ | |||||
warehouseId > 0 ? warehouse.find((w) => w.id == warehouseId)?.name | |||||
: warehouse.find((w) => w.id == 3)?.name | |||||
// : warehouse.find((w) => w.id == itemDetail.defaultWarehouseId)?.name //TODO fix empty | |||||
} | |||||
// {...register("acceptedQty", { | |||||
// required: "acceptedQty required!", | |||||
// })} | |||||
error={!verified} | |||||
helperText={verified ? "掃碼完成" : "等待掃碼"} | |||||
/> | |||||
</Grid> | |||||
{/* <Stack direction="row" justifyContent="flex-end" sx={{ mt: 1 }}> */} | |||||
<Grid item xs={3}> | |||||
<Button | |||||
id="putawaySubmit" | |||||
type="submit" | |||||
variant="contained" | |||||
startIcon={<Check />} | |||||
color="primary" | |||||
// sx={{ mx: 3, minWidth: "120px", height: "120px", | |||||
// padding: "12px 12px", fontSize: "24px"}} | |||||
sx={{ | |||||
flex: "0 0 auto", | |||||
padding: "8px 16px", | |||||
fontSize: { xs: "16px", sm: "20px", md: "24px" }, | |||||
whiteSpace: "nowrap", | |||||
textAlign: "center",}} | |||||
// onClick={formProps.handleSubmit()} | |||||
disabled={!verified} | |||||
> | |||||
{t("confirm putaway")} | |||||
</Button> | |||||
</Grid> | |||||
</Grid> | |||||
</Paper> | |||||
</Stack> | |||||
</> | |||||
) : ( | |||||
// <ReactQrCodeScanner scannerConfig={scannerConfig} /> | |||||
<> | |||||
<Typography variant="h4"> | |||||
{t("scan loading")} | |||||
</Typography> | |||||
<LoadingComponent/> | |||||
</> | |||||
)} | |||||
</Grid> | |||||
</Grid> | |||||
<Modal open={isOpenScanner} onClose={scannerCloseHandler}> | |||||
<Box sx={scannerStyle}> | |||||
<Typography variant="h4" sx={{ | |||||
display: 'flex', | |||||
flexDirection: 'column', | |||||
justifyContent: 'center', | |||||
margin: 0, | |||||
alignItems: 'center', | |||||
textAlign: 'center',}} | |||||
> | |||||
{t("Please scan warehouse qr code")} | |||||
</Typography> | |||||
{/* <ReactQrCodeScanner scannerConfig={scannerConfig} /> */} | |||||
</Box> | |||||
</Modal> | |||||
</Box> | |||||
</Modal> | |||||
</FormProvider> | |||||
); | |||||
}; | |||||
export default PutAwayModal; |
@@ -1,12 +1,198 @@ | |||||
import React from "react"; | |||||
"use client"; | |||||
import { | |||||
Box, | |||||
Button, | |||||
Paper, | |||||
Grid, | |||||
Modal, | |||||
ModalProps, | |||||
Stack, | |||||
Typography, | |||||
} from "@mui/material"; | |||||
import { useCallback, useEffect, useMemo, useState } from "react"; | |||||
import ReactQrCodeScanner, { | |||||
ScannerConfig, | |||||
} from "../ReactQrCodeScanner/ReactQrCodeScanner"; | |||||
import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; | |||||
import { | |||||
fetchStockInLineInfo, | |||||
ModalFormInput, | |||||
StockInLineEntry, | |||||
updateStockInLine, | |||||
} from "@/app/api/po/actions"; | |||||
import { StockInLine } from "@/app/api/po"; | |||||
import { WarehouseResult } from "@/app/api/warehouse"; | |||||
import { QrCodeInfo } from "@/app/api/qrcode"; | |||||
import { Check, QrCodeScanner, Warehouse } from "@mui/icons-material"; | |||||
import { useTranslation } from "react-i18next"; | |||||
import { useSearchParams } from "next/navigation"; | |||||
import { useQrCodeScannerContext } from "../QrCodeScannerProvider/QrCodeScannerProvider"; | |||||
import PutAwayModal from "./PutAwayModal"; | |||||
type Props = { | type Props = { | ||||
warehouse : WarehouseResult[]; | |||||
}; | }; | ||||
const PutAwayScan: React.FC<Props> = ({} ) => { | |||||
return <></> | |||||
type ScanStatusType = "pending" | "rescan"; | |||||
const PutAwayScan: React.FC<Props> = ({ warehouse }) => { | |||||
const { t } = useTranslation("putAway"); | |||||
const [scanDisplay, setScanDisplay] = useState<ScanStatusType>("pending"); | |||||
const [openPutAwayModal, setOpenPutAwayModal] = useState(false); | |||||
const [scannedSilId, setScannedSilId] = useState<number>(0); // TODO use QR code info | |||||
const [scannedWareHouseId, setScannedWareHouseId] = useState<number>(0); // TODO use QR code info | |||||
// QR Code Scanner | |||||
const scanner = useQrCodeScannerContext(); | |||||
useEffect(() => { | |||||
if (!scanner.isScanning) { | |||||
scanner.startScan(); | |||||
console.log("%c Scanning started ", "color:cyan"); | |||||
} else if (scanner.isScanning) { | |||||
scanner.stopScan(); | |||||
console.log("%c Scanning stopped ", "color:cyan"); | |||||
} | |||||
}, []); | |||||
const resetScan = (error : string = "") => { | |||||
if (error !== "") { | |||||
console.log("%c Scan failed, error: ", "color:red", error); | |||||
setScanDisplay("rescan"); | |||||
} else { | |||||
console.log("%c Scan reset", "color:red"); | |||||
} | |||||
scanner.resetScan(); | |||||
}; | |||||
const openModal = () => { | |||||
console.log("Scanned successfully and open modal"); | |||||
scanner.stopScan(); | |||||
console.log("%c Scanning stopped ", "color:cyan"); | |||||
setOpenPutAwayModal(true); | |||||
setScanDisplay("pending"); | |||||
}; | |||||
const closeModal = () => { | |||||
setScannedSilId(0); | |||||
setScannedWareHouseId(0); | |||||
setScanDisplay("pending"); | |||||
console.log("Modal Closed"); | |||||
scanner.startScan(); | |||||
console.log("%c Scanning started ", "color:cyan"); | |||||
setOpenPutAwayModal(false); | |||||
} | |||||
const findIdByRoughMatch = (inputString : string, keyword : string) => { | |||||
const keywordIndex = inputString.indexOf(keyword); | |||||
if (keywordIndex === -1) { | |||||
return { | |||||
keywordFound: false, | |||||
number: null, | |||||
message: `${keyword} not found in the input`, | |||||
}; | |||||
} | |||||
const substringAfterKeyword = inputString.slice(keywordIndex + keyword.length); | |||||
const numberMatch = substringAfterKeyword.match(/\d+/); | |||||
if (!numberMatch) { | |||||
return { | |||||
keywordFound: true, | |||||
number: null, | |||||
message: `No valid number found after ${keyword}`, | |||||
}; | |||||
} | |||||
return { | |||||
keywordFound: true, | |||||
number: parseInt(numberMatch[0], 10), | |||||
message: `Found ${keyword} at index ${keywordIndex}, first number found after is: ${numberMatch[0]}`, | |||||
}; | |||||
} | |||||
useEffect(() => { | |||||
if (scannedSilId > 0) { | |||||
openModal(); | |||||
} | |||||
}, [scannedSilId]) | |||||
// Get Scanned Values | |||||
useEffect(() => { | |||||
if (scanner.values.length > 0) {//} && !Boolean(itemDetail)) { | |||||
const scannedValues = scanner.values[0]; | |||||
console.log("%c Scanned: ", "color:cyan", scannedValues); | |||||
if (scannedValues.substring(0, 8) == "{2fitest") { // DEBUGGING | |||||
const number = scannedValues.substring(8, scannedValues.length - 1); | |||||
if (/^\d+$/.test(number)) { // Check if number contains only digits | |||||
console.log("%c DEBUG: Testing SIL ID: ", "color:cyan", number); | |||||
if (scannedSilId === 0) { | |||||
setScannedSilId(Number(number)); | |||||
} else setScannedWareHouseId(Number(number)); | |||||
} else { | |||||
console.error("%c DEBUG: Invalid number format: ", "color:red", number); | |||||
resetScan(); | |||||
} | |||||
return; | |||||
} | |||||
try { | |||||
const data: QrCodeInfo = JSON.parse(scannedValues); | |||||
console.log("%c Scanned with data", "color:green", data); | |||||
if (scannedSilId == 0) { // Initial State | |||||
if (data.stockInLineId !== undefined) { | |||||
setScannedSilId(Number(data.stockInLineId)); | |||||
} else resetScan("Cannot read Stock In Line Id"); | |||||
} else { // Processing | |||||
if (data.warehouseId !== undefined) { | |||||
setScannedWareHouseId(Number(data.warehouseId)); | |||||
} else resetScan("Cannot read Warehouse Id"); | |||||
} | |||||
} catch (error) { // Rought match for other scanner -- Pending Review | |||||
if (scannedSilId == 0) { | |||||
const silId = findIdByRoughMatch(scannedValues, "StockInLine").number ?? 0; | |||||
setScannedSilId(silId); | |||||
} else { | |||||
const whId = findIdByRoughMatch(scannedValues, "warehouseId").number ?? 0; | |||||
setScannedWareHouseId(whId); | |||||
} | |||||
resetScan(String(error)); | |||||
} | |||||
scanner.resetScan(); | |||||
} | |||||
}, [scanner.values]); | |||||
return (<> | |||||
<Paper sx={{ | |||||
display: 'flex', | |||||
flexDirection: 'column', | |||||
justifyContent: 'center', | |||||
alignItems: 'center', | |||||
textAlign: 'center',}} | |||||
> | |||||
<Typography variant="h4"> | |||||
{scanDisplay == "pending" ? t("Pending scan") : t("Rescan")} | |||||
</Typography> | |||||
<QrCodeScanner sx={{padding: "10px", fontSize : "150px"}}/> | |||||
</Paper> | |||||
<PutAwayModal | |||||
open={openPutAwayModal} | |||||
onClose={closeModal} | |||||
warehouse={warehouse} | |||||
stockInLineId={scannedSilId} | |||||
warehouseId={scannedWareHouseId} | |||||
scanner={scanner} | |||||
/> | |||||
</>) | |||||
} | } | ||||
export default PutAwayScan; | export default PutAwayScan; |
@@ -1,13 +1,20 @@ | |||||
import React from "react"; | import React from "react"; | ||||
import GeneralLoading from "../General/GeneralLoading"; | import GeneralLoading from "../General/GeneralLoading"; | ||||
import PutAwayScan from "./PutAwayScan"; | import PutAwayScan from "./PutAwayScan"; | ||||
import { fetchWarehouseList } from "@/app/api/warehouse"; | |||||
interface SubComponents { | interface SubComponents { | ||||
Loading: typeof GeneralLoading; | Loading: typeof GeneralLoading; | ||||
} | } | ||||
const PutAwayScanWrapper: React.FC & SubComponents = async () => { | const PutAwayScanWrapper: React.FC & SubComponents = async () => { | ||||
return <PutAwayScan/> | |||||
const [ | |||||
warehouse, | |||||
] = await Promise.all([ | |||||
fetchWarehouseList(), | |||||
]) | |||||
return <PutAwayScan warehouse={warehouse}/> | |||||
} | } | ||||
PutAwayScanWrapper.Loading = GeneralLoading; | PutAwayScanWrapper.Loading = GeneralLoading; | ||||
@@ -8,7 +8,7 @@ import { | |||||
useState, | useState, | ||||
} from "react"; | } from "react"; | ||||
interface QrCodeScanner { | |||||
export interface QrCodeScanner { | |||||
values: string[]; | values: string[]; | ||||
isScanning: boolean; | isScanning: boolean; | ||||
startScan: () => void; | startScan: () => void; | ||||
@@ -82,7 +82,7 @@ const QrCodeScannerProvider: React.FC<QrCodeScannerProviderProps> = ({ | |||||
keys.join("").substring(startBrace, endBrace + 1), | keys.join("").substring(startBrace, endBrace + 1), | ||||
]); | ]); | ||||
console.log(keys); | console.log(keys); | ||||
console.log(qrCodeScannerValues); | |||||
console.log("%c QR Scanner Values:", "color:cyan", qrCodeScannerValues); | |||||
// reset | // reset | ||||
setKeys(() => []); | setKeys(() => []); | ||||
@@ -120,7 +120,7 @@ | |||||
"Escalation Result": "上報結果", | "Escalation Result": "上報結果", | ||||
"update qc info": "更新品檢資料", | "update qc info": "更新品檢資料", | ||||
"email supplier": "電郵供應商", | "email supplier": "電郵供應商", | ||||
"confirm putaway": "確定及上架", | |||||
"confirm putaway": "確定及完成上架", | |||||
"confirm qc result": "確定品檢結果", | "confirm qc result": "確定品檢結果", | ||||
"warehouse": "倉庫", | "warehouse": "倉庫", | ||||
"qcItem": "品檢項目", | "qcItem": "品檢項目", | ||||
@@ -1,4 +1,17 @@ | |||||
{ | { | ||||
"Put Away": "上架", | |||||
"Put Away Scan": "上架掃碼" | |||||
} | |||||
"Put Away": "上架", | |||||
"Put Away Scan": "上架掃碼", | |||||
"Pending scan": "等待掃瞄中,請掃瞄貨品二維碼開始上架程序", | |||||
"Rescan": "讀取不成功,請重新掃瞄", | |||||
"acceptedQty": "是次來貨數量", | |||||
"confirm putaway": "確定及上架貨物", | |||||
"scan warehouse": "掃瞄倉庫二維碼", | |||||
"Please scan warehouse qr code": "請掃瞄倉庫二維碼", | |||||
"scan loading": "載入中,請稍候…", | |||||
"warehouse": "倉庫", | |||||
"putQty": "上架數量", | |||||
"minimal value is 1": "最小為1", | |||||
"putQty must not greater than": "上架數量不得大於", | |||||
"value must be integer": "必須是整數", | |||||
"value must be a number": "必須是數字" | |||||
} |