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

250 lines
9.1 KiB

  1. import { JoDetail, JoDetailPickLine } from "@/app/api/jo";
  2. import { decimalFormatter } from "@/app/utils/formatUtil";
  3. import { GridColDef, GridRenderCellParams, GridValidRowModel } from "@mui/x-data-grid";
  4. import { isEmpty, pick, upperFirst } from "lodash";
  5. import { useCallback, useMemo } from "react";
  6. import { useFormContext } from "react-hook-form";
  7. import { useTranslation } from "react-i18next";
  8. import StyledDataGrid from "../StyledDataGrid/StyledDataGrid";
  9. import { Box, Grid, Icon, IconButton, Stack, Typography } from "@mui/material";
  10. import PendingOutlinedIcon from '@mui/icons-material/PendingOutlined';
  11. import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined';
  12. import HelpOutlineOutlinedIcon from '@mui/icons-material/HelpOutlineOutlined';
  13. import { fetchInventories } from "@/app/api/inventory/actions";
  14. import { InventoryResult } from "@/app/api/inventory";
  15. import { useEffect, useState } from "react";
  16. import DoDisturbAltRoundedIcon from '@mui/icons-material/DoDisturbAltRounded';
  17. type JoDetailPickLineWithCalculations = JoDetailPickLine & {
  18. stockAvailable: number;
  19. isStockSufficient: boolean;
  20. };
  21. type Props = {
  22. };
  23. const PickTable: React.FC<Props> = ({
  24. }) => {
  25. const { t } = useTranslation("jo")
  26. const {
  27. watch
  28. } = useFormContext<JoDetail>()
  29. const [inventoryData, setInventoryData] = useState<InventoryResult[]>([]);
  30. const pickLines = watch("pickLines");
  31. useEffect(() => {
  32. const fetchInventoryData = async () => {
  33. try {
  34. const inventoryResponse = await fetchInventories({
  35. code: "",
  36. name: "",
  37. type: "",
  38. pageNum: 0,
  39. pageSize: 1000
  40. });
  41. setInventoryData(inventoryResponse.records);
  42. } catch (error) {
  43. console.error("Error fetching inventory data:", error);
  44. }
  45. };
  46. fetchInventoryData();
  47. }, [pickLines]);
  48. const getStockAvailable = (pickLine: JoDetailPickLine) => {
  49. const inventory = inventoryData.find(inventory =>
  50. inventory.itemCode === pickLine.code || inventory.itemName === pickLine.name
  51. );
  52. if (inventory) {
  53. return inventory.availableQty || (inventory.onHandQty - inventory.onHoldQty - inventory.unavailableQty);
  54. }
  55. return 0;
  56. };
  57. const getUomShortDesc = (pickLine: JoDetailPickLine) => {
  58. const inventory = inventoryData.find(inventory =>
  59. inventory.itemCode === pickLine.code || inventory.itemName === pickLine.name
  60. );
  61. return inventory?.uomShortDesc; // || pickLine.uom;
  62. };
  63. const isStockSufficient = (pickLine: JoDetailPickLine) => {
  64. const stockAvailable = getStockAvailable(pickLine);
  65. return stockAvailable >= pickLine.reqQty;
  66. };
  67. const sufficientStockIcon = useMemo(() => {
  68. return <CheckCircleOutlineOutlinedIcon fontSize={"large"} color="success" />
  69. }, []);
  70. const insufficientStockIcon = useMemo(() => {
  71. return <DoDisturbAltRoundedIcon fontSize={"large"} color="error" />
  72. }, []);
  73. const rowsWithCalculatedFields = useMemo(() => {
  74. return pickLines.map((pickLine, index) => ({
  75. ...pickLine,
  76. id: pickLine.id || index,
  77. sequence: index + 1,
  78. stockAvailable: getStockAvailable(pickLine),
  79. isStockSufficient: isStockSufficient(pickLine),
  80. }));
  81. }, [pickLines, inventoryData]);
  82. const notPickedStatusColumn = useMemo(() => {
  83. return (<HelpOutlineOutlinedIcon fontSize={"large"} color={"error"} />)
  84. }, [])
  85. const scanStatusColumn = useCallback((status: boolean) => {
  86. return status ?
  87. <CheckCircleOutlineOutlinedIcon fontSize={"large"} sx={{ ml: "5px" }} color="success" />
  88. : <PendingOutlinedIcon fontSize={"large"} sx={{ ml: "5px" }} color="warning" />
  89. }, [])
  90. const columns = useMemo<GridColDef[]>(() => [
  91. {
  92. field: "sequence",
  93. headerName: t("Sequence"),
  94. flex: 0.2,
  95. align: "left",
  96. headerAlign: "left",
  97. type: "number",
  98. renderCell: (params: GridRenderCellParams<JoDetailPickLine>) => {
  99. return params.value;
  100. },
  101. },
  102. {
  103. field: "code",
  104. headerName: t("Item Code"),
  105. flex: 0.6,
  106. },
  107. {
  108. field: "name",
  109. headerName: t("Item Name"),
  110. flex: 1,
  111. renderCell: (params: GridRenderCellParams<JoDetailPickLine>) => {
  112. return `${params.value} (${params.row.uom})`;
  113. },
  114. },
  115. /*{
  116. field: "scanStatus",
  117. headerName: t("Scan Status"),
  118. flex: 0.4,
  119. align: "right",
  120. headerAlign: "right",
  121. renderCell: (params: GridRenderCellParams<JoDetailPickLine>) => {
  122. if (params.row.pickedLotNo === null || params.row.pickedLotNo === undefined) {
  123. return notPickedStatusColumn
  124. }
  125. const scanStatus = params.row.pickedLotNo.map((pln) => Boolean(pln.isScanned))
  126. return isEmpty(scanStatus) ? notPickedStatusColumn : <Stack direction={"column"}>{scanStatus.map((status) => scanStatusColumn(status))}</Stack>
  127. },
  128. },
  129. {
  130. field: "lotNo",
  131. headerName: t("Lot No."),
  132. flex: 1,
  133. renderCell: (params: GridRenderCellParams<JoDetailPickLine>) => {
  134. if (params.row.pickedLotNo === null || params.row.pickedLotNo === undefined) {
  135. return t("Pending for pick")
  136. }
  137. const lotNos = params.row.pickedLotNo.map((pln) => pln.lotNo)
  138. return isEmpty(lotNos) ? t("Pending for pick") : lotNos.map((lotNo) => (<>{lotNo}<br /></>))
  139. },
  140. },
  141. {
  142. field: "pickedQty",
  143. headerName: t("Picked Qty"),
  144. flex: 0.7,
  145. align: "right",
  146. headerAlign: "right",
  147. renderCell: (params: GridRenderCellParams<JoDetailPickLine>) => {
  148. if (params.row.pickedLotNo === null || params.row.pickedLotNo === undefined) {
  149. return t("Pending for pick")
  150. }
  151. const qtys = params.row.pickedLotNo.map((pln) => pln.qty)
  152. return isEmpty(qtys) ? t("Pending for pick") : qtys.map((qty) => <>{qty}<br /></>)
  153. },
  154. },*/
  155. {
  156. field: "reqQty",
  157. headerName: t("Req. Qty"),
  158. flex: 0.7,
  159. align: "right",
  160. headerAlign: "right",
  161. renderCell: (params: GridRenderCellParams<JoDetailPickLine>) => {
  162. const uomShortDesc = getUomShortDesc(params.row);
  163. return `${decimalFormatter.format(params.value)} ${uomShortDesc}`;
  164. },
  165. },
  166. {
  167. field: "stockAvailable",
  168. headerName: t("Stock Available"),
  169. flex: 0.7,
  170. align: "right",
  171. headerAlign: "right",
  172. type: "number",
  173. renderCell: (params: GridRenderCellParams<JoDetailPickLine>) => {
  174. const uomShortDesc = getUomShortDesc(params.row);
  175. return `${decimalFormatter.format(params.value)} ${uomShortDesc}`;
  176. },
  177. },
  178. {
  179. field: "stockStatus",
  180. headerName: t("Stock Status"),
  181. flex: 0.5,
  182. align: "right",
  183. headerAlign: "right",
  184. type: "boolean",
  185. renderCell: (params: GridRenderCellParams<JoDetailPickLineWithCalculations>) => {
  186. return params.row.isStockSufficient ? sufficientStockIcon : insufficientStockIcon;
  187. },
  188. }
  189. /*{
  190. field: "status",
  191. headerName: t("Status"),
  192. flex: 1,
  193. align: "right",
  194. headerAlign: "right",
  195. renderCell: (params: GridRenderCellParams<JoDetailPickLine>) => {
  196. return (
  197. <>
  198. {params.row.pickedLotNo?.every((lotNo) => Boolean(lotNo.isScanned)) ? t("Scanned") : t(upperFirst(params.value))}
  199. {scanStatusColumn(Boolean(params.row.pickedLotNo?.every((lotNo) => Boolean(lotNo.isScanned))))}
  200. </>
  201. )
  202. },
  203. },*/
  204. ], [t, inventoryData])
  205. return (
  206. <>
  207. <StyledDataGrid
  208. sx={{
  209. "--DataGrid-overlayHeight": "100px",
  210. ".MuiDataGrid-row .MuiDataGrid-cell.hasError": {
  211. border: "1px solid",
  212. borderColor: "error.main",
  213. },
  214. ".MuiDataGrid-row .MuiDataGrid-cell.hasWarning": {
  215. border: "1px solid",
  216. borderColor: "warning.main",
  217. },
  218. }}
  219. disableColumnMenu
  220. rows={rowsWithCalculatedFields}
  221. columns={columns}
  222. getRowHeight={() => 'auto'}
  223. />
  224. </>
  225. )
  226. }
  227. export default PickTable;