FPSMS-frontend
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

1017 lines
31 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 poLineId = searchParams.get("poLineId");
  255. const closeNewModal = useCallback(() => {
  256. const newParams = new URLSearchParams(searchParams.toString());
  257. newParams.delete("stockInLineId"); // Remove the parameter
  258. router.replace(`${pathname}?${newParams.toString()}`);
  259. fetchPoDetail(itemDetail.purchaseOrderId.toString());
  260. setTimeout(() => {
  261. setNewOpen(false); // Close the modal first
  262. }, 300); // Add a delay to avoid immediate re-trigger of useEffect
  263. }, [searchParams, pathname, router]);
  264. // Open modal
  265. const openNewModal = useCallback(() => {
  266. setNewOpen(() => true);
  267. }, []);
  268. // Button handler to update the URL and open the modal
  269. const handleNewQC = useCallback(
  270. (id: GridRowId, params: any) => async() => {
  271. // console.log(id)
  272. // console.log(params)
  273. // setBtnIsLoading(true);
  274. setRowModesModel((prev) => ({
  275. ...prev,
  276. [id]: { mode: GridRowModes.View },
  277. }));
  278. const qcResult = await fetchQcDefaultValue(id);
  279. setModalInfo(() => ({
  280. ...params.row,
  281. qcResult: qcResult,
  282. receivedQty: itemDetail.receivedQty,
  283. }));
  284. setTimeout(() => {
  285. const newParams = new URLSearchParams(searchParams.toString());
  286. newParams.set("stockInLineId", id.toString()); // Ensure `set` to avoid duplicates
  287. router.replace(`${pathname}?${newParams.toString()}`);
  288. // console.log("hello")
  289. openNewModal()
  290. // setBtnIsLoading(false);
  291. }, 200);
  292. },
  293. [fetchQcDefaultValue, openNewModal, pathname, router, searchParams]
  294. );
  295. // Open modal if `stockInLineId` exists in the URL
  296. const [firstCheckForSil, setFirstCheckForSil] = useState(false)
  297. useEffect(() => {
  298. if (stockInLineId && itemDetail && !firstCheckForSil) {
  299. // console.log("heeloo")
  300. // console.log(stockInLineId)
  301. // console.log(apiRef.current.getRow(stockInLineId))
  302. setFirstCheckForSil(true)
  303. const fn = handleNewQC(stockInLineId, {row: apiRef.current.getRow(stockInLineId)});
  304. fn();
  305. }
  306. }, [stockInLineId, poLineId, itemDetail]);
  307. const handleEscalation = useCallback(
  308. (id: GridRowId, params: any) => () => {
  309. // setBtnIsLoading(true);
  310. setRowModesModel((prev) => ({
  311. ...prev,
  312. [id]: { mode: GridRowModes.View },
  313. }));
  314. setModalInfo(params.row);
  315. setTimeout(() => {
  316. // open qc modal
  317. console.log("delayed");
  318. openEscalationModal();
  319. // setBtnIsLoading(false);
  320. }, 200);
  321. },
  322. [openEscalationModal],
  323. );
  324. const handleReject = useCallback(
  325. (id: GridRowId, params: any) => () => {
  326. setRowModesModel((prev) => ({
  327. ...prev,
  328. [id]: { mode: GridRowModes.View },
  329. }));
  330. setModalInfo(params.row);
  331. setTimeout(() => {
  332. // open stock in modal
  333. // openPutAwayModal();
  334. // return the record with its status as pending
  335. // update layout
  336. console.log("delayed");
  337. openRejectModal();
  338. // printQrcode(params.row);
  339. }, 200);
  340. },
  341. [openRejectModal],
  342. );
  343. const handleStockIn = useCallback(
  344. (id: GridRowId, params: any) => () => {
  345. // setBtnIsLoading(true);
  346. setRowModesModel((prev) => ({
  347. ...prev,
  348. [id]: { mode: GridRowModes.View },
  349. }));
  350. setModalInfo(params.row);
  351. setTimeout(() => {
  352. // open stock in modal
  353. openStockInModal();
  354. // return the record with its status as pending
  355. // update layout
  356. console.log("delayed");
  357. // setBtnIsLoading(false);
  358. }, 200);
  359. },
  360. [openStockInModal],
  361. );
  362. const handlePutAway = useCallback(
  363. (id: GridRowId, params: any) => () => {
  364. // setBtnIsLoading(true);
  365. setRowModesModel((prev) => ({
  366. ...prev,
  367. [id]: { mode: GridRowModes.View },
  368. }));
  369. setModalInfo(params.row);
  370. setTimeout(() => {
  371. // open stock in modal
  372. openPutAwayModal();
  373. // return the record with its status as pending
  374. // update layout
  375. console.log("delayed");
  376. // setBtnIsLoading(false);
  377. }, 200);
  378. },
  379. [openPutAwayModal],
  380. );
  381. const printQrcode = useCallback(
  382. async (row: any) => {
  383. setBtnIsLoading(true);
  384. console.log(row.id);
  385. const postData = { stockInLineIds: [row.id] };
  386. // const postData = { stockInLineIds: [42,43,44] };
  387. const response = await fetchPoQrcode(postData);
  388. if (response) {
  389. console.log(response);
  390. downloadFile(new Uint8Array(response.blobValue), response.filename!);
  391. }
  392. setBtnIsLoading(false);
  393. },
  394. [],
  395. );
  396. const getButtonSx = (status : string) => {
  397. let btnSx = {label:"", color:""};
  398. switch (status) {
  399. case "received": btnSx = {label: t("putaway processing"), color:"secondary.main"}; break;
  400. case "rejected":
  401. case "completed": btnSx = {label: t("view stockin"), color:"info.main"}; break;
  402. default: btnSx = {label: t("qc processing"), color:"success.main"};
  403. }
  404. return btnSx
  405. };
  406. // const handleQrCode = useCallback(
  407. // (id: GridRowId, params: any) => () => {
  408. // setRowModesModel((prev) => ({
  409. // ...prev,
  410. // [id]: { mode: GridRowModes.View },
  411. // }));
  412. // setModalInfo(params.row);
  413. // setTimeout(() => {
  414. // // open stock in modal
  415. // // openPutAwayModal();
  416. // // return the record with its status as pending
  417. // // update layout
  418. // console.log("delayed");
  419. // printQrcode(params.row);
  420. // }, 200);
  421. // },
  422. // [printQrcode],
  423. // );
  424. const columns = useMemo<GridColDef[]>(
  425. () => [
  426. // {
  427. // field: "itemNo",
  428. // headerName: t("itemNo"),
  429. // width: 100,
  430. // // flex: 0.4,
  431. // },
  432. {
  433. field: "dnNo",
  434. headerName: t("dnNo"),
  435. width: 125,
  436. // renderCell: () => {
  437. // return <>DN0000001</>
  438. // }
  439. // flex: 0.4,
  440. },
  441. {
  442. field: "dnDate",
  443. headerName: t("dnDate"),
  444. width: 125,
  445. renderCell: (params) => {
  446. console.log(params.row)
  447. // return <>07/08/2025</>
  448. return arrayToDateString(params.value)
  449. }
  450. // flex: 0.4,
  451. },
  452. {
  453. field: "productLotNo",
  454. headerName: t("productLotNo"),
  455. width: 125,
  456. },
  457. // {
  458. // field: "itemName",
  459. // headerName: t("itemName"),
  460. // width: 100,
  461. // // flex: 0.6,
  462. // },
  463. {
  464. field: "acceptedQty",
  465. headerName: t("acceptedQty"),
  466. // flex: 0.5,
  467. width: 125,
  468. type: "number",
  469. // editable: true,
  470. // replace with tooltip + content
  471. renderCell: (params) => {
  472. return integerFormatter.format(params.value)
  473. }
  474. },
  475. {
  476. field: "uom",
  477. headerName: t("uom"),
  478. width: 120,
  479. // flex: 0.5,
  480. renderCell: (params) => {
  481. return params.row.uom.code;
  482. },
  483. },
  484. {
  485. field: "stockQty",
  486. headerName: t("Stock In Qty"),
  487. // flex: 0.5,
  488. width: 125,
  489. type: "number",
  490. // editable: true,
  491. // replace with tooltip + content
  492. renderCell: (params) => {
  493. const baseQty = (params.row.acceptedQty ?? 0) * (itemDetail.stockUom.purchaseRatioN ?? 1) / (itemDetail.stockUom.purchaseRatioD ?? 1)
  494. const stockQty = baseQty * (itemDetail.stockUom.stockRatioD ?? 1) / (itemDetail.stockUom.stockRatioN ?? 1)
  495. return decimalFormatter.format(stockQty)
  496. }
  497. },
  498. {
  499. field: "stockUom",
  500. headerName: t("Stock UoM"),
  501. width: 120,
  502. // flex: 0.5,
  503. renderCell: (params) => {
  504. return itemDetail.stockUom.stockUomCode;
  505. },
  506. },
  507. // {
  508. // field: "weight",
  509. // headerName: t("weight"),
  510. // width: 120,
  511. // // flex: 0.5,
  512. // renderCell: (params) => {
  513. // const weight = calculateWeight(
  514. // params.row.acceptedQty,
  515. // params.row.uom,
  516. // );
  517. // const weightUnit = returnWeightUnit(params.row.uom);
  518. // return `${decimalFormatter.format(weight)} ${weightUnit}`;
  519. // },
  520. // },
  521. {
  522. field: "status",
  523. headerName: t("Status"),
  524. width: 140,
  525. // flex: 0.5,
  526. renderCell: (params) => {
  527. return t(`${params.row.status}`);
  528. },
  529. },
  530. {
  531. field: "actions",
  532. type: "actions",
  533. // headerName: `${t("start")} | ${t("qc")} | ${t("escalation")} | ${t(
  534. // "stock in",
  535. // )} | ${t("putaway")} | ${t("delete")}`,
  536. headerName: "動作",
  537. // headerName: "start | qc | escalation | stock in | putaway | delete",
  538. width: 350, //200
  539. // flex: 2,
  540. cellClassName: "actions",
  541. getActions: (params) => {
  542. // console.log(params.row.status);
  543. const status = params.row.status.toLowerCase();
  544. const btnSx = getButtonSx(status);
  545. // console.log(stockInLineStatusMap[status]);
  546. // console.log(session?.user?.abilities?.includes("APPROVAL"));
  547. return [
  548. <GridActionsCellItem
  549. icon={<Button variant="contained" sx={{ width: '150px', backgroundColor: btnSx.color }}>
  550. {btnSx.label}</Button>}
  551. label="start"
  552. sx={{
  553. // color: "primary.main",
  554. // marginRight: 1,
  555. }}
  556. onClick={handleNewQC(params.row.id, params)}
  557. color="inherit"
  558. key={`edit`}
  559. />,
  560. <GridActionsCellItem
  561. icon={<Button
  562. id="emailSupplier"
  563. type="button"
  564. variant="contained"
  565. color="primary"
  566. sx={{ width: '150px' }}
  567. // onClick={formProps.handleSubmit(onSubmitEmailSupplier)}
  568. >
  569. {t("email supplier")}
  570. </Button>}
  571. label="start"
  572. sx={{
  573. // color: "primary.main",
  574. // marginRight: 1,
  575. }}
  576. onClick={handleNewQC(params.row.id, params)}
  577. color="inherit"
  578. key="edit"
  579. />,
  580. // <GridActionsCellItem
  581. // icon={<Button variant="contained">{t("putawayBtn")}</Button>}
  582. // label="start"
  583. // sx={{
  584. // color: "primary.main",
  585. // // marginRight: 1,
  586. // }}
  587. // // disabled={!(stockInLineStatusMap[status] === 0)}
  588. // // set _isNew to false after posting
  589. // // or check status
  590. // onClick={handleStart(params.row.id, params)}
  591. // color="inherit"
  592. // key="edit"
  593. // />,
  594. // <GridActionsCellItem
  595. // icon={<Button variant="contained">{t("qc processing")}</Button>}
  596. // label="start"
  597. // sx={{
  598. // color: "primary.main",
  599. // // marginRight: 1,
  600. // }}
  601. // disabled={!(stockInLineStatusMap[status] === 0)}
  602. // // set _isNew to false after posting
  603. // // or check status
  604. // onClick={handleStart(params.row.id, params)}
  605. // color="inherit"
  606. // key="edit"
  607. // />,
  608. // <GridActionsCellItem
  609. // icon={<FactCheckIcon />}
  610. // label="qc"
  611. // sx={{
  612. // color: "primary.main",
  613. // // marginRight: 1,
  614. // }}
  615. // disabled={
  616. // // stockInLineStatusMap[status] === 9 ||
  617. // stockInLineStatusMap[status] < 1
  618. // }
  619. // // set _isNew to false after posting
  620. // // or check status
  621. // onClick={handleQC(params.row.id, params)}
  622. // color="inherit"
  623. // key="edit"
  624. // />,
  625. // <GridActionsCellItem
  626. // icon={<NotificationImportantIcon />}
  627. // label="escalation"
  628. // sx={{
  629. // color: "primary.main",
  630. // // marginRight: 1,
  631. // }}
  632. // disabled={
  633. // stockInLineStatusMap[status] === 9 ||
  634. // stockInLineStatusMap[status] <= 0 ||
  635. // stockInLineStatusMap[status] >= 5
  636. // }
  637. // // set _isNew to false after posting
  638. // // or check status
  639. // onClick={handleEscalation(params.row.id, params)}
  640. // color="inherit"
  641. // key="edit"
  642. // />,
  643. // <GridActionsCellItem
  644. // icon={<ShoppingCartIcon />}
  645. // label="stockin"
  646. // sx={{
  647. // color: "primary.main",
  648. // // marginRight: 1,
  649. // }}
  650. // disabled={
  651. // stockInLineStatusMap[status] === 9 ||
  652. // stockInLineStatusMap[status] <= 2 ||
  653. // stockInLineStatusMap[status] >= 7 ||
  654. // (stockInLineStatusMap[status] >= 3 &&
  655. // stockInLineStatusMap[status] <= 5 &&
  656. // !session?.user?.abilities?.includes("APPROVAL"))
  657. // }
  658. // // set _isNew to false after posting
  659. // // or check status
  660. // onClick={handleStockIn(params.row.id, params)}
  661. // color="inherit"
  662. // key="edit"
  663. // />,
  664. // <GridActionsCellItem
  665. // icon={<ShoppingCartIcon />}
  666. // label="putaway"
  667. // sx={{
  668. // color: "primary.main",
  669. // // marginRight: 1,
  670. // }}
  671. // disabled={
  672. // stockInLineStatusMap[status] === 9 ||
  673. // stockInLineStatusMap[status] < 7
  674. // }
  675. // // set _isNew to false after posting
  676. // // or check status
  677. // onClick={handlePutAway(params.row.id, params)}
  678. // color="inherit"
  679. // key="edit"
  680. // />,
  681. // // <GridActionsCellItem
  682. // // icon={<QrCodeIcon />}
  683. // // label="putaway"
  684. // // sx={{
  685. // // color: "primary.main",
  686. // // // marginRight: 1,
  687. // // }}
  688. // // disabled={stockInLineStatusMap[status] === 9 || stockInLineStatusMap[status] !== 8}
  689. // // // set _isNew to false after posting
  690. // // // or check status
  691. // // onClick={handleQrCode(params.row.id, params)}
  692. // // color="inherit"
  693. // // key="edit"
  694. // // />,
  695. // <GridActionsCellItem
  696. // icon={
  697. // stockInLineStatusMap[status] >= 1 ? (
  698. // <DoDisturbIcon />
  699. // ) : (
  700. // <DeleteIcon />
  701. // )
  702. // }
  703. // label="Delete"
  704. // sx={{
  705. // color: "error.main",
  706. // }}
  707. // disabled={
  708. // stockInLineStatusMap[status] >= 7 &&
  709. // stockInLineStatusMap[status] <= 9
  710. // }
  711. // onClick={
  712. // stockInLineStatusMap[status] === 0
  713. // ? handleDelete(params.row.id)
  714. // : handleReject(params.row.id, params)
  715. // }
  716. // color="inherit"
  717. // key="edit"
  718. // />,
  719. ];
  720. },
  721. },
  722. ],
  723. [t, handleStart, handleQC, handleEscalation, session?.user?.abilities, handleStockIn, handlePutAway, handleDelete, handleReject, itemDetail],
  724. );
  725. const addRow = useCallback(() => {
  726. console.log(itemDetail);
  727. const newEntry = {
  728. id: Date.now(),
  729. _isNew: true,
  730. itemId: itemDetail.itemId,
  731. purchaseOrderId: itemDetail.purchaseOrderId,
  732. purchaseOrderLineId: itemDetail.id,
  733. itemNo: itemDetail.itemNo,
  734. itemName: itemDetail.itemName,
  735. acceptedQty: itemDetail.qty - currQty, // this bug
  736. uom: itemDetail.uom,
  737. status: "draft",
  738. };
  739. setEntries((e) => [...e, newEntry]);
  740. setRowModesModel((model) => ({
  741. ...model,
  742. [getRowId(newEntry)]: {
  743. mode: GridRowModes.Edit,
  744. // fieldToFocus: "projectId",
  745. },
  746. }));
  747. }, [currQty, getRowId, itemDetail]);
  748. const validation = useCallback(
  749. (
  750. newRow: GridRowModel<StockInLineRow>,
  751. // rowModel: GridRowSelectionModel
  752. ): StockInLineEntryError | undefined => {
  753. const error: StockInLineEntryError = {};
  754. console.log(newRow);
  755. console.log(currQty);
  756. if (newRow.acceptedQty && newRow.acceptedQty > itemDetail.qty) {
  757. error["acceptedQty"] = t("qty cannot be greater than remaining qty");
  758. }
  759. return Object.keys(error).length > 0 ? error : undefined;
  760. },
  761. [currQty, itemDetail.qty, t],
  762. );
  763. const processRowUpdate = useCallback(
  764. (
  765. newRow: GridRowModel<StockInLineRow>,
  766. originalRow: GridRowModel<StockInLineRow>,
  767. ) => {
  768. const errors = validation(newRow); // change to validation
  769. if (errors) {
  770. throw new ProcessRowUpdateError(
  771. originalRow,
  772. "validation error",
  773. errors,
  774. );
  775. }
  776. const { _isNew, _error, ...updatedRow } = newRow;
  777. const rowToSave = {
  778. ...updatedRow,
  779. } satisfies StockInLineRow;
  780. const newEntries = entries.map((e) =>
  781. getRowId(e) === getRowId(originalRow) ? rowToSave : e,
  782. );
  783. setStockInLine(newEntries as StockInLine[]);
  784. console.log("triggered");
  785. setEntries(newEntries);
  786. //update remaining qty
  787. const total = newEntries.reduce(
  788. (acc, curr) => acc + (curr.acceptedQty || 0),
  789. 0,
  790. );
  791. setCurrQty(total);
  792. return rowToSave;
  793. },
  794. [validation, entries, setStockInLine, getRowId],
  795. );
  796. const onProcessRowUpdateError = useCallback(
  797. (updateError: ProcessRowUpdateError) => {
  798. const errors = updateError.errors;
  799. const oldRow = updateError.row;
  800. apiRef.current.updateRows([{ ...oldRow, _error: errors }]);
  801. },
  802. [apiRef],
  803. );
  804. const footer = (
  805. <>
  806. {/* <Box display="flex" gap={2} alignItems="center">
  807. <Button
  808. disableRipple
  809. variant="outlined"
  810. startIcon={<Add />}
  811. disabled={itemDetail.qty - currQty <= 0}
  812. onClick={addRow}
  813. size="small"
  814. >
  815. {t("Record pol")}
  816. </Button>
  817. </Box> */}
  818. </>
  819. );
  820. return (
  821. <>
  822. <StyledDataGrid
  823. getRowId={getRowId}
  824. apiRef={apiRef}
  825. autoHeight
  826. sx={{
  827. "--DataGrid-overlayHeight": "100px",
  828. ".MuiDataGrid-row .MuiDataGrid-cell.hasError": {
  829. border: "1px solid",
  830. borderColor: "error.main",
  831. },
  832. ".MuiDataGrid-row .MuiDataGrid-cell.hasWarning": {
  833. border: "1px solid",
  834. borderColor: "warning.main",
  835. },
  836. }}
  837. disableColumnMenu
  838. editMode="row"
  839. rows={entries}
  840. rowModesModel={rowModesModel}
  841. onRowModesModelChange={setRowModesModel}
  842. processRowUpdate={processRowUpdate}
  843. onProcessRowUpdateError={onProcessRowUpdateError}
  844. columns={columns}
  845. isCellEditable={(params) => {
  846. const status = params.row.status.toLowerCase();
  847. return (
  848. stockInLineStatusMap[status] >= 0 ||
  849. stockInLineStatusMap[status] <= 1
  850. );
  851. }}
  852. getCellClassName={(params: GridCellParams<StockInLineRow>) => {
  853. let classname = "";
  854. if (params.row._error) {
  855. classname = "hasError";
  856. }
  857. return classname;
  858. }}
  859. slots={{
  860. footer: FooterToolbar,
  861. noRowsOverlay: NoRowsOverlay,
  862. }}
  863. slotProps={{
  864. footer: { child: footer },
  865. }}
  866. />
  867. {modalInfo !== undefined && (
  868. <>
  869. <PoQcStockInModalVer2
  870. // setRows={setRows}
  871. setEntries={setEntries}
  872. setStockInLine={setStockInLine}
  873. setItemDetail={setModalInfo}
  874. qc={qc}
  875. warehouse={warehouse}
  876. open={newOpen}
  877. onClose={closeNewModal}
  878. itemDetail={modalInfo}
  879. />
  880. </>
  881. )
  882. }
  883. {modalInfo !== undefined && (
  884. <>
  885. <PoQcStockInModal
  886. type={"qc"}
  887. // setRows={setRows}
  888. setEntries={setEntries}
  889. setStockInLine={setStockInLine}
  890. setItemDetail={setModalInfo}
  891. qc={qc}
  892. open={qcOpen}
  893. onClose={closeQcModal}
  894. itemDetail={modalInfo}
  895. />
  896. </>
  897. )}
  898. {modalInfo !== undefined && (
  899. <>
  900. <PoQcStockInModal
  901. type={"escalation"}
  902. // setRows={setRows}
  903. setEntries={setEntries}
  904. setStockInLine={setStockInLine}
  905. setItemDetail={setModalInfo}
  906. // qc={qc}
  907. open={escalOpen}
  908. onClose={closeEscalationModal}
  909. itemDetail={modalInfo}
  910. />
  911. </>
  912. )}
  913. {modalInfo !== undefined && (
  914. <>
  915. <PoQcStockInModal
  916. type={"reject"}
  917. // setRows={setRows}
  918. setEntries={setEntries}
  919. setStockInLine={setStockInLine}
  920. setItemDetail={setModalInfo}
  921. // qc={qc}
  922. open={rejectOpen}
  923. onClose={closeRejectModal}
  924. itemDetail={modalInfo}
  925. />
  926. </>
  927. )}
  928. {modalInfo !== undefined && (
  929. <>
  930. <PoQcStockInModal
  931. type={"stockIn"}
  932. // setRows={setRows}
  933. setEntries={setEntries}
  934. setStockInLine={setStockInLine}
  935. // qc={qc}
  936. setItemDetail={setModalInfo}
  937. open={stockInOpen}
  938. onClose={closeStockInModal}
  939. itemDetail={modalInfo}
  940. />
  941. </>
  942. )}
  943. {modalInfo !== undefined && (
  944. <>
  945. <PoQcStockInModal
  946. type={"putaway"}
  947. // setRows={setRows}
  948. setEntries={setEntries}
  949. setStockInLine={setStockInLine}
  950. setItemDetail={setModalInfo}
  951. open={putAwayOpen}
  952. warehouse={warehouse}
  953. onClose={closePutAwayModal}
  954. itemDetail={modalInfo}
  955. />
  956. </>
  957. )}
  958. </>
  959. );
  960. }
  961. const NoRowsOverlay: React.FC = () => {
  962. const { t } = useTranslation("home");
  963. return (
  964. <Box
  965. display="flex"
  966. justifyContent="center"
  967. alignItems="center"
  968. height="100%"
  969. >
  970. <Typography variant="caption">{t("Add some entries!")}</Typography>
  971. </Box>
  972. );
  973. };
  974. const FooterToolbar: React.FC<FooterPropsOverrides> = ({ child }) => {
  975. return <GridToolbarContainer sx={{ p: 2 }}>{child}</GridToolbarContainer>;
  976. };
  977. export default PoInputGrid;