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

QcStockInModal.tsx 26 KiB

1ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
3週間前
3週間前
3週間前
1ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
3週間前
3週間前
3週間前
3週間前
3週間前
3週間前
1ヶ月前
1ヶ月前
3週間前
1ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  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;