|
- "use client";
-
- import {
- Box,
- Button,
- Stack,
- Typography,
- Chip,
- CircularProgress,
- Table,
- TableBody,
- TableCell,
- TableContainer,
- TableHead,
- TableRow,
- Paper,
- TextField,
- TablePagination,
- } from "@mui/material";
- import { useState, useCallback, useEffect, useRef } from "react";
- import { useTranslation } from "react-i18next";
- import {
- AllPickedStockTakeListReponse,
- InventoryLotDetailResponse,
- saveStockTakeRecord,
- SaveStockTakeRecordRequest,
- BatchSaveStockTakeRecordRequest,
- batchSaveStockTakeRecords,
- batchSavePickerStockTakeInputs,
- getInventoryLotDetailsBySectionNotMatch
- } from "@/app/api/stockTake/actions";
- import { buildPickerBatchSaveRequests } from "./buildPickerBatchSaveRequests";
- import { stockTakeQtyEndAdornment, StockTakeQtyWithUnit } from "./stockTakeQtyAdornment";
- import StockTakeQtyGapHint from "./StockTakeQtyGapHint";
- import { stockTakeHiddenOnHand, stockTakeQtyGapWarnText } from "./stockTakeQtyGapWarning";
- import { useStockTakeQtyGapWarnPercent } from "./useStockTakeQtyGapWarnPercent";
- import PickerBatchSaveFab from "./PickerBatchSaveFab";
- import { useSession } from "next-auth/react";
- import { SessionWithTokens } from "@/config/authConfig";
- import dayjs from "dayjs";
- import {
- OUTPUT_DATE_FORMAT,
- sanitizeStockTakeQtyInput,
- validateStockTakeQtyString,
- } from "@/app/utils/formatUtil";
-
- interface PickerReStockTakeProps {
- selectedSession: AllPickedStockTakeListReponse;
- onBack: () => void;
- onSnackbar: (message: string, severity: "success" | "error" | "warning") => void;
- }
-
- const PickerReStockTake: React.FC<PickerReStockTakeProps> = ({
- selectedSession,
- onBack,
- onSnackbar,
- }) => {
- const { t } = useTranslation(["stockTake", "common"]);
- const qtyGapWarnPercent = useStockTakeQtyGapWarnPercent();
- 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 [saving, setSaving] = useState(false);
- const [gapCheckOpen, setGapCheckOpen] = useState<Record<string, boolean>>({});
- 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 currentUserId = session?.id ? parseInt(session.id) : undefined;
- const handleBatchTestAllRef = useRef<() => Promise<void>>();
- const batchInFlightRef = useRef(false);
- const isSessionCompleted = selectedSession?.status?.toLowerCase() === "completed";
-
- const handleChangePage = useCallback((event: unknown, newPage: number) => {
- setPage(newPage);
- }, []);
- const blockNonIntegerKeys = (e: React.KeyboardEvent<HTMLInputElement>) => {
- // 禁止小数点、逗号、科学计数、正负号
- if ([".", ",", "e", "E", "+", "-"].includes(e.key)) {
- e.preventDefault();
- }
- };
- 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) => {
- setLoadingDetails(true);
- try {
- let actualSize: number;
- if (size === "all") {
- if (selectedSession.totalInventoryLotNumber > 0) {
- actualSize = selectedSession.totalInventoryLotNumber;
- } else if (total > 0) {
- actualSize = total;
- } else {
- actualSize = 10000;
- }
- } else {
- actualSize = typeof size === 'string' ? parseInt(size, 10) : size;
- }
-
- const response = await getInventoryLotDetailsBySectionNotMatch(
- 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(() => {
- const inputs: Record<number, { firstQty: string; secondQty: string; firstBadQty: string; secondBadQty: string; remark: string }> = {};
- inventoryLotDetails.forEach((detail) => {
- const firstTotal = detail.firstStockTakeQty != null
- ? (detail.firstStockTakeQty + (detail.firstBadQty ?? 0)).toString()
- : "";
- const secondTotal = detail.secondStockTakeQty != null
- ? (detail.secondStockTakeQty + (detail.secondBadQty ?? 0)).toString()
- : "";
- inputs[detail.id] = {
- firstQty: firstTotal,
- secondQty: secondTotal,
- firstBadQty: detail.firstBadQty?.toString() || "",
- secondBadQty: detail.secondBadQty?.toString() || "",
- remark: detail.remarks || "",
- };
- });
- setRecordInputs(inputs);
- }, [inventoryLotDetails]);
- */}
- 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.firstStockTakeQty == null;
- const isSecondSubmit =
- detail.firstStockTakeQty != null && detail.secondStockTakeQty == null;
-
- // 用戶輸入為 total 和 bad,需計算 available = total - bad(與 PickerStockTake 一致)
- const totalQtyStr = isFirstSubmit ? recordInputs[detail.id]?.firstQty : recordInputs[detail.id]?.secondQty;
- const badQtyStr = isFirstSubmit ? recordInputs[detail.id]?.firstBadQty : recordInputs[detail.id]?.secondBadQty;
-
- if (!totalQtyStr) {
- onSnackbar(
- isFirstSubmit
- ? t("Please enter QTY")
- : t("Please enter Second QTY"),
- "error"
- );
- return;
- }
-
- const totalValidated = validateStockTakeQtyString(totalQtyStr);
- if (!totalValidated.ok) {
- onSnackbar(t(totalValidated.errorKey), "error");
- return;
- }
- const badValidated = validateStockTakeQtyString(badQtyStr, { allowEmpty: true });
- if (!badValidated.ok) {
- onSnackbar(t(badValidated.errorKey), "error");
- return;
- }
-
- const availableQty = totalValidated.qty - badValidated.qty;
-
- if (availableQty < 0) {
- onSnackbar(t("Available QTY cannot be negative"), "error");
- return;
- }
- const availableValidated = validateStockTakeQtyString(String(availableQty));
- if (!availableValidated.ok) {
- onSnackbar(t(availableValidated.errorKey), "error");
- return;
- }
-
- setSaving(true);
- try {
- const request: SaveStockTakeRecordRequest = {
- stockTakeRecordId: detail.stockTakeRecordId || null,
- inventoryLotLineId: detail.id,
- qty: availableValidated.qty,
- badQty: badValidated.qty,
- remark: isSecondSubmit ? (recordInputs[detail.id]?.remark || null) : null,
- };
- const result = await saveStockTakeRecord(
- request,
- selectedSession.stockTakeId,
- currentUserId
- );
-
- const gapText = stockTakeQtyGapWarnText(
- t,
- totalQtyStr ?? "",
- stockTakeHiddenOnHand(detail),
- qtyGapWarnPercent,
- );
- setGapCheckOpen((prev) => ({ ...prev, [`${detail.id}:${isFirstSubmit ? "first" : "second"}`]: true }));
- onSnackbar(
- gapText
- ? `${t("Stock take record saved successfully")} ${gapText}`
- : t("Stock take record saved successfully"),
- gapText ? "warning" : "success",
- );
-
- const savedId = result?.id ?? detail.stockTakeRecordId;
- setInventoryLotDetails((prev) =>
- prev.map((d) =>
- d.id === detail.id
- ? {
- ...d,
- stockTakeRecordId: savedId ?? d.stockTakeRecordId,
- firstStockTakeQty: isFirstSubmit ? availableQty : d.firstStockTakeQty,
- firstBadQty: isFirstSubmit ?
- badValidated.qty : d.firstBadQty ?? null,
- secondStockTakeQty: isSecondSubmit ? availableQty : d.secondStockTakeQty,
- secondBadQty: isSecondSubmit ?
- badValidated.qty : 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, t, currentUserId, onSnackbar, page, pageSize, loadDetails, qtyGapWarnPercent]);
-
- 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 handleBatchTestAutoFill = useCallback(async () => {
- if (!selectedSession || !currentUserId || batchInFlightRef.current) {
- return;
- }
-
- batchInFlightRef.current = true;
- setBatchSaving(true);
- try {
- const request: BatchSaveStockTakeRecordRequest = {
- stockTakeId: selectedSession.stockTakeId,
- stockTakeSection: selectedSession.stockTakeSession,
- stockTakerId: currentUserId,
- };
-
- const result = await batchSaveStockTakeRecords(request);
-
- 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: unknown) {
- console.error("handleBatchTestAutoFill:", e);
- let errorMessage = t("Failed to batch save stock take records");
- if (e instanceof Error && e.message) {
- errorMessage = e.message;
- }
- onSnackbar(errorMessage, "error");
- } finally {
- setBatchSaving(false);
- batchInFlightRef.current = false;
- }
- }, [selectedSession, t, currentUserId, onSnackbar, page, pageSize, loadDetails]);
-
- const handleBatchSaveInputted = useCallback(async () => {
- if (!selectedSession || !currentUserId || batchInFlightRef.current) return;
-
- const built = buildPickerBatchSaveRequests(
- inventoryLotDetails,
- recordInputs,
- isSubmitDisabled
- );
- if (!built.ok) {
- onSnackbar(t(built.message), "error");
- return;
- }
- if (built.records.length === 0) {
- onSnackbar(t("No valid input to submit"), "warning");
- return;
- }
-
- batchInFlightRef.current = true;
- setBatchSaving(true);
- try {
- const result = await batchSavePickerStockTakeInputs({
- stockTakeId: selectedSession.stockTakeId,
- stockTakeSection: selectedSession.stockTakeSession,
- stockTakerId: currentUserId,
- records: built.records,
- });
-
- 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: unknown) {
- console.error("handleBatchSaveInputted:", e);
- let errorMessage = t("Failed to batch save stock take records");
- if (e instanceof Error && e.message) {
- errorMessage = e.message;
- }
- onSnackbar(errorMessage, "error");
- } finally {
- setBatchSaving(false);
- batchInFlightRef.current = false;
- }
- }, [
- selectedSession,
- currentUserId,
- inventoryLotDetails,
- recordInputs,
- isSubmitDisabled,
- t,
- onSnackbar,
- page,
- pageSize,
- loadDetails,
- ]);
-
- useEffect(() => {
- handleBatchTestAllRef.current = handleBatchTestAutoFill;
- }, [handleBatchTestAutoFill]);
-
- 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}') {
- setTimeout(() => {
- handleBatchTestAllRef.current?.().catch((err) => {
- console.error("Error in handleBatchTestAutoFill:", err);
- });
- }, 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 uniqueWarehouses = Array.from(
- new Set(
- inventoryLotDetails
- .map(detail => detail.warehouse)
- .filter(warehouse => warehouse && warehouse.trim() !== "")
- )
- ).join(", ");
-
- const defaultInputs = { firstQty: "", secondQty: "", firstBadQty: "", secondBadQty: "", remark: "" };
-
- return (
- <Box sx={{ pb: 10 }}>
- <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>
- {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 sx={{ width: 250, minWidth: 250 }}>{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.firstStockTakeQty == null;
- const isSecondSubmit =
- detail.firstStockTakeQty != null &&
- detail.secondStockTakeQty == null;
- const inputs = recordInputs[detail.id] ?? defaultInputs;
-
- return (
- <TableRow key={detail.id}>
- <TableCell>{detail.warehouseArea || "-"}{detail.warehouseSlot || "-"}</TableCell>
- <TableCell sx={{
- maxWidth: 280,
- wordBreak: 'break-word',
- whiteSpace: 'normal',
- lineHeight: 1.5
- }}>
- <Stack spacing={0.5}>
- <Typography
- component="div"
- sx={{ fontWeight: 800, fontSize: "1.15rem", lineHeight: 1.3, color: "text.primary" }}
- >
- {detail.itemCode || "-"} {detail.itemName || "-"}
- </Typography>
- <Box>{detail.lotNo || "-"}</Box>
- <Box>{detail.expiryDate ? dayjs(detail.expiryDate).format(OUTPUT_DATE_FORMAT) : "-"}</Box>
- </Stack>
- </TableCell>
- <TableCell>{detail.uom || "-"}</TableCell>
- <TableCell sx={{ width: 250, minWidth: 250 }}>
- <Stack spacing={1}>
- {/* First */}
- {!submitDisabled && isFirstSubmit ? (
- <Stack spacing={0.5} alignItems="flex-start">
- <Stack direction="row" spacing={1} alignItems="center">
- <Typography variant="body2">{t("First")}:</Typography>
- <TextField
- size="small"
- type="number"
- value={inputs.firstQty}
- onFocus={() =>
- setGapCheckOpen((prev) => ({ ...prev, [`${detail.id}:first`]: false }))
- }
- onBlur={() =>
- setGapCheckOpen((prev) => ({ ...prev, [`${detail.id}:first`]: true }))
- }
- inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }}
- onKeyDown={blockNonIntegerKeys}
- onChange={(e) => {
- const clean = sanitizeStockTakeQtyInput(e.target.value);
- const val = clean;
- if (val.includes("-")) return;
- setRecordInputs(prev => ({
- ...prev,
- [detail.id]: { ...(prev[detail.id] ?? defaultInputs), firstQty: val }
- }));
- }}
- InputProps={{
- endAdornment: stockTakeQtyEndAdornment(detail.uomShortDesc),
- }}
- sx={{
- width: 148,
- minWidth: 148,
- "& .MuiInputBase-input": {
- height: "1.4375em",
- padding: "4px 8px",
- },
- }}
- placeholder={t("Stock Take Qty")}
- />
- {/*
- <TextField
- size="small"
- type="number"
- value={inputs.firstBadQty}
- inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }}
- onKeyDown={blockNonIntegerKeys}
- onChange={(e) => {
- const clean = sanitizeStockTakeQtyInput(e.target.value);
- const val = clean;
- if (val.includes("-")) return;
- setRecordInputs(prev => ({
- ...prev,
- [detail.id]: { ...(prev[detail.id] ?? defaultInputs), firstBadQty: val }
- }));
- }}
- sx={{
- width: 130,
- minWidth: 130,
- "& .MuiInputBase-input": {
- height: "1.4375em",
- padding: "4px 8px",
- },
- }}
- placeholder={t("Bad Qty")}
- />
- */}
- </Stack>
- <StockTakeQtyGapHint
- open={!!gapCheckOpen[`${detail.id}:first`]}
- entered={inputs.firstQty}
- currentQty={stockTakeHiddenOnHand(detail)}
- threshold={qtyGapWarnPercent}
- />
- </Stack>
- ) : detail.firstStockTakeQty != null ? (
- <Typography variant="body2">
- {t("First")}:{" "}
- <StockTakeQtyWithUnit
- qty={formatNumber(detail.firstStockTakeQty ?? 0)}
- uomShortDesc={detail.uomShortDesc}
- />
- </Typography>
- ) : null}
-
- {/* Second */}
- {!submitDisabled && isSecondSubmit ? (
- <Stack spacing={0.5} alignItems="flex-start">
- <Stack direction="row" spacing={1} alignItems="center">
- <Typography variant="body2">{t("Second")}:</Typography>
- <TextField
- size="small"
- type="number"
- value={inputs.secondQty}
- onFocus={() =>
- setGapCheckOpen((prev) => ({ ...prev, [`${detail.id}:second`]: false }))
- }
- onBlur={() =>
- setGapCheckOpen((prev) => ({ ...prev, [`${detail.id}:second`]: true }))
- }
- inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }}
- onKeyDown={blockNonIntegerKeys}
- onChange={(e) => {
- const clean = sanitizeStockTakeQtyInput(e.target.value);
- const val = clean;
- if (val.includes("-")) return;
- setRecordInputs(prev => ({
- ...prev,
- [detail.id]: { ...(prev[detail.id] ?? defaultInputs), secondQty: clean }
- }));
- }}
- InputProps={{
- endAdornment: stockTakeQtyEndAdornment(detail.uomShortDesc),
- }}
- sx={{
- width: 148,
- minWidth: 148,
- "& .MuiInputBase-input": {
- height: "1.4375em",
- padding: "4px 8px",
- },
- }}
- placeholder={t("Stock Take Qty")}
- />
- {/*
- <TextField
- size="small"
- type="number"
- value={inputs.secondBadQty}
- inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }}
- onKeyDown={blockNonIntegerKeys}
- onChange={(e) => {
- const clean = sanitizeStockTakeQtyInput(e.target.value);
- const val = clean;
- if (val.includes("-")) return;
- setRecordInputs(prev => ({
- ...prev,
- [detail.id]: { ...(prev[detail.id] ?? defaultInputs), secondBadQty: clean }
- }));
- }}
- sx={{
- width: 130,
- minWidth: 130,
- "& .MuiInputBase-input": {
- height: "1.4375em",
- padding: "4px 8px",
- },
- }}
- placeholder={t("Bad Qty")}
- />
- */}
- </Stack>
- <StockTakeQtyGapHint
- open={!!gapCheckOpen[`${detail.id}:second`]}
- entered={inputs.secondQty}
- currentQty={stockTakeHiddenOnHand(detail)}
- threshold={qtyGapWarnPercent}
- />
- </Stack>
- ) : detail.secondStockTakeQty != null ? (
- <Typography variant="body2">
- {t("Second")}:{" "}
- <StockTakeQtyWithUnit
- qty={formatNumber(detail.secondStockTakeQty ?? 0)}
- uomShortDesc={detail.uomShortDesc}
- />
- </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>
- {/*
- <TableCell sx={{ width: 180 }}>
- {!submitDisabled && isSecondSubmit ? (
- <>
- <Typography variant="body2">{t("Remark")}</Typography>
- <TextField
- size="small"
- value={inputs.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] ?? defaultInputs), 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")}
- />
- </>
- )}
- <PickerBatchSaveFab
- onClick={handleBatchSaveInputted}
- disabled={batchSaving || loadingDetails || isSessionCompleted}
- loading={batchSaving}
- label={t("Batch Save All")}
- />
- </Box>
- );
- };
-
- export default PickerReStockTake;
|