|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853 |
- "use client";
-
- import {
- Box,
- Button,
- Stack,
- Typography,
- Chip,
- CircularProgress,
- Table,
- TableBody,
- TableCell,
- TableContainer,
- TableHead,
- TableRow,
- Paper,
- TextField,
- TablePagination,
- Select, // Add this
- MenuItem, // Add this
- FormControl, // Add this
- InputLabel,
- } from "@mui/material";
- import { SelectChangeEvent } from "@mui/material/Select";
- import { useState, useCallback, useEffect, useRef } from "react";
- import { useTranslation } from "react-i18next";
- import {
- AllPickedStockTakeListReponse,
- getInventoryLotDetailsBySection,
- InventoryLotDetailResponse,
- saveStockTakeRecord,
- SaveStockTakeRecordRequest,
- BatchSaveStockTakeRecordRequest,
- batchSaveStockTakeRecords,
- } from "@/app/api/stockTake/actions";
- import { useSession } from "next-auth/react";
- import { SessionWithTokens } from "@/config/authConfig";
- import dayjs from "dayjs";
- import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
-
- interface PickerStockTakeProps {
- selectedSession: AllPickedStockTakeListReponse;
- onBack: () => void;
- onSnackbar: (message: string, severity: "success" | "error" | "warning") => void;
- }
-
- const PickerStockTake: React.FC<PickerStockTakeProps> = ({
- selectedSession,
- onBack,
- onSnackbar,
- }) => {
- const { t } = useTranslation(["inventory", "common"]);
- const { data: session } = useSession() as { data: SessionWithTokens | null };
-
- const [inventoryLotDetails, setInventoryLotDetails] = useState<InventoryLotDetailResponse[]>([]);
- const [loadingDetails, setLoadingDetails] = useState(false);
-
- const [recordInputs, setRecordInputs] = useState<Record<number, {
- firstQty: string;
- secondQty: string;
- firstBadQty: string;
- secondBadQty: string;
- remark: string;
- }>>({});
- const [savingRecordId, setSavingRecordId] = useState<number | null>(null);
- const [remark, setRemark] = useState<string>("");
- const [saving, setSaving] = useState(false);
- const [batchSaving, setBatchSaving] = useState(false);
- const [shortcutInput, setShortcutInput] = useState<string>("");
- const [page, setPage] = useState(0);
- const [pageSize, setPageSize] = useState<number | string>("all");
-
- const [total, setTotal] = useState(0);
- const totalPages = pageSize === "all" ? 1 : Math.ceil(total / (pageSize as number));
-
- const currentUserId = session?.id ? parseInt(session.id) : undefined;
- const handleBatchSubmitAllRef = useRef<() => Promise<void>>();
- const handleChangePage = useCallback((event: unknown, newPage: number) => {
- setPage(newPage);
- }, []);
- const handlePageSelectChange = useCallback((event: SelectChangeEvent<number>) => {
- const newPage = parseInt(event.target.value as string, 10) - 1; // Convert to 0-indexed
- setPage(Math.max(0, Math.min(newPage, totalPages - 1)));
- }, [totalPages]);
-
- const handleChangeRowsPerPage = useCallback((event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
- const newSize = parseInt(event.target.value, 10);
- if (newSize === -1) {
- setPageSize("all");
- } else if (!isNaN(newSize)) {
- setPageSize(newSize);
- }
- setPage(0);
- }, []);
- const loadDetails = useCallback(async (
- pageNum: number,
- size: number | string,
- options?: { silent?: boolean }
- ) => {
- console.log('loadDetails called with:', { pageNum, size, selectedSessionTotal: selectedSession.totalInventoryLotNumber });
- setLoadingDetails(true);
- try {
- let actualSize: number;
- if (size === "all") {
- // Use totalInventoryLotNumber from selectedSession if available
- if (selectedSession.totalInventoryLotNumber > 0) {
- actualSize = selectedSession.totalInventoryLotNumber;
- console.log('Using "all" - actualSize set to totalInventoryLotNumber:', actualSize);
- } else if (total > 0) {
- // Fallback to total from previous response
- actualSize = total;
- console.log('Using "all" - actualSize set to total from state:', actualSize);
- } else {
- // Last resort: use a large number
- actualSize = 10000;
- console.log('Using "all" - actualSize set to default 10000');
- }
- } else {
- actualSize = typeof size === 'string' ? parseInt(size, 10) : size;
- console.log('Using specific size - actualSize set to:', actualSize);
- }
-
- console.log('Calling getInventoryLotDetailsBySection with actualSize:', actualSize);
- const response = await getInventoryLotDetailsBySection(
- selectedSession.stockTakeSession,
- selectedSession.stockTakeId > 0 ? selectedSession.stockTakeId : null,
- pageNum,
- actualSize,
- selectedSession.stockTakeRoundId != null && selectedSession.stockTakeRoundId > 0
- ? selectedSession.stockTakeRoundId
- : null
- );
- setInventoryLotDetails(Array.isArray(response.records) ? response.records : []);
- setTotal(response.total || 0);
- } catch (e) {
- console.error(e);
- setInventoryLotDetails([]);
- setTotal(0);
- } finally {
- setLoadingDetails(false);
- }
- }, [selectedSession, total]);
-
- useEffect(() => {
- setRecordInputs((prev) => {
- const next: Record<number, { firstQty: string; secondQty: string; firstBadQty: string; secondBadQty: string; remark: string }> = {};
- inventoryLotDetails.forEach((detail) => {
- const hasServerFirst = detail.firstStockTakeQty != null;
- const hasServerSecond = detail.secondStockTakeQty != null;
- const firstTotal = hasServerFirst
- ? (detail.firstStockTakeQty! + (detail.firstBadQty ?? 0)).toString()
- : "";
- const secondTotal = hasServerSecond
- ? (detail.secondStockTakeQty! + (detail.secondBadQty ?? 0)).toString()
- : "";
- const existing = prev[detail.id];
- next[detail.id] = {
- firstQty: hasServerFirst ? firstTotal : (existing?.firstQty ?? firstTotal),
- secondQty: hasServerSecond ? secondTotal : (existing?.secondQty ?? secondTotal),
- firstBadQty: hasServerFirst ? (detail.firstBadQty?.toString() || "") : (existing?.firstBadQty ?? ""),
- secondBadQty: hasServerSecond ? (detail.secondBadQty?.toString() || "") : (existing?.secondBadQty ?? ""),
- remark: hasServerSecond ? (detail.remarks || "") : (existing?.remark ?? detail.remarks ?? ""),
- };
- });
- return next;
- });
- }, [inventoryLotDetails]);
- useEffect(() => {
- loadDetails(page, pageSize);
- }, [page, pageSize, loadDetails]);
-
- const formatNumber = (num: number | null | undefined): string => {
- if (num == null || Number.isNaN(num)) return "0";
- return num.toLocaleString("en-US", {
- minimumFractionDigits: 0,
- maximumFractionDigits: 0,
- });
- };
-
- const handleSaveStockTake = useCallback(
- async (detail: InventoryLotDetailResponse) => {
- if (!selectedSession || !currentUserId) {
- return;
- }
-
- const isFirstSubmit = !detail.stockTakeRecordId || !detail.firstStockTakeQty;
- const isSecondSubmit =
- detail.stockTakeRecordId && detail.firstStockTakeQty && !detail.secondStockTakeQty;
-
- // 现在用户输入的是 total 和 bad,需要算 available = total - bad
- const totalQtyStr = isFirstSubmit ? recordInputs[detail.id]?.firstQty : recordInputs[detail.id]?.secondQty;
- const badQtyStr = isFirstSubmit ? recordInputs[detail.id]?.firstBadQty : recordInputs[detail.id]?.secondBadQty;
-
- // 只檢查 totalQty,Bad Qty 未輸入時預設為 0
- if (!totalQtyStr) {
- onSnackbar(
- isFirstSubmit
- ? t("Please enter QTY")
- : t("Please enter Second QTY"),
- "error"
- );
- return;
- }
-
- const totalQty = parseFloat(totalQtyStr);
- const badQty = parseFloat(badQtyStr || "0") || 0; // 空字串時為 0
-
- if (Number.isNaN(totalQty)) {
- onSnackbar(t("Invalid QTY"), "error");
- return;
- }
-
- const availableQty = totalQty - badQty;
-
- if (availableQty < 0) {
- onSnackbar(t("Available QTY cannot be negative"), "error");
- return;
- }
-
- setSaving(true);
- try {
- const request: SaveStockTakeRecordRequest = {
- stockTakeRecordId: detail.stockTakeRecordId || null,
- inventoryLotLineId: detail.id,
- qty: availableQty, // 保存 available qty
- badQty: badQty, // 保存 bad qty
- remark: isSecondSubmit ? (recordInputs[detail.id]?.remark || null) : null,
- };
- console.log("handleSaveStockTake: request:", request);
- console.log("handleSaveStockTake: selectedSession.stockTakeId:", selectedSession.stockTakeId);
- console.log("handleSaveStockTake: currentUserId:", currentUserId);
-
- await saveStockTakeRecord(request, selectedSession.stockTakeId, currentUserId);
-
- onSnackbar(t("Stock take record saved successfully"), "success");
-
- //await loadDetails(page, pageSize, { silent: true });
- setInventoryLotDetails((prev) =>
- prev.map((d) =>
- d.id === detail.id
- ? {
- ...d,
- stockTakeRecordId: d.stockTakeRecordId ?? null, // 首次儲存後可從 response 取得,此處先保留
- firstStockTakeQty: isFirstSubmit ? availableQty : d.firstStockTakeQty,
- firstBadQty: isFirstSubmit ? badQty : d.firstBadQty ?? null,
- secondStockTakeQty: isSecondSubmit ? availableQty : d.secondStockTakeQty,
- secondBadQty: isSecondSubmit ? badQty : d.secondBadQty ?? null,
- remarks: isSecondSubmit ? (recordInputs[detail.id]?.remark || null) : d.remarks,
- stockTakeRecordStatus: "pass",
- }
- : d
- )
- );
- } catch (e: any) {
- console.error("Save stock take record error:", e);
- let errorMessage = t("Failed to save stock take record");
-
- if (e?.message) {
- errorMessage = e.message;
- } else if (e?.response) {
- try {
- const errorData = await e.response.json();
- errorMessage = errorData.message || errorData.error || errorMessage;
- } catch {
- // ignore
- }
- }
-
- onSnackbar(errorMessage, "error");
- } finally {
- setSaving(false);
- }
- },
- [
- selectedSession,
- recordInputs,
- remark,
- t,
- currentUserId,
- onSnackbar,
- ]
- );
-
- const handleBatchSubmitAll = useCallback(
- async () => {
- if (!selectedSession || !currentUserId) {
- console.log("handleBatchSubmitAll: Missing selectedSession or currentUserId");
- return;
- }
-
- console.log("handleBatchSubmitAll: Starting batch save...");
- setBatchSaving(true);
- try {
- const request: BatchSaveStockTakeRecordRequest = {
- stockTakeId: selectedSession.stockTakeId,
- stockTakeSection: selectedSession.stockTakeSession,
- stockTakerId: currentUserId,
- };
-
- const result = await batchSaveStockTakeRecords(request);
- console.log("handleBatchSubmitAll: Result:", result);
-
- onSnackbar(
- t("Batch save completed: {{success}} success, {{errors}} errors", {
- success: result.successCount,
- errors: result.errorCount,
- }),
- result.errorCount > 0 ? "warning" : "success"
- );
-
- await loadDetails(page, pageSize);
- } catch (e: any) {
- console.error("handleBatchSubmitAll: Error:", e);
- let errorMessage = t("Failed to batch save stock take records");
-
- if (e?.message) {
- errorMessage = e.message;
- } else if (e?.response) {
- try {
- const errorData = await e.response.json();
- errorMessage = errorData.message || errorData.error || errorMessage;
- } catch {
- // ignore
- }
- }
-
- onSnackbar(errorMessage, "error");
- } finally {
- setBatchSaving(false);
- }
- },
- [selectedSession, t, currentUserId, onSnackbar]
- );
-
- useEffect(() => {
- handleBatchSubmitAllRef.current = handleBatchSubmitAll;
- }, [handleBatchSubmitAll]);
-
- useEffect(() => {
- const handleKeyPress = (e: KeyboardEvent) => {
- const target = e.target as HTMLElement;
- if (
- target &&
- (target.tagName === "INPUT" ||
- target.tagName === "TEXTAREA" ||
- target.isContentEditable)
- ) {
- return;
- }
-
- if (e.ctrlKey || e.metaKey || e.altKey) {
- return;
- }
-
- if (e.key.length === 1) {
- setShortcutInput((prev) => {
- const newInput = prev + e.key;
-
- if (newInput === "{2fitestall}") {
- console.log("✅ Shortcut {2fitestall} detected!");
- setTimeout(() => {
- if (handleBatchSubmitAllRef.current) {
- console.log("Calling handleBatchSubmitAll...");
- handleBatchSubmitAllRef.current().catch((err) => {
- console.error("Error in handleBatchSubmitAll:", err);
- });
- } else {
- console.error("handleBatchSubmitAllRef.current is null");
- }
- }, 0);
- return "";
- }
-
- if (newInput.length > 15) {
- return "";
- }
-
- if (newInput.length > 0 && !newInput.startsWith("{")) {
- return "";
- }
-
- if (newInput.length > 5 && !newInput.startsWith("{2fi")) {
- return "";
- }
-
- return newInput;
- });
- } else if (e.key === "Backspace") {
- setShortcutInput((prev) => prev.slice(0, -1));
- } else if (e.key === "Escape") {
- setShortcutInput("");
- }
- };
-
- window.addEventListener("keydown", handleKeyPress);
- return () => {
- window.removeEventListener("keydown", handleKeyPress);
- };
- }, []);
- const blockNonIntegerKeys = (e: React.KeyboardEvent<HTMLInputElement>) => {
- // 禁止小数点、逗号、科学计数、正负号
- if ([".", ",", "e", "E", "+", "-"].includes(e.key)) {
- e.preventDefault();
- }
- };
-
- const sanitizeIntegerInput = (value: string) => {
- // 只保留数字
- return value.replace(/[^\d]/g, "");
- };
-
- const isIntegerString = (value: string) => /^\d+$/.test(value);
- const isSubmitDisabled = useCallback((detail: InventoryLotDetailResponse): boolean => {
- if (selectedSession?.status?.toLowerCase() === "completed") {
- return true;
- }
- const recordStatus = detail.stockTakeRecordStatus?.toLowerCase();
- if (recordStatus === "pass" || recordStatus === "completed") {
- return true;
- }
- return false;
- }, [selectedSession?.status]);
- const handleSubmitAllInputted = useCallback(async () => {
- if (!selectedSession || !currentUserId) return;
-
- const toSave = inventoryLotDetails.filter((detail) => {
- const submitDisabled = isSubmitDisabled(detail);
- if (submitDisabled) return false;
- const isFirstSubmit = !detail.stockTakeRecordId || !detail.firstStockTakeQty;
- const isSecondSubmit = detail.stockTakeRecordId && detail.firstStockTakeQty && !detail.secondStockTakeQty;
- const totalQtyStr = isFirstSubmit ? recordInputs[detail.id]?.firstQty : recordInputs[detail.id]?.secondQty;
- return !!totalQtyStr && totalQtyStr.trim() !== "";
- });
-
- if (toSave.length === 0) {
- onSnackbar(t("No valid input to submit"), "warning");
- return;
- }
-
- setBatchSaving(true);
- let successCount = 0;
- let errorCount = 0;
- for (const detail of toSave) {
- try {
- await handleSaveStockTake(detail);
- successCount++;
- } catch {
- errorCount++;
- }
- }
- setBatchSaving(false);
- onSnackbar(
- t("Submit completed: {{success}} success, {{errors}} errors", { success: successCount, errors: errorCount }),
- errorCount > 0 ? "warning" : "success"
- );
- }, [inventoryLotDetails, recordInputs, isSubmitDisabled, handleSaveStockTake, selectedSession, currentUserId, onSnackbar, t]);
- const uniqueWarehouses = Array.from(
- new Set(
- inventoryLotDetails
- .map((detail) => detail.warehouse)
- .filter((warehouse) => warehouse && warehouse.trim() !== "")
- )
- ).join(", ");
-
- return (
- <Box>
- <Button
- onClick={onBack}
- sx={{ mb: 2, border: "1px solid", borderColor: "primary.main" }}
- >
- {t("Back to List")}
- </Button>
- <Typography variant="h6" sx={{ mb: 2 }}>
- {t("Stock Take Section")}: {selectedSession.stockTakeSession}
- {uniqueWarehouses && (
- <> {t("Warehouse")}: {uniqueWarehouses}</>
- )}
- </Typography>
- {/*
- <Button
- variant="contained"
- onClick={handleSubmitAllInputted}
- disabled={batchSaving || saving}
- >
- {t("Submit All Inputted")}
- </Button>
- */}
- {/* 如果需要显示快捷键输入,可以把这块注释打开 */}
- {/*
- {shortcutInput && (
- <Box
- sx={{
- mb: 2,
- p: 1.5,
- bgcolor: "info.light",
- borderRadius: 1,
- border: "1px solid",
- borderColor: "info.main",
- }}
- >
- <Typography variant="body2" color="info.dark" fontWeight={500}>
- {t("Shortcut Input")}:{" "}
- <strong style={{ fontFamily: "monospace", fontSize: "1.1em" }}>
- {shortcutInput}
- </strong>
- </Typography>
- </Box>
- )}
- */}
- {loadingDetails ? (
- <Box sx={{ display: "flex", justifyContent: "center", p: 3 }}>
- <CircularProgress />
- </Box>
- ) : (
- <>
- <TablePagination
-
- component="div"
- count={total}
- page={page}
- onPageChange={handleChangePage}
- rowsPerPage={pageSize === "all" ? total : (pageSize as number)}
- onRowsPerPageChange={handleChangeRowsPerPage}
- rowsPerPageOptions={[10, 25, 50, 100, { value: -1, label: t("All") }]}
- labelRowsPerPage={t("Rows per page")}
- />
- <TableContainer component={Paper}>
- <Table>
- <TableHead>
- <TableRow>
- <TableCell>{t("Warehouse Location")}</TableCell>
- <TableCell>{t("Item-lotNo-ExpiryDate")}</TableCell>
- <TableCell>{t("UOM")}</TableCell>
- <TableCell>{t("Stock Take Qty(include Bad Qty)= Available Qty")}</TableCell>
- <TableCell>{t("Action")}</TableCell>
- <TableCell>{t("Remark")}</TableCell>
- <TableCell>{t("Record Status")}</TableCell>
-
- </TableRow>
- </TableHead>
- <TableBody>
- {inventoryLotDetails.length === 0 ? (
- <TableRow>
- <TableCell colSpan={7} align="center">
- <Typography variant="body2" color="text.secondary">
- {t("No data")}
- </Typography>
- </TableCell>
- </TableRow>
- ) : (
- inventoryLotDetails.map((detail) => {
- const submitDisabled = isSubmitDisabled(detail);
- const isFirstSubmit =
- !detail.stockTakeRecordId || !detail.firstStockTakeQty;
- const isSecondSubmit =
- detail.stockTakeRecordId &&
- detail.firstStockTakeQty &&
- !detail.secondStockTakeQty;
-
- return (
- <TableRow key={detail.id}>
- <TableCell>
- {detail.warehouseArea || "-"}
- {detail.warehouseSlot || "-"}
- </TableCell>
- <TableCell
- sx={{
- maxWidth: 150,
- wordBreak: "break-word",
- whiteSpace: "normal",
- lineHeight: 1.5,
- }}
- >
- <Stack spacing={0.5}>
- <Box>
- {detail.itemCode || "-"} {detail.itemName || "-"}
- </Box>
- <Box>{detail.lotNo || "-"}</Box>
- <Box>
- {detail.expiryDate
- ? dayjs(detail.expiryDate).format(OUTPUT_DATE_FORMAT)
- : "-"}
- </Box>
- </Stack>
- </TableCell>
- <TableCell>{detail.uom || "-"}</TableCell>
- {/* Qty + Bad Qty 合并显示/输入 */}
- <TableCell sx={{ minWidth: 300 }}>
- <Stack spacing={1}>
- {/* First */}
- {!submitDisabled && isFirstSubmit ? (
- <Stack direction="row" spacing={1} alignItems="center">
- <Typography variant="body2">{t("First")}:</Typography>
- <TextField
- size="small"
- type="number"
- value={recordInputs[detail.id]?.firstQty || ""}
- inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }}
- onKeyDown={blockNonIntegerKeys}
- onChange={(e) => {
- const clean = sanitizeIntegerInput(e.target.value);
- const val = clean;
- if (val.includes("-")) return;
- setRecordInputs(prev => ({ ...prev, [detail.id]: { ...prev[detail.id], firstQty: val } }));
- }}
- sx={{
- width: 130,
- minWidth: 130,
- "& .MuiInputBase-input": {
- height: "1.4375em",
- padding: "4px 8px",
- },
- "& .MuiInputBase-input::placeholder": {
- color: "grey.400", // MUI light grey
- opacity: 1,
- },
- }}
- placeholder={t("Stock Take Qty")}
- />
- <TextField
- size="small"
- type="number"
- value={recordInputs[detail.id]?.firstBadQty || ""}
- inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }}
- onKeyDown={blockNonIntegerKeys}
- onChange={(e) => {
- const clean = sanitizeIntegerInput(e.target.value);
- const val = clean;
- if (val.includes("-")) return;
- setRecordInputs(prev => ({ ...prev, [detail.id]: { ...prev[detail.id], firstBadQty: val } }));
- }}
- sx={{
- width: 130,
- minWidth: 130,
- "& .MuiInputBase-input": {
- height: "1.4375em",
- padding: "4px 8px",
- },
- "& .MuiInputBase-input::placeholder": {
- color: "grey.400", // MUI light grey
- opacity: 1,
- },
- }}
- placeholder={t("Bad Qty")}
- />
- <Typography variant="body2">
- =
- {formatNumber(
- parseFloat(recordInputs[detail.id]?.firstQty || "0") -
- parseFloat(recordInputs[detail.id]?.firstBadQty || "0")
- )}
- </Typography>
- </Stack>
- ) : detail.firstStockTakeQty != null ? (
- <Typography variant="body2">
- {t("First")}:{" "}
- {formatNumber(
- (detail.firstStockTakeQty ?? 0) +
- (detail.firstBadQty ?? 0)
- )}{" "}
- (
- {formatNumber(
- detail.firstBadQty ?? 0
- )}
- ) ={" "}
- {formatNumber(detail.firstStockTakeQty ?? 0)}
- </Typography>
- ) : null}
-
- {/* Second */}
- {!submitDisabled && isSecondSubmit ? (
- <Stack direction="row" spacing={1} alignItems="center">
- <Typography variant="body2">{t("Second")}:</Typography>
- <TextField
- size="small"
- type="number"
- value={recordInputs[detail.id]?.secondQty || ""}
- inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }}
- onKeyDown={blockNonIntegerKeys}
- onChange={(e) => {
- const clean = sanitizeIntegerInput(e.target.value);
- const val = clean;
- if (val.includes("-")) return;
- setRecordInputs(prev => ({ ...prev, [detail.id]: { ...prev[detail.id], secondQty: val } }));
- }}
- sx={{
- width: 130,
- minWidth: 130,
- "& .MuiInputBase-input": {
- height: "1.4375em",
- padding: "4px 8px",
- },
- }}
- placeholder={t("Stock Take Qty")}
- />
- <TextField
- size="small"
- type="number"
- value={recordInputs[detail.id]?.secondBadQty || ""}
- inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }}
- onKeyDown={blockNonIntegerKeys}
- onChange={(e) => {
- const clean = sanitizeIntegerInput(e.target.value);
- const val = clean;
- if (val.includes("-")) return;
- setRecordInputs(prev => ({ ...prev, [detail.id]: { ...prev[detail.id], secondBadQty: val } }));
- }}
- sx={{
- width: 130,
- minWidth: 130,
- "& .MuiInputBase-input": {
- height: "1.4375em",
- padding: "4px 8px",
- },
- }}
- placeholder={t("Bad Qty")}
- />
- <Typography variant="body2">
- =
- {formatNumber(
- parseFloat(recordInputs[detail.id]?.secondQty || "0") -
- parseFloat(recordInputs[detail.id]?.secondBadQty || "0")
- )}
- </Typography>
- </Stack>
- ) : detail.secondStockTakeQty != null ? (
- <Typography variant="body2">
- {t("Second")}:{" "}
- {formatNumber(
- (detail.secondStockTakeQty ?? 0) +
- (detail.secondBadQty ?? 0)
- )}{" "}
- (
- {formatNumber(
- detail.secondBadQty ?? 0
- )}
- ) ={" "}
- {formatNumber(detail.secondStockTakeQty ?? 0)}
- </Typography>
- ) : null}
-
- {!detail.firstStockTakeQty &&
- !detail.secondStockTakeQty &&
- !submitDisabled && (
- <Typography
- variant="body2"
- color="text.secondary"
- >
- -
- </Typography>
- )}
- </Stack>
- </TableCell>
- <TableCell>
-
- <Stack direction="row" spacing={1}>
- <Button
- size="small"
- variant="contained"
- onClick={() => handleSaveStockTake(detail)}
- disabled={saving || submitDisabled}
- >
- {t("Save")}
- </Button>
-
- </Stack>
-
- </TableCell>
- {/* Remark */}
- <TableCell sx={{ width: 180 }}>
- {!submitDisabled && isSecondSubmit ? (
- <>
- <Typography variant="body2">{t("Remark")}</Typography>
- <TextField
- size="small"
- value={recordInputs[detail.id]?.remark || ""}
- // onKeyDown={blockNonIntegerKeys}
- //inputProps={{ inputMode: "text", pattern: "[0-9]*" }}
- onChange={(e) => {
- // const clean = sanitizeIntegerInput(e.target.value);
- setRecordInputs(prev => ({
- ...prev,
- [detail.id]: {
- ...(prev[detail.id] ?? { firstQty: "", secondQty: "", firstBadQty: "", secondBadQty: "", remark: "" }),
- remark: e.target.value
- }
- }));
- }}
- sx={{ width: 150 }}
- />
- </>
- ) : (
- <Typography variant="body2">
- {detail.remarks || "-"}
- </Typography>
- )}
- </TableCell>
-
-
-
- <TableCell>
- {detail.stockTakeRecordStatus === "completed" ? (
- <Chip
- size="small"
- label={t(detail.stockTakeRecordStatus)}
- color="success"
- />
- ) : detail.stockTakeRecordStatus === "pass" ? (
- <Chip
- size="small"
- label={t(detail.stockTakeRecordStatus)}
- color="default"
- />
- ) : detail.stockTakeRecordStatus === "notMatch" ? (
- <Chip
- size="small"
- label={t(detail.stockTakeRecordStatus)}
- color="warning"
- />
- ) : (
- <Chip
- size="small"
- label={t(detail.stockTakeRecordStatus || "")}
- color="default"
- />
- )}
- </TableCell>
-
-
- </TableRow>
- );
- })
- )}
- </TableBody>
- </Table>
- </TableContainer>
- <TablePagination
- component="div"
- count={total}
- page={page}
- onPageChange={handleChangePage}
- rowsPerPage={pageSize === "all" ? total : (pageSize as number)}
- onRowsPerPageChange={handleChangeRowsPerPage}
- rowsPerPageOptions={[10, 25, 50, 100, { value: -1, label: t("All") }]}
- labelRowsPerPage={t("Rows per page")}
- />
- </>
- )}
- </Box>
- );
- };
-
- export default PickerStockTake;
|