|
|
|
@@ -31,6 +31,11 @@ import { |
|
|
|
CardContent, |
|
|
|
Radio, |
|
|
|
alpha, |
|
|
|
Autocomplete, |
|
|
|
Dialog, |
|
|
|
DialogActions, |
|
|
|
DialogContent, |
|
|
|
DialogTitle, |
|
|
|
} from "@mui/material"; |
|
|
|
import { useTranslation } from "react-i18next"; |
|
|
|
import { submitDialogWithWarning } from "../Swal/CustomAlerts"; |
|
|
|
@@ -69,8 +74,8 @@ import QrModal from "./QrModal"; |
|
|
|
import { PlayArrow } from "@mui/icons-material"; |
|
|
|
import DoneIcon from "@mui/icons-material/Done"; |
|
|
|
import { downloadFile, getCustomWidth } from "@/app/utils/commonUtil"; |
|
|
|
import PoInfoCard from "./PoInfoCard"; |
|
|
|
import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil"; |
|
|
|
import { arrayToDateString } from "@/app/utils/formatUtil"; |
|
|
|
import { List, ListItem, ListItemButton, ListItemText, Divider } from "@mui/material"; |
|
|
|
import { Controller, FormProvider, useForm } from "react-hook-form"; |
|
|
|
import dayjs, { Dayjs } from "dayjs"; |
|
|
|
@@ -81,6 +86,7 @@ import { getMailTemplatePdfForStockInLine } from "@/app/api/mailTemplate/actions |
|
|
|
import { PrinterCombo } from "@/app/api/settings/printer"; |
|
|
|
import { EscalationCombo } from "@/app/api/user"; |
|
|
|
import { StockInLine } from "@/app/api/stockIn"; |
|
|
|
import { printQrCodeForSil } from "@/app/api/stockIn/actions"; |
|
|
|
import { useSession } from "next-auth/react"; |
|
|
|
import { AUTH } from "@/authorities"; |
|
|
|
//import { useRouter } from "next/navigation"; |
|
|
|
@@ -155,7 +161,16 @@ const PoSearchList: React.FC<{ |
|
|
|
}, [poList, searchTerm, t]); |
|
|
|
|
|
|
|
return ( |
|
|
|
<Paper sx={{ p: 2, maxHeight: "480px", overflow: "auto", minWidth: "300px", height: "480px" }}> |
|
|
|
<Paper |
|
|
|
sx={{ |
|
|
|
p: 2, |
|
|
|
minWidth: "300px", |
|
|
|
height: "100%", |
|
|
|
display: "flex", |
|
|
|
flexDirection: "column", |
|
|
|
overflow: "hidden", |
|
|
|
}} |
|
|
|
> |
|
|
|
<Typography variant="h6" gutterBottom> |
|
|
|
{t("Purchase Order")} |
|
|
|
</Typography> |
|
|
|
@@ -175,51 +190,53 @@ const PoSearchList: React.FC<{ |
|
|
|
), |
|
|
|
}} |
|
|
|
/> |
|
|
|
{loading ? ( |
|
|
|
<LoadingComponent /> |
|
|
|
) : filteredPoList.length > 0 ? ( |
|
|
|
<List dense sx={{ width: "100%" }}> |
|
|
|
{filteredPoList.map((poItem, index) => ( |
|
|
|
<div key={poItem.id}> |
|
|
|
<ListItem disablePadding sx={{ width: "100%" }}> |
|
|
|
<ListItemButton |
|
|
|
selected={selectedPoId === poItem.id} |
|
|
|
onClick={() => onSelect(poItem)} |
|
|
|
sx={{ |
|
|
|
width: "100%", |
|
|
|
"&.Mui-selected": { |
|
|
|
backgroundColor: "primary.light", |
|
|
|
"&:hover": { |
|
|
|
<Box sx={{ flex: 1, overflow: "auto" }}> |
|
|
|
{loading ? ( |
|
|
|
<LoadingComponent /> |
|
|
|
) : filteredPoList.length > 0 ? ( |
|
|
|
<List dense sx={{ width: "100%" }}> |
|
|
|
{filteredPoList.map((poItem, index) => ( |
|
|
|
<div key={poItem.id}> |
|
|
|
<ListItem disablePadding sx={{ width: "100%" }}> |
|
|
|
<ListItemButton |
|
|
|
selected={selectedPoId === poItem.id} |
|
|
|
onClick={() => onSelect(poItem)} |
|
|
|
sx={{ |
|
|
|
width: "100%", |
|
|
|
"&.Mui-selected": { |
|
|
|
backgroundColor: "primary.light", |
|
|
|
"&:hover": { |
|
|
|
backgroundColor: "primary.light", |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}} |
|
|
|
> |
|
|
|
<ListItemText |
|
|
|
primary={ |
|
|
|
<Typography variant="body2" sx={{ wordBreak: "break-all" }}> |
|
|
|
{poItem.code} |
|
|
|
</Typography> |
|
|
|
} |
|
|
|
secondary={ |
|
|
|
<Typography variant="caption" color="text.secondary"> |
|
|
|
{t(`${poItem.status.toLowerCase()}`)} |
|
|
|
</Typography> |
|
|
|
} |
|
|
|
/> |
|
|
|
</ListItemButton> |
|
|
|
</ListItem> |
|
|
|
{index < filteredPoList.length - 1 && <Divider />} |
|
|
|
</div> |
|
|
|
))} |
|
|
|
</List> |
|
|
|
) : ( |
|
|
|
<Typography variant="body2" color="text.secondary" sx={{ py: 2 }}> |
|
|
|
{searchTerm.trim() |
|
|
|
? t("No purchase orders match your search", { defaultValue: "沒有符合搜尋的採購單" }) |
|
|
|
: t("No purchase orders to show", { defaultValue: "沒有可顯示的採購單" })} |
|
|
|
</Typography> |
|
|
|
)} |
|
|
|
}} |
|
|
|
> |
|
|
|
<ListItemText |
|
|
|
primary={ |
|
|
|
<Typography variant="body2" sx={{ wordBreak: "break-all" }}> |
|
|
|
{poItem.code} |
|
|
|
</Typography> |
|
|
|
} |
|
|
|
secondary={ |
|
|
|
<Typography variant="caption" color="text.secondary"> |
|
|
|
{t(`${poItem.status.toLowerCase()}`)} |
|
|
|
</Typography> |
|
|
|
} |
|
|
|
/> |
|
|
|
</ListItemButton> |
|
|
|
</ListItem> |
|
|
|
{index < filteredPoList.length - 1 && <Divider />} |
|
|
|
</div> |
|
|
|
))} |
|
|
|
</List> |
|
|
|
) : ( |
|
|
|
<Typography variant="body2" color="text.secondary" sx={{ py: 2 }}> |
|
|
|
{searchTerm.trim() |
|
|
|
? t("No purchase orders match your search", { defaultValue: "沒有符合搜尋的採購單" }) |
|
|
|
: t("No purchase orders to show", { defaultValue: "沒有可顯示的採購單" })} |
|
|
|
</Typography> |
|
|
|
)} |
|
|
|
</Box> |
|
|
|
{searchTerm && ( |
|
|
|
<Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: "block" }}> |
|
|
|
{`${t("Found")} ${filteredPoList.length} ${t("Purchase Order")}`} |
|
|
|
@@ -311,6 +328,86 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => { |
|
|
|
receiptDate: dayjsToDateString(dayjs()) |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
const [selectedPrinter, setSelectedPrinter] = useState<PrinterCombo | undefined>( |
|
|
|
printerCombo?.[0], |
|
|
|
); |
|
|
|
const [printQty, setPrintQty] = useState(1); |
|
|
|
const [printDialogOpen, setPrintDialogOpen] = useState(false); |
|
|
|
const [isBulkPrinting, setIsBulkPrinting] = useState(false); |
|
|
|
const [printStatusFilter, setPrintStatusFilter] = useState({ |
|
|
|
received: true, |
|
|
|
completed: false, |
|
|
|
}); |
|
|
|
const [selectedPrintSilIds, setSelectedPrintSilIds] = useState<Set<number>>( |
|
|
|
() => new Set(), |
|
|
|
); |
|
|
|
|
|
|
|
const eligiblePrintSils = useMemo(() => { |
|
|
|
const statusSet = new Set<string>(); |
|
|
|
if (printStatusFilter.received) statusSet.add("received"); |
|
|
|
if (printStatusFilter.completed) statusSet.add("completed"); |
|
|
|
const pols = purchaseOrder.pol ?? []; |
|
|
|
return pols |
|
|
|
.flatMap((pol) => pol.stockInLine ?? []) |
|
|
|
.filter((sil) => statusSet.has((sil.status ?? "").toLowerCase().trim())); |
|
|
|
}, [purchaseOrder.pol, printStatusFilter.completed, printStatusFilter.received]); |
|
|
|
|
|
|
|
const openPrintDialog = useCallback(() => { |
|
|
|
setSelectedPrintSilIds(new Set()); |
|
|
|
setPrintDialogOpen(true); |
|
|
|
}, []); |
|
|
|
|
|
|
|
const closePrintDialog = useCallback(() => { |
|
|
|
if (isBulkPrinting) return; |
|
|
|
setPrintDialogOpen(false); |
|
|
|
}, [isBulkPrinting]); |
|
|
|
|
|
|
|
const togglePrintSilSelection = useCallback((id: number, checked: boolean) => { |
|
|
|
setSelectedPrintSilIds((prev) => { |
|
|
|
const next = new Set(prev); |
|
|
|
if (checked) next.add(id); |
|
|
|
else next.delete(id); |
|
|
|
return next; |
|
|
|
}); |
|
|
|
}, []); |
|
|
|
|
|
|
|
const setAllVisiblePrintSilsSelected = useCallback((checked: boolean) => { |
|
|
|
setSelectedPrintSilIds(() => { |
|
|
|
if (!checked) return new Set(); |
|
|
|
return new Set(eligiblePrintSils.map((s) => s.id)); |
|
|
|
}); |
|
|
|
}, [eligiblePrintSils]); |
|
|
|
|
|
|
|
const handleBulkPrint = useCallback(async () => { |
|
|
|
if (!selectedPrinter) { |
|
|
|
alert("請先選擇印表機"); |
|
|
|
return; |
|
|
|
} |
|
|
|
if (!Number.isFinite(printQty) || printQty <= 0) { |
|
|
|
alert("列印數量必須大於 0"); |
|
|
|
return; |
|
|
|
} |
|
|
|
const ids = Array.from(selectedPrintSilIds.values()); |
|
|
|
if (ids.length <= 0) { |
|
|
|
alert("請先選擇要列印的項目"); |
|
|
|
return; |
|
|
|
} |
|
|
|
setIsBulkPrinting(true); |
|
|
|
try { |
|
|
|
for (const id of ids) { |
|
|
|
await printQrCodeForSil({ |
|
|
|
stockInLineId: id, |
|
|
|
printerId: selectedPrinter.id, |
|
|
|
printQty, |
|
|
|
}); |
|
|
|
} |
|
|
|
setPrintDialogOpen(false); |
|
|
|
} finally { |
|
|
|
setIsBulkPrinting(false); |
|
|
|
} |
|
|
|
}, [printQty, selectedPrinter, selectedPrintSilIds]); |
|
|
|
|
|
|
|
/** Only loads sidebar list when `selectedIds` is in the URL; otherwise show current PO only (no /po/list fetch). */ |
|
|
|
const fetchPoList = useCallback(async () => { |
|
|
|
if (!selectedIdsParam) return; |
|
|
|
@@ -836,6 +933,21 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => { |
|
|
|
} |
|
|
|
}, []) |
|
|
|
|
|
|
|
const fillTodayLotNo = useCallback(() => { |
|
|
|
const today = dayjs().format("YYYYMMDD"); |
|
|
|
setPolInputList((prev) => { |
|
|
|
const next: Record<number, PolInputResult> = { ...prev }; |
|
|
|
(rows ?? []).forEach((r) => { |
|
|
|
const current = next[r.id] ?? { lotNo: "", dnQty: "" }; |
|
|
|
const lotNo = (current.lotNo ?? "").trim(); |
|
|
|
if (!lotNo) { |
|
|
|
next[r.id] = { ...current, lotNo: today }; |
|
|
|
} |
|
|
|
}); |
|
|
|
return next; |
|
|
|
}); |
|
|
|
}, [rows]); |
|
|
|
|
|
|
|
return ( |
|
|
|
<> |
|
|
|
<Stack spacing={2}> |
|
|
|
@@ -850,10 +962,10 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => { |
|
|
|
</Grid> |
|
|
|
|
|
|
|
{/* area2: dn info */} |
|
|
|
<Grid container spacing={3} sx={{ maxWidth: 'fit-content' }}> |
|
|
|
<Grid container spacing={3} sx={{ maxWidth: 'fit-content' }} alignItems="stretch"> |
|
|
|
{/* left side select po */} |
|
|
|
<Grid item xs={4}> |
|
|
|
<Stack spacing={1}> |
|
|
|
<Grid item xs={4} sx={{ display: "flex" }}> |
|
|
|
<Stack spacing={1} sx={{ flex: 1 }}> |
|
|
|
<PoSearchList |
|
|
|
poList={poList} |
|
|
|
selectedPoId={selectedPoId} |
|
|
|
@@ -867,71 +979,131 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => { |
|
|
|
<Grid item xs={8}> |
|
|
|
<Grid container spacing={3} sx={{ maxWidth: 'fit-content' }}> |
|
|
|
<Grid item xs={12}> |
|
|
|
<PoInfoCard po={purchaseOrder} /> |
|
|
|
</Grid> |
|
|
|
<Grid item xs={12}> |
|
|
|
{true ? ( |
|
|
|
<FormProvider {...dnFormProps}> |
|
|
|
<Stack component={"form"} spacing={2}> |
|
|
|
<Card sx={{ display: "block", height: "230px" }}> |
|
|
|
<CardContent component={Stack} spacing={2}> |
|
|
|
<Grid container spacing={2} sx={{ maxWidth: 'fit-content' }}> |
|
|
|
<Grid item xs={12}> |
|
|
|
<Card sx={{ display: "block" }}> |
|
|
|
<CardContent component={Stack} spacing={2}> |
|
|
|
<TextField |
|
|
|
label={t("Supplier")} |
|
|
|
fullWidth |
|
|
|
disabled={true} |
|
|
|
value={purchaseOrder.supplier ?? ""} |
|
|
|
/> |
|
|
|
|
|
|
|
<Grid container spacing={2}> |
|
|
|
<Grid item xs={6}> |
|
|
|
<Stack spacing={2}> |
|
|
|
<TextField |
|
|
|
label={t("Order Date")} |
|
|
|
fullWidth |
|
|
|
disabled={true} |
|
|
|
value={arrayToDateString(purchaseOrder.orderDate as any)} |
|
|
|
/> |
|
|
|
<TextField |
|
|
|
{...dnFormProps.register("dnNo")} |
|
|
|
label={t("dnNo")} |
|
|
|
type="text" |
|
|
|
variant="outlined" |
|
|
|
fullWidth |
|
|
|
// InputProps={{ |
|
|
|
// inputProps: { |
|
|
|
// min: 0, |
|
|
|
// step: 1 |
|
|
|
// } |
|
|
|
// }} |
|
|
|
/> |
|
|
|
</Grid> |
|
|
|
<Grid item xs={12}> |
|
|
|
<LocalizationProvider |
|
|
|
dateAdapter={AdapterDayjs} |
|
|
|
// TODO: Should maybe use a custom adapterLocale here to support YYYY-MM-DD |
|
|
|
adapterLocale="zh-hk" |
|
|
|
localeText={zhHK.components.MuiLocalizationProvider.defaultProps.localeText} |
|
|
|
> |
|
|
|
</Stack> |
|
|
|
</Grid> |
|
|
|
|
|
|
|
<Grid item xs={6}> |
|
|
|
<Stack spacing={2}> |
|
|
|
<TextField |
|
|
|
label={t("ETA")} |
|
|
|
fullWidth |
|
|
|
disabled={true} |
|
|
|
value={arrayToDateString(purchaseOrder.estimatedArrivalDate as any)} |
|
|
|
/> |
|
|
|
<LocalizationProvider |
|
|
|
dateAdapter={AdapterDayjs} |
|
|
|
adapterLocale="zh-hk" |
|
|
|
localeText={zhHK.components.MuiLocalizationProvider.defaultProps.localeText} |
|
|
|
> |
|
|
|
<Controller |
|
|
|
control={dnFormProps.control} |
|
|
|
name="receiptDate" |
|
|
|
render={({ field }) => ( |
|
|
|
|
|
|
|
<DatePicker |
|
|
|
label={t("receiptDate")} |
|
|
|
format={`${OUTPUT_DATE_FORMAT}`} |
|
|
|
defaultValue={dateStringToDayjs(field.value)} |
|
|
|
onChange={(newValue: Dayjs | null) => { |
|
|
|
handleDatePickerChange(newValue, field.onChange) |
|
|
|
}} |
|
|
|
slotProps={{ textField: { fullWidth: true }}} |
|
|
|
/> |
|
|
|
<DatePicker |
|
|
|
label={t("receiptDate")} |
|
|
|
format={`${OUTPUT_DATE_FORMAT}`} |
|
|
|
defaultValue={dateStringToDayjs(field.value)} |
|
|
|
onChange={(newValue: Dayjs | null) => { |
|
|
|
handleDatePickerChange(newValue, field.onChange); |
|
|
|
}} |
|
|
|
slotProps={{ textField: { fullWidth: true } }} |
|
|
|
/> |
|
|
|
)} |
|
|
|
/> |
|
|
|
</LocalizationProvider> |
|
|
|
</Grid> |
|
|
|
{/* <TextField |
|
|
|
label={t("dnDate")} |
|
|
|
type="text" |
|
|
|
variant="outlined" |
|
|
|
defaultValue={"11/08/2025"} |
|
|
|
fullWidth |
|
|
|
/> */} |
|
|
|
/> |
|
|
|
</LocalizationProvider> |
|
|
|
</Stack> |
|
|
|
</Grid> |
|
|
|
{/* <Button variant="contained" onClick={onOpenScanner} fullWidth> |
|
|
|
提交 |
|
|
|
</Button> */} |
|
|
|
</Grid> |
|
|
|
</CardContent> |
|
|
|
</Card> |
|
|
|
</FormProvider> |
|
|
|
</Grid> |
|
|
|
<Grid item xs={12}> |
|
|
|
<Grid container spacing={2} alignItems="stretch"> |
|
|
|
<Grid item xs={6} sx={{ display: "flex" }}> |
|
|
|
<Card sx={{ display: "block", flex: 1 }}> |
|
|
|
<CardContent component={Stack} spacing={2}> |
|
|
|
<Typography variant="h6">列印</Typography> |
|
|
|
<Autocomplete |
|
|
|
disableClearable |
|
|
|
options={printerCombo} |
|
|
|
value={selectedPrinter} |
|
|
|
onChange={(_event, value) => setSelectedPrinter(value)} |
|
|
|
renderInput={(params) => ( |
|
|
|
<TextField |
|
|
|
{...params} |
|
|
|
variant="outlined" |
|
|
|
label={t("Printer")} |
|
|
|
fullWidth |
|
|
|
/> |
|
|
|
)} |
|
|
|
/> |
|
|
|
<TextField |
|
|
|
variant="outlined" |
|
|
|
label={t("Print Qty")} |
|
|
|
value={printQty} |
|
|
|
onChange={(event) => { |
|
|
|
const cleaned = String(event.target.value).replace(/[^0-9]/g, ""); |
|
|
|
setPrintQty(Number(cleaned || 0)); |
|
|
|
}} |
|
|
|
fullWidth |
|
|
|
/> |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
onClick={openPrintDialog} |
|
|
|
disabled={(printerCombo?.length ?? 0) <= 0} |
|
|
|
> |
|
|
|
選擇列印項目 |
|
|
|
</Button> |
|
|
|
<Typography variant="caption" color="text.secondary"> |
|
|
|
只會顯示「待上架 / 已上架」的來貨記錄 |
|
|
|
</Typography> |
|
|
|
</CardContent> |
|
|
|
</Card> |
|
|
|
</Stack> |
|
|
|
</FormProvider> |
|
|
|
) : undefined} |
|
|
|
</Grid> |
|
|
|
<Grid item xs={6} sx={{ display: "flex" }}> |
|
|
|
<Card sx={{ display: "block", flex: 1 }}> |
|
|
|
<CardContent component={Stack} spacing={2} sx={{ height: "100%" }}> |
|
|
|
<Typography variant="h6" sx={{ visibility: "hidden" }}> |
|
|
|
列印 |
|
|
|
</Typography> |
|
|
|
<Button |
|
|
|
variant="outlined" |
|
|
|
onClick={fillTodayLotNo} |
|
|
|
sx={{ flex: 1 }} |
|
|
|
> |
|
|
|
一鍵填入來貨編號(今日) |
|
|
|
</Button> |
|
|
|
</CardContent> |
|
|
|
</Card> |
|
|
|
</Grid> |
|
|
|
</Grid> |
|
|
|
</Grid> |
|
|
|
</Grid> |
|
|
|
</Grid> |
|
|
|
@@ -1016,6 +1188,128 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => { |
|
|
|
/> */} |
|
|
|
</Grid> |
|
|
|
</Stack> |
|
|
|
<Dialog open={printDialogOpen} onClose={closePrintDialog} fullWidth maxWidth="md"> |
|
|
|
<DialogTitle>列印標籤</DialogTitle> |
|
|
|
<DialogContent> |
|
|
|
<Stack spacing={1.5} sx={{ mt: 1 }}> |
|
|
|
<Stack direction="row" spacing={2} alignItems="center" flexWrap="wrap"> |
|
|
|
<FormControlLabel |
|
|
|
control={ |
|
|
|
<Checkbox |
|
|
|
checked={printStatusFilter.received} |
|
|
|
onChange={(e) => |
|
|
|
setPrintStatusFilter((p) => ({ ...p, received: e.target.checked })) |
|
|
|
} |
|
|
|
/> |
|
|
|
} |
|
|
|
label="待上架" |
|
|
|
/> |
|
|
|
<FormControlLabel |
|
|
|
control={ |
|
|
|
<Checkbox |
|
|
|
checked={printStatusFilter.completed} |
|
|
|
onChange={(e) => |
|
|
|
setPrintStatusFilter((p) => ({ ...p, completed: e.target.checked })) |
|
|
|
} |
|
|
|
/> |
|
|
|
} |
|
|
|
label="已上架" |
|
|
|
/> |
|
|
|
<FormControlLabel |
|
|
|
control={ |
|
|
|
<Checkbox |
|
|
|
checked={ |
|
|
|
eligiblePrintSils.length > 0 && |
|
|
|
selectedPrintSilIds.size === eligiblePrintSils.length |
|
|
|
} |
|
|
|
indeterminate={ |
|
|
|
selectedPrintSilIds.size > 0 && |
|
|
|
selectedPrintSilIds.size < eligiblePrintSils.length |
|
|
|
} |
|
|
|
onChange={(e) => setAllVisiblePrintSilsSelected(e.target.checked)} |
|
|
|
/> |
|
|
|
} |
|
|
|
label="全選(目前篩選結果)" |
|
|
|
/> |
|
|
|
<Typography variant="caption" color="text.secondary"> |
|
|
|
已選擇 {selectedPrintSilIds.size} / {eligiblePrintSils.length} |
|
|
|
</Typography> |
|
|
|
</Stack> |
|
|
|
|
|
|
|
<TableContainer component={Paper} variant="outlined"> |
|
|
|
<Table size="small" stickyHeader> |
|
|
|
<TableHead> |
|
|
|
<TableRow> |
|
|
|
<TableCell padding="checkbox"></TableCell> |
|
|
|
<TableCell>貨品編號</TableCell> |
|
|
|
<TableCell>貨品名稱</TableCell> |
|
|
|
<TableCell align="right">換算庫存數量</TableCell> |
|
|
|
<TableCell>庫存單位</TableCell> |
|
|
|
<TableCell>收貨日期</TableCell> |
|
|
|
<TableCell>來貨批號</TableCell> |
|
|
|
<TableCell>來貨狀態</TableCell> |
|
|
|
</TableRow> |
|
|
|
</TableHead> |
|
|
|
<TableBody> |
|
|
|
{eligiblePrintSils.map((sil) => { |
|
|
|
const status = (sil.status ?? "").toLowerCase().trim(); |
|
|
|
const statusText = |
|
|
|
status === "received" ? "待上架" : status === "completed" ? "已上架" : sil.status; |
|
|
|
const receiptText = sil.receiptDate |
|
|
|
? Array.isArray(sil.receiptDate) |
|
|
|
? arrayToDateString(sil.receiptDate) |
|
|
|
: String(sil.receiptDate) |
|
|
|
: "-"; |
|
|
|
const stockQty = Number(sil.acceptedQty ?? 0); |
|
|
|
const stockQtyText = |
|
|
|
Number.isFinite(stockQty) && stockQty > 0 |
|
|
|
? decimalFormatter.format(stockQty) |
|
|
|
: decimalFormatter.format(0); |
|
|
|
return ( |
|
|
|
<TableRow key={sil.id} hover> |
|
|
|
<TableCell padding="checkbox"> |
|
|
|
<Checkbox |
|
|
|
checked={selectedPrintSilIds.has(sil.id)} |
|
|
|
onChange={(e) => togglePrintSilSelection(sil.id, e.target.checked)} |
|
|
|
/> |
|
|
|
</TableCell> |
|
|
|
<TableCell>{sil.itemNo}</TableCell> |
|
|
|
<TableCell>{sil.itemName}</TableCell> |
|
|
|
<TableCell align="right">{stockQtyText}</TableCell> |
|
|
|
<TableCell>{sil.stockUomDesc || "-"}</TableCell> |
|
|
|
<TableCell>{receiptText}</TableCell> |
|
|
|
<TableCell>{sil.productLotNo || "-"}</TableCell> |
|
|
|
<TableCell>{statusText}</TableCell> |
|
|
|
</TableRow> |
|
|
|
); |
|
|
|
})} |
|
|
|
{eligiblePrintSils.length === 0 && ( |
|
|
|
<TableRow> |
|
|
|
<TableCell colSpan={8}> |
|
|
|
<Typography variant="body2" color="text.secondary"> |
|
|
|
沒有符合條件的項目 |
|
|
|
</Typography> |
|
|
|
</TableCell> |
|
|
|
</TableRow> |
|
|
|
)} |
|
|
|
</TableBody> |
|
|
|
</Table> |
|
|
|
</TableContainer> |
|
|
|
</Stack> |
|
|
|
</DialogContent> |
|
|
|
<DialogActions> |
|
|
|
<Button onClick={closePrintDialog} disabled={isBulkPrinting}> |
|
|
|
取消 |
|
|
|
</Button> |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
onClick={handleBulkPrint} |
|
|
|
disabled={isBulkPrinting || selectedPrintSilIds.size <= 0 || !selectedPrinter} |
|
|
|
> |
|
|
|
{isBulkPrinting ? "列印中..." : "列印"} |
|
|
|
</Button> |
|
|
|
</DialogActions> |
|
|
|
</Dialog> |
|
|
|
{/* {itemInfo !== undefined && ( |
|
|
|
<> |
|
|
|
<PoQcStockInModal |
|
|
|
|