FPSMS-frontend
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 

483 рядки
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 "./PutAwayForm1";
  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. useEffect(() => {
  152. formProps.clearErrors("acceptedQty")
  153. }, [accQty])
  154. const productLotNo = formProps.watch("productLotNo");
  155. const checkStockIn = useCallback(
  156. (data: ModalFormInput): boolean => {
  157. let hasErrors = false;
  158. if (accQty! <= 0 ) {
  159. formProps.setError("acceptedQty", {
  160. message: `${t("Accepted qty must greater than")} ${
  161. 0
  162. }`,
  163. type: "required",
  164. });
  165. hasErrors = true;
  166. } else if (accQty! > itemDetail.acceptedQty) {
  167. formProps.setError("acceptedQty", {
  168. message: `${t("Accepted qty must not greater than")} ${
  169. itemDetail.acceptedQty
  170. }`,
  171. type: "required",
  172. });
  173. hasErrors = true;
  174. }
  175. if (isEmpty(productLotNo)) {
  176. formProps.setError("productLotNo", {
  177. message: `${t("Product Lot No must not be empty")}`,
  178. type: "required",
  179. });
  180. hasErrors = true;
  181. }
  182. if (itemDetail.shelfLife && !data.productionDate && !data.expiryDate) {
  183. formProps.setError("productionDate", {
  184. message: "Please provide at least one",
  185. type: "invalid",
  186. });
  187. formProps.setError("expiryDate", {
  188. message: "Please provide at least one",
  189. type: "invalid",
  190. });
  191. hasErrors = true;
  192. }
  193. if (!itemDetail.shelfLife && !data.expiryDate) {
  194. formProps.setError("expiryDate", {
  195. message: "Please provide expiry date",
  196. type: "invalid",
  197. });
  198. hasErrors = true;
  199. }
  200. if (data.expiryDate && data.expiryDate < data.receiptDate!) {
  201. formProps.setError("expiryDate", {
  202. message: "Expired",
  203. type: "invalid",
  204. });
  205. hasErrors = true;
  206. }
  207. return hasErrors;
  208. },
  209. [accQty, itemDetail.acceptedQty, itemDetail.shelfLife, productLotNo, formProps, t],
  210. );
  211. const checkPutaway = useCallback(
  212. (data: ModalFormInput): boolean => {
  213. let hasErrors = false;
  214. console.log(data.warehouseId);
  215. if (!data.warehouseId || data.warehouseId <= 0) {
  216. formProps.setError("warehouseId", {
  217. message: "Please provide warehouseId",
  218. type: "invalid",
  219. });
  220. hasErrors = true;
  221. }
  222. return hasErrors;
  223. },
  224. [itemDetail, formProps],
  225. );
  226. const onSubmit = useCallback<SubmitHandler<ModalFormInput>>(
  227. async (data, event) => {
  228. setBtnIsLoading(true);
  229. setIsUploading(true);
  230. formProps.clearErrors();
  231. let hasErrors = false;
  232. console.log(errors);
  233. console.log(data);
  234. console.log(itemDetail);
  235. // console.log(fix0IndexedDate(data.receiptDate));
  236. try {
  237. // add checking
  238. if (type === "stockIn") {
  239. hasErrors = checkStockIn(data);
  240. console.log(hasErrors);
  241. }
  242. if (type === "putaway") {
  243. hasErrors = checkPutaway(data);
  244. console.log(hasErrors);
  245. }
  246. //////////////////////// modify this mess later //////////////////////
  247. let productionDate = null;
  248. let expiryDate = null;
  249. let receiptDate = null;
  250. let acceptedQty = null;
  251. if (data.productionDate) {
  252. productionDate = dayjs(data.productionDate).format(INPUT_DATE_FORMAT);
  253. }
  254. if (data.expiryDate) {
  255. expiryDate = dayjs(data.expiryDate).format(INPUT_DATE_FORMAT);
  256. }
  257. if (data.receiptDate) {
  258. receiptDate = dayjs(data.receiptDate).format(INPUT_DATE_FORMAT);
  259. }
  260. // if ()
  261. if (data.qcResult) {
  262. acceptedQty =
  263. itemDetail.acceptedQty -
  264. data.qcResult.reduce((acc, curr) => acc + curr.failQty, 0);
  265. }
  266. const args = {
  267. id: itemDetail.id,
  268. purchaseOrderId: parseInt(params.get("id")!),
  269. purchaseOrderLineId: itemDetail.purchaseOrderLineId,
  270. itemId: itemDetail.itemId,
  271. ...data,
  272. productionDate: productionDate,
  273. expiryDate: expiryDate,
  274. receiptDate: receiptDate,
  275. } as StockInLineEntry & ModalFormInput;
  276. //////////////////////////////////////////////////////////////////////
  277. if (hasErrors) {
  278. console.log(args);
  279. setServerError(t("An error has occurred. Please try again later."));
  280. setBtnIsLoading(false);
  281. setIsUploading(false);
  282. return;
  283. }
  284. console.log(args);
  285. // setBtnIsLoading(false);
  286. // setIsUploading(false)
  287. // return
  288. const res = await updateStockInLine(args);
  289. if (Boolean(res.id)) {
  290. // update entries
  291. const newEntries = res.entity as StockInLine[];
  292. console.log(newEntries);
  293. if (setEntries) {
  294. setEntries((prev) => {
  295. const updatedEntries = [...prev]; // Create a new array
  296. newEntries.forEach((item) => {
  297. const index = updatedEntries.findIndex((p) => p.id === item.id);
  298. if (index !== -1) {
  299. // Update existing item
  300. console.log(item);
  301. updatedEntries[index] = item;
  302. } else {
  303. // Add new item
  304. updatedEntries.push(item);
  305. }
  306. });
  307. return updatedEntries; // Return the new array
  308. });
  309. }
  310. if (setStockInLine) {
  311. setStockInLine((prev) => {
  312. const updatedEntries = [...prev]; // Create a new array
  313. newEntries.forEach((item) => {
  314. const index = updatedEntries.findIndex((p) => p.id === item.id);
  315. if (index !== -1) {
  316. // Update existing item
  317. console.log(item);
  318. updatedEntries[index] = item;
  319. } else {
  320. // Add new item
  321. updatedEntries.push(item);
  322. }
  323. });
  324. return updatedEntries; // Return the new array
  325. });
  326. }
  327. // add loading
  328. setBtnIsLoading(false);
  329. setIsUploading(false);
  330. setItemDetail(undefined);
  331. closeHandler({}, "backdropClick");
  332. }
  333. console.log(res);
  334. // if (res)
  335. } catch (e) {
  336. // server error
  337. setBtnIsLoading(false);
  338. setIsUploading(false);
  339. setServerError(t("An error has occurred. Please try again later."));
  340. console.log(e);
  341. }
  342. },
  343. [setIsUploading, formProps, errors, itemDetail, type, params, checkStockIn, checkPutaway, t, setEntries, setStockInLine, setItemDetail, closeHandler],
  344. );
  345. const printQrcode = useCallback(async () => {
  346. setBtnIsLoading(true);
  347. setIsUploading(true);
  348. const postData = { stockInLineIds: [itemDetail.id] };
  349. // const postData = { stockInLineIds: [42,43,44] };
  350. const response = await fetchPoQrcode(postData);
  351. if (response) {
  352. console.log(response);
  353. downloadFile(new Uint8Array(response.blobValue), response.filename!);
  354. }
  355. setBtnIsLoading(false);
  356. setIsUploading(false);
  357. }, [setIsUploading, itemDetail.id]);
  358. const renderSubmitButton = useMemo((): boolean => {
  359. if (itemDetail) {
  360. const status = itemDetail.status;
  361. console.log(status);
  362. switch (type) {
  363. case "qc":
  364. return (
  365. stockInLineStatusMap[status] >= 1 &&
  366. stockInLineStatusMap[status] <= 2
  367. );
  368. case "escalation":
  369. return (
  370. stockInLineStatusMap[status] === 1 ||
  371. stockInLineStatusMap[status] >= 3 ||
  372. stockInLineStatusMap[status] <= 5
  373. );
  374. case "stockIn":
  375. return (
  376. stockInLineStatusMap[status] >= 3 &&
  377. stockInLineStatusMap[status] <= 6
  378. );
  379. case "putaway":
  380. return stockInLineStatusMap[status] === 7;
  381. case "reject":
  382. return (
  383. stockInLineStatusMap[status] >= 1 &&
  384. stockInLineStatusMap[status] <= 6
  385. );
  386. default:
  387. return false; // Handle unexpected type
  388. }
  389. } else return false;
  390. }, [type, itemDetail]);
  391. // useEffect(() => {
  392. // console.log(renderSubmitButton)
  393. // }, [renderSubmitButton])
  394. return (
  395. <>
  396. <FormProvider {...formProps}>
  397. <Modal open={open} onClose={closeHandler} sx={{ overflow: "scroll" }}>
  398. <Box
  399. sx={style}
  400. component="form"
  401. onSubmit={formProps.handleSubmit(onSubmit)}
  402. >
  403. {itemDetail !== undefined && type === "qc" && (
  404. <QcForm
  405. qc={qc!}
  406. itemDetail={itemDetail}
  407. disabled={!renderSubmitButton}
  408. />
  409. )}
  410. {itemDetail !== undefined && type === "escalation" && (
  411. <EscalationForm
  412. itemDetail={itemDetail}
  413. disabled={!renderSubmitButton}
  414. />
  415. )}
  416. {itemDetail !== undefined && type === "stockIn" && (
  417. <StockInForm
  418. itemDetail={itemDetail}
  419. disabled={!renderSubmitButton}
  420. />
  421. )}
  422. {itemDetail !== undefined && type === "putaway" && (
  423. <PutAwayForm
  424. itemDetail={itemDetail}
  425. warehouse={warehouse!}
  426. disabled={!renderSubmitButton}
  427. />
  428. )}
  429. {itemDetail !== undefined && type === "reject" && (
  430. <RejectForm
  431. itemDetail={itemDetail}
  432. disabled={!renderSubmitButton}
  433. />
  434. )}
  435. <Stack direction="row" justifyContent="flex-end" gap={1}>
  436. {renderSubmitButton ? (
  437. <Button
  438. name="submit"
  439. variant="contained"
  440. startIcon={<Check />}
  441. type="submit"
  442. disabled={btnIsLoading}
  443. >
  444. {t("submit")}
  445. </Button>
  446. ) : undefined}
  447. {itemDetail !== undefined && type === "putaway" && (
  448. <Button
  449. name="print"
  450. variant="contained"
  451. // startIcon={<Check />}
  452. onClick={printQrcode}
  453. disabled={btnIsLoading}
  454. >
  455. {t("print")}
  456. </Button>
  457. )}
  458. </Stack>
  459. </Box>
  460. </Modal>
  461. </FormProvider>
  462. </>
  463. );
  464. };
  465. export default PoQcStockInModal;