浏览代码

UPDATE

production_process
MSI\derek 2 个月前
父节点
当前提交
6ee3a96bac
共有 7 个文件被更改,包括 195 次插入121 次删除
  1. +15
    -0
      src/app/api/po/actions.ts
  2. +1
    -1
      src/components/PoDetail/PoDetail.tsx
  3. +14
    -14
      src/components/PoDetail/PoInputGrid.tsx
  4. +17
    -3
      src/components/PoDetail/PutawayForm.tsx
  5. +59
    -43
      src/components/PoDetail/QrModal.tsx
  6. +88
    -60
      src/components/PoSearch/PoSearch.tsx
  7. +1
    -0
      src/i18n/zh/purchaseOrder.json

+ 15
- 0
src/app/api/po/actions.ts 查看文件

@@ -140,3 +140,18 @@ export const fetchPoInClient = cache(async (id: number) => {
} }
}); });


export const testing = cache(async (queryParams?: Record<string, any>) => {
if (queryParams) {
const queryString = new URLSearchParams(queryParams).toString();
return serverFetchJson<RecordsRes<PoResult[]>>(`${BASE_API_URL}/po/testing?${queryString}`, {
method: 'GET',
next: { tags: ["po"] },
});
} else {
return serverFetchJson<RecordsRes<PoResult[]>>(`${BASE_API_URL}/po/testing`, {
method: 'GET',
next: { tags: ["po"] },
});
}
});


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

@@ -295,7 +295,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
color={buttonData.buttonColor as ButtonProps["color"]} color={buttonData.buttonColor as ButtonProps["color"]}
startIcon={buttonData.buttonIcon} startIcon={buttonData.buttonIcon}
> >
{t(buttonData.buttonText)}
{buttonData.buttonText}
</Button> </Button>
</Grid> </Grid>
{/* {purchaseOrder.status.toLowerCase() === "pending" && ( {/* {purchaseOrder.status.toLowerCase() === "pending" && (


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

@@ -510,20 +510,20 @@ function PoInputGrid({
color="inherit" color="inherit"
key="edit" key="edit"
/>, />,
<GridActionsCellItem
icon={<QrCodeIcon />}
label="putaway"
sx={{
color: "primary.main",
// marginRight: 1,
}}
disabled={stockInLineStatusMap[status] === 9 || stockInLineStatusMap[status] !== 8}
// set _isNew to false after posting
// or check status
onClick={handleQrCode(params.row.id, params)}
color="inherit"
key="edit"
/>,
// <GridActionsCellItem
// icon={<QrCodeIcon />}
// label="putaway"
// sx={{
// color: "primary.main",
// // marginRight: 1,
// }}
// disabled={stockInLineStatusMap[status] === 9 || stockInLineStatusMap[status] !== 8}
// // set _isNew to false after posting
// // or check status
// onClick={handleQrCode(params.row.id, params)}
// color="inherit"
// key="edit"
// />,
<GridActionsCellItem <GridActionsCellItem
icon={ icon={
stockInLineStatusMap[status] >= 1 ? ( stockInLineStatusMap[status] >= 1 ? (


+ 17
- 3
src/components/PoDetail/PutawayForm.tsx 查看文件

@@ -237,6 +237,7 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {


useEffect(() => { useEffect(() => {
setValue("status", "completed"); setValue("status", "completed");
setValue("warehouseId", options[0].value);
}, []); }, []);


useEffect(() => { useEffect(() => {
@@ -245,7 +246,16 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
clearErrors("warehouseId") clearErrors("warehouseId")
} }
}, [warehouseId]); }, [warehouseId]);

const getWarningTextHardcode = useCallback((): string | undefined => {
const defaultWarehouseId = options[0].value
const currWarehouseId = watch("warehouseId")
if (defaultWarehouseId !== currWarehouseId) {
return t("not default warehosue")
}
return undefined
}, [options])
return ( return (
<Grid container justifyContent="flex-start" alignItems="flex-start"> <Grid container justifyContent="flex-start" alignItems="flex-start">
<Grid item xs={12}> <Grid item xs={12}>
@@ -331,7 +341,7 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
disableClearable disableClearable
disabled disabled
fullWidth fullWidth
defaultValue={options.find((o) => o.value === 1)} /// modify this later
defaultValue={options[0]} /// modify this later
// onChange={onChange} // onChange={onChange}
getOptionLabel={(option) => option.label} getOptionLabel={(option) => option.label}
options={options} options={options}
@@ -396,6 +406,8 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
// value={warehouseId > 0 // value={warehouseId > 0
// ? options.find((o) => o.value === warehouseId) // ? options.find((o) => o.value === warehouseId)
// : undefined} // : undefined}
defaultValue={options[0]}
// defaultValue={options.find((o) => o.value === 1)}
value={currentValue} value={currentValue}
onChange={onChange} onChange={onChange}
getOptionLabel={(option) => option.label} getOptionLabel={(option) => option.label}
@@ -406,7 +418,9 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
// label={"Select warehouse"} // label={"Select warehouse"}
disabled={disabled} disabled={disabled}
error={Boolean(errors.warehouseId?.message)} error={Boolean(errors.warehouseId?.message)}
helperText={errors.warehouseId?.message}
helperText={errors.warehouseId?.message ??
getWarningTextHardcode()
}
// helperText={warehouseHelperText} // helperText={warehouseHelperText}
/> />
)} )}


+ 59
- 43
src/components/PoDetail/QrModal.tsx 查看文件

@@ -1,6 +1,14 @@
"use client"; "use client";


import { Box, Button, Grid, Modal, ModalProps, Stack, Typography } from "@mui/material";
import {
Box,
Button,
Grid,
Modal,
ModalProps,
Stack,
Typography,
} from "@mui/material";
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import ReactQrCodeScanner, { import ReactQrCodeScanner, {
ScannerConfig, ScannerConfig,
@@ -74,18 +82,18 @@ const QrModal: React.FC<Props> = ({ open, onClose, warehouse }) => {
); );


// QR Code Scanner // QR Code Scanner
const scanner = useQcCodeScanner()
const scanner = useQcCodeScanner();
useEffect(() => { useEffect(() => {
if (open && !scanner.isScanning) { if (open && !scanner.isScanning) {
scanner.startScan()
scanner.startScan();
} else if (!open && scanner.isScanning) { } else if (!open && scanner.isScanning) {
scanner.stopScan()
scanner.stopScan();
} }
}, [open])
}, [open]);


useEffect(() => { useEffect(() => {
if (scanner.values.length > 0 && !Boolean(itemDetail)) { if (scanner.values.length > 0 && !Boolean(itemDetail)) {
console.log(scanner.values[0])
console.log(scanner.values[0]);
const data: QrCodeInfo = JSON.parse(scanner.values[0]); const data: QrCodeInfo = JSON.parse(scanner.values[0]);
console.log(data); console.log(data);
if (data.stockInLineId) { if (data.stockInLineId) {
@@ -93,28 +101,29 @@ const QrModal: React.FC<Props> = ({ open, onClose, warehouse }) => {
console.log(data.stockInLineId); console.log(data.stockInLineId);
setStockInLineId(data.stockInLineId); setStockInLineId(data.stockInLineId);
} }
scanner.resetScan()
scanner.resetScan();
} }
}, [scanner.values])
}, [scanner.values]);


const [itemDetail, setItemDetail] = useState<StockInLine>(); const [itemDetail, setItemDetail] = useState<StockInLine>();
const [disabledSubmit, setDisabledSubmit] = useState(false); const [disabledSubmit, setDisabledSubmit] = useState(false);
const [unavailableText, setUnavailableText] = useState<string | undefined>(undefined)
const [unavailableText, setUnavailableText] = useState<string | undefined>(undefined);
const fetchStockInLine = useCallback( const fetchStockInLine = useCallback(
async (stockInLineId: number) => { async (stockInLineId: number) => {
setUnavailableText(undefined)
setUnavailableText(undefined);
const res = await fetchStockInLineInfo(stockInLineId); const res = await fetchStockInLineInfo(stockInLineId);
if (res.status.toLowerCase() === "received") { if (res.status.toLowerCase() === "received") {
console.log(res.acceptedQty)
formProps.setValue("acceptedQty", res.acceptedQty)
setDisabledSubmit(false)
console.log(res.acceptedQty);
formProps.setValue("acceptedQty", res.acceptedQty);
setDisabledSubmit(false);
setItemDetail(res); setItemDetail(res);
} else if (res.status.toLowerCase() === "completed") { } else if (res.status.toLowerCase() === "completed") {
setDisabledSubmit(true)
setDisabledSubmit(true);
} else { } else {
// //
setUnavailableText("Item Not Available")
setDisabledSubmit(true)
setUnavailableText("Item Not Available");
setDisabledSubmit(true);
} }
// return // return
}, },
@@ -156,13 +165,13 @@ const QrModal: React.FC<Props> = ({ open, onClose, warehouse }) => {
setServerError(t("An error has occurred. Please try again later.")); setServerError(t("An error has occurred. Please try again later."));
return false; return false;
} }
return;
// return;
const res = await updateStockInLine(args); const res = await updateStockInLine(args);
if (Boolean(res.id)) { if (Boolean(res.id)) {
// update entries // update entries
console.log(res); console.log(res);
// add loading // add loading
// closeHandler({}, "backdropClick");
closeHandler({}, "backdropClick");
} }
console.log(res); console.log(res);
// if (res) // if (res)
@@ -185,32 +194,39 @@ const QrModal: React.FC<Props> = ({ open, onClose, warehouse }) => {
> >
<Grid container xs={12}> <Grid container xs={12}>
<Grid item xs={12}> <Grid item xs={12}>
{
itemDetail != undefined ? (
unavailableText != undefined ? <Typography variant="h4" marginInlineEnd={2}>{unavailableText}</Typography>
: (
<>
<PutawayForm itemDetail={itemDetail} warehouse={warehouse} disabled={false} />
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Button
name="submit"
variant="contained"
startIcon={<Check />}
type="submit"
disabled={disabledSubmit}
>
{t("submit")}
</Button>
</Stack>
</>

)
{itemDetail != undefined ? (
unavailableText != undefined ? (
<Typography variant="h4" marginInlineEnd={2}>
{unavailableText}
</Typography>
) : (
<>
<PutawayForm
itemDetail={itemDetail}
warehouse={warehouse}
disabled={false}
/>
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Button
name="submit"
variant="contained"
startIcon={<Check />}
type="submit"
disabled={disabledSubmit}
>
{t("submit")}
</Button>
</Stack>
</>
) )
: (
// <ReactQrCodeScanner scannerConfig={scannerConfig} />
<Typography variant="h4">{t("Will start binding procedure after scanning item qr code.")}</Typography>
)
}
) : (
// <ReactQrCodeScanner scannerConfig={scannerConfig} />
<Typography variant="h4">
{t(
"Will start binding procedure after scanning item qr code."
)}
</Typography>
)}
</Grid> </Grid>
</Grid> </Grid>
</Box> </Box>


+ 88
- 60
src/components/PoSearch/PoSearch.tsx 查看文件

@@ -10,10 +10,14 @@ import { EditNote } from "@mui/icons-material";
import { Button, Grid, Tab, Tabs, TabsProps, Typography } from "@mui/material"; import { Button, Grid, Tab, Tabs, TabsProps, Typography } from "@mui/material";
import QrModal from "../PoDetail/QrModal"; import QrModal from "../PoDetail/QrModal";
import { WarehouseResult } from "@/app/api/warehouse"; import { WarehouseResult } from "@/app/api/warehouse";
import NotificationIcon from '@mui/icons-material/NotificationImportant';
import NotificationIcon from "@mui/icons-material/NotificationImportant";
import { useSession } from "next-auth/react"; import { useSession } from "next-auth/react";
import { defaultPagingController } from "../SearchResults/SearchResults"; import { defaultPagingController } from "../SearchResults/SearchResults";
import { fetchPoListClient } from "@/app/api/po/actions";
import { fetchPoListClient, testing } from "@/app/api/po/actions";
import dayjs from "dayjs";
import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
import arraySupport from "dayjs/plugin/arraySupport";
dayjs.extend(arraySupport);


type Props = { type Props = {
po: PoResult[]; po: PoResult[];
@@ -24,23 +28,39 @@ type SearchQuery = Partial<Omit<PoResult, "id">>;
type SearchParamNames = keyof SearchQuery; type SearchParamNames = keyof SearchQuery;


// cal offset (pageSize) // cal offset (pageSize)
// cal limit (pageSize)
const PoSearch: React.FC<Props> = ({ po, warehouse, totalCount: initTotalCount }) => {
// cal limit (pageSize)
const PoSearch: React.FC<Props> = ({
po,
warehouse,
totalCount: initTotalCount,
}) => {
const [filteredPo, setFilteredPo] = useState<PoResult[]>(po); const [filteredPo, setFilteredPo] = useState<PoResult[]>(po);
const [filterArgs, setFilterArgs] = useState<Record<string, any>>({});
const { t } = useTranslation("purchaseOrder"); const { t } = useTranslation("purchaseOrder");
const router = useRouter(); const router = useRouter();
const [pagingController, setPagingController] = useState(defaultPagingController)
const [totalCount, setTotalCount] = useState(initTotalCount)
const [pagingController, setPagingController] = useState(
defaultPagingController
);
const [totalCount, setTotalCount] = useState(initTotalCount);
const searchCriteria: Criterion<SearchParamNames>[] = useMemo(() => { const searchCriteria: Criterion<SearchParamNames>[] = useMemo(() => {
var searchCriteria: Criterion<SearchParamNames>[] = [ var searchCriteria: Criterion<SearchParamNames>[] = [
{ label: t("Code"), paramName: "code", type: "text" }, { label: t("Code"), paramName: "code", type: "text" },
{ label: t("Status"), paramName: "status", type: "select", options: ["PENDING", "RECEIVING", "COMPLETED"] },
{ label: t("Escalated"), paramName: "escalated", type: "select", options: [t("Escalated"), t("NotEscalated")] },
{
label: t("Status"),
paramName: "status",
type: "select",
options: [t(`pending`), t(`receiving`), t(`completed`)],
},
{
label: t("Escalated"),
paramName: "escalated",
type: "select",
options: [t("Escalated"), t("NotEscalated")],
},
]; ];
return searchCriteria; return searchCriteria;
}, [t, po]); }, [t, po]);



const onDetailClick = useCallback( const onDetailClick = useCallback(
(po: PoResult) => { (po: PoResult) => {
router.push(`/po/edit?id=${po.id}`); router.push(`/po/edit?id=${po.id}`);
@@ -65,6 +85,11 @@ const PoSearch: React.FC<Props> = ({ po, warehouse, totalCount: initTotalCount }
{ {
name: "orderDate", name: "orderDate",
label: t("OrderDate"), label: t("OrderDate"),
renderCell: (params) => {
return dayjs(params.orderDate)
.add(-1, "month")
.format(OUTPUT_DATE_FORMAT);
},
}, },
{ {
name: "supplier", name: "supplier",
@@ -73,13 +98,18 @@ const PoSearch: React.FC<Props> = ({ po, warehouse, totalCount: initTotalCount }
{ {
name: "status", name: "status",
label: t("Status"), label: t("Status"),
renderCell: (params) => {
return t(`${params.status.toLowerCase()}`);
},
}, },
{ {
name: "escalated", name: "escalated",
label: t("Escalated"), label: t("Escalated"),
renderCell: (params) => { renderCell: (params) => {
return params.escalated ? <NotificationIcon color="warning"/> : undefined
}
return params.escalated ? (
<NotificationIcon color="warning" />
) : undefined;
},
}, },
// { // {
// name: "name", // name: "name",
@@ -108,17 +138,30 @@ const PoSearch: React.FC<Props> = ({ po, warehouse, totalCount: initTotalCount }
setOpenScanner(false); setOpenScanner(false);
}, []); }, []);


const newPageFetch = useCallback(async (pagingController: Record<string, number>) => {
const res = await fetchPoListClient(pagingController)
if (res) {
setFilteredPo(res.records)
setTotalCount(res.total)
}
}, [fetchPoListClient, pagingController])
const newPageFetch = useCallback(
async (
pagingController: Record<string, number>,
filterArgs: Record<string, number>
) => {
console.log(pagingController);
const params = {
...pagingController,
...filterArgs,
};
// const res = await fetchPoListClient(params);
const res = await testing(params);
if (res) {
console.log(res.records);
setFilteredPo(res.records);
setTotalCount(res.total);
}
},
[fetchPoListClient, pagingController]
);


useEffect(() => { useEffect(() => {
newPageFetch(pagingController)
}, [newPageFetch, pagingController])
newPageFetch(pagingController, filterArgs);
}, [newPageFetch, pagingController, filterArgs]);
return ( return (
<> <>
<Grid container> <Grid container>
@@ -127,47 +170,32 @@ const PoSearch: React.FC<Props> = ({ po, warehouse, totalCount: initTotalCount }
{t("Purchase Order")} {t("Purchase Order")}
</Typography> </Typography>
</Grid> </Grid>
<Grid
item
xs={4}
display="flex"
justifyContent="end"
alignItems="end"
>
<QrModal
open={isOpenScanner}
onClose={onCloseScanner}
warehouse={warehouse}
/>
<Button onClick={onOpenScanner}>bind</Button>
</Grid>
</Grid>
<>
<SearchBox
criteria={searchCriteria}
onSearch={(query) => {
setFilteredPo((prev) =>
prev.filter((p) => {
return (
p.code.toLowerCase().includes(query.code.toLowerCase()) &&
(query.status === "All" || p.status === query.status) &&
(query.escalated === "All" || p.escalated === (query.escalated === t("Escalated")))
)
})
);
}}
onReset={onReset}
<Grid item xs={4} display="flex" justifyContent="end" alignItems="end">
<QrModal
open={isOpenScanner}
onClose={onCloseScanner}
warehouse={warehouse}
/> />
<SearchResults<PoResult>
items={filteredPo}
columns={columns}
pagingController={pagingController}
setPagingController={setPagingController}
totalCount={totalCount}
isAutoPaging={false}
/>
</>

<Button onClick={onOpenScanner}>bind</Button>
</Grid>
</Grid>
<>
<SearchBox
criteria={searchCriteria}
onSearch={(query) => {
setFilterArgs({ ...query });
}}
onReset={onReset}
/>
<SearchResults<PoResult>
items={filteredPo}
columns={columns}
pagingController={pagingController}
setPagingController={setPagingController}
totalCount={totalCount}
isAutoPaging={false}
/>
</>
</> </>
); );
}; };


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

@@ -6,6 +6,7 @@
"Supplier": "供應商", "Supplier": "供應商",
"Status": "狀態", "Status": "狀態",
"Escalated": "已上報", "Escalated": "已上報",
"NotEscalated": "無上報",


"Do you want to start?": "確定開始嗎?", "Do you want to start?": "確定開始嗎?",
"Start": "開始", "Start": "開始",


正在加载...
取消
保存