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.
 
 

711 lines
26 KiB

  1. "use client";
  2. import { QcItemWithChecks, QcData } from "@/app/api/qc";
  3. import {
  4. Autocomplete,
  5. Box,
  6. Button,
  7. Divider,
  8. Grid,
  9. Modal,
  10. ModalProps,
  11. Stack,
  12. Tab,
  13. Tabs,
  14. TabsProps,
  15. TextField,
  16. Typography,
  17. } from "@mui/material";
  18. import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react";
  19. import { FormProvider, SubmitErrorHandler, SubmitHandler, useForm } from "react-hook-form";
  20. import { StockInLineRow } from "../PoDetail/PoInputGrid";
  21. import { useTranslation } from "react-i18next";
  22. import StockInForm from "../StockIn/StockInForm";
  23. import QcComponent from "./QcComponent";
  24. import PutAwayForm from "../PoDetail/PutAwayForm";
  25. import { GridRowModes, GridRowSelectionModel, useGridApiRef } from "@mui/x-data-grid";
  26. import {msg, submitDialogWithWarning} from "../Swal/CustomAlerts";
  27. import { INPUT_DATE_FORMAT, arrayToDateString, dayjsToDateTimeString } from "@/app/utils/formatUtil";
  28. import dayjs from "dayjs";
  29. import { fetchPoQrcode } from "@/app/api/pdf/actions";
  30. import { downloadFile } from "@/app/utils/commonUtil";
  31. import { PrinterCombo } from "@/app/api/settings/printer";
  32. import { EscalationResult } from "@/app/api/escalation";
  33. import { SessionWithTokens } from "@/config/authConfig";
  34. import { GridRowModesModel } from "@mui/x-data-grid";
  35. import { isEmpty } from "lodash";
  36. import { EscalationCombo } from "@/app/api/user";
  37. import { truncateSync } from "fs";
  38. import { ModalFormInput, StockInLineInput, StockInLine, StockInStatus } from "@/app/api/stockIn";
  39. import { StockInLineEntry, updateStockInLine, printQrCodeForSil, PrintQrCodeForSilRequest } from "@/app/api/stockIn/actions";
  40. import { fetchStockInLineInfo } from "@/app/api/stockIn/actions";
  41. import FgStockInForm from "../StockIn/FgStockInForm";
  42. import LoadingComponent from "../General/LoadingComponent";
  43. import { printFGStockInLabel, PrintFGStockInLabelRequest } from "@/app/api/jo/actions";
  44. const style = {
  45. position: "absolute",
  46. top: "50%",
  47. left: "50%",
  48. transform: "translate(-50%, -50%)",
  49. bgcolor: "background.paper",
  50. // pt: 5,
  51. // px: 5,
  52. // pb: 10,
  53. display: "block",
  54. width: { xs: "90%", sm: "90%", md: "90%" },
  55. height: { xs: "90%", sm: "90%", md: "90%" },
  56. };
  57. interface CommonProps extends Omit<ModalProps, "children"> {
  58. // itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] } | undefined;
  59. inputDetail: StockInLineInput | undefined;
  60. session: SessionWithTokens | null;
  61. warehouse?: any[];
  62. printerCombo: PrinterCombo[];
  63. onClose: () => void;
  64. skipQc?: Boolean;
  65. printSource?: "stockIn" | "productionProcess";
  66. }
  67. interface Props extends CommonProps {
  68. // itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] };
  69. }
  70. const QcStockInModal: React.FC<Props> = ({
  71. open,
  72. onClose,
  73. // itemDetail,
  74. inputDetail,
  75. session,
  76. warehouse,
  77. printerCombo,
  78. skipQc = false,
  79. printSource = "stockIn",
  80. }) => {
  81. const {
  82. t,
  83. i18n: { language },
  84. } = useTranslation("purchaseOrder");
  85. const [stockInLineInfo, setStockInLineInfo] = useState<StockInLine>();
  86. const [isLoading, setIsLoading] = useState<boolean>(false);
  87. const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
  88. // const [skipQc, setSkipQc] = useState<Boolean>(false);
  89. // const [viewOnly, setViewOnly] = useState(false);
  90. const printerStorageKey = useMemo(
  91. () => `qcStockInModal_selectedPrinterId_${session?.id ?? "guest"}`,
  92. [session?.id],
  93. );
  94. const getDefaultPrinter = useMemo(() => {
  95. if (!printerCombo.length) return undefined;
  96. if (typeof window === "undefined") return printerCombo[0];
  97. const savedId = sessionStorage.getItem(printerStorageKey);
  98. const matched = savedId ? printerCombo.find(p => p.id === Number(savedId)) : undefined;
  99. return matched ?? printerCombo[0];
  100. }, [printerCombo, printerStorageKey]);
  101. const [selectedPrinter, setSelectedPrinter] = useState(printerCombo[0]);
  102. const [printQty, setPrintQty] = useState(1);
  103. const [tabIndex, setTabIndex] = useState(0);
  104. const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
  105. (_e, newValue) => {
  106. setTabIndex(newValue);
  107. },
  108. [],
  109. );
  110. const fetchStockInLineData = useCallback(
  111. async (stockInLineId: number) => {
  112. try {
  113. const res = await fetchStockInLineInfo(stockInLineId);
  114. if (res) {
  115. console.log("%c Fetched Stock In Line: ", "color:orange", res);
  116. setStockInLineInfo({...inputDetail, ...res, expiryDate: inputDetail?.expiryDate}); // TODO review to overwrite res with inputDetail instead (revise PO fetching data)
  117. // fetchQcResultData(stockInLineId);
  118. } else throw("Result is undefined");
  119. } catch (e) {
  120. console.log("%c Error when fetching Stock In Line: ", "color:red", e);
  121. alert("Something went wrong, please retry");
  122. closeHandler({}, "backdropClick");
  123. }
  124. },[fetchStockInLineInfo, inputDetail]
  125. );
  126. // Fetch info if id is input
  127. useEffect(() => {
  128. setIsLoading(true);
  129. setIsSubmitting(false);
  130. if (inputDetail && open) {
  131. console.log("%c Opened Modal with input:", "color:yellow", inputDetail);
  132. if (inputDetail.id) {
  133. const id = inputDetail.id;
  134. fetchStockInLineData(id);
  135. }
  136. }
  137. }, [open]);
  138. // Make sure stock in line info is fetched
  139. useEffect(() => {
  140. if (stockInLineInfo) {
  141. if (stockInLineInfo.id) {
  142. if (isLoading) {
  143. formProps.reset({
  144. ...defaultNewValue
  145. });
  146. console.log("%c Modal loaded successfully", "color:lime");
  147. setIsLoading(false);
  148. }
  149. }
  150. }
  151. }, [stockInLineInfo]);
  152. const defaultNewValue = useMemo(() => {
  153. const d = stockInLineInfo;
  154. if (d !== undefined) {
  155. // console.log("%c sil info", "color:yellow", d )
  156. return (
  157. {
  158. ...d,
  159. // status: d.status ?? "pending",
  160. productionDate: d.productionDate ? arrayToDateString(d.productionDate, "input") : undefined,
  161. expiryDate: d.expiryDate ? arrayToDateString(d.expiryDate, "input") : undefined,
  162. receiptDate: d.receiptDate ? arrayToDateString(d.receiptDate, "input")
  163. : dayjs().add(0, "month").format(INPUT_DATE_FORMAT),
  164. acceptQty: d.status != StockInStatus.REJECTED ? (d.demandQty?? d.acceptedQty) : 0,
  165. // escResult: (d.escResult && d.escResult?.length > 0) ? d.escResult : [],
  166. // qcResult: (d.qcResult && d.qcResult?.length > 0) ? d.qcResult : [],//[...dummyQCData],
  167. warehouseId: d.defaultWarehouseId ?? 1,
  168. putAwayLines: d.putAwayLines?.map((line) => ({...line, printQty: 1, _isNew: false, _disableDelete: true})) ?? [],
  169. } as ModalFormInput
  170. )
  171. } return undefined
  172. }, [stockInLineInfo])
  173. // const [qcItems, setQcItems] = useState(dummyQCData)
  174. const formProps = useForm<ModalFormInput>({
  175. defaultValues: {
  176. ...defaultNewValue,
  177. },
  178. });
  179. const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>(
  180. () => {
  181. setStockInLineInfo(undefined);
  182. formProps.reset({});
  183. onClose?.();
  184. },
  185. [onClose],
  186. );
  187. const isPutaway = () => {
  188. if (stockInLineInfo) {
  189. const status = stockInLineInfo.status;
  190. return status == "received";
  191. } else return false;
  192. };
  193. // Get show putaway
  194. const showPutaway = useMemo(() => {
  195. if (stockInLineInfo) {
  196. const status = stockInLineInfo.status;
  197. return status !== StockInStatus.PENDING && status !== StockInStatus.ESCALATED && status !== StockInStatus.REJECTED;
  198. }
  199. return false;
  200. }, [stockInLineInfo]);
  201. // Get is view only
  202. const viewOnly = useMemo(() => {
  203. if (stockInLineInfo) {
  204. if (stockInLineInfo.status) {
  205. const status = stockInLineInfo.status;
  206. const isViewOnly = status.toLowerCase() == StockInStatus.COMPLETED
  207. || status.toLowerCase() == StockInStatus.PARTIALLY_COMPLETED // TODO update DB
  208. || status.toLowerCase() == StockInStatus.REJECTED
  209. || (status.toLowerCase() == StockInStatus.ESCALATED && session?.id != stockInLineInfo.handlerId)
  210. if (showPutaway) { setTabIndex(1); } else { setTabIndex(0); }
  211. return isViewOnly;
  212. }
  213. }
  214. return true;
  215. }, [stockInLineInfo])
  216. const [openPutaway, setOpenPutaway] = useState(false);
  217. const onOpenPutaway = useCallback(() => {
  218. setOpenPutaway(true);
  219. }, []);
  220. const onClosePutaway = useCallback(() => {
  221. setOpenPutaway(false);
  222. }, []);
  223. // Stock In submission handler
  224. const onSubmitStockIn = useCallback<SubmitHandler<ModalFormInput>>(
  225. async (data, event) => {
  226. console.log("Stock In Submission:", event!.nativeEvent);
  227. // Extract only stock-in related fields
  228. const stockInData = {
  229. // quantity: data.quantity,
  230. // receiptDate: data.receiptDate,
  231. // batchNumber: data.batchNumber,
  232. // expiryDate: data.expiryDate,
  233. // warehouseId: data.warehouseId,
  234. // location: data.location,
  235. // unitCost: data.unitCost,
  236. data: data,
  237. // Add other stock-in specific fields from your form
  238. };
  239. console.log("Stock In Data:", stockInData);
  240. // Handle stock-in submission logic here
  241. // e.g., call API, update state, etc.
  242. },
  243. [],
  244. );
  245. // QC submission handler
  246. const onSubmitErrorQc = useCallback<SubmitErrorHandler<ModalFormInput>>(
  247. async (data, event) => {
  248. console.log("Error", data);
  249. }, []
  250. );
  251. // QC submission handler
  252. const onSubmitQc = useCallback<SubmitHandler<ModalFormInput>>(
  253. async (data, event) => {
  254. console.log("QC Submission:", event!.nativeEvent);
  255. console.log("Validating:", data.qcResult);
  256. // TODO: Move validation into QC page
  257. // if (errors.length > 0) {
  258. // alert(`未完成品檢: ${errors.map((err) => err[1].message)}`);
  259. // return;
  260. // }
  261. // Get QC data from the shared form context
  262. const qcAccept = data.qcDecision == 1;
  263. // const qcAccept = data.qcAccept;
  264. let acceptQty = Number(data.acceptQty);
  265. const qcResults = data.qcResult?.filter((qc) => qc.escalationLogId === undefined) || []; // Remove old QC data
  266. // const qcResults = data.qcResult as PurchaseQcResult[]; // qcItems;
  267. // const qcResults = viewOnly? data.qcResult as PurchaseQcResult[] : qcItems;
  268. // Validate QC data
  269. const validationErrors : string[] = [];
  270. // Check if failed items have failed quantity
  271. const failedItemsWithoutQty = qcResults.filter(item =>
  272. item.qcPassed === false && (!item.failQty || item.failQty <= 0)
  273. );
  274. if (failedItemsWithoutQty.length > 0) {
  275. validationErrors.push(`${t("Failed items must have failed quantity")}`);
  276. // validationErrors.push(`${t("Failed items must have failed quantity")}: ${failedItemsWithoutQty.map(item => item.code).join(', ')}`);
  277. }
  278. // Check if QC accept decision is made
  279. if (data.qcDecision === undefined) {
  280. // if (qcAccept === undefined) {
  281. validationErrors.push(t("QC decision is required"));
  282. }
  283. // Check if accept quantity is valid
  284. if (data.qcDecision == 2) {
  285. acceptQty = 0;
  286. } else {
  287. if (acceptQty === undefined || acceptQty <= 0) {
  288. validationErrors.push("Accept quantity must be greater than 0");
  289. }
  290. }
  291. // Check if dates are input
  292. // if (data.productionDate === undefined || data.productionDate == null) {
  293. // alert("請輸入生產日期!");
  294. // return;
  295. // }
  296. if (data.expiryDate === undefined || data.expiryDate == null) {
  297. alert("請輸入到期日!");
  298. return;
  299. }
  300. if (!qcResults.every((qc) => qc.qcPassed) && qcAccept && stockInLineInfo?.status != StockInStatus.ESCALATED) { //TODO: fix it please!
  301. validationErrors.push("有不合格檢查項目,無法收貨!");
  302. // submitDialogWithWarning(() => postStockInLineWithQc(qcData), t, {title:"有不合格檢查項目,確認接受收貨?",
  303. // confirmButtonText: t("confirm putaway"), html: ""});
  304. // return;
  305. }
  306. // Check if all QC items have results
  307. const itemsWithoutResult = qcResults.filter(item => item.qcPassed === undefined);
  308. if (itemsWithoutResult.length > 0 && stockInLineInfo?.status != StockInStatus.ESCALATED) { //TODO: fix it please!
  309. validationErrors.push(`${t("QC items without result")}`);
  310. // validationErrors.push(`${t("QC items without result")}: ${itemsWithoutResult.map(item => item.code).join(', ')}`);
  311. }
  312. if (validationErrors.length > 0 && !skipQc) {
  313. console.error("QC Validation failed:", validationErrors);
  314. alert(`未完成品檢: ${validationErrors}`);
  315. return;
  316. }
  317. const qcData = {
  318. dnNo : data.dnNo? data.dnNo : "DN00000",
  319. // dnDate : data.dnDate? arrayToDateString(data.dnDate, "input") : dayjsToInputDateString(dayjs()),
  320. productionDate : arrayToDateString(data.productionDate, "input"),
  321. expiryDate : arrayToDateString(data.expiryDate, "input"),
  322. receiptDate : arrayToDateString(data.receiptDate, "input"),
  323. qcAccept: qcAccept? qcAccept : false,
  324. acceptQty: acceptQty? acceptQty : 0,
  325. // qcResult: itemDetail.status != "escalated" ? qcResults.map(item => ({
  326. qcResult: qcResults.map(item => ({
  327. // id: item.id,
  328. qcItemId: item.qcItemId,
  329. // code: item.code,
  330. // qcDescription: item.qcDescription,
  331. qcPassed: item.qcPassed? item.qcPassed : false,
  332. failQty: (item.failQty && !item.qcPassed) ? item.failQty : 0,
  333. // failedQty: (typeof item.failedQty === "number" && !item.isPassed) ? item.failedQty : 0,
  334. remarks: item.remarks || '',
  335. })),
  336. };
  337. // const qcData = data;
  338. console.log("QC Data for submission:", qcData);
  339. if (data.qcDecision == 3) { // Escalate
  340. if (data.escalationLog?.handlerId == undefined) { alert("請選擇上報負責同事!"); return; }
  341. else if (data.escalationLog?.handlerId < 1) { alert("上報負責同事資料有誤"); return; }
  342. const escalationLog = {
  343. type : "qc",
  344. status : "pending", // TODO: update with supervisor decision
  345. reason : data.escalationLog?.reason,
  346. recordDate : dayjsToDateTimeString(dayjs()),
  347. handlerId : data.escalationLog?.handlerId,
  348. }
  349. console.log("Escalation Data for submission", escalationLog);
  350. setIsSubmitting(true); //TODO improve
  351. await postStockInLine({...qcData, escalationLog});
  352. } else {
  353. setIsSubmitting(true); //TODO improve
  354. await postStockInLine(qcData);
  355. }
  356. if (qcData.qcAccept) {
  357. // submitDialogWithWarning(onOpenPutaway, t, {title:"Save success, confirm to proceed?",
  358. // confirmButtonText: t("confirm putaway"), html: ""});
  359. // onOpenPutaway();
  360. closeHandler({}, "backdropClick");
  361. // setTabIndex(1); // Need to go Putaway tab?
  362. } else {
  363. closeHandler({}, "backdropClick");
  364. }
  365. setIsSubmitting(false);
  366. msg("已更新來貨狀態");
  367. return ;
  368. },
  369. [onOpenPutaway, formProps.formState.errors],
  370. );
  371. const postStockInLine = useCallback(async (args: ModalFormInput) => {
  372. const submitData = {
  373. ...stockInLineInfo, ...args
  374. } as StockInLineEntry & ModalFormInput;
  375. console.log("Submitting", submitData);
  376. const res = await updateStockInLine(submitData);
  377. return res;
  378. }, [stockInLineInfo])
  379. // Put away model
  380. const [pafRowModesModel, setPafRowModesModel] = useState<GridRowModesModel>({})
  381. const [pafRowSelectionModel, setPafRowSelectionModel] = useState<GridRowSelectionModel>([])
  382. const pafSubmitDisable = useMemo(() => {
  383. return Object.entries(pafRowModesModel).length > 0 || Object.entries(pafRowModesModel).some(([key, value], index) => value.mode === GridRowModes.Edit)
  384. }, [pafRowModesModel])
  385. // Putaway submission handler
  386. const onSubmitPutaway = useCallback<SubmitHandler<ModalFormInput>>(
  387. async (data, event) => {
  388. // Extract only putaway related fields
  389. const putawayData = {
  390. acceptQty: Number(data.acceptQty?? (stockInLineInfo?.demandQty?? (stockInLineInfo?.acceptedQty))), //TODO improve
  391. warehouseId: data.warehouseId,
  392. status: data.status, //TODO Fix it!
  393. // ...data,
  394. // dnDate : data.dnDate? arrayToDateString(data.dnDate, "input") : dayjsToInputDateString(dayjs()),
  395. productionDate : arrayToDateString(data.productionDate, "input"),
  396. expiryDate : arrayToDateString(data.expiryDate, "input"),
  397. receiptDate : arrayToDateString(data.receiptDate, "input"),
  398. // for putaway data
  399. inventoryLotLines: data.putAwayLines?.filter((line) => line._isNew !== false)
  400. // Add other putaway specific fields
  401. } as ModalFormInput;
  402. console.log("Putaway Data:", putawayData);
  403. console.log("DEBUG",data.putAwayLines);
  404. // if (data.putAwayLines!!.filter((line) => line._isNew !== false).length <= 0) {
  405. // alert("請新增上架資料!");
  406. // return;
  407. // }
  408. if (data.putAwayLines!!.filter((line) => /[^0-9]/.test(String(line.qty))).length > 0) { //TODO Improve
  409. alert("上架數量不正確!");
  410. return;
  411. }
  412. if (data.putAwayLines!!.reduce((acc, cur) => acc + Number(cur.qty), 0) > putawayData.acceptQty!!) {
  413. alert(`上架數量不能大於 ${putawayData.acceptQty}!`);
  414. return;
  415. }
  416. // Handle putaway submission logic here
  417. const res = await postStockInLine(putawayData);
  418. console.log("result ", res);
  419. // Close modal after successful putaway
  420. closeHandler({}, "backdropClick");
  421. },
  422. [closeHandler],
  423. );
  424. // Print handler
  425. useEffect(() => {
  426. if (!printerCombo.length) return;
  427. if (typeof window === "undefined") {
  428. setSelectedPrinter(printerCombo[0]);
  429. return;
  430. }
  431. const savedId = sessionStorage.getItem(printerStorageKey);
  432. const matched = savedId ? printerCombo.find(p => p.id === Number(savedId)) : undefined;
  433. setSelectedPrinter(matched ?? printerCombo[0]);
  434. }, [printerCombo, printerStorageKey]);
  435. const [isPrinting, setIsPrinting] = useState(false)
  436. const handlePrint = useCallback(async () => {
  437. // console.log("Print putaway documents");
  438. console.log("%c data", "background: white; color: red", formProps.watch("putAwayLines"));
  439. // Handle print logic here
  440. // window.print();
  441. // const postData = { stockInLineIds: [itemDetail.id]};
  442. // const response = await fetchPoQrcode(postData);
  443. // if (response) {
  444. // downloadFile(new Uint8Array(response.blobValue), response.filename)
  445. // }
  446. try {
  447. setIsPrinting(() => true)
  448. if ((formProps.watch("putAwayLines") ?? []).filter((line) => /[^0-9]/.test(String(line.printQty))).length > 0) { //TODO Improve
  449. alert("列印數量不正確!");
  450. return;
  451. }
  452. // Conditionally call different APIs based on source
  453. let response;
  454. if (printSource === "productionProcess") {
  455. // Use FG Stock In Label print API for production process
  456. const data: PrintFGStockInLabelRequest = {
  457. stockInLineId: stockInLineInfo?.id ?? 0,
  458. printerId: selectedPrinter.id,
  459. printQty: printQty
  460. }
  461. response = await printFGStockInLabel(data);
  462. } else {
  463. // Use stock-in print API (default)
  464. const data: PrintQrCodeForSilRequest = {
  465. stockInLineId: stockInLineInfo?.id ?? 0,
  466. printerId: selectedPrinter.id,
  467. printQty: printQty
  468. }
  469. response = await printQrCodeForSil(data);
  470. }
  471. if (response) {
  472. console.log(response)
  473. }
  474. if (typeof window !== 'undefined' && selectedPrinter) {
  475. sessionStorage.setItem(printerStorageKey, String(selectedPrinter.id));
  476. }
  477. } finally {
  478. setIsPrinting(() => false)
  479. }
  480. }, [stockInLineInfo?.id, pafRowSelectionModel, printQty, selectedPrinter, printSource]);
  481. // const checkQcIsPassed = useCallback((qcItems: PurchaseQcResult[]) => {
  482. // const isPassed = qcItems.every((qc) => qc.qcPassed);
  483. // console.log(isPassed)
  484. // if (isPassed) {
  485. // formProps.setValue("passingQty", acceptQty)
  486. // } else {
  487. // formProps.setValue("passingQty", 0)
  488. // }
  489. // return isPassed
  490. // }, [acceptQty, formProps])
  491. const printQrcode = useCallback(
  492. async () => {
  493. setIsPrinting(true);
  494. try {
  495. const postData = { stockInLineIds: [stockInLineInfo?.id] };
  496. const response = await fetchPoQrcode(postData);
  497. if (response) {
  498. console.log(response);
  499. downloadFile(new Uint8Array(response.blobValue), response.filename!);
  500. }
  501. } catch (e) {
  502. console.log("%c Error downloading QR Code", "color:red", e);
  503. } finally {
  504. setIsPrinting(false);
  505. }
  506. },
  507. [stockInLineInfo],
  508. );
  509. return (
  510. <>
  511. <FormProvider {...formProps}>
  512. <Modal open={open} onClose={closeHandler}>
  513. <Box
  514. sx={{
  515. ...style,
  516. // padding: 2,
  517. maxHeight: "90vh",
  518. overflowY: "auto",
  519. marginLeft: 3,
  520. marginRight: 3,
  521. // overflow: "hidden",
  522. display: 'flex',
  523. flexDirection: 'column',
  524. }}
  525. >
  526. {(!isLoading && stockInLineInfo) ? (<>
  527. <Box sx={{ position: 'sticky', top: 0, bgcolor: 'background.paper',
  528. zIndex: 5, borderBottom: 2, borderColor: 'divider', width: "100%"}}>
  529. <Tabs
  530. value={tabIndex}
  531. onChange={handleTabChange}
  532. variant="scrollable"
  533. sx={{pl: 2, pr: 2, pt: 2}}
  534. >
  535. <Tab label={
  536. showPutaway ? t("dn and qc info") : t("qc processing")
  537. } iconPosition="end" />
  538. {showPutaway && <Tab label={t("putaway processing")} iconPosition="end" />}
  539. </Tabs>
  540. </Box>
  541. <Grid
  542. container
  543. justifyContent="flex-start"
  544. alignItems="flex-start"
  545. sx={{padding: 2}}
  546. >
  547. <Grid item xs={12}>
  548. {tabIndex === 0 &&
  549. <Box>
  550. <Grid item xs={12}>
  551. <Typography variant="h6" display="block" marginBlockEnd={1}>
  552. {t("Delivery Detail")}
  553. </Typography>
  554. </Grid>
  555. {stockInLineInfo.jobOrderId ? (
  556. <FgStockInForm itemDetail={stockInLineInfo} disabled={viewOnly || showPutaway} />
  557. ) : (
  558. <StockInForm itemDetail={stockInLineInfo} disabled={viewOnly || showPutaway} />
  559. )
  560. }
  561. {skipQc === false && (
  562. <QcComponent
  563. itemDetail={stockInLineInfo}
  564. disabled={viewOnly || showPutaway}
  565. />)
  566. }
  567. <Stack direction="row" justifyContent="flex-end" gap={1} sx={{pt:2}}>
  568. {(!viewOnly && !showPutaway) && (<Button
  569. id="Submit"
  570. type="button"
  571. variant="contained"
  572. color="primary"
  573. sx={{ mt: 1 }}
  574. onClick={formProps.handleSubmit(onSubmitQc, onSubmitErrorQc)}
  575. disabled={isSubmitting || isLoading}
  576. >
  577. {isSubmitting ? (t("submitting")) : (skipQc ? t("confirm") : t("confirm qc result"))}
  578. </Button>)}
  579. </Stack>
  580. </Box>
  581. }
  582. {tabIndex === 1 &&
  583. <Box>
  584. <PutAwayForm
  585. itemDetail={stockInLineInfo}
  586. warehouse={warehouse!}
  587. disabled={viewOnly}
  588. setRowModesModel={setPafRowModesModel}
  589. setRowSelectionModel={setPafRowSelectionModel}
  590. />
  591. </Box>
  592. }
  593. </Grid>
  594. </Grid>
  595. {tabIndex == 1 && (
  596. <Stack direction="row" justifyContent="flex-end" gap={1} sx={{m:3, mt:"auto"}}>
  597. <Autocomplete
  598. disableClearable
  599. options={printerCombo}
  600. defaultValue={selectedPrinter}
  601. onChange={(event, value) => {
  602. setSelectedPrinter(value)
  603. }}
  604. renderInput={(params) => (
  605. <TextField
  606. {...params}
  607. variant="outlined"
  608. label={t("Printer")}
  609. sx={{ width: 300}}
  610. />
  611. )}
  612. />
  613. <TextField
  614. variant="outlined"
  615. label={t("Print Qty")}
  616. defaultValue={printQty}
  617. onChange={(event) => {
  618. event.target.value = event.target.value.replace(/[^0-9]/g, '')
  619. setPrintQty(Number(event.target.value))
  620. }}
  621. sx={{ width: 300}}
  622. />
  623. <Button
  624. id="printButton"
  625. type="button"
  626. variant="contained"
  627. color="primary"
  628. sx={{ mt: 1 }}
  629. onClick={handlePrint}
  630. disabled={isPrinting || printerCombo.length <= 0 || pafSubmitDisable}
  631. >
  632. {isPrinting ? t("Printing") : t("print")}
  633. </Button>
  634. <Button
  635. id="demoPrint"
  636. type="button"
  637. variant="contained"
  638. color="primary"
  639. sx={{ mt: 1 }}
  640. onClick={printQrcode}
  641. disabled={isPrinting}
  642. >
  643. {isPrinting ? t("downloading") : t("download Qr Code")}
  644. </Button>
  645. </Stack>
  646. )}
  647. </>) : <LoadingComponent/>}
  648. </Box>
  649. </Modal>
  650. </FormProvider>
  651. </>
  652. );
  653. };
  654. export default QcStockInModal;