瀏覽代碼

update putaway scan

master
kelvinsuen 3 月之前
父節點
當前提交
f85eeaf7c3
共有 8 個檔案被更改,包括 130 行新增47 行删除
  1. +4
    -0
      src/app/global.css
  2. +1
    -1
      src/components/PoDetail/PoDetail.tsx
  3. +2
    -1
      src/components/PoDetail/QcStockInModal.tsx
  4. +32
    -14
      src/components/PoDetail/StockInForm.tsx
  5. +86
    -29
      src/components/PutAwayScan/PutAwayModal.tsx
  6. +3
    -0
      src/components/Swal/CustomAlerts.tsx
  7. +1
    -1
      src/i18n/zh/purchaseOrder.json
  8. +1
    -1
      src/i18n/zh/putAway.json

+ 4
- 0
src/app/global.css 查看文件

@@ -4,4 +4,8 @@


html, body { html, body {
overscroll-behavior: none; overscroll-behavior: none;
}

.swal2-custom-zindex {
z-index: 10000 !important;
} }

+ 1
- 1
src/components/PoDetail/PoDetail.tsx 查看文件

@@ -815,7 +815,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => {
<TableCell sx={{ width: '125px' }}>{t("itemNo")}</TableCell> <TableCell sx={{ width: '125px' }}>{t("itemNo")}</TableCell>
<TableCell align="left" sx={{ width: '125px' }}>{t("itemName")}</TableCell> <TableCell align="left" sx={{ width: '125px' }}>{t("itemName")}</TableCell>
<TableCell align="right">{t("qty")}</TableCell> <TableCell align="right">{t("qty")}</TableCell>
<TableCell align="right">{t("processed")}</TableCell>
<TableCell align="right">{t("processedQty")}</TableCell>
<TableCell align="left">{t("uom")}</TableCell> <TableCell align="left">{t("uom")}</TableCell>
<TableCell align="right">{t("receivedTotal")}</TableCell> <TableCell align="right">{t("receivedTotal")}</TableCell>
<TableCell align="left">{t("Stock UoM")}</TableCell> <TableCell align="left">{t("Stock UoM")}</TableCell>


+ 2
- 1
src/components/PoDetail/QcStockInModal.tsx 查看文件

@@ -26,7 +26,7 @@ import QcComponent from "./QcComponent";
import { dummyPutAwayLine, dummyQCData } from "./dummyQcTemplate"; import { dummyPutAwayLine, dummyQCData } from "./dummyQcTemplate";
import PutAwayForm from "./PutAwayForm"; import PutAwayForm from "./PutAwayForm";
import { GridRowModes, GridRowSelectionModel, useGridApiRef } from "@mui/x-data-grid"; import { GridRowModes, GridRowSelectionModel, useGridApiRef } from "@mui/x-data-grid";
import {submitDialogWithWarning} from "../Swal/CustomAlerts";
import {msg, submitDialogWithWarning} from "../Swal/CustomAlerts";
import { INPUT_DATE_FORMAT, arrayToDateString, dayjsToInputDateString } from "@/app/utils/formatUtil"; import { INPUT_DATE_FORMAT, arrayToDateString, dayjsToInputDateString } from "@/app/utils/formatUtil";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { fetchPoQrcode } from "@/app/api/pdf/actions"; import { fetchPoQrcode } from "@/app/api/pdf/actions";
@@ -342,6 +342,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)
} else { } else {
closeHandler({}, "backdropClick"); closeHandler({}, "backdropClick");
} }
msg("已更新來貨狀態");
return ; return ;


}, },


+ 32
- 14
src/components/PoDetail/StockInForm.tsx 查看文件

@@ -338,20 +338,38 @@ const StockInForm: React.FC<Props> = ({
disabled={true} disabled={true}
/> />
</Grid> </Grid>
<Grid item xs={6}>
<TextField
label={t("acceptedQty")}
fullWidth
disabled={true}
{...register("acceptedQty", {
required: "acceptedQty required!",
})}
// disabled={true}
// disabled={disabled}
// error={Boolean(errors.acceptedQty)}
// helperText={errors.acceptedQty?.message}
/>
</Grid>
{putawayMode ? (
<Grid item xs={6}>
<TextField
label={`${t("processedQty")} / ${t("acceptedQty")}`}
fullWidth
disabled={true}
value={
`${itemDetail.putAwayLines?.reduce((sum, p) => sum + p.qty, 0) ?? 0} / ${itemDetail.acceptedQty}`
}
// disabled={true}
// disabled={disabled}
// error={Boolean(errors.acceptedQty)}
// helperText={errors.acceptedQty?.message}
/>
</Grid>
) : (
<Grid item xs={6}>
<TextField
label={t("acceptedQty")}
fullWidth
disabled={true}
{...register("acceptedQty", {
required: "acceptedQty required!",
})}
// disabled={true}
// disabled={disabled}
// error={Boolean(errors.acceptedQty)}
// helperText={errors.acceptedQty?.message}
/>
</Grid>
)
}
{/* <Grid item xs={4}> {/* <Grid item xs={4}>
<TextField <TextField
label={t("acceptedWeight")} label={t("acceptedWeight")}


+ 86
- 29
src/components/PutAwayScan/PutAwayModal.tsx 查看文件

@@ -51,7 +51,7 @@ const style = {
bgcolor: "background.paper", bgcolor: "background.paper",
pt: 5, pt: 5,
px: 5, px: 5,
pb: 10,
pb: 5,
// width: "auto", // width: "auto",
width: { xs: "90%", sm: "90%", md: "90%" }, width: { xs: "90%", sm: "90%", md: "90%" },
}; };
@@ -77,11 +77,14 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId
const [isOpenScanner, setIsOpenScanner] = useState<boolean>(false); const [isOpenScanner, setIsOpenScanner] = useState<boolean>(false);
const [itemDetail, setItemDetail] = useState<StockInLine>(); const [itemDetail, setItemDetail] = useState<StockInLine>();
const [putAwayQty, setPutAwayQty] = useState<number>(0);
const [unavailableText, setUnavailableText] = useState<string | undefined>( const [unavailableText, setUnavailableText] = useState<string | undefined>(
undefined, undefined,
); );
const [putQty, setPutQty] = useState<number>(itemDetail?.demandQty ?? 0); const [putQty, setPutQty] = useState<number>(itemDetail?.demandQty ?? 0);
const [verified, setVerified] = useState<boolean>(false);
const [qtyError, setQtyError] = useState<string>("");
const defaultNewValue = useMemo(() => { const defaultNewValue = useMemo(() => {
// console.log("%c ItemDetail", "color:purple", itemDetail); // console.log("%c ItemDetail", "color:purple", itemDetail);
@@ -121,6 +124,7 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId
(...args) => { (...args) => {
setVerified(false); setVerified(false);
setItemDetail(undefined); setItemDetail(undefined);
setPutAwayQty(0);
onClose?.(...args); onClose?.(...args);
// reset(); // reset();
}, },
@@ -153,11 +157,19 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId
if (scanner.isScanning) { if (scanner.isScanning) {
setIsOpenScanner(false); setIsOpenScanner(false);
setVerified(true); setVerified(true);
scanner.stopScan();
console.log("%c Scanner stopped", "color:cyan");
msg("貨倉掃瞄成功!")
scanner.resetScan();
console.log("%c Scanner reset", "color:cyan");
} }
} }
}, [warehouseId]) }, [warehouseId])
// useEffect(() => { // Restart scanner for changing warehouse
// if (warehouseId > 0) {
// scanner.startScan();
// console.log("%c Scanner restarted", "color:cyan");
// }
// }, [isOpenScanner])


useLayoutEffect(() => { useLayoutEffect(() => {
if (itemDetail !== undefined) { if (itemDetail !== undefined) {
@@ -185,20 +197,25 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId
const fetchStockInLine = useCallback( const fetchStockInLine = useCallback(
async (stockInLineId: number) => { async (stockInLineId: number) => {
setUnavailableText(undefined); setUnavailableText(undefined);
const res = await fetchStockInLineInfo(stockInLineId);
console.log("%c Fetched Stock In Line Info:", "color:gold", res);
setItemDetail(res);
try {
const res = await fetchStockInLineInfo(stockInLineId);
console.log("%c Fetched Stock In Line Info:", "color:gold", res);

const totalPutAwayQty = res.putAwayLines?.reduce((sum, p) => sum + p.qty, 0) ?? 0;
setPutAwayQty(totalPutAwayQty);
setItemDetail(res);
} catch (e) {
console.log("%c Error when fetching Stock In Line: ", "color:red", e);
alert("錯誤的二維碼");
closeHandler({}, "backdropClick");
}
}, },
[formProps, itemDetail, fetchStockInLineInfo], [formProps, itemDetail, fetchStockInLineInfo],
); );


useEffect(() => { useEffect(() => {
if (stockInLineId) { fetchStockInLine(stockInLineId); } if (stockInLineId) { fetchStockInLine(stockInLineId); }
}, [stockInLineId]);

const [verified, setVerified] = useState<boolean>(false);

const [qtyError, setQtyError] = useState<string>("");
}, [stockInLineId]);


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") {
@@ -207,10 +224,14 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId
if (!Number.isInteger(qty)) { if (!Number.isInteger(qty)) {
setQtyError(t("value must be integer")); setQtyError(t("value must be integer"));
} }
if (qty > itemDetail?.acceptedQty!!) {
if (qty > itemDetail?.acceptedQty!! - putAwayQty) {
setQtyError(`${t("putQty must not greater than")} ${ setQtyError(`${t("putQty must not greater than")} ${
itemDetail?.acceptedQty}` );
} else
itemDetail?.acceptedQty!! - putAwayQty}` );
} else
// if (qty > itemDetail?.acceptedQty!!) {
// setQtyError(`${t("putQty must not greater than")} ${
// itemDetail?.acceptedQty}` );
// } else
if (qty < 1) { if (qty < 1) {
setQtyError(t("minimal value is 1")); setQtyError(t("minimal value is 1"));
} else { } else {
@@ -303,7 +324,7 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId
<Grid item xs={12}> <Grid item xs={12}>
<StockInForm itemDetail={itemDetail} disabled={true} putawayMode={true}/> <StockInForm itemDetail={itemDetail} disabled={true} putawayMode={true}/>
</Grid> </Grid>
<Paper sx={{ padding: 2, width: "100%", backgroundColor: verified ? '#bceb19' : '#FCD34D' }}>
<Paper sx={{ mt: 2, padding: 2, width: "100%", backgroundColor: verified ? '#bceb19' : '#FCD34D' }}>
<Grid <Grid
container container
spacing={2} spacing={2}
@@ -320,14 +341,14 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId
<Grid container> <Grid container>
{verified? ( {verified? (
<> <>
<CheckCircle sx={{color:"green", fontSize: "35px"}}/>
<CheckCircle sx={{color:"green", fontSize: "35px", mr: 1}}/>
<Typography variant="h5" component="h2" sx={{ fontWeight: 'bold', color: 'black' }} noWrap> <Typography variant="h5" component="h2" sx={{ fontWeight: 'bold', color: 'black' }} noWrap>
掃瞄完成 掃瞄完成
</Typography> </Typography>
</> </>
) : ( ) : (
<> <>
<ErrorOutline sx={{color:"red", fontSize: "35px"}}/>
<ErrorOutline sx={{color:"red", fontSize: "35px", mr: 1}}/>
<Typography variant="h5" component="h2" sx={{ fontWeight: 'bold', color: 'black' }} noWrap> <Typography variant="h5" component="h2" sx={{ fontWeight: 'bold', color: 'black' }} noWrap>
請掃瞄倉庫二維碼 請掃瞄倉庫二維碼
</Typography> </Typography>
@@ -338,8 +359,8 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId
</Grid> </Grid>
<Grid container> <Grid container>
<Typography variant="h4" sx={{ fontWeight: 'bold', color: 'black' }} noWrap> <Typography variant="h4" sx={{ fontWeight: 'bold', color: 'black' }} noWrap>
{`${warehouseId > 0 ? warehouse.find((w) => w.id == warehouseId)?.name
: warehouse.find((w) => w.id == 3)?.name}`}
{warehouseId > 0 ? `${warehouse.find((w) => w.id == warehouseId)?.name}`
: `${warehouse.find((w) => w.id == 1)?.name} (預設)`}
</Typography> </Typography>
</Grid> </Grid>


@@ -351,13 +372,40 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId
type="number" // TODO fix the "e" input type="number" // TODO fix the "e" input
label={t("putQty")} label={t("putQty")}
fullWidth fullWidth
sx={{fontSize: "25px", height: "100%"}}
defaultValue={itemDetail.demandQty}
sx={{
"& .MuiInputBase-input": {
padding: "20px 14px 5px",
fontSize: "50px",
fontWeight: "bold",
},
"& .MuiInputBase-root": {
height: "100%",
borderColor: "black",
},
"& .MuiInputLabel-root": {
fontSize: "30px",
top: "-5px",
color: "black",
},
"& .MuiFormHelperText-root": {
fontSize: "20px",
marginTop: "8px",
lineHeight: "1.2",
},
}}
defaultValue={itemDetail?.acceptedQty!! - putAwayQty}
// defaultValue={itemDetail.demandQty}
onChange={(e) => { onChange={(e) => {
const value = e.target.value; const value = e.target.value;
validateQty(Number(value)); validateQty(Number(value));
setPutQty(Number(value)); setPutQty(Number(value));
}} }}
onKeyDown={(e) => {
// Prevent non-numeric characters
if (["e", "E", "+", "-", "."].includes(e.key)) {
e.preventDefault();
}
}}
// onBlur={(e) => { // onBlur={(e) => {
// const value = e.target.value; // const value = e.target.value;
// setPutQty(Number(value)); // setPutQty(Number(value));
@@ -375,7 +423,7 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId
<Grid item xs={4}> <Grid item xs={4}>
<Box <Box
sx={{ sx={{
p: 2,
p: 1,
textAlign: 'center', textAlign: 'center',
height: '100%', // Ensure D stretches to full height height: '100%', // Ensure D stretches to full height
display: 'flex', display: 'flex',
@@ -387,17 +435,27 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId
type="submit" type="submit"
variant="contained" variant="contained"
startIcon={<Check />} startIcon={<Check />}
color="primary"
// sx={{ mx: 3, minWidth: "120px", height: "120px",
// padding: "12px 12px", fontSize: "24px"}}
color="secondary"
sx={{ sx={{
height: "100%",
flex: "0 0 auto", flex: "0 0 auto",
padding: "8px 16px", padding: "8px 16px",
fontSize: { xs: "16px", sm: "20px", md: "24px" },
fontSize: { xs: "20px", sm: "24px", md: "30px" },
whiteSpace: "nowrap", whiteSpace: "nowrap",
textAlign: "center",}}
textAlign: "center",
border: "3px solid", // Add outline
borderColor: "blue",
"&:hover": {
borderColor: "grey.200", // Slightly different color on hover
backgroundColor: "secondary.dark", // Darker background on hover
},
"&:disabled": {
borderColor: "grey.400", // Visible outline even when disabled
opacity: 0.7, // Slightly faded but still visible
},
}}
// onClick={formProps.handleSubmit()} // onClick={formProps.handleSubmit()}
disabled={!verified}
disabled={!verified || qtyError != ""}
> >
{t("confirm putaway")} {t("confirm putaway")}
</Button> </Button>
@@ -435,7 +493,6 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId
)} )}
</Grid> </Grid>
</Grid> </Grid>

<Modal open={isOpenScanner} onClose={scannerCloseHandler}> <Modal open={isOpenScanner} onClose={scannerCloseHandler}>
<Box sx={scannerStyle}> <Box sx={scannerStyle}>
<Typography variant="h4" sx={{ <Typography variant="h4" sx={{


+ 3
- 0
src/components/Swal/CustomAlerts.tsx 查看文件

@@ -19,6 +19,9 @@ export const msg = (title: SweetAlertTitle) => {
toast.onmouseenter = Swal.stopTimer; toast.onmouseenter = Swal.stopTimer;
toast.onmouseleave = Swal.resumeTimer; toast.onmouseleave = Swal.resumeTimer;
}, },
customClass: {
container: 'swal2-custom-zindex',
},
}).fire({ }).fire({
icon: "success", icon: "success",
title: title, title: title,


+ 1
- 1
src/i18n/zh/purchaseOrder.json 查看文件

@@ -43,7 +43,7 @@
"total weight": "總重量", "total weight": "總重量",
"weight unit": "重量單位", "weight unit": "重量單位",
"price": "訂單貨值", "price": "訂單貨值",
"processed": "已上架數量",
"processedQty": "已上架數量",
"expiryDate": "到期日", "expiryDate": "到期日",
"acceptedQty": "是次來貨數量", "acceptedQty": "是次來貨數量",
"putawayQty": "上架數量", "putawayQty": "上架數量",


+ 1
- 1
src/i18n/zh/putAway.json 查看文件

@@ -9,7 +9,7 @@
"Please scan warehouse qr code": "請掃瞄倉庫二維碼", "Please scan warehouse qr code": "請掃瞄倉庫二維碼",
"scan loading": "載入中,請稍候…", "scan loading": "載入中,請稍候…",
"warehouse": "倉庫", "warehouse": "倉庫",
"putQty": "上架數量",
"putQty": "是次上架數量",
"minimal value is 1": "最小為1", "minimal value is 1": "最小為1",
"putQty must not greater than": "上架數量不得大於", "putQty must not greater than": "上架數量不得大於",
"value must be integer": "必須是整數", "value must be integer": "必須是整數",


Loading…
取消
儲存