FPSMS-frontend
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 

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