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

PutAwayForm.tsx 16 KiB

6ヶ月前
6ヶ月前
3ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
3ヶ月前
6ヶ月前
3ヶ月前
3ヶ月前
6ヶ月前
3ヶ月前
4ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
4ヶ月前
6ヶ月前
6ヶ月前
4ヶ月前
3ヶ月前
3ヶ月前
3ヶ月前
4ヶ月前
3ヶ月前
3ヶ月前
4ヶ月前
3ヶ月前
3ヶ月前
4ヶ月前
3ヶ月前
4ヶ月前
4ヶ月前
3ヶ月前
4ヶ月前
3ヶ月前
3ヶ月前
3ヶ月前
3ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
4ヶ月前
4ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
4ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
4ヶ月前
4ヶ月前
6ヶ月前
4ヶ月前
6ヶ月前
6ヶ月前
4ヶ月前
3ヶ月前
4ヶ月前
3ヶ月前
4ヶ月前
3ヶ月前
4ヶ月前
6ヶ月前
3ヶ月前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. "use client";
  2. import { PutAwayInput, PutAwayLine } from "@/app/api/stockIn/actions";
  3. import {
  4. Autocomplete,
  5. Box,
  6. Button,
  7. Card,
  8. CardContent,
  9. FormControl,
  10. Grid,
  11. Modal,
  12. ModalProps,
  13. Stack,
  14. TextField,
  15. Tooltip,
  16. Typography,
  17. } from "@mui/material";
  18. import { Controller, useFormContext } from "react-hook-form";
  19. import { useTranslation } from "react-i18next";
  20. import StyledDataGrid from "../StyledDataGrid";
  21. import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react";
  22. import {
  23. GridColDef,
  24. GridRowIdGetter,
  25. GridRowModel,
  26. useGridApiContext,
  27. GridRenderCellParams,
  28. GridRenderEditCellParams,
  29. useGridApiRef,
  30. GridRowSelectionModel,
  31. } from "@mui/x-data-grid";
  32. import InputDataGrid from "../InputDataGrid";
  33. import { TableRow } from "../InputDataGrid/InputDataGrid";
  34. import TwoLineCell from "./TwoLineCell";
  35. import QcSelect from "./QcSelect";
  36. import { QcItemWithChecks } from "@/app/api/qc";
  37. import { GridEditInputCell } from "@mui/x-data-grid";
  38. import { StockInLine } from "@/app/api/stockIn";
  39. import { WarehouseResult } from "@/app/api/warehouse";
  40. import {
  41. arrayToDateString,
  42. arrayToDateTimeString,
  43. OUTPUT_DATE_FORMAT,
  44. stockInLineStatusMap,
  45. } from "@/app/utils/formatUtil";
  46. import { QRCodeSVG } from "qrcode.react";
  47. import { QrCode } from "../QrCode";
  48. import ReactQrCodeScanner, {
  49. ScannerConfig,
  50. } from "../ReactQrCodeScanner/ReactQrCodeScanner";
  51. import { QrCodeInfo } from "@/app/api/qrcode";
  52. import { useQrCodeScannerContext } from "../QrCodeScannerProvider/QrCodeScannerProvider";
  53. import dayjs from "dayjs";
  54. import arraySupport from "dayjs/plugin/arraySupport";
  55. import { dummyPutAwayLine } from "./dummyQcTemplate";
  56. import { GridRowModesModel } from "@mui/x-data-grid";
  57. dayjs.extend(arraySupport);
  58. interface Props {
  59. itemDetail: StockInLine;
  60. warehouse?: WarehouseResult[];
  61. disabled: boolean;
  62. // qc: QcItemWithChecks[];
  63. setRowModesModel: Dispatch<SetStateAction<GridRowModesModel>>;
  64. setRowSelectionModel: Dispatch<SetStateAction<GridRowSelectionModel>>;
  65. }
  66. type EntryError =
  67. | {
  68. [field in keyof PutAwayLine]?: string;
  69. }
  70. | undefined;
  71. type PutAwayRow = TableRow<Partial<PutAwayLine>, EntryError>;
  72. const style = {
  73. position: "absolute",
  74. top: "50%",
  75. left: "50%",
  76. transform: "translate(-50%, -50%)",
  77. bgcolor: "background.paper",
  78. pt: 5,
  79. px: 5,
  80. pb: 10,
  81. width: "auto",
  82. };
  83. const PutAwayForm: React.FC<Props> = ({ itemDetail, warehouse=[], disabled, setRowModesModel, setRowSelectionModel }) => {
  84. const { t } = useTranslation("purchaseOrder");
  85. const apiRef = useGridApiRef();
  86. const {
  87. register,
  88. formState: { errors, defaultValues, touchedFields },
  89. watch,
  90. control,
  91. setValue,
  92. getValues,
  93. reset,
  94. resetField,
  95. setError,
  96. clearErrors,
  97. } = useFormContext<PutAwayInput>();
  98. // const [recordQty, setRecordQty] = useState(0);
  99. const [warehouseId, setWarehouseId] = useState(itemDetail.defaultWarehouseId ?? 1);
  100. const filteredWarehouse = useMemo(() => {
  101. // do filtering here if any
  102. return warehouse;
  103. }, []);
  104. const defaultOption = {
  105. value: 0, // think think sin
  106. label: t("Select warehouse"),
  107. group: "default",
  108. };
  109. const options = useMemo(() => {
  110. return [
  111. {
  112. value: 1,
  113. label: t("W001 - 憶兆 3樓A倉"),
  114. group: "default",
  115. },
  116. ...filteredWarehouse.map((w) => ({
  117. value: w.id,
  118. label: `${w.code} - ${w.name}`,
  119. group: "existing",
  120. })),
  121. ];
  122. }, [filteredWarehouse]);
  123. const currentValue =
  124. warehouseId > 0
  125. ? options.find((o) => o.value === warehouseId)
  126. : options.find((o) => o.value === getValues("warehouseId")) ||
  127. defaultOption;
  128. const onChange = useCallback(
  129. (
  130. event: React.SyntheticEvent,
  131. newValue: { value: number; group: string } | { value: number }[],
  132. ) => {
  133. const singleNewVal = newValue as {
  134. value: number;
  135. group: string;
  136. };
  137. // console.log(singleNewVal);
  138. // console.log("onChange");
  139. // setValue("warehouseId", singleNewVal.value);
  140. setWarehouseId(singleNewVal.value);
  141. },
  142. [],
  143. );
  144. // const accQty = watch("acceptedQty");
  145. // const validateForm = useCallback(() => {
  146. // console.log(accQty);
  147. // if (accQty > itemDetail.acceptedQty) {
  148. // setError("acceptedQty", {
  149. // message: `acceptedQty must not greater than ${itemDetail.acceptedQty}`,
  150. // type: "required",
  151. // });
  152. // }
  153. // if (accQty < 1) {
  154. // setError("acceptedQty", {
  155. // message: `minimal value is 1`,
  156. // type: "required",
  157. // });
  158. // }
  159. // if (isNaN(accQty)) {
  160. // setError("acceptedQty", {
  161. // message: `value must be a number`,
  162. // type: "required",
  163. // });
  164. // }
  165. // }, [accQty]);
  166. // useEffect(() => {
  167. // clearErrors();
  168. // validateForm();
  169. // }, [validateForm]);
  170. useEffect(() => {
  171. setValue("status", "received");
  172. // setValue("status", "completed");
  173. // setValue("warehouseId", options[0].value); //TODO: save all warehouse entry?
  174. }, []);
  175. useEffect(() => {
  176. if (warehouseId > 0) {
  177. setValue("warehouseId", warehouseId);
  178. clearErrors("warehouseId");
  179. }
  180. }, [warehouseId]);
  181. const getWarningTextHardcode = useCallback((): string | undefined => {
  182. console.log(options)
  183. if (options.length === 0) return undefined
  184. const defaultWarehouseId = options[0].value;
  185. const currWarehouseId = watch("warehouseId");
  186. if (defaultWarehouseId !== currWarehouseId) {
  187. return t("not default warehosue");
  188. }
  189. return undefined;
  190. }, [options]);
  191. const columns = useMemo<GridColDef[]>(
  192. () => [
  193. {
  194. field: "id",
  195. headerName: t(""),
  196. flex: 0.2,
  197. editable: false,
  198. renderCell(params) {
  199. return <span style={{fontSize:18}}>
  200. {`${params.api.getRowIndexRelativeToVisibleRows(params.id) + 1}.`}
  201. </span>
  202. },
  203. },
  204. {
  205. field: "putawayDate",
  206. headerName: t("putawayDatetime"),
  207. flex: 1,
  208. editable: false,
  209. renderCell(params) {
  210. return <span style={{fontSize:24}}>
  211. {`${(arrayToDateTimeString(params.value))}`}
  212. </span>
  213. },
  214. },
  215. {
  216. field: "putawayUser",
  217. headerName: t("putawayUser"),
  218. flex: 1,
  219. editable: false,
  220. renderCell(params) {
  221. return <span style={{fontSize:24}}>
  222. {params.value}
  223. </span>
  224. }
  225. },
  226. {
  227. field: "qty",
  228. headerName: t("putawayQty"),
  229. flex: 0.5,
  230. editable: false,
  231. headerAlign: "right",
  232. align: "right",
  233. renderCell(params) {
  234. return <span style={{fontSize:24}}>
  235. {params.value}
  236. </span>
  237. }
  238. },
  239. {
  240. field: "warehouse",
  241. headerName: t("warehouse"),
  242. flex: 2,
  243. editable: false,
  244. renderCell(params) {
  245. return <span style={{fontSize:24}}>
  246. {params.value}
  247. </span>
  248. }
  249. // renderEditCell: (params) => {
  250. // const index = params.api.getRowIndexRelativeToVisibleRows(params.row.id)
  251. // // console.log(index)
  252. // // console.log(watch(`putAwayLines.${index}.warehouse`))
  253. // return <Autocomplete
  254. // fullWidth
  255. // disableClearable
  256. // options={options}
  257. // // defaultValue={options.find((o) => o.value === itemDetail.defaultWarehouseId)}
  258. // // value={options.find((o) => o.value === watch(`putAwayLines.${index}.warehouseId`))}
  259. // defaultValue={options.find((o) => o.value === watch(`putAwayLines.${index}.warehouseId`))}
  260. // onChange={(event, value) => {
  261. // params.api.setEditCellValue({ id: params.id, field: params.field, value: options.find((o) => o.value === value.value)?.label ?? ""})
  262. // params.api.setEditCellValue({ id: params.id, field: "warehouseId", value: value.value})
  263. // // setValue(`putAwayLines.${index}.warehouseId`, value.value)
  264. // // setValue(`putAwayLines.${index}.warehouse`, options.find((o) => o.value === value.value)?.label ?? "")
  265. // }}
  266. // sx={{
  267. // // Style the dropdown options
  268. // "& .MuiAutocomplete-option": {
  269. // fontSize: 24,
  270. // },
  271. // }}
  272. // renderInput={(params) => (
  273. // <TextField
  274. // {...params}
  275. // variant="outlined"
  276. // InputProps={{style: {fontSize: 24}}}
  277. // // label={t("Warehouse")}
  278. // />
  279. // )}
  280. // />
  281. // }
  282. // renderCell(params) {
  283. // return <>{filteredWarehouse[0].name}</>
  284. // },
  285. },
  286. // {
  287. // field: "printQty",
  288. // headerName: t("printQty"),
  289. // flex: 1,
  290. // editable: false,
  291. // renderCell(params) {
  292. // return <>100</>
  293. // },
  294. // },
  295. ], [])
  296. const validation = useCallback(
  297. (newRow: GridRowModel<PutAwayRow>): EntryError => {
  298. const error: EntryError = {};
  299. const { qty, warehouseId, printQty } = newRow;
  300. return Object.keys(error).length > 0 ? error : undefined;
  301. },
  302. [],
  303. );
  304. const addRowDefaultValue = useMemo(() => {
  305. const defaultMaxQty = Number(itemDetail.demandQty?? itemDetail.acceptedQty)//watch("acceptedQty")
  306. - watch("putAwayLines").reduce((acc, cur) => acc + cur.qty, 0)
  307. const defaultWarehouseId = itemDetail.defaultWarehouseId ?? 1
  308. const defaultWarehouse = "W001 - 憶兆 3樓A倉"//options.find((o) => o.value === defaultWarehouseId)?.label
  309. return {qty: defaultMaxQty, warehouseId: defaultWarehouseId, warehouse: defaultWarehouse, printQty: 1, _isNew: true } as Partial<PutAwayLine>
  310. }, [])
  311. return (
  312. <Grid container justifyContent="flex-start" alignItems="flex-start">
  313. <Grid item xs={12}>
  314. <Typography variant="h6" display="block" marginBlockEnd={1}>
  315. {t("Putaway Detail")}
  316. </Typography>
  317. </Grid>
  318. <Grid
  319. container
  320. justifyContent="flex-start"
  321. alignItems="flex-start"
  322. spacing={2}
  323. sx={{ mt: 0.5 }}
  324. >
  325. {/* <Grid item xs={6}>
  326. <TextField
  327. label={t("Supplier")}
  328. fullWidth
  329. value={itemDetail.supplier}
  330. disabled
  331. />
  332. </Grid> */}
  333. {/* <Grid item xs={6}>
  334. <TextField
  335. label={t("Po Code")}
  336. fullWidth
  337. value={itemDetail.poCode}
  338. disabled
  339. />
  340. </Grid> */}
  341. <Grid item xs={6}>
  342. <TextField
  343. label={t("itemNo")}
  344. fullWidth
  345. value={itemDetail.itemNo}
  346. disabled
  347. />
  348. </Grid>
  349. <Grid item xs={6}>
  350. <TextField
  351. label={t("itemName")}
  352. fullWidth
  353. value={itemDetail.itemName}
  354. disabled
  355. />
  356. </Grid>
  357. <Grid item xs={6}>
  358. <TextField
  359. label={t("stockLotNo")}
  360. fullWidth
  361. value={itemDetail.lotNo}
  362. disabled
  363. />
  364. </Grid>
  365. <Grid item xs={6}>
  366. <TextField
  367. label={t("expiryDate")}
  368. fullWidth
  369. value={arrayToDateString(itemDetail.expiryDate, "output")}
  370. disabled
  371. />
  372. </Grid>
  373. <Grid item xs={3}>
  374. <TextField
  375. label={t("acceptedPutawayQty")} // TODO: fix it back to acceptedQty after db is fixed
  376. fullWidth
  377. value={itemDetail.demandQty ?? itemDetail.acceptedQty}
  378. disabled
  379. />
  380. </Grid>
  381. <Grid item xs={3}>
  382. <TextField
  383. label={t("uom")}
  384. fullWidth
  385. value={itemDetail.uom?.udfudesc}
  386. disabled
  387. />
  388. </Grid>
  389. {/* <Grid item xs={6}>
  390. <TextField
  391. label={t("productionDate")}
  392. fullWidth
  393. value={
  394. // dayjs(itemDetail.productionDate)
  395. dayjs()
  396. // .add(-1, "month")
  397. .format(OUTPUT_DATE_FORMAT)}
  398. disabled
  399. />
  400. </Grid> */}
  401. <Grid item xs={6}>
  402. <FormControl fullWidth>
  403. <Autocomplete
  404. noOptionsText={t("No Warehouse")}
  405. disableClearable
  406. disabled
  407. fullWidth
  408. defaultValue={options[0]} /// modify this later
  409. // onChange={onChange}
  410. getOptionLabel={(option) => option.label}
  411. options={options}
  412. renderInput={(params) => (
  413. <TextField {...params} label={t("Default Warehouse")} />
  414. )}
  415. />
  416. </FormControl>
  417. </Grid>
  418. {/* <Grid item xs={5.5}>
  419. <TextField
  420. label={t("acceptedQty")}
  421. fullWidth
  422. {...register("acceptedQty", {
  423. required: "acceptedQty required!",
  424. min: 1,
  425. max: itemDetail.acceptedQty,
  426. valueAsNumber: true,
  427. })}
  428. // defaultValue={itemDetail.acceptedQty}
  429. disabled={disabled}
  430. error={Boolean(errors.acceptedQty)}
  431. helperText={errors.acceptedQty?.message}
  432. />
  433. </Grid>
  434. <Grid item xs={1}>
  435. <Button disabled={disabled} onClick={onOpenScanner}>
  436. {t("bind")}
  437. </Button>
  438. </Grid> */}
  439. {/* <Grid item xs={5.5}>
  440. <Controller
  441. control={control}
  442. name="warehouseId"
  443. render={({ field }) => {
  444. console.log(field);
  445. return (
  446. <Autocomplete
  447. noOptionsText={t("No Warehouse")}
  448. disableClearable
  449. fullWidth
  450. value={options.find((o) => o.value == field.value)}
  451. onChange={onChange}
  452. getOptionLabel={(option) => option.label}
  453. options={options}
  454. renderInput={(params) => (
  455. <TextField
  456. {...params}
  457. label={"Select warehouse"}
  458. error={Boolean(errors.warehouseId?.message)}
  459. helperText={warehouseHelperText}
  460. // helperText={errors.warehouseId?.message}
  461. />
  462. )}
  463. />
  464. );
  465. }}
  466. />
  467. <FormControl fullWidth>
  468. <Autocomplete
  469. noOptionsText={t("No Warehouse")}
  470. disableClearable
  471. fullWidth
  472. // value={warehouseId > 0
  473. // ? options.find((o) => o.value === warehouseId)
  474. // : undefined}
  475. defaultValue={options[0]}
  476. // defaultValue={options.find((o) => o.value === 1)}
  477. value={currentValue}
  478. onChange={onChange}
  479. getOptionLabel={(option) => option.label}
  480. options={options}
  481. renderInput={(params) => (
  482. <TextField
  483. {...params}
  484. // label={"Select warehouse"}
  485. disabled={disabled}
  486. error={Boolean(errors.warehouseId?.message)}
  487. helperText={
  488. errors.warehouseId?.message ?? getWarningTextHardcode()
  489. }
  490. // helperText={warehouseHelperText}
  491. />
  492. )}
  493. />
  494. </FormControl>
  495. </Grid> */}
  496. <Grid
  497. item
  498. xs={12}
  499. style={{ display: "flex", justifyContent: "center", marginTop:5 }}
  500. >
  501. {/* <QrCode content={qrContent} sx={{ width: 200, height: 200 }} /> */}
  502. <InputDataGrid<PutAwayInput, PutAwayLine, EntryError>
  503. apiRef={apiRef}
  504. checkboxSelection={false}
  505. _formKey={"putAwayLines"}
  506. columns={columns}
  507. validateRow={validation}
  508. needAdd={false}
  509. needActions={false}
  510. showRemoveBtn={false}
  511. addRowDefaultValue={addRowDefaultValue}
  512. _setRowModesModel={setRowModesModel}
  513. _setRowSelectionModel={setRowSelectionModel}
  514. />
  515. </Grid>
  516. </Grid>
  517. </Grid>
  518. );
  519. };
  520. export default PutAwayForm;