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.

QcStockInModal.tsx 26 KiB

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