FPSMS-frontend
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

PoDetail.tsx 23 KiB

6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
6ヶ月前
4ヶ月前
6ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
6ヶ月前
4ヶ月前
4ヶ月前
6ヶ月前
4ヶ月前
6ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
6ヶ月前
4ヶ月前
6ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
6ヶ月前
4ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
6ヶ月前
6ヶ月前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. "use client";
  2. import {
  3. fetchPoWithStockInLines,
  4. PoResult,
  5. PurchaseOrderLine,
  6. StockInLine,
  7. } from "@/app/api/po";
  8. import {
  9. Box,
  10. Button,
  11. ButtonProps,
  12. Collapse,
  13. Grid,
  14. IconButton,
  15. Paper,
  16. Stack,
  17. Tab,
  18. Table,
  19. TableBody,
  20. TableCell,
  21. TableContainer,
  22. TableHead,
  23. TableRow,
  24. Tabs,
  25. TabsProps,
  26. TextField,
  27. Typography,
  28. Checkbox,
  29. FormControlLabel,
  30. } from "@mui/material";
  31. import { useTranslation } from "react-i18next";
  32. // import InputDataGrid, { TableRow } from "../InputDataGrid/InputDataGrid";
  33. import {
  34. GridColDef,
  35. GridRowId,
  36. GridRowModel,
  37. useGridApiRef,
  38. } from "@mui/x-data-grid";
  39. import {
  40. checkPolAndCompletePo,
  41. fetchPoInClient,
  42. fetchStockInLineInfo,
  43. PurchaseQcResult,
  44. startPo,
  45. } from "@/app/api/po/actions";
  46. import {
  47. useCallback,
  48. useContext,
  49. useEffect,
  50. useMemo,
  51. useState,
  52. } from "react";
  53. import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
  54. import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
  55. import PoInputGrid from "./PoInputGrid";
  56. import { QcItemWithChecks } from "@/app/api/qc";
  57. import { useRouter, useSearchParams, usePathname } from "next/navigation";
  58. import { WarehouseResult } from "@/app/api/warehouse";
  59. import { calculateWeight, returnWeightUnit } from "@/app/utils/formatUtil";
  60. import { CameraContext } from "../Cameras/CameraProvider";
  61. import PoQcStockInModal from "./PoQcStockInModal";
  62. import QrModal from "./QrModal";
  63. import { PlayArrow } from "@mui/icons-material";
  64. import DoneIcon from "@mui/icons-material/Done";
  65. import { getCustomWidth } from "@/app/utils/commonUtil";
  66. import PoInfoCard from "./PoInfoCard";
  67. import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil";
  68. import { fetchPoListClient } from "@/app/api/po/actions";
  69. import { List, ListItem, ListItemButton, ListItemText, Divider } from "@mui/material";
  70. import { createStockInLine } from "@/app/api/dashboard/actions";
  71. //import { useRouter } from "next/navigation";
  72. type Props = {
  73. po: PoResult;
  74. qc: QcItemWithChecks[];
  75. warehouse: WarehouseResult[];
  76. };
  77. type EntryError =
  78. | {
  79. [field in keyof StockInLine]?: string;
  80. }
  81. | undefined;
  82. // type PolRow = TableRow<Partial<StockInLine>, EntryError>;
  83. const PoSearchList: React.FC<{
  84. poList: PoResult[];
  85. selectedPoId: number;
  86. onSelect: (po: PoResult) => void;
  87. }> = ({ poList, selectedPoId, onSelect }) => {
  88. const { t } = useTranslation("purchaseOrder");
  89. const [searchTerm, setSearchTerm] = useState('');
  90. const filteredPoList = useMemo(() => {
  91. if (searchTerm.trim() === '') {
  92. return poList;
  93. }
  94. return poList.filter(poItem =>
  95. poItem.code.toLowerCase().includes(searchTerm.toLowerCase()) ||
  96. poItem.supplier?.toLowerCase().includes(searchTerm.toLowerCase()) ||
  97. t(`${poItem.status.toLowerCase()}`).toLowerCase().includes(searchTerm.toLowerCase())
  98. );
  99. }, [poList, searchTerm, t]);
  100. return (
  101. <Paper sx={{ p: 2, maxHeight: "400px", overflow: "auto", minWidth: "300px" }}>
  102. <Typography variant="h6" gutterBottom>
  103. {t("Purchase Orders")}
  104. </Typography>
  105. <TextField
  106. label={t("Search")}
  107. variant="outlined"
  108. size="small"
  109. fullWidth
  110. value={searchTerm}
  111. onChange={(e) => setSearchTerm(e.target.value)}
  112. sx={{ mb: 2 }}
  113. InputProps={{
  114. startAdornment: (
  115. <Typography variant="body2" color="text.secondary" sx={{ mr: 1 }}>
  116. </Typography>
  117. ),
  118. }}
  119. />
  120. <List dense sx={{ width: '100%' }}>
  121. {filteredPoList.map((poItem, index) => (
  122. <div key={poItem.id}>
  123. <ListItem disablePadding sx={{ width: '100%' }}>
  124. <ListItemButton
  125. selected={selectedPoId === poItem.id}
  126. onClick={() => onSelect(poItem)}
  127. sx={{
  128. width: '100%',
  129. "&.Mui-selected": {
  130. backgroundColor: "primary.light",
  131. "&:hover": {
  132. backgroundColor: "primary.light",
  133. },
  134. },
  135. }}
  136. >
  137. <ListItemText
  138. primary={
  139. <Typography variant="body2" sx={{ wordBreak: 'break-all' }}>
  140. {poItem.code}
  141. </Typography>
  142. }
  143. secondary={
  144. <Typography variant="caption" color="text.secondary">
  145. {t(`${poItem.status.toLowerCase()}`)}
  146. </Typography>
  147. }
  148. />
  149. </ListItemButton>
  150. </ListItem>
  151. {index < filteredPoList.length - 1 && <Divider />}
  152. </div>
  153. ))}
  154. </List>
  155. {searchTerm && (
  156. <Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: "block" }}>
  157. {t("Found")} {filteredPoList.length} {t("of")} {poList.length} {t("items")}
  158. </Typography>
  159. )}
  160. </Paper>
  161. );
  162. };
  163. const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
  164. const cameras = useContext(CameraContext);
  165. console.log(cameras);
  166. const { t } = useTranslation("purchaseOrder");
  167. const apiRef = useGridApiRef();
  168. const [purchaseOrder, setPurchaseOrder] = useState({ ...po });
  169. const [rows, setRows] = useState<PurchaseOrderLine[]>(
  170. purchaseOrder.pol || [],
  171. );
  172. const pathname = usePathname()
  173. const searchParams = useSearchParams();
  174. const [row, setRow] = useState<PurchaseOrderLine>({});
  175. const [stockInLine, setStockInLine] = useState<StockInLine[]>([]);
  176. const [processedQty, setProcessedQty] = useState(0);
  177. const router = useRouter();
  178. const [poList, setPoList] = useState<PoResult[]>([]);
  179. const [selectedPoId, setSelectedPoId] = useState(po.id);
  180. const currentPoId = searchParams.get('id');
  181. const selectedIdsParam = searchParams.get('selectedIds');
  182. const fetchPoList = useCallback(async () => {
  183. try {
  184. if (selectedIdsParam) {
  185. const selectedIds = selectedIdsParam.split(',').map(id => parseInt(id));
  186. const promises = selectedIds.map(id => fetchPoInClient(id));
  187. const results = await Promise.all(promises);
  188. setPoList(results.filter(Boolean));
  189. } else {
  190. const result = await fetchPoListClient({ limit: 20, offset: 0 });
  191. if (result && result.records) {
  192. setPoList(result.records);
  193. }
  194. }
  195. } catch (error) {
  196. console.error("Failed to fetch PO list:", error);
  197. }
  198. }, [selectedIdsParam]);
  199. const handlePoSelect = useCallback(
  200. (selectedPo: PoResult) => {
  201. setSelectedPoId(selectedPo.id);
  202. const newSelectedIds = selectedIdsParam || selectedPo.id.toString();
  203. router.push(`/po/edit?id=${selectedPo.id}&start=true&selectedIds=${newSelectedIds}`, { scroll: false });
  204. },
  205. [router, selectedIdsParam]
  206. );
  207. const fetchPoDetail = useCallback(async (poId: string) => {
  208. try {
  209. const result = await fetchPoInClient(parseInt(poId));
  210. if (result) {
  211. setPurchaseOrder(result);
  212. setRows(result.pol || []);
  213. if (result.pol && result.pol.length > 0) {
  214. setRow(result.pol[0]);
  215. setStockInLine(result.pol[0].stockInLine);
  216. setProcessedQty(result.pol[0].processed);
  217. }
  218. }
  219. } catch (error) {
  220. console.error("Failed to fetch PO detail:", error);
  221. }
  222. }, []);
  223. useEffect(() => {
  224. if (currentPoId && currentPoId !== selectedPoId.toString()) {
  225. setSelectedPoId(parseInt(currentPoId));
  226. fetchPoDetail(currentPoId);
  227. }
  228. }, [currentPoId, selectedPoId, fetchPoDetail]);
  229. useEffect(() => {
  230. fetchPoList();
  231. }, [fetchPoList]);
  232. useEffect(() => {
  233. if (currentPoId) {
  234. setSelectedPoId(parseInt(currentPoId));
  235. }
  236. }, [currentPoId]);
  237. const removeParam = (paramToRemove: string) => {
  238. const newParams = new URLSearchParams(searchParams.toString());
  239. newParams.delete(paramToRemove);
  240. window.history.replaceState({}, '', `${window.location.pathname}?${newParams}`);
  241. };
  242. const handleCompletePo = useCallback(async () => {
  243. const checkRes = await checkPolAndCompletePo(purchaseOrder.id);
  244. console.log(checkRes);
  245. const newPo = await fetchPoInClient(purchaseOrder.id);
  246. setPurchaseOrder(newPo);
  247. }, [purchaseOrder.id]);
  248. const handleStartPo = useCallback(async () => {
  249. const startRes = await startPo(purchaseOrder.id);
  250. console.log(startRes);
  251. const newPo = await fetchPoInClient(purchaseOrder.id);
  252. setPurchaseOrder(newPo);
  253. }, [purchaseOrder.id]);
  254. useEffect(() => {
  255. setRows(purchaseOrder.pol || []);
  256. }, [purchaseOrder]);
  257. // useEffect(() => {
  258. // setStockInLine([])
  259. // }, []);
  260. function Row(props: { row: PurchaseOrderLine }) {
  261. const { row } = props;
  262. // const [firstReceiveQty, setFirstReceiveQty] = useState<number>()
  263. const [secondReceiveQty, setSecondReceiveQty] = useState<number>()
  264. // const [open, setOpen] = useState(false);
  265. const [processedQty, setProcessedQty] = useState(row.processed);
  266. const [currStatus, setCurrStatus] = useState(row.status);
  267. // const [stockInLine, setStockInLine] = useState(row.stockInLine);
  268. const totalWeight = useMemo(
  269. () => calculateWeight(row.qty, row.uom),
  270. [row.qty, row.uom],
  271. );
  272. const weightUnit = useMemo(
  273. () => returnWeightUnit(row.uom),
  274. [row.uom],
  275. );
  276. useEffect(() => {
  277. const polId = searchParams.get("polId") != null ? parseInt(searchParams.get("polId")!) : null
  278. if (polId) {
  279. setStockInLine(rows.find((r) => r.id == polId)!.stockInLine)
  280. }
  281. }, []);
  282. useEffect(() => {
  283. if (processedQty === row.qty) {
  284. setCurrStatus("completed".toUpperCase());
  285. } else if (processedQty > 0) {
  286. setCurrStatus("receiving".toUpperCase());
  287. } else {
  288. setCurrStatus("pending".toUpperCase());
  289. }
  290. }, [processedQty, row.qty]);
  291. const changeStockInLines = useCallback(
  292. (id: number) => {
  293. console.log(id)
  294. //rows = purchaseOrderLine
  295. console.log(rows)
  296. const target = rows.find((r) => r.id === id)
  297. const stockInLine = target!.stockInLine
  298. console.log(stockInLine)
  299. setStockInLine(stockInLine)
  300. setRow(target!)
  301. // console.log(pathname)
  302. // router.replace(`/po/edit?id=${item.poId}&polId=${item.polId}&stockInLineId=${item.stockInLineId}`);
  303. },
  304. [rows]
  305. );
  306. const handleStart = useCallback(
  307. () => {
  308. setTimeout(async () => {
  309. // post stock in line
  310. const oldId = row.id;
  311. const postData = {
  312. itemId: row.itemId,
  313. itemNo: row.itemNo,
  314. itemName: row.itemName,
  315. purchaseOrderId: row.purchaseOrderId,
  316. purchaseOrderLineId: row.id,
  317. acceptedQty: secondReceiveQty || 0,
  318. // acceptedQty: row.acceptedQty,
  319. };
  320. if (secondReceiveQty === 0) return
  321. const res = await createStockInLine(postData);
  322. console.log(res);
  323. }, 200);
  324. },
  325. [],
  326. );
  327. const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  328. const raw = e.target.value;
  329. // Allow empty input
  330. if (raw.trim() === '') {
  331. setSecondReceiveQty(undefined);
  332. return;
  333. }
  334. // Keep digits only
  335. const cleaned = raw.replace(/[^\d]/g, '');
  336. if (cleaned === '') {
  337. // If the user typed only non-digits, keep previous value
  338. return;
  339. }
  340. // Parse and clamp to non-negative integer
  341. const next = Math.max(0, Math.floor(Number(cleaned)));
  342. setSecondReceiveQty(next);
  343. };
  344. return (
  345. <>
  346. <TableRow
  347. sx={{ "& > *": { borderBottom: "unset" },
  348. color: "black"
  349. }}
  350. onClick={() => changeStockInLines(row.id)}
  351. >
  352. {/* <TableCell>
  353. <IconButton
  354. disabled={purchaseOrder.status.toLowerCase() === "pending"}
  355. aria-label="expand row"
  356. size="small"
  357. onClick={() => setOpen(!open)}
  358. >
  359. {open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
  360. </IconButton>
  361. </TableCell> */}
  362. <TableCell align="left">{row.itemNo}</TableCell>
  363. <TableCell align="left">{row.itemName}</TableCell>
  364. <TableCell align="right">{integerFormatter.format(row.qty)}</TableCell>
  365. <TableCell align="right">{integerFormatter.format(processedQty)}</TableCell>
  366. <TableCell align="left">{row.uom?.code}</TableCell>
  367. {/* <TableCell align="right">
  368. {decimalFormatter.format(totalWeight)} {weightUnit}
  369. </TableCell> */}
  370. {/* <TableCell align="left">{weightUnit}</TableCell> */}
  371. <TableCell align="right">{decimalFormatter.format(row.price)}</TableCell>
  372. {/* <TableCell align="left">{row.expiryDate}</TableCell> */}
  373. <TableCell align="left">{t(`${currStatus.toLowerCase()}`)}</TableCell>
  374. <TableCell align="right">{integerFormatter.format(row.receivedQty)}</TableCell>
  375. <TableCell align="center">
  376. <TextField
  377. label="輸入來貨數量"
  378. type="text" // Use type="text" to allow validation in the change handler
  379. variant="outlined"
  380. value={secondReceiveQty}
  381. onChange={handleChange}
  382. InputProps={{
  383. inputProps: {
  384. min: 0, // Optional: set a minimum value
  385. step: 1 // Optional: set the step for the number input
  386. }
  387. }}
  388. />
  389. </TableCell>
  390. <TableCell align="center">
  391. <Button
  392. variant="contained"
  393. onClick={() =>
  394. handleStart()
  395. }
  396. >
  397. 提交
  398. </Button>
  399. </TableCell>
  400. </TableRow>
  401. {/* <TableRow> */}
  402. {/* <TableCell /> */}
  403. {/* <TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={12}> */}
  404. {/* <Collapse in={true} timeout="auto" unmountOnExit> */}
  405. {/* <Collapse in={open} timeout="auto" unmountOnExit> */}
  406. {/* <Table>
  407. <TableBody>
  408. <TableRow>
  409. <TableCell align="right">
  410. <Box>
  411. <PoInputGrid
  412. qc={qc}
  413. setRows={setRows}
  414. stockInLine={stockInLine}
  415. setStockInLine={setStockInLine}
  416. setProcessedQty={setProcessedQty}
  417. itemDetail={row}
  418. warehouse={warehouse}
  419. />
  420. </Box>
  421. </TableCell>
  422. </TableRow>
  423. </TableBody>
  424. </Table> */}
  425. {/* </Collapse> */}
  426. {/* </TableCell> */}
  427. {/* </TableRow> */}
  428. </>
  429. );
  430. }
  431. const [tabIndex, setTabIndex] = useState(0);
  432. const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
  433. (_e, newValue) => {
  434. setTabIndex(newValue);
  435. },
  436. [],
  437. );
  438. const [isOpenScanner, setOpenScanner] = useState(false);
  439. // const testing = useCallback(() => {
  440. // // setOpenScanner(true);
  441. // const newParams = new URLSearchParams(searchParams.toString());
  442. // console.log(pathname)
  443. // }, [pathname, router, searchParams]);
  444. const onOpenScanner = useCallback(() => {
  445. setOpenScanner(true);
  446. }, []);
  447. const onCloseScanner = useCallback(() => {
  448. setOpenScanner(false);
  449. }, []);
  450. const [itemInfo, setItemInfo] = useState<
  451. StockInLine & { warehouseId?: number }
  452. >();
  453. const [putAwayOpen, setPutAwayOpen] = useState(false);
  454. // const [scannedInfo, setScannedInfo] = useState<QrCodeInfo>({} as QrCodeInfo);
  455. const closePutAwayModal = useCallback(() => {
  456. setPutAwayOpen(false);
  457. setItemInfo(undefined);
  458. }, []);
  459. const openPutAwayModal = useCallback(() => {
  460. setPutAwayOpen(true);
  461. }, []);
  462. const buttonData = useMemo(() => {
  463. switch (purchaseOrder.status.toLowerCase()) {
  464. case "pending":
  465. return {
  466. buttonName: "start",
  467. title: t("Do you want to start?"),
  468. confirmButtonText: t("Start"),
  469. successTitle: t("Start Success"),
  470. errorTitle: t("Start Fail"),
  471. buttonText: t("Start PO"),
  472. buttonIcon: <PlayArrow />,
  473. buttonColor: "success",
  474. disabled: false,
  475. onClick: handleStartPo,
  476. };
  477. case "receiving":
  478. return {
  479. buttonName: "complete",
  480. title: t("Do you want to complete?"),
  481. confirmButtonText: t("Complete"),
  482. successTitle: t("Complete Success"),
  483. errorTitle: t("Complete Fail"),
  484. buttonText: t("Complete PO"),
  485. buttonIcon: <DoneIcon />,
  486. buttonColor: "info",
  487. disabled: false,
  488. onClick: handleCompletePo,
  489. };
  490. default:
  491. return {
  492. buttonName: "complete",
  493. title: t("Do you want to complete?"),
  494. confirmButtonText: t("Complete"),
  495. successTitle: t("Complete Success"),
  496. errorTitle: t("Complete Fail"),
  497. buttonText: t("Complete PO"),
  498. buttonIcon: <DoneIcon />,
  499. buttonColor: "info",
  500. disabled: true,
  501. };
  502. // break;
  503. }
  504. }, [purchaseOrder.status, t, handleStartPo, handleCompletePo]);
  505. const FIRST_IN_FIELD = "firstInQty"
  506. const SECOND_IN_FIELD = "secondInQty"
  507. const renderFieldCondition = useCallback((field: "firstInQty" | "secondInQty"): boolean => {
  508. switch (field) {
  509. case FIRST_IN_FIELD:
  510. return true;
  511. case SECOND_IN_FIELD:
  512. return true;
  513. default:
  514. return false; // Default case
  515. }
  516. }, []);
  517. useEffect(() => {
  518. const params = searchParams.get("polId")
  519. if (params) {
  520. const polId = parseInt(params)
  521. }
  522. }, [searchParams])
  523. return (
  524. <>
  525. <Stack spacing={2}>
  526. {/* Area1: title */}
  527. <Grid container xs={12} justifyContent="start">
  528. <Grid item>
  529. <Typography mb={2} variant="h4">
  530. {purchaseOrder.code} -{" "}
  531. {t(`${purchaseOrder.status.toLowerCase()}`)}
  532. </Typography>
  533. </Grid>
  534. </Grid>
  535. {/* area2: dn info */}
  536. <Grid container spacing={3} sx={{ maxWidth: 'fit-content' }}>
  537. {/* left side select po */}
  538. <Grid item xs={4}>
  539. <PoSearchList
  540. poList={poList}
  541. selectedPoId={selectedPoId}
  542. onSelect={handlePoSelect}
  543. />
  544. </Grid>
  545. {/* right side po info */}
  546. <Grid item xs={8}>
  547. <PoInfoCard po={purchaseOrder} />
  548. {true ? (
  549. <Stack spacing={2}>
  550. <TextField
  551. label={t("dnNo")}
  552. type="text"
  553. variant="outlined"
  554. fullWidth
  555. // InputProps={{
  556. // inputProps: {
  557. // min: 0,
  558. // step: 1
  559. // }
  560. // }}
  561. />
  562. <TextField
  563. label={t("dnDate")}
  564. type="text"
  565. variant="outlined"
  566. defaultValue={"11/08/2025"}
  567. fullWidth
  568. InputProps={{
  569. inputProps: {
  570. min: 0,
  571. step: 1
  572. }
  573. }}
  574. />
  575. <Button variant="contained" onClick={onOpenScanner} fullWidth>
  576. 提交
  577. </Button>
  578. </Stack>
  579. ) : undefined}
  580. </Grid>
  581. </Grid>
  582. {/* Area4: Main Table */}
  583. <Grid container xs={12} justifyContent="start">
  584. <Grid item xs={12}>
  585. <TableContainer component={Paper} sx={{ width: 'fit-content', overflow: 'auto' }}>
  586. <Table aria-label="collapsible table" stickyHeader>
  587. <TableHead>
  588. <TableRow>
  589. <TableCell sx={{ width: '125px' }}>{t("itemNo")}</TableCell>
  590. <TableCell align="left" sx={{ width: '125px' }}>{t("itemName")}</TableCell>
  591. <TableCell align="right">{t("qty")}</TableCell>
  592. <TableCell align="right">{t("processed")}</TableCell>
  593. <TableCell align="left">{t("uom")}</TableCell>
  594. {/* <TableCell align="right">{t("total weight")}</TableCell> */}
  595. <TableCell align="right">{`${t("price")} (HKD)`}</TableCell>
  596. <TableCell align="left" sx={{ width: '75px' }}>{t("status")}</TableCell>
  597. {renderFieldCondition(FIRST_IN_FIELD) ? <TableCell align="right">{t("receivedQty")}</TableCell> : undefined}
  598. {renderFieldCondition(SECOND_IN_FIELD) ? <TableCell align="center" sx={{ width: '150px' }}>{t("dnQty")}(以訂單單位計算)</TableCell> : undefined}
  599. <TableCell align="center" sx={{ width: '100px' }}></TableCell>
  600. </TableRow>
  601. </TableHead>
  602. <TableBody>
  603. {rows.map((row) => (
  604. <Row key={row.id} row={row} />
  605. ))}
  606. </TableBody>
  607. </Table>
  608. </TableContainer>
  609. </Grid>
  610. </Grid>
  611. {/* area5: selected item info */}
  612. <Grid container xs={12} justifyContent="start">
  613. <Grid item xs={12}>
  614. <Typography variant="h6">{row.itemNo && row.itemName ? `已選擇: ${row.itemNo}-${row.itemName}` : `未選擇貨品`}</Typography>
  615. </Grid>
  616. <Grid item xs={12}>
  617. <TableContainer component={Paper} sx={{ width: 'fit-content', overflow: 'auto' }}>
  618. <Table>
  619. <TableBody>
  620. <TableRow>
  621. <TableCell align="right">
  622. <Box>
  623. <PoInputGrid
  624. qc={qc}
  625. setRows={setRows}
  626. stockInLine={stockInLine}
  627. setStockInLine={setStockInLine}
  628. setProcessedQty={setProcessedQty}
  629. itemDetail={row}
  630. warehouse={warehouse}
  631. />
  632. </Box>
  633. </TableCell>
  634. </TableRow>
  635. </TableBody>
  636. </Table>
  637. </TableContainer>
  638. </Grid>
  639. </Grid>
  640. {/* tab 2 */}
  641. <Grid sx={{ display: tabIndex === 1 ? "block" : "none" }}>
  642. {/* <StyledDataGrid
  643. /> */}
  644. </Grid>
  645. </Stack>
  646. {itemInfo !== undefined && (
  647. <>
  648. <PoQcStockInModal
  649. type={"putaway"}
  650. open={putAwayOpen}
  651. warehouse={warehouse}
  652. setItemDetail={setItemInfo}
  653. onClose={closePutAwayModal}
  654. itemDetail={itemInfo}
  655. />
  656. </>
  657. )}
  658. </>
  659. );
  660. };
  661. export default PoDetail;