FPSMS-frontend
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 

479 Zeilen
14 KiB

  1. "use client";
  2. import {
  3. ModalFormInput,
  4. PurchaseQCInput,
  5. PurchaseQcResult,
  6. StockInInput,
  7. StockInLineEntry,
  8. updateStockInLine,
  9. } from "@/app/api/po/actions";
  10. import { Box, Button, Modal, ModalProps, Stack } from "@mui/material";
  11. import {
  12. Dispatch,
  13. SetStateAction,
  14. useCallback,
  15. useContext,
  16. useEffect,
  17. useMemo,
  18. useState,
  19. } from "react";
  20. import { FormProvider, SubmitHandler, useForm } from "react-hook-form";
  21. import { useTranslation } from "react-i18next";
  22. import QcForm from "./QcForm";
  23. import { QcItemWithChecks } from "@/app/api/qc";
  24. import { Check, CurrencyYuanRounded, TtyTwoTone } from "@mui/icons-material";
  25. import { PurchaseOrderLine, StockInLine } from "@/app/api/po";
  26. import { useSearchParams } from "next/navigation";
  27. import { StockInLineRow } from "./PoInputGrid";
  28. import EscalationForm from "./EscalationForm";
  29. import StockInForm from "./StockInForm";
  30. import PutawayForm from "./PutawayForm";
  31. import {
  32. INPUT_DATE_FORMAT,
  33. stockInLineStatusMap,
  34. } from "@/app/utils/formatUtil";
  35. import dayjs from "dayjs";
  36. import arraySupport from "dayjs/plugin/arraySupport";
  37. import { downloadFile } from "@/app/utils/commonUtil";
  38. import { fetchPoQrcode } from "@/app/api/pdf/actions";
  39. import UploadContext from "../UploadProvider/UploadProvider";
  40. import useUploadContext from "../UploadProvider/useUploadContext";
  41. import RejectForm from "./RejectForm";
  42. import { isNullOrUndefined } from "html5-qrcode/esm/core";
  43. import { isEmpty, isFinite } from "lodash";
  44. dayjs.extend(arraySupport);
  45. interface CommonProps extends Omit<ModalProps, "children"> {
  46. // setRows: Dispatch<SetStateAction<PurchaseOrderLine[]>>;
  47. setEntries?: Dispatch<SetStateAction<StockInLineRow[]>>;
  48. setStockInLine?: Dispatch<SetStateAction<StockInLine[]>>;
  49. itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] };
  50. setItemDetail: Dispatch<
  51. SetStateAction<
  52. | (StockInLine & {
  53. warehouseId?: number;
  54. })
  55. | undefined
  56. >
  57. >;
  58. qc?: QcItemWithChecks[];
  59. warehouse?: any[];
  60. type: "qc" | "stockIn" | "escalation" | "putaway" | "reject";
  61. }
  62. interface QcProps extends CommonProps {
  63. qc: QcItemWithChecks[];
  64. type: "qc";
  65. }
  66. interface StockInProps extends CommonProps {
  67. // naming
  68. type: "stockIn";
  69. }
  70. interface PutawayProps extends CommonProps {
  71. warehouse: any[];
  72. type: "putaway";
  73. }
  74. interface EscalationProps extends CommonProps {
  75. // naming
  76. type: "escalation";
  77. }
  78. interface RejectProps extends CommonProps {
  79. // naming
  80. type: "reject";
  81. }
  82. type Props =
  83. | QcProps
  84. | StockInProps
  85. | PutawayProps
  86. | EscalationProps
  87. | RejectProps;
  88. const style = {
  89. position: "absolute",
  90. top: "50%",
  91. left: "50%",
  92. transform: "translate(-50%, -50%)",
  93. overflow: "scroll",
  94. bgcolor: "background.paper",
  95. pt: 5,
  96. px: 5,
  97. pb: 10,
  98. display: "block",
  99. width: { xs: "60%", sm: "60%", md: "60%" },
  100. };
  101. const PoQcStockInModal: React.FC<Props> = ({
  102. type,
  103. // setRows,
  104. setEntries,
  105. setStockInLine,
  106. open,
  107. onClose,
  108. itemDetail,
  109. setItemDetail,
  110. qc,
  111. warehouse,
  112. }) => {
  113. const { setIsUploading } = useUploadContext();
  114. const [serverError, setServerError] = useState("");
  115. const { t } = useTranslation("purchaseOrder");
  116. const params = useSearchParams();
  117. const [btnIsLoading, setBtnIsLoading] = useState(false);
  118. console.log(params.get("id"));
  119. console.log(itemDetail);
  120. console.log(itemDetail.qcResult);
  121. const formProps = useForm<ModalFormInput>({
  122. defaultValues: {
  123. ...itemDetail,
  124. // receiptDate: itemDetail.receiptDate || dayjs().add(-1, "month").format(INPUT_DATE_FORMAT),
  125. // warehouseId: itemDetail.defaultWarehouseId || 0
  126. },
  127. });
  128. // console.log(formProps);
  129. const errors = formProps.formState.errors;
  130. const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>(
  131. (...args) => {
  132. onClose?.(...args);
  133. // reset();
  134. },
  135. [onClose],
  136. );
  137. useEffect(() => {
  138. // setDefaultValues({...itemDetail});
  139. if (!itemDetail) {
  140. console.log(itemDetail);
  141. }
  142. }, [itemDetail]);
  143. // const fix0IndexedDate = useCallback((date: string | number[] | undefined) => {
  144. // if (Array.isArray(date)) {
  145. // console.log(date);
  146. // return dayjs([date[0], date[1] - 1, date[2]]).format("YYYY-MM-DD");
  147. // }
  148. // return date;
  149. // }, []);
  150. const accQty = formProps.watch("acceptedQty");
  151. const productLotNo = formProps.watch("productLotNo");
  152. const checkStockIn = useCallback(
  153. (data: ModalFormInput): boolean => {
  154. let hasErrors = false;
  155. if (!isFinite(accQty) || accQty! <= 0 ) {
  156. formProps.setError("acceptedQty", {
  157. message: `${t("Accepted qty must greater than")} ${
  158. 0
  159. }`,
  160. type: "required",
  161. });
  162. hasErrors = true;
  163. } else if (accQty! > itemDetail.acceptedQty) {
  164. formProps.setError("acceptedQty", {
  165. message: `${t("Accepted qty must not greater than")} ${
  166. itemDetail.acceptedQty
  167. }`,
  168. type: "required",
  169. });
  170. hasErrors = true;
  171. }
  172. if (isEmpty(productLotNo)) {
  173. formProps.setError("productLotNo", {
  174. message: `${t("Product Lot No must not be empty")}`,
  175. type: "required",
  176. });
  177. hasErrors = true;
  178. }
  179. if (itemDetail.shelfLife && !data.productionDate && !data.expiryDate) {
  180. formProps.setError("productionDate", {
  181. message: "Please provide at least one",
  182. type: "invalid",
  183. });
  184. formProps.setError("expiryDate", {
  185. message: "Please provide at least one",
  186. type: "invalid",
  187. });
  188. hasErrors = true;
  189. }
  190. if (!itemDetail.shelfLife && !data.expiryDate) {
  191. formProps.setError("expiryDate", {
  192. message: "Please provide expiry date",
  193. type: "invalid",
  194. });
  195. hasErrors = true;
  196. }
  197. if (data.expiryDate && data.expiryDate < data.receiptDate!) {
  198. formProps.setError("expiryDate", {
  199. message: "Expired",
  200. type: "invalid",
  201. });
  202. hasErrors = true;
  203. }
  204. return hasErrors;
  205. },
  206. [accQty, itemDetail.acceptedQty, itemDetail.shelfLife, productLotNo, formProps, t],
  207. );
  208. const checkPutaway = useCallback(
  209. (data: ModalFormInput): boolean => {
  210. let hasErrors = false;
  211. console.log(data.warehouseId);
  212. if (!data.warehouseId || data.warehouseId <= 0) {
  213. formProps.setError("warehouseId", {
  214. message: "Please provide warehouseId",
  215. type: "invalid",
  216. });
  217. hasErrors = true;
  218. }
  219. return hasErrors;
  220. },
  221. [itemDetail, formProps],
  222. );
  223. const onSubmit = useCallback<SubmitHandler<ModalFormInput>>(
  224. async (data, event) => {
  225. setBtnIsLoading(true);
  226. setIsUploading(true);
  227. formProps.clearErrors();
  228. let hasErrors = false;
  229. console.log(errors);
  230. console.log(data);
  231. console.log(itemDetail);
  232. // console.log(fix0IndexedDate(data.receiptDate));
  233. try {
  234. // add checking
  235. if (type === "stockIn") {
  236. hasErrors = checkStockIn(data);
  237. console.log(hasErrors);
  238. }
  239. if (type === "putaway") {
  240. hasErrors = checkPutaway(data);
  241. console.log(hasErrors);
  242. }
  243. //////////////////////// modify this mess later //////////////////////
  244. let productionDate = null;
  245. let expiryDate = null;
  246. let receiptDate = null;
  247. let acceptedQty = null;
  248. if (data.productionDate) {
  249. productionDate = dayjs(data.productionDate).format(INPUT_DATE_FORMAT);
  250. }
  251. if (data.expiryDate) {
  252. expiryDate = dayjs(data.expiryDate).format(INPUT_DATE_FORMAT);
  253. }
  254. if (data.receiptDate) {
  255. receiptDate = dayjs(data.receiptDate).format(INPUT_DATE_FORMAT);
  256. }
  257. // if ()
  258. if (data.qcResult) {
  259. acceptedQty =
  260. itemDetail.acceptedQty -
  261. data.qcResult.reduce((acc, curr) => acc + curr.failQty, 0);
  262. }
  263. const args = {
  264. id: itemDetail.id,
  265. purchaseOrderId: parseInt(params.get("id")!),
  266. purchaseOrderLineId: itemDetail.purchaseOrderLineId,
  267. itemId: itemDetail.itemId,
  268. ...data,
  269. productionDate: productionDate,
  270. expiryDate: expiryDate,
  271. receiptDate: receiptDate,
  272. } as StockInLineEntry & ModalFormInput;
  273. //////////////////////////////////////////////////////////////////////
  274. if (hasErrors) {
  275. console.log(args);
  276. setServerError(t("An error has occurred. Please try again later."));
  277. setBtnIsLoading(false);
  278. setIsUploading(false);
  279. return;
  280. }
  281. console.log(args);
  282. // setBtnIsLoading(false);
  283. // setIsUploading(false)
  284. // return
  285. const res = await updateStockInLine(args);
  286. if (Boolean(res.id)) {
  287. // update entries
  288. const newEntries = res.entity as StockInLine[];
  289. console.log(newEntries);
  290. if (setEntries) {
  291. setEntries((prev) => {
  292. const updatedEntries = [...prev]; // Create a new array
  293. newEntries.forEach((item) => {
  294. const index = updatedEntries.findIndex((p) => p.id === item.id);
  295. if (index !== -1) {
  296. // Update existing item
  297. console.log(item);
  298. updatedEntries[index] = item;
  299. } else {
  300. // Add new item
  301. updatedEntries.push(item);
  302. }
  303. });
  304. return updatedEntries; // Return the new array
  305. });
  306. }
  307. if (setStockInLine) {
  308. setStockInLine((prev) => {
  309. const updatedEntries = [...prev]; // Create a new array
  310. newEntries.forEach((item) => {
  311. const index = updatedEntries.findIndex((p) => p.id === item.id);
  312. if (index !== -1) {
  313. // Update existing item
  314. console.log(item);
  315. updatedEntries[index] = item;
  316. } else {
  317. // Add new item
  318. updatedEntries.push(item);
  319. }
  320. });
  321. return updatedEntries; // Return the new array
  322. });
  323. }
  324. // add loading
  325. setBtnIsLoading(false);
  326. setIsUploading(false);
  327. setItemDetail(undefined);
  328. closeHandler({}, "backdropClick");
  329. }
  330. console.log(res);
  331. // if (res)
  332. } catch (e) {
  333. // server error
  334. setBtnIsLoading(false);
  335. setIsUploading(false);
  336. setServerError(t("An error has occurred. Please try again later."));
  337. console.log(e);
  338. }
  339. },
  340. [t, itemDetail, checkStockIn, checkPutaway],
  341. );
  342. const printQrcode = useCallback(async () => {
  343. setBtnIsLoading(true);
  344. setIsUploading(true);
  345. const postData = { stockInLineIds: [itemDetail.id] };
  346. // const postData = { stockInLineIds: [42,43,44] };
  347. const response = await fetchPoQrcode(postData);
  348. if (response) {
  349. console.log(response);
  350. downloadFile(new Uint8Array(response.blobValue), response.filename!);
  351. }
  352. setBtnIsLoading(false);
  353. setIsUploading(false);
  354. }, [itemDetail, fetchPoQrcode, downloadFile]);
  355. const renderSubmitButton = useMemo((): boolean => {
  356. if (itemDetail) {
  357. const status = itemDetail.status;
  358. console.log(status);
  359. switch (type) {
  360. case "qc":
  361. return (
  362. stockInLineStatusMap[status] >= 1 &&
  363. stockInLineStatusMap[status] <= 2
  364. );
  365. case "escalation":
  366. return (
  367. stockInLineStatusMap[status] === 1 ||
  368. stockInLineStatusMap[status] >= 3 ||
  369. stockInLineStatusMap[status] <= 5
  370. );
  371. case "stockIn":
  372. return (
  373. stockInLineStatusMap[status] >= 3 &&
  374. stockInLineStatusMap[status] <= 6
  375. );
  376. case "putaway":
  377. return stockInLineStatusMap[status] === 7;
  378. case "reject":
  379. return (
  380. stockInLineStatusMap[status] >= 1 &&
  381. stockInLineStatusMap[status] <= 6
  382. );
  383. default:
  384. return false; // Handle unexpected type
  385. }
  386. } else return false;
  387. }, [type, itemDetail]);
  388. // useEffect(() => {
  389. // console.log(renderSubmitButton)
  390. // }, [renderSubmitButton])
  391. return (
  392. <>
  393. <FormProvider {...formProps}>
  394. <Modal open={open} onClose={closeHandler} sx={{ overflow: "scroll" }}>
  395. <Box
  396. sx={style}
  397. component="form"
  398. onSubmit={formProps.handleSubmit(onSubmit)}
  399. >
  400. {itemDetail !== undefined && type === "qc" && (
  401. <QcForm
  402. qc={qc!}
  403. itemDetail={itemDetail}
  404. disabled={!renderSubmitButton}
  405. />
  406. )}
  407. {itemDetail !== undefined && type === "escalation" && (
  408. <EscalationForm
  409. itemDetail={itemDetail}
  410. disabled={!renderSubmitButton}
  411. />
  412. )}
  413. {itemDetail !== undefined && type === "stockIn" && (
  414. <StockInForm
  415. itemDetail={itemDetail}
  416. disabled={!renderSubmitButton}
  417. />
  418. )}
  419. {itemDetail !== undefined && type === "putaway" && (
  420. <PutawayForm
  421. itemDetail={itemDetail}
  422. warehouse={warehouse!}
  423. disabled={!renderSubmitButton}
  424. />
  425. )}
  426. {itemDetail !== undefined && type === "reject" && (
  427. <RejectForm
  428. itemDetail={itemDetail}
  429. disabled={!renderSubmitButton}
  430. />
  431. )}
  432. <Stack direction="row" justifyContent="flex-end" gap={1}>
  433. {renderSubmitButton ? (
  434. <Button
  435. name="submit"
  436. variant="contained"
  437. startIcon={<Check />}
  438. type="submit"
  439. disabled={btnIsLoading}
  440. >
  441. {t("submit")}
  442. </Button>
  443. ) : undefined}
  444. {itemDetail !== undefined && type === "putaway" && (
  445. <Button
  446. name="print"
  447. variant="contained"
  448. // startIcon={<Check />}
  449. onClick={printQrcode}
  450. disabled={btnIsLoading}
  451. >
  452. {t("print")}
  453. </Button>
  454. )}
  455. </Stack>
  456. </Box>
  457. </Modal>
  458. </FormProvider>
  459. </>
  460. );
  461. };
  462. export default PoQcStockInModal;