FPSMS-frontend
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 

573 líneas
17 KiB

  1. "use client";
  2. import { PurchaseQcResult, PutAwayInput, PutAwayLine } from "@/app/api/po/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/po";
  39. import { WarehouseResult } from "@/app/api/warehouse";
  40. import {
  41. OUTPUT_DATE_FORMAT,
  42. stockInLineStatusMap,
  43. } from "@/app/utils/formatUtil";
  44. import { QRCodeSVG } from "qrcode.react";
  45. import { QrCode } from "../QrCode";
  46. import ReactQrCodeScanner, {
  47. ScannerConfig,
  48. } from "../ReactQrCodeScanner/ReactQrCodeScanner";
  49. import { QrCodeInfo } from "@/app/api/qrcode";
  50. import { useQrCodeScannerContext } from "../QrCodeScannerProvider/QrCodeScannerProvider";
  51. import dayjs from "dayjs";
  52. import arraySupport from "dayjs/plugin/arraySupport";
  53. import { dummyPutAwayLine } from "./dummyQcTemplate";
  54. import { GridRowModesModel } from "@mui/x-data-grid";
  55. dayjs.extend(arraySupport);
  56. interface Props {
  57. itemDetail: StockInLine;
  58. warehouse: WarehouseResult[];
  59. disabled: boolean;
  60. // qc: QcItemWithChecks[];
  61. setRowModesModel: Dispatch<SetStateAction<GridRowModesModel>>;
  62. setRowSelectionModel: Dispatch<SetStateAction<GridRowSelectionModel>>;
  63. }
  64. type EntryError =
  65. | {
  66. [field in keyof PutAwayLine]?: string;
  67. }
  68. | undefined;
  69. type PutAwayRow = TableRow<Partial<PutAwayLine>, EntryError>;
  70. const style = {
  71. position: "absolute",
  72. top: "50%",
  73. left: "50%",
  74. transform: "translate(-50%, -50%)",
  75. bgcolor: "background.paper",
  76. pt: 5,
  77. px: 5,
  78. pb: 10,
  79. width: "auto",
  80. };
  81. const PutAwayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled, setRowModesModel, setRowSelectionModel }) => {
  82. const { t } = useTranslation("purchaseOrder");
  83. const apiRef = useGridApiRef();
  84. const {
  85. register,
  86. formState: { errors, defaultValues, touchedFields },
  87. watch,
  88. control,
  89. setValue,
  90. getValues,
  91. reset,
  92. resetField,
  93. setError,
  94. clearErrors,
  95. } = useFormContext<PutAwayInput>();
  96. console.log(itemDetail);
  97. // const [recordQty, setRecordQty] = useState(0);
  98. const [warehouseId, setWarehouseId] = useState(itemDetail.defaultWarehouseId);
  99. const filteredWarehouse = useMemo(() => {
  100. // do filtering here if any
  101. return warehouse;
  102. }, []);
  103. const defaultOption = {
  104. value: 0, // think think sin
  105. label: t("Select warehouse"),
  106. group: "default",
  107. };
  108. const options = useMemo(() => {
  109. return [
  110. // {
  111. // value: 0, // think think sin
  112. // label: t("Select warehouse"),
  113. // group: "default",
  114. // },
  115. ...filteredWarehouse.map((w) => ({
  116. value: w.id,
  117. label: `${w.code} - ${w.name}`,
  118. group: "existing",
  119. })),
  120. ];
  121. }, [filteredWarehouse]);
  122. const currentValue =
  123. warehouseId > 0
  124. ? options.find((o) => o.value === warehouseId)
  125. : options.find((o) => o.value === getValues("warehouseId")) ||
  126. defaultOption;
  127. const onChange = useCallback(
  128. (
  129. event: React.SyntheticEvent,
  130. newValue: { value: number; group: string } | { value: number }[],
  131. ) => {
  132. const singleNewVal = newValue as {
  133. value: number;
  134. group: string;
  135. };
  136. console.log(singleNewVal);
  137. console.log("onChange");
  138. // setValue("warehouseId", singleNewVal.value);
  139. setWarehouseId(singleNewVal.value);
  140. },
  141. [],
  142. );
  143. console.log(watch("putAwayLines"))
  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. const qrContent = useMemo(
  171. () => ({
  172. stockInLineId: itemDetail.id,
  173. itemId: itemDetail.itemId,
  174. lotNo: itemDetail.lotNo,
  175. // warehouseId: 2 // for testing
  176. // expiryDate: itemDetail.expiryDate,
  177. // productionDate: itemDetail.productionDate,
  178. // supplier: itemDetail.supplier,
  179. // poCode: itemDetail.poCode,
  180. }),
  181. [itemDetail],
  182. );
  183. const [isOpenScanner, setOpenScanner] = useState(false);
  184. const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>(
  185. (...args) => {
  186. setOpenScanner(false);
  187. },
  188. [],
  189. );
  190. const onOpenScanner = useCallback(() => {
  191. setOpenScanner(true);
  192. }, []);
  193. const onCloseScanner = useCallback(() => {
  194. setOpenScanner(false);
  195. }, []);
  196. const scannerConfig = useMemo<ScannerConfig>(
  197. () => ({
  198. onUpdate: (err, result) => {
  199. console.log(result);
  200. console.log(Boolean(result));
  201. if (result) {
  202. const data: QrCodeInfo = JSON.parse(result.getText());
  203. console.log(data);
  204. if (data.warehouseId) {
  205. console.log(data.warehouseId);
  206. setWarehouseId(data.warehouseId);
  207. onCloseScanner();
  208. }
  209. } else return;
  210. },
  211. }),
  212. [onCloseScanner],
  213. );
  214. // QR Code Scanner
  215. const scanner = useQrCodeScannerContext();
  216. useEffect(() => {
  217. if (isOpenScanner) {
  218. scanner.startScan();
  219. } else if (!isOpenScanner) {
  220. scanner.stopScan();
  221. }
  222. }, [isOpenScanner]);
  223. useEffect(() => {
  224. if (scanner.values.length > 0) {
  225. console.log(scanner.values[0]);
  226. const data: QrCodeInfo = JSON.parse(scanner.values[0]);
  227. console.log(data);
  228. if (data.warehouseId) {
  229. console.log(data.warehouseId);
  230. setWarehouseId(data.warehouseId);
  231. onCloseScanner();
  232. }
  233. scanner.resetScan();
  234. }
  235. }, [scanner.values]);
  236. useEffect(() => {
  237. setValue("status", "received");
  238. // setValue("status", "completed");
  239. setValue("warehouseId", options[0].value); //TODO: save all warehouse entry?
  240. }, []);
  241. useEffect(() => {
  242. if (warehouseId > 0) {
  243. setValue("warehouseId", warehouseId);
  244. clearErrors("warehouseId");
  245. }
  246. }, [warehouseId]);
  247. const getWarningTextHardcode = useCallback((): string | undefined => {
  248. console.log(options)
  249. if (options.length === 0) return undefined
  250. const defaultWarehouseId = options[0].value;
  251. const currWarehouseId = watch("warehouseId");
  252. if (defaultWarehouseId !== currWarehouseId) {
  253. return t("not default warehosue");
  254. }
  255. return undefined;
  256. }, [options]);
  257. const columns = useMemo<GridColDef[]>(
  258. () => [
  259. {
  260. field: "qty",
  261. headerName: t("qty"),
  262. flex: 1,
  263. editable: true,
  264. // renderCell(params) {
  265. // return <>100</>
  266. // },
  267. },
  268. {
  269. field: "warehouse",
  270. headerName: t("warehouse"),
  271. flex: 1,
  272. editable: true,
  273. renderEditCell: (params) => {
  274. const index = params.api.getRowIndexRelativeToVisibleRows(params.row.id)
  275. // console.log(index)
  276. // console.log(watch(`putAwayLines.${index}.warehouse`))
  277. return <Autocomplete
  278. fullWidth
  279. disableClearable
  280. options={options}
  281. // defaultValue={options.find((o) => o.value === itemDetail.defaultWarehouseId)}
  282. // value={options.find((o) => o.value === watch(`putAwayLines.${index}.warehouseId`))}
  283. defaultValue={options.find((o) => o.value === watch(`putAwayLines.${index}.warehouseId`))}
  284. onChange={(event, value) => {
  285. params.api.setEditCellValue({ id: params.id, field: params.field, value: options.find((o) => o.value === value.value)?.label ?? ""})
  286. params.api.setEditCellValue({ id: params.id, field: "warehouseId", value: value.value})
  287. // setValue(`putAwayLines.${index}.warehouseId`, value.value)
  288. // setValue(`putAwayLines.${index}.warehouse`, options.find((o) => o.value === value.value)?.label ?? "")
  289. }}
  290. renderInput={(params) => (
  291. <TextField
  292. {...params}
  293. variant="outlined"
  294. // label={t("Warehouse")}
  295. />
  296. )}
  297. />
  298. }
  299. // renderCell(params) {
  300. // return <>{filteredWarehouse[0].name}</>
  301. // },
  302. },
  303. {
  304. field: "printQty",
  305. headerName: t("printQty"),
  306. flex: 1,
  307. editable: true,
  308. // renderCell(params) {
  309. // return <>100</>
  310. // },
  311. },
  312. ], [])
  313. const validation = useCallback(
  314. (newRow: GridRowModel<PutAwayRow>): EntryError => {
  315. const error: EntryError = {};
  316. const { qty, warehouseId, printQty } = newRow;
  317. return Object.keys(error).length > 0 ? error : undefined;
  318. },
  319. [],
  320. );
  321. const addRowDefaultValue = useMemo(() => {
  322. const defaultMaxQty = Number(itemDetail.demandQty?? itemDetail.acceptedQty)//watch("acceptedQty")
  323. - watch("putAwayLines").reduce((acc, cur) => acc + cur.qty, 0)
  324. const defaultWarehouseId = itemDetail.defaultWarehouseId ?? 1
  325. const defaultWarehouse = options.find((o) => o.value === defaultWarehouseId)?.label
  326. return {qty: defaultMaxQty, warehouseId: defaultWarehouseId, warehouse: defaultWarehouse, printQty: 1, _isNew: true } as Partial<PutAwayLine>
  327. }, [])
  328. return (
  329. <Grid container justifyContent="flex-start" alignItems="flex-start">
  330. <Grid item xs={12}>
  331. <Typography variant="h6" display="block" marginBlockEnd={1}>
  332. {t("Putaway Detail")}
  333. </Typography>
  334. </Grid>
  335. <Grid
  336. container
  337. justifyContent="flex-start"
  338. alignItems="flex-start"
  339. spacing={2}
  340. sx={{ mt: 0.5 }}
  341. >
  342. <Grid item xs={12}>
  343. <TextField
  344. label={t("LotNo")}
  345. fullWidth
  346. value={itemDetail.lotNo}
  347. disabled
  348. />
  349. </Grid>
  350. <Grid item xs={6}>
  351. <TextField
  352. label={t("Supplier")}
  353. fullWidth
  354. value={itemDetail.supplier}
  355. disabled
  356. />
  357. </Grid>
  358. <Grid item xs={6}>
  359. <TextField
  360. label={t("Po Code")}
  361. fullWidth
  362. value={itemDetail.poCode}
  363. disabled
  364. />
  365. </Grid>
  366. <Grid item xs={6}>
  367. <TextField
  368. label={t("itemName")}
  369. fullWidth
  370. value={itemDetail.itemName}
  371. disabled
  372. />
  373. </Grid>
  374. <Grid item xs={6}>
  375. <TextField
  376. label={t("itemNo")}
  377. fullWidth
  378. value={itemDetail.itemNo}
  379. disabled
  380. />
  381. </Grid>
  382. <Grid item xs={6}>
  383. <TextField
  384. label={t("qty")}
  385. fullWidth
  386. value={itemDetail.acceptedQty}
  387. disabled
  388. />
  389. </Grid>
  390. <Grid item xs={6}>
  391. <TextField
  392. label={t("productionDate")}
  393. fullWidth
  394. value={
  395. // dayjs(itemDetail.productionDate)
  396. dayjs()
  397. // .add(-1, "month")
  398. .format(OUTPUT_DATE_FORMAT)}
  399. disabled
  400. />
  401. </Grid>
  402. <Grid item xs={6}>
  403. <TextField
  404. label={t("expiryDate")}
  405. fullWidth
  406. value={
  407. // dayjs(itemDetail.expiryDate)
  408. dayjs()
  409. .add(20, "day")
  410. .format(OUTPUT_DATE_FORMAT)}
  411. disabled
  412. />
  413. </Grid>
  414. <Grid item xs={6}>
  415. <FormControl fullWidth>
  416. <Autocomplete
  417. noOptionsText={t("No Warehouse")}
  418. disableClearable
  419. disabled
  420. fullWidth
  421. defaultValue={options[0]} /// modify this later
  422. // onChange={onChange}
  423. getOptionLabel={(option) => option.label}
  424. options={options}
  425. renderInput={(params) => (
  426. <TextField {...params} label={t("Default Warehouse")} />
  427. )}
  428. />
  429. </FormControl>
  430. </Grid>
  431. {/* <Grid item xs={5.5}>
  432. <TextField
  433. label={t("acceptedQty")}
  434. fullWidth
  435. {...register("acceptedQty", {
  436. required: "acceptedQty required!",
  437. min: 1,
  438. max: itemDetail.acceptedQty,
  439. valueAsNumber: true,
  440. })}
  441. // defaultValue={itemDetail.acceptedQty}
  442. disabled={disabled}
  443. error={Boolean(errors.acceptedQty)}
  444. helperText={errors.acceptedQty?.message}
  445. />
  446. </Grid>
  447. <Grid item xs={1}>
  448. <Button disabled={disabled} onClick={onOpenScanner}>
  449. {t("bind")}
  450. </Button>
  451. </Grid> */}
  452. {/* <Grid item xs={5.5}>
  453. <Controller
  454. control={control}
  455. name="warehouseId"
  456. render={({ field }) => {
  457. console.log(field);
  458. return (
  459. <Autocomplete
  460. noOptionsText={t("No Warehouse")}
  461. disableClearable
  462. fullWidth
  463. value={options.find((o) => o.value == field.value)}
  464. onChange={onChange}
  465. getOptionLabel={(option) => option.label}
  466. options={options}
  467. renderInput={(params) => (
  468. <TextField
  469. {...params}
  470. label={"Select warehouse"}
  471. error={Boolean(errors.warehouseId?.message)}
  472. helperText={warehouseHelperText}
  473. // helperText={errors.warehouseId?.message}
  474. />
  475. )}
  476. />
  477. );
  478. }}
  479. />
  480. <FormControl fullWidth>
  481. <Autocomplete
  482. noOptionsText={t("No Warehouse")}
  483. disableClearable
  484. fullWidth
  485. // value={warehouseId > 0
  486. // ? options.find((o) => o.value === warehouseId)
  487. // : undefined}
  488. defaultValue={options[0]}
  489. // defaultValue={options.find((o) => o.value === 1)}
  490. value={currentValue}
  491. onChange={onChange}
  492. getOptionLabel={(option) => option.label}
  493. options={options}
  494. renderInput={(params) => (
  495. <TextField
  496. {...params}
  497. // label={"Select warehouse"}
  498. disabled={disabled}
  499. error={Boolean(errors.warehouseId?.message)}
  500. helperText={
  501. errors.warehouseId?.message ?? getWarningTextHardcode()
  502. }
  503. // helperText={warehouseHelperText}
  504. />
  505. )}
  506. />
  507. </FormControl>
  508. </Grid> */}
  509. <Grid
  510. item
  511. xs={12}
  512. style={{ display: "flex", justifyContent: "center" }}
  513. >
  514. {/* <QrCode content={qrContent} sx={{ width: 200, height: 200 }} /> */}
  515. <InputDataGrid<PutAwayInput, PutAwayLine, EntryError>
  516. apiRef={apiRef}
  517. checkboxSelection={true}
  518. _formKey={"putAwayLines"}
  519. columns={columns}
  520. validateRow={validation}
  521. needAdd={false}
  522. showRemoveBtn={false}
  523. addRowDefaultValue={addRowDefaultValue}
  524. _setRowModesModel={setRowModesModel}
  525. _setRowSelectionModel={setRowSelectionModel}
  526. />
  527. </Grid>
  528. </Grid>
  529. {/* <Grid
  530. container
  531. justifyContent="flex-start"
  532. alignItems="flex-start"
  533. spacing={2}
  534. sx={{ mt: 0.5 }}
  535. >
  536. <Button onClick={onOpenScanner}>bind</Button>
  537. </Grid> */}
  538. <Modal open={isOpenScanner} onClose={closeHandler}>
  539. <Box sx={style}>
  540. <Typography variant="h4">
  541. {t("Please scan warehouse qr code.")}
  542. </Typography>
  543. {/* <ReactQrCodeScanner scannerConfig={scannerConfig} /> */}
  544. </Box>
  545. </Modal>
  546. </Grid>
  547. );
  548. };
  549. export default PutAwayForm;