FPSMS-frontend
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

1011 行
30 KiB

  1. "use client";
  2. import {
  3. FooterPropsOverrides,
  4. GridActionsCellItem,
  5. GridCellParams,
  6. GridRowId,
  7. GridRowIdGetter,
  8. GridRowModel,
  9. GridRowModes,
  10. GridRowModesModel,
  11. GridToolbarContainer,
  12. useGridApiRef,
  13. } from "@mui/x-data-grid";
  14. import {
  15. Dispatch,
  16. MutableRefObject,
  17. SetStateAction,
  18. useCallback,
  19. useEffect,
  20. useMemo,
  21. useState,
  22. } from "react";
  23. import StyledDataGrid from "../StyledDataGrid";
  24. import { GridColDef } from "@mui/x-data-grid";
  25. import { Box, Button, Grid, Typography } from "@mui/material";
  26. import { useTranslation } from "react-i18next";
  27. import { Add } from "@mui/icons-material";
  28. import SaveIcon from "@mui/icons-material/Save";
  29. import DeleteIcon from "@mui/icons-material/Delete";
  30. import CancelIcon from "@mui/icons-material/Cancel";
  31. import FactCheckIcon from "@mui/icons-material/FactCheck";
  32. import ShoppingCartIcon from "@mui/icons-material/ShoppingCart";
  33. import { QcItemWithChecks } from "src/app/api/qc";
  34. import PlayArrowIcon from "@mui/icons-material/PlayArrow";
  35. import { PurchaseOrderLine, StockInLine } from "@/app/api/po";
  36. import { createStockInLine, PurchaseQcResult } from "@/app/api/po/actions";
  37. import { usePathname, useRouter, useSearchParams } from "next/navigation";
  38. import {
  39. returnWeightUnit,
  40. calculateWeight,
  41. stockInLineStatusMap,
  42. arrayToDateString,
  43. } from "@/app/utils/formatUtil";
  44. // import PoQcStockInModal from "./PoQcStockInModal";
  45. import NotificationImportantIcon from "@mui/icons-material/NotificationImportant";
  46. import { WarehouseResult } from "@/app/api/warehouse";
  47. import LooksOneIcon from "@mui/icons-material/LooksOne";
  48. import LooksTwoIcon from "@mui/icons-material/LooksTwo";
  49. import Looks3Icon from "@mui/icons-material/Looks3";
  50. import axiosInstance from "@/app/(main)/axios/axiosInstance";
  51. // import axios, { AxiosRequestConfig } from "axios";
  52. import { BASE_API_URL, NEXT_PUBLIC_API_URL } from "@/config/api";
  53. import qs from "qs";
  54. import QrCodeIcon from "@mui/icons-material/QrCode";
  55. import { downloadFile } from "@/app/utils/commonUtil";
  56. import { fetchPoQrcode } from "@/app/api/pdf/actions";
  57. import { fetchQcResult } from "@/app/api/qc/actions";
  58. import PoQcStockInModal from "./PoQcStockInModal";
  59. import DoDisturbIcon from "@mui/icons-material/DoDisturb";
  60. import { useSession } from "next-auth/react";
  61. import PoQcStockInModalVer2 from "./QcStockInModalVer2";
  62. import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil";
  63. interface ResultWithId {
  64. id: number;
  65. }
  66. interface Props {
  67. qc: QcItemWithChecks[];
  68. setRows: Dispatch<SetStateAction<PurchaseOrderLine[]>>;
  69. setStockInLine: Dispatch<SetStateAction<StockInLine[]>>;
  70. setProcessedQty: Dispatch<SetStateAction<number>>;
  71. itemDetail: PurchaseOrderLine;
  72. stockInLine: StockInLine[];
  73. warehouse: WarehouseResult[];
  74. fetchPoDetail: (poId: string) => void;
  75. }
  76. export type StockInLineEntryError = {
  77. [field in keyof StockInLine]?: string;
  78. };
  79. export type StockInLineRow = Partial<
  80. StockInLine & {
  81. isActive: boolean | undefined;
  82. _isNew: boolean;
  83. _error: StockInLineEntryError;
  84. } & ResultWithId
  85. >;
  86. class ProcessRowUpdateError extends Error {
  87. public readonly row: StockInLineRow;
  88. public readonly errors: StockInLineEntryError | undefined;
  89. constructor(
  90. row: StockInLineRow,
  91. message?: string,
  92. errors?: StockInLineEntryError,
  93. ) {
  94. super(message);
  95. this.row = row;
  96. this.errors = errors;
  97. Object.setPrototypeOf(this, ProcessRowUpdateError.prototype);
  98. }
  99. }
  100. function PoInputGrid({
  101. qc,
  102. setRows,
  103. setStockInLine,
  104. setProcessedQty,
  105. itemDetail,
  106. stockInLine,
  107. warehouse,
  108. fetchPoDetail
  109. }: Props) {
  110. console.log(itemDetail);
  111. const { t } = useTranslation("purchaseOrder");
  112. const apiRef = useGridApiRef();
  113. const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});
  114. const getRowId = useCallback<GridRowIdGetter<StockInLineRow>>(
  115. (row) => row.id as number,
  116. [],
  117. );
  118. console.log(stockInLine);
  119. const [entries, setEntries] = useState<StockInLineRow[]>(stockInLine || []);
  120. useEffect(() => {
  121. setEntries(stockInLine)
  122. }, [stockInLine])
  123. const [modalInfo, setModalInfo] = useState<
  124. StockInLine & { qcResult?: PurchaseQcResult[] }
  125. >();
  126. const pathname = usePathname()
  127. const router = useRouter();
  128. const searchParams = useSearchParams();
  129. const [qcOpen, setQcOpen] = useState(false);
  130. const [escalOpen, setEscalOpen] = useState(false);
  131. const [stockInOpen, setStockInOpen] = useState(false);
  132. const [putAwayOpen, setPutAwayOpen] = useState(false);
  133. const [rejectOpen, setRejectOpen] = useState(false);
  134. const [btnIsLoading, setBtnIsLoading] = useState(false);
  135. const [currQty, setCurrQty] = useState(() => {
  136. const total = entries.reduce(
  137. (acc, curr) => acc + (curr.acceptedQty || 0),
  138. 0,
  139. );
  140. return total;
  141. });
  142. const { data: session } = useSession();
  143. useEffect(() => {
  144. const completedList = entries.filter(
  145. (e) => stockInLineStatusMap[e.status!] >= 8,
  146. );
  147. const processedQty = completedList.reduce(
  148. (acc, curr) => acc + (curr.acceptedQty || 0),
  149. 0,
  150. );
  151. setProcessedQty(processedQty);
  152. }, [entries, setProcessedQty]);
  153. const handleDelete = useCallback(
  154. (id: GridRowId) => () => {
  155. setEntries((es) => es.filter((e) => getRowId(e) !== id));
  156. },
  157. [getRowId],
  158. );
  159. const closeQcModal = useCallback(() => {
  160. setQcOpen(false);
  161. }, []);
  162. const openQcModal = useCallback(() => {
  163. setQcOpen(true);
  164. }, []);
  165. const closeStockInModal = useCallback(() => {
  166. setStockInOpen(false);
  167. }, []);
  168. const openStockInModal = useCallback(() => {
  169. setStockInOpen(true);
  170. }, []);
  171. const closePutAwayModal = useCallback(() => {
  172. setPutAwayOpen(false);
  173. }, []);
  174. const openPutAwayModal = useCallback(() => {
  175. setPutAwayOpen(true);
  176. }, []);
  177. const closeEscalationModal = useCallback(() => {
  178. setEscalOpen(false);
  179. }, []);
  180. const openEscalationModal = useCallback(() => {
  181. setEscalOpen(true);
  182. }, []);
  183. const closeRejectModal = useCallback(() => {
  184. setRejectOpen(false);
  185. }, []);
  186. const openRejectModal = useCallback(() => {
  187. setRejectOpen(true);
  188. }, []);
  189. const handleStart = useCallback(
  190. (id: GridRowId, params: any) => () => {
  191. setBtnIsLoading(true);
  192. setRowModesModel((prev) => ({
  193. ...prev,
  194. [id]: { mode: GridRowModes.View },
  195. }));
  196. setTimeout(async () => {
  197. // post stock in line
  198. const oldId = params.row.id;
  199. const postData = {
  200. itemId: params.row.itemId,
  201. itemNo: params.row.itemNo,
  202. itemName: params.row.itemName,
  203. purchaseOrderId: params.row.purchaseOrderId,
  204. purchaseOrderLineId: params.row.purchaseOrderLineId,
  205. acceptedQty: params.row.acceptedQty,
  206. };
  207. const res = await createStockInLine(postData);
  208. console.log(res);
  209. setEntries((prev) =>
  210. prev.map((p) => (p.id === oldId ? (res.entity as StockInLine) : p)),
  211. );
  212. setStockInLine(
  213. (prev) =>
  214. prev.map((p) =>
  215. p.id === oldId ? (res.entity as StockInLine) : p,
  216. ) as StockInLine[],
  217. );
  218. setBtnIsLoading(false);
  219. // do post directly to test
  220. // openStartModal();
  221. }, 200);
  222. },
  223. [setStockInLine],
  224. );
  225. const fetchQcDefaultValue = useCallback(async (stockInLineId: GridRowId) => {
  226. return await fetchQcResult(stockInLineId as number);
  227. }, []);
  228. const handleQC = useCallback(
  229. (id: GridRowId, params: any) => async () => {
  230. setBtnIsLoading(true);
  231. setRowModesModel((prev) => ({
  232. ...prev,
  233. [id]: { mode: GridRowModes.View },
  234. }));
  235. const qcResult = await fetchQcDefaultValue(id);
  236. console.log(params.row);
  237. console.log(qcResult);
  238. setModalInfo({
  239. ...params.row,
  240. qcResult: qcResult,
  241. });
  242. // set default values
  243. setTimeout(() => {
  244. // open qc modal
  245. console.log("delayed");
  246. openQcModal();
  247. setBtnIsLoading(false);
  248. }, 200);
  249. },
  250. [fetchQcDefaultValue, openQcModal],
  251. );
  252. const [newOpen, setNewOpen] = useState(false);
  253. const stockInLineId = searchParams.get("stockInLineId");
  254. const closeNewModal = useCallback(() => {
  255. const newParams = new URLSearchParams(searchParams.toString());
  256. newParams.delete("stockInLineId"); // Remove the parameter
  257. router.replace(`${pathname}?${newParams.toString()}`);
  258. fetchPoDetail(itemDetail.purchaseOrderId.toString());
  259. setTimeout(() => {
  260. setNewOpen(false); // Close the modal first
  261. }, 300); // Add a delay to avoid immediate re-trigger of useEffect
  262. }, [searchParams, pathname, router]);
  263. // Open modal
  264. const openNewModal = useCallback(() => {
  265. setNewOpen(true);
  266. }, []);
  267. // Button handler to update the URL and open the modal
  268. const handleNewQC = useCallback(
  269. (id: GridRowId, params: any) => async () => {
  270. // console.log(id)
  271. // console.log(params)
  272. setBtnIsLoading(true);
  273. setRowModesModel((prev) => ({
  274. ...prev,
  275. [id]: { mode: GridRowModes.View },
  276. }));
  277. const qcResult = await fetchQcDefaultValue(id);
  278. setModalInfo({
  279. ...params.row,
  280. qcResult: qcResult,
  281. receivedQty: itemDetail.receivedQty,
  282. });
  283. setTimeout(() => {
  284. const newParams = new URLSearchParams(searchParams.toString());
  285. newParams.set("stockInLineId", id.toString()); // Ensure `set` to avoid duplicates
  286. router.replace(`${pathname}?${newParams.toString()}`);
  287. console.log("hello")
  288. openNewModal()
  289. setBtnIsLoading(false);
  290. }, 200);
  291. },
  292. [fetchQcDefaultValue, openNewModal, pathname, router, searchParams]
  293. );
  294. // Open modal if `stockInLineId` exists in the URL
  295. useEffect(() => {
  296. if (stockInLineId) {
  297. console.log("heeloo")
  298. console.log(stockInLineId)
  299. handleNewQC(stockInLineId, apiRef.current.getRow(stockInLineId));
  300. }
  301. }, [stockInLineId, newOpen, handleNewQC, apiRef]);
  302. const handleEscalation = useCallback(
  303. (id: GridRowId, params: any) => () => {
  304. // setBtnIsLoading(true);
  305. setRowModesModel((prev) => ({
  306. ...prev,
  307. [id]: { mode: GridRowModes.View },
  308. }));
  309. setModalInfo(params.row);
  310. setTimeout(() => {
  311. // open qc modal
  312. console.log("delayed");
  313. openEscalationModal();
  314. // setBtnIsLoading(false);
  315. }, 200);
  316. },
  317. [openEscalationModal],
  318. );
  319. const handleReject = useCallback(
  320. (id: GridRowId, params: any) => () => {
  321. setRowModesModel((prev) => ({
  322. ...prev,
  323. [id]: { mode: GridRowModes.View },
  324. }));
  325. setModalInfo(params.row);
  326. setTimeout(() => {
  327. // open stock in modal
  328. // openPutAwayModal();
  329. // return the record with its status as pending
  330. // update layout
  331. console.log("delayed");
  332. openRejectModal();
  333. // printQrcode(params.row);
  334. }, 200);
  335. },
  336. [openRejectModal],
  337. );
  338. const handleStockIn = useCallback(
  339. (id: GridRowId, params: any) => () => {
  340. // setBtnIsLoading(true);
  341. setRowModesModel((prev) => ({
  342. ...prev,
  343. [id]: { mode: GridRowModes.View },
  344. }));
  345. setModalInfo(params.row);
  346. setTimeout(() => {
  347. // open stock in modal
  348. openStockInModal();
  349. // return the record with its status as pending
  350. // update layout
  351. console.log("delayed");
  352. // setBtnIsLoading(false);
  353. }, 200);
  354. },
  355. [openStockInModal],
  356. );
  357. const handlePutAway = useCallback(
  358. (id: GridRowId, params: any) => () => {
  359. // setBtnIsLoading(true);
  360. setRowModesModel((prev) => ({
  361. ...prev,
  362. [id]: { mode: GridRowModes.View },
  363. }));
  364. setModalInfo(params.row);
  365. setTimeout(() => {
  366. // open stock in modal
  367. openPutAwayModal();
  368. // return the record with its status as pending
  369. // update layout
  370. console.log("delayed");
  371. // setBtnIsLoading(false);
  372. }, 200);
  373. },
  374. [openPutAwayModal],
  375. );
  376. const printQrcode = useCallback(
  377. async (row: any) => {
  378. setBtnIsLoading(true);
  379. console.log(row.id);
  380. const postData = { stockInLineIds: [row.id] };
  381. // const postData = { stockInLineIds: [42,43,44] };
  382. const response = await fetchPoQrcode(postData);
  383. if (response) {
  384. console.log(response);
  385. downloadFile(new Uint8Array(response.blobValue), response.filename!);
  386. }
  387. setBtnIsLoading(false);
  388. },
  389. [],
  390. );
  391. const getButtonSx = (status : string) => {
  392. let btnSx = {label:"", color:""};
  393. switch (status) {
  394. case "received": btnSx = {label: t("putaway processing"), color:"secondary.main"}; break;
  395. case "rejected":
  396. case "completed": btnSx = {label: t("view stockin"), color:"info.main"}; break;
  397. default: btnSx = {label: t("qc processing"), color:"success.main"};
  398. }
  399. return btnSx
  400. };
  401. // const handleQrCode = useCallback(
  402. // (id: GridRowId, params: any) => () => {
  403. // setRowModesModel((prev) => ({
  404. // ...prev,
  405. // [id]: { mode: GridRowModes.View },
  406. // }));
  407. // setModalInfo(params.row);
  408. // setTimeout(() => {
  409. // // open stock in modal
  410. // // openPutAwayModal();
  411. // // return the record with its status as pending
  412. // // update layout
  413. // console.log("delayed");
  414. // printQrcode(params.row);
  415. // }, 200);
  416. // },
  417. // [printQrcode],
  418. // );
  419. const columns = useMemo<GridColDef[]>(
  420. () => [
  421. // {
  422. // field: "itemNo",
  423. // headerName: t("itemNo"),
  424. // width: 100,
  425. // // flex: 0.4,
  426. // },
  427. {
  428. field: "dnNo",
  429. headerName: t("dnNo"),
  430. width: 125,
  431. // renderCell: () => {
  432. // return <>DN0000001</>
  433. // }
  434. // flex: 0.4,
  435. },
  436. {
  437. field: "dnDate",
  438. headerName: t("dnDate"),
  439. width: 125,
  440. renderCell: (params) => {
  441. console.log(params.row)
  442. // return <>07/08/2025</>
  443. return arrayToDateString(params.value)
  444. }
  445. // flex: 0.4,
  446. },
  447. {
  448. field: "productLotNo",
  449. headerName: t("productLotNo"),
  450. width: 125,
  451. },
  452. // {
  453. // field: "itemName",
  454. // headerName: t("itemName"),
  455. // width: 100,
  456. // // flex: 0.6,
  457. // },
  458. {
  459. field: "acceptedQty",
  460. headerName: t("acceptedQty"),
  461. // flex: 0.5,
  462. width: 125,
  463. type: "number",
  464. // editable: true,
  465. // replace with tooltip + content
  466. renderCell: (params) => {
  467. return integerFormatter.format(params.value)
  468. }
  469. },
  470. {
  471. field: "uom",
  472. headerName: t("uom"),
  473. width: 120,
  474. // flex: 0.5,
  475. renderCell: (params) => {
  476. return params.row.uom.code;
  477. },
  478. },
  479. {
  480. field: "stockQty",
  481. headerName: t("Stock In Qty"),
  482. // flex: 0.5,
  483. width: 125,
  484. type: "number",
  485. // editable: true,
  486. // replace with tooltip + content
  487. renderCell: (params) => {
  488. const baseQty = (params.row.acceptedQty ?? 0) * (itemDetail.stockUom.purchaseRatioN ?? 1) / (itemDetail.stockUom.purchaseRatioD ?? 1)
  489. const stockQty = baseQty * (itemDetail.stockUom.stockRatioD ?? 1) / (itemDetail.stockUom.stockRatioN ?? 1)
  490. return decimalFormatter.format(stockQty)
  491. }
  492. },
  493. {
  494. field: "stockUom",
  495. headerName: t("Stock UoM"),
  496. width: 120,
  497. // flex: 0.5,
  498. renderCell: (params) => {
  499. return itemDetail.stockUom.stockUomCode;
  500. },
  501. },
  502. // {
  503. // field: "weight",
  504. // headerName: t("weight"),
  505. // width: 120,
  506. // // flex: 0.5,
  507. // renderCell: (params) => {
  508. // const weight = calculateWeight(
  509. // params.row.acceptedQty,
  510. // params.row.uom,
  511. // );
  512. // const weightUnit = returnWeightUnit(params.row.uom);
  513. // return `${decimalFormatter.format(weight)} ${weightUnit}`;
  514. // },
  515. // },
  516. {
  517. field: "status",
  518. headerName: t("Status"),
  519. width: 140,
  520. // flex: 0.5,
  521. renderCell: (params) => {
  522. return t(`${params.row.status}`);
  523. },
  524. },
  525. {
  526. field: "actions",
  527. type: "actions",
  528. // headerName: `${t("start")} | ${t("qc")} | ${t("escalation")} | ${t(
  529. // "stock in",
  530. // )} | ${t("putaway")} | ${t("delete")}`,
  531. headerName: "動作",
  532. // headerName: "start | qc | escalation | stock in | putaway | delete",
  533. width: 350, //200
  534. // flex: 2,
  535. cellClassName: "actions",
  536. getActions: (params) => {
  537. // console.log(params.row.status);
  538. const status = params.row.status.toLowerCase();
  539. const btnSx = getButtonSx(status);
  540. // console.log(stockInLineStatusMap[status]);
  541. // console.log(session?.user?.abilities?.includes("APPROVAL"));
  542. return [
  543. <GridActionsCellItem
  544. icon={<Button variant="contained" sx={{ width: '150px', backgroundColor: btnSx.color }}>
  545. {btnSx.label}</Button>}
  546. label="start"
  547. sx={{
  548. // color: "primary.main",
  549. // marginRight: 1,
  550. }}
  551. onClick={handleNewQC(params.row.id, params)}
  552. color="inherit"
  553. key="edit"
  554. />,
  555. <GridActionsCellItem
  556. icon={<Button
  557. id="emailSupplier"
  558. type="button"
  559. variant="contained"
  560. color="primary"
  561. sx={{ width: '150px' }}
  562. // onClick={formProps.handleSubmit(onSubmitEmailSupplier)}
  563. >
  564. {t("email supplier")}
  565. </Button>}
  566. label="start"
  567. sx={{
  568. // color: "primary.main",
  569. // marginRight: 1,
  570. }}
  571. onClick={handleNewQC(params.row.id, params)}
  572. color="inherit"
  573. key="edit"
  574. />,
  575. // <GridActionsCellItem
  576. // icon={<Button variant="contained">{t("putawayBtn")}</Button>}
  577. // label="start"
  578. // sx={{
  579. // color: "primary.main",
  580. // // marginRight: 1,
  581. // }}
  582. // // disabled={!(stockInLineStatusMap[status] === 0)}
  583. // // set _isNew to false after posting
  584. // // or check status
  585. // onClick={handleStart(params.row.id, params)}
  586. // color="inherit"
  587. // key="edit"
  588. // />,
  589. // <GridActionsCellItem
  590. // icon={<Button variant="contained">{t("qc processing")}</Button>}
  591. // label="start"
  592. // sx={{
  593. // color: "primary.main",
  594. // // marginRight: 1,
  595. // }}
  596. // disabled={!(stockInLineStatusMap[status] === 0)}
  597. // // set _isNew to false after posting
  598. // // or check status
  599. // onClick={handleStart(params.row.id, params)}
  600. // color="inherit"
  601. // key="edit"
  602. // />,
  603. // <GridActionsCellItem
  604. // icon={<FactCheckIcon />}
  605. // label="qc"
  606. // sx={{
  607. // color: "primary.main",
  608. // // marginRight: 1,
  609. // }}
  610. // disabled={
  611. // // stockInLineStatusMap[status] === 9 ||
  612. // stockInLineStatusMap[status] < 1
  613. // }
  614. // // set _isNew to false after posting
  615. // // or check status
  616. // onClick={handleQC(params.row.id, params)}
  617. // color="inherit"
  618. // key="edit"
  619. // />,
  620. // <GridActionsCellItem
  621. // icon={<NotificationImportantIcon />}
  622. // label="escalation"
  623. // sx={{
  624. // color: "primary.main",
  625. // // marginRight: 1,
  626. // }}
  627. // disabled={
  628. // stockInLineStatusMap[status] === 9 ||
  629. // stockInLineStatusMap[status] <= 0 ||
  630. // stockInLineStatusMap[status] >= 5
  631. // }
  632. // // set _isNew to false after posting
  633. // // or check status
  634. // onClick={handleEscalation(params.row.id, params)}
  635. // color="inherit"
  636. // key="edit"
  637. // />,
  638. // <GridActionsCellItem
  639. // icon={<ShoppingCartIcon />}
  640. // label="stockin"
  641. // sx={{
  642. // color: "primary.main",
  643. // // marginRight: 1,
  644. // }}
  645. // disabled={
  646. // stockInLineStatusMap[status] === 9 ||
  647. // stockInLineStatusMap[status] <= 2 ||
  648. // stockInLineStatusMap[status] >= 7 ||
  649. // (stockInLineStatusMap[status] >= 3 &&
  650. // stockInLineStatusMap[status] <= 5 &&
  651. // !session?.user?.abilities?.includes("APPROVAL"))
  652. // }
  653. // // set _isNew to false after posting
  654. // // or check status
  655. // onClick={handleStockIn(params.row.id, params)}
  656. // color="inherit"
  657. // key="edit"
  658. // />,
  659. // <GridActionsCellItem
  660. // icon={<ShoppingCartIcon />}
  661. // label="putaway"
  662. // sx={{
  663. // color: "primary.main",
  664. // // marginRight: 1,
  665. // }}
  666. // disabled={
  667. // stockInLineStatusMap[status] === 9 ||
  668. // stockInLineStatusMap[status] < 7
  669. // }
  670. // // set _isNew to false after posting
  671. // // or check status
  672. // onClick={handlePutAway(params.row.id, params)}
  673. // color="inherit"
  674. // key="edit"
  675. // />,
  676. // // <GridActionsCellItem
  677. // // icon={<QrCodeIcon />}
  678. // // label="putaway"
  679. // // sx={{
  680. // // color: "primary.main",
  681. // // // marginRight: 1,
  682. // // }}
  683. // // disabled={stockInLineStatusMap[status] === 9 || stockInLineStatusMap[status] !== 8}
  684. // // // set _isNew to false after posting
  685. // // // or check status
  686. // // onClick={handleQrCode(params.row.id, params)}
  687. // // color="inherit"
  688. // // key="edit"
  689. // // />,
  690. // <GridActionsCellItem
  691. // icon={
  692. // stockInLineStatusMap[status] >= 1 ? (
  693. // <DoDisturbIcon />
  694. // ) : (
  695. // <DeleteIcon />
  696. // )
  697. // }
  698. // label="Delete"
  699. // sx={{
  700. // color: "error.main",
  701. // }}
  702. // disabled={
  703. // stockInLineStatusMap[status] >= 7 &&
  704. // stockInLineStatusMap[status] <= 9
  705. // }
  706. // onClick={
  707. // stockInLineStatusMap[status] === 0
  708. // ? handleDelete(params.row.id)
  709. // : handleReject(params.row.id, params)
  710. // }
  711. // color="inherit"
  712. // key="edit"
  713. // />,
  714. ];
  715. },
  716. },
  717. ],
  718. [t, handleStart, handleQC, handleEscalation, session?.user?.abilities, handleStockIn, handlePutAway, handleDelete, handleReject, itemDetail],
  719. );
  720. const addRow = useCallback(() => {
  721. console.log(itemDetail);
  722. const newEntry = {
  723. id: Date.now(),
  724. _isNew: true,
  725. itemId: itemDetail.itemId,
  726. purchaseOrderId: itemDetail.purchaseOrderId,
  727. purchaseOrderLineId: itemDetail.id,
  728. itemNo: itemDetail.itemNo,
  729. itemName: itemDetail.itemName,
  730. acceptedQty: itemDetail.qty - currQty, // this bug
  731. uom: itemDetail.uom,
  732. status: "draft",
  733. };
  734. setEntries((e) => [...e, newEntry]);
  735. setRowModesModel((model) => ({
  736. ...model,
  737. [getRowId(newEntry)]: {
  738. mode: GridRowModes.Edit,
  739. // fieldToFocus: "projectId",
  740. },
  741. }));
  742. }, [currQty, getRowId, itemDetail]);
  743. const validation = useCallback(
  744. (
  745. newRow: GridRowModel<StockInLineRow>,
  746. // rowModel: GridRowSelectionModel
  747. ): StockInLineEntryError | undefined => {
  748. const error: StockInLineEntryError = {};
  749. console.log(newRow);
  750. console.log(currQty);
  751. if (newRow.acceptedQty && newRow.acceptedQty > itemDetail.qty) {
  752. error["acceptedQty"] = t("qty cannot be greater than remaining qty");
  753. }
  754. return Object.keys(error).length > 0 ? error : undefined;
  755. },
  756. [currQty, itemDetail.qty, t],
  757. );
  758. const processRowUpdate = useCallback(
  759. (
  760. newRow: GridRowModel<StockInLineRow>,
  761. originalRow: GridRowModel<StockInLineRow>,
  762. ) => {
  763. const errors = validation(newRow); // change to validation
  764. if (errors) {
  765. throw new ProcessRowUpdateError(
  766. originalRow,
  767. "validation error",
  768. errors,
  769. );
  770. }
  771. const { _isNew, _error, ...updatedRow } = newRow;
  772. const rowToSave = {
  773. ...updatedRow,
  774. } satisfies StockInLineRow;
  775. const newEntries = entries.map((e) =>
  776. getRowId(e) === getRowId(originalRow) ? rowToSave : e,
  777. );
  778. setStockInLine(newEntries as StockInLine[]);
  779. console.log("triggered");
  780. setEntries(newEntries);
  781. //update remaining qty
  782. const total = newEntries.reduce(
  783. (acc, curr) => acc + (curr.acceptedQty || 0),
  784. 0,
  785. );
  786. setCurrQty(total);
  787. return rowToSave;
  788. },
  789. [validation, entries, setStockInLine, getRowId],
  790. );
  791. const onProcessRowUpdateError = useCallback(
  792. (updateError: ProcessRowUpdateError) => {
  793. const errors = updateError.errors;
  794. const oldRow = updateError.row;
  795. apiRef.current.updateRows([{ ...oldRow, _error: errors }]);
  796. },
  797. [apiRef],
  798. );
  799. const footer = (
  800. <>
  801. {/* <Box display="flex" gap={2} alignItems="center">
  802. <Button
  803. disableRipple
  804. variant="outlined"
  805. startIcon={<Add />}
  806. disabled={itemDetail.qty - currQty <= 0}
  807. onClick={addRow}
  808. size="small"
  809. >
  810. {t("Record pol")}
  811. </Button>
  812. </Box> */}
  813. </>
  814. );
  815. return (
  816. <>
  817. <StyledDataGrid
  818. getRowId={getRowId}
  819. apiRef={apiRef}
  820. autoHeight
  821. sx={{
  822. "--DataGrid-overlayHeight": "100px",
  823. ".MuiDataGrid-row .MuiDataGrid-cell.hasError": {
  824. border: "1px solid",
  825. borderColor: "error.main",
  826. },
  827. ".MuiDataGrid-row .MuiDataGrid-cell.hasWarning": {
  828. border: "1px solid",
  829. borderColor: "warning.main",
  830. },
  831. }}
  832. disableColumnMenu
  833. editMode="row"
  834. rows={entries}
  835. rowModesModel={rowModesModel}
  836. onRowModesModelChange={setRowModesModel}
  837. processRowUpdate={processRowUpdate}
  838. onProcessRowUpdateError={onProcessRowUpdateError}
  839. columns={columns}
  840. isCellEditable={(params) => {
  841. const status = params.row.status.toLowerCase();
  842. return (
  843. stockInLineStatusMap[status] >= 0 ||
  844. stockInLineStatusMap[status] <= 1
  845. );
  846. }}
  847. getCellClassName={(params: GridCellParams<StockInLineRow>) => {
  848. let classname = "";
  849. if (params.row._error) {
  850. classname = "hasError";
  851. }
  852. return classname;
  853. }}
  854. slots={{
  855. footer: FooterToolbar,
  856. noRowsOverlay: NoRowsOverlay,
  857. }}
  858. slotProps={{
  859. footer: { child: footer },
  860. }}
  861. />
  862. {modalInfo !== undefined && (
  863. <>
  864. <PoQcStockInModalVer2
  865. // setRows={setRows}
  866. setEntries={setEntries}
  867. setStockInLine={setStockInLine}
  868. setItemDetail={setModalInfo}
  869. qc={qc}
  870. warehouse={warehouse}
  871. open={newOpen}
  872. onClose={closeNewModal}
  873. itemDetail={modalInfo}
  874. />
  875. </>
  876. )
  877. }
  878. {modalInfo !== undefined && (
  879. <>
  880. <PoQcStockInModal
  881. type={"qc"}
  882. // setRows={setRows}
  883. setEntries={setEntries}
  884. setStockInLine={setStockInLine}
  885. setItemDetail={setModalInfo}
  886. qc={qc}
  887. open={qcOpen}
  888. onClose={closeQcModal}
  889. itemDetail={modalInfo}
  890. />
  891. </>
  892. )}
  893. {modalInfo !== undefined && (
  894. <>
  895. <PoQcStockInModal
  896. type={"escalation"}
  897. // setRows={setRows}
  898. setEntries={setEntries}
  899. setStockInLine={setStockInLine}
  900. setItemDetail={setModalInfo}
  901. // qc={qc}
  902. open={escalOpen}
  903. onClose={closeEscalationModal}
  904. itemDetail={modalInfo}
  905. />
  906. </>
  907. )}
  908. {modalInfo !== undefined && (
  909. <>
  910. <PoQcStockInModal
  911. type={"reject"}
  912. // setRows={setRows}
  913. setEntries={setEntries}
  914. setStockInLine={setStockInLine}
  915. setItemDetail={setModalInfo}
  916. // qc={qc}
  917. open={rejectOpen}
  918. onClose={closeRejectModal}
  919. itemDetail={modalInfo}
  920. />
  921. </>
  922. )}
  923. {modalInfo !== undefined && (
  924. <>
  925. <PoQcStockInModal
  926. type={"stockIn"}
  927. // setRows={setRows}
  928. setEntries={setEntries}
  929. setStockInLine={setStockInLine}
  930. // qc={qc}
  931. setItemDetail={setModalInfo}
  932. open={stockInOpen}
  933. onClose={closeStockInModal}
  934. itemDetail={modalInfo}
  935. />
  936. </>
  937. )}
  938. {modalInfo !== undefined && (
  939. <>
  940. <PoQcStockInModal
  941. type={"putaway"}
  942. // setRows={setRows}
  943. setEntries={setEntries}
  944. setStockInLine={setStockInLine}
  945. setItemDetail={setModalInfo}
  946. open={putAwayOpen}
  947. warehouse={warehouse}
  948. onClose={closePutAwayModal}
  949. itemDetail={modalInfo}
  950. />
  951. </>
  952. )}
  953. </>
  954. );
  955. }
  956. const NoRowsOverlay: React.FC = () => {
  957. const { t } = useTranslation("home");
  958. return (
  959. <Box
  960. display="flex"
  961. justifyContent="center"
  962. alignItems="center"
  963. height="100%"
  964. >
  965. <Typography variant="caption">{t("Add some entries!")}</Typography>
  966. </Box>
  967. );
  968. };
  969. const FooterToolbar: React.FC<FooterPropsOverrides> = ({ child }) => {
  970. return <GridToolbarContainer sx={{ p: 2 }}>{child}</GridToolbarContainer>;
  971. };
  972. export default PoInputGrid;