FPSMS-frontend
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

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