FPSMS-frontend
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

PoInputGrid.tsx 30 KiB

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