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

PoInputGrid.tsx 32 KiB

6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
7ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
4ヶ月前
4ヶ月前
3ヶ月前
6ヶ月前
3ヶ月前
3ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
6ヶ月前
7ヶ月前
7ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
3ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
6ヶ月前
4ヶ月前
6ヶ月前
4ヶ月前
4ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
7ヶ月前
4ヶ月前
6ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
6ヶ月前
4ヶ月前
6ヶ月前
4ヶ月前
6ヶ月前
6ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
7ヶ月前
6ヶ月前
4ヶ月前
6ヶ月前
4ヶ月前
6ヶ月前
4ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
3ヶ月前
4ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045
  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;