|
|
@@ -1,7 +1,6 @@ |
|
|
"use client"; |
|
|
"use client"; |
|
|
import { |
|
|
import { |
|
|
FooterPropsOverrides, |
|
|
FooterPropsOverrides, |
|
|
GridActionsCellItem, |
|
|
|
|
|
GridCellParams, |
|
|
GridCellParams, |
|
|
GridRowId, |
|
|
GridRowId, |
|
|
GridRowIdGetter, |
|
|
GridRowIdGetter, |
|
|
@@ -19,11 +18,12 @@ import { |
|
|
useCallback, |
|
|
useCallback, |
|
|
useEffect, |
|
|
useEffect, |
|
|
useMemo, |
|
|
useMemo, |
|
|
|
|
|
useRef, |
|
|
useState, |
|
|
useState, |
|
|
} from "react"; |
|
|
} from "react"; |
|
|
import StyledDataGrid from "../StyledDataGrid"; |
|
|
import StyledDataGrid from "../StyledDataGrid"; |
|
|
import { GridColDef } from "@mui/x-data-grid"; |
|
|
import { GridColDef } from "@mui/x-data-grid"; |
|
|
import { Box, Button, Grid, Typography } from "@mui/material"; |
|
|
|
|
|
|
|
|
import { Box, Button, Grid, Typography, useMediaQuery, useTheme } from "@mui/material"; |
|
|
import { useTranslation } from "react-i18next"; |
|
|
import { useTranslation } from "react-i18next"; |
|
|
import { Add } from "@mui/icons-material"; |
|
|
import { Add } from "@mui/icons-material"; |
|
|
import SaveIcon from "@mui/icons-material/Save"; |
|
|
import SaveIcon from "@mui/icons-material/Save"; |
|
|
@@ -35,7 +35,7 @@ import ShoppingCartIcon from "@mui/icons-material/ShoppingCart"; |
|
|
import PlayArrowIcon from "@mui/icons-material/PlayArrow"; |
|
|
import PlayArrowIcon from "@mui/icons-material/PlayArrow"; |
|
|
import { PurchaseOrderLine } from "@/app/api/po"; |
|
|
import { PurchaseOrderLine } from "@/app/api/po"; |
|
|
import { StockInLine } from "@/app/api/stockIn"; |
|
|
import { StockInLine } from "@/app/api/stockIn"; |
|
|
import { createStockInLine, QcResult } from "@/app/api/stockIn/actions"; |
|
|
|
|
|
|
|
|
import { createStockInLine, deleteStockInLine, QcResult } from "@/app/api/stockIn/actions"; |
|
|
import { usePathname, useRouter, useSearchParams } from "next/navigation"; |
|
|
import { usePathname, useRouter, useSearchParams } from "next/navigation"; |
|
|
import { |
|
|
import { |
|
|
returnWeightUnit, |
|
|
returnWeightUnit, |
|
|
@@ -67,6 +67,63 @@ import { EscalationResult } from "@/app/api/escalation"; |
|
|
import { fetchEscalationLogsByStockInLines } from "@/app/api/escalation/actions"; |
|
|
import { fetchEscalationLogsByStockInLines } from "@/app/api/escalation/actions"; |
|
|
import { SessionWithTokens } from "@/config/authConfig"; |
|
|
import { SessionWithTokens } from "@/config/authConfig"; |
|
|
import { EscalationCombo } from "@/app/api/user"; |
|
|
import { EscalationCombo } from "@/app/api/user"; |
|
|
|
|
|
import { deleteDialog } from "../Swal/CustomAlerts"; |
|
|
|
|
|
import StockInLineRowActions from "./StockInLineRowActions"; |
|
|
|
|
|
|
|
|
|
|
|
/** Sum of fixed column widths (desktop) so the grid can scroll horizontally without squeezing cells. */ |
|
|
|
|
|
const STOCK_IN_GRID_MIN_WIDTH_DESKTOP = 1062; |
|
|
|
|
|
|
|
|
|
|
|
const ACTIONS_COLUMN_WIDTH = 168; |
|
|
|
|
|
|
|
|
|
|
|
const ACTION_BUTTON_HEIGHT = 38; |
|
|
|
|
|
const ACTION_BUTTON_GAP = 6; |
|
|
|
|
|
/** Extra space for cell padding + outlined button borders */ |
|
|
|
|
|
const ACTION_ROW_EXTRA_PADDING = 36; |
|
|
|
|
|
|
|
|
|
|
|
function getActionRowHeight(buttonCount: number): number { |
|
|
|
|
|
return ( |
|
|
|
|
|
buttonCount * ACTION_BUTTON_HEIGHT + |
|
|
|
|
|
Math.max(0, buttonCount - 1) * ACTION_BUTTON_GAP + |
|
|
|
|
|
ACTION_ROW_EXTRA_PADDING |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function countActionButtonsForRow(row: StockInLineRow): number { |
|
|
|
|
|
let count = 1; |
|
|
|
|
|
const status = (row.status ?? "").toLowerCase(); |
|
|
|
|
|
if (status === "rejected" || status === "partially_completed") { |
|
|
|
|
|
count += 1; |
|
|
|
|
|
} |
|
|
|
|
|
if (status === "received") { |
|
|
|
|
|
count += 1; |
|
|
|
|
|
} |
|
|
|
|
|
if (canDeleteStockInLine(row)) { |
|
|
|
|
|
count += 1; |
|
|
|
|
|
} |
|
|
|
|
|
return count; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Tighter horizontal padding for narrow data columns (headers unchanged). */ |
|
|
|
|
|
const COMPACT_STOCK_IN_CELL_FIELDS = [ |
|
|
|
|
|
"dnNo", |
|
|
|
|
|
"productLotNo", |
|
|
|
|
|
"purchaseAcceptedQty", |
|
|
|
|
|
"uom", |
|
|
|
|
|
"stockUom", |
|
|
|
|
|
"status", |
|
|
|
|
|
] as const; |
|
|
|
|
|
|
|
|
|
|
|
function canDeleteStockInLine(sil: StockInLineRow): boolean { |
|
|
|
|
|
if (sil._isNew || sil.status === "draft") { |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
const hasPutAway = (sil.putAwayLines ?? []).some( |
|
|
|
|
|
(p) => Number(p.stockQty ?? p.qty ?? 0) > 0, |
|
|
|
|
|
); |
|
|
|
|
|
if (hasPutAway) return false; |
|
|
|
|
|
const status = (sil.status ?? "").toLowerCase(); |
|
|
|
|
|
return status !== "completed" && status !== "partially_completed"; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
interface ResultWithId { |
|
|
interface ResultWithId { |
|
|
id: number; |
|
|
id: number; |
|
|
@@ -80,7 +137,7 @@ interface Props { |
|
|
itemDetail: PurchaseOrderLine; |
|
|
itemDetail: PurchaseOrderLine; |
|
|
stockInLine: StockInLine[]; |
|
|
stockInLine: StockInLine[]; |
|
|
warehouse: WarehouseResult[]; |
|
|
warehouse: WarehouseResult[]; |
|
|
fetchPoDetail: (poId: string) => void; |
|
|
|
|
|
|
|
|
fetchPoDetail: (poId: string, preserveDnNo?: boolean, preferredPolId?: number) => void; |
|
|
handleMailTemplateForStockInLine: (stockInLineId: number) => void; |
|
|
handleMailTemplateForStockInLine: (stockInLineId: number) => void; |
|
|
printerCombo: PrinterCombo[]; |
|
|
printerCombo: PrinterCombo[]; |
|
|
} |
|
|
} |
|
|
@@ -127,6 +184,11 @@ function PoInputGrid({ |
|
|
}: Props) { |
|
|
}: Props) { |
|
|
|
|
|
|
|
|
const { t } = useTranslation("purchaseOrder"); |
|
|
const { t } = useTranslation("purchaseOrder"); |
|
|
|
|
|
const theme = useTheme(); |
|
|
|
|
|
/** Narrow phones: hide low-priority columns. */ |
|
|
|
|
|
const isCompact = useMediaQuery(theme.breakpoints.down("md"), { noSsr: true }); |
|
|
|
|
|
/** Tablet / sub-desktop (< xl): flex columns to fill available width. Desktop (≥ xl) keeps fixed widths. */ |
|
|
|
|
|
const isTablet = useMediaQuery(theme.breakpoints.down("xl"), { noSsr: true }); |
|
|
const apiRef = useGridApiRef(); |
|
|
const apiRef = useGridApiRef(); |
|
|
const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({}); |
|
|
const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({}); |
|
|
const getRowId = useCallback<GridRowIdGetter<StockInLineRow>>( |
|
|
const getRowId = useCallback<GridRowIdGetter<StockInLineRow>>( |
|
|
@@ -151,6 +213,8 @@ function PoInputGrid({ |
|
|
const [putAwayOpen, setPutAwayOpen] = useState(false); |
|
|
const [putAwayOpen, setPutAwayOpen] = useState(false); |
|
|
const [rejectOpen, setRejectOpen] = useState(false); |
|
|
const [rejectOpen, setRejectOpen] = useState(false); |
|
|
const [btnIsLoading, setBtnIsLoading] = useState(false); |
|
|
const [btnIsLoading, setBtnIsLoading] = useState(false); |
|
|
|
|
|
const [isDeleting, setIsDeleting] = useState(false); |
|
|
|
|
|
const deleteInFlightRef = useRef(false); |
|
|
const [currQty, setCurrQty] = useState(() => { |
|
|
const [currQty, setCurrQty] = useState(() => { |
|
|
const total = entries.reduce( |
|
|
const total = entries.reduce( |
|
|
// remaining qty (M18 unit) |
|
|
// remaining qty (M18 unit) |
|
|
@@ -180,6 +244,42 @@ function PoInputGrid({ |
|
|
}, |
|
|
}, |
|
|
[getRowId], |
|
|
[getRowId], |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
const handleSoftDelete = useCallback( |
|
|
|
|
|
(row: StockInLineRow) => { |
|
|
|
|
|
if (deleteInFlightRef.current || isDeleting) return; |
|
|
|
|
|
|
|
|
|
|
|
const rowId = row.id as number; |
|
|
|
|
|
const isDraft = row._isNew || row.status === "draft"; |
|
|
|
|
|
|
|
|
|
|
|
const doDelete = async () => { |
|
|
|
|
|
if (deleteInFlightRef.current) return; |
|
|
|
|
|
deleteInFlightRef.current = true; |
|
|
|
|
|
setIsDeleting(true); |
|
|
|
|
|
try { |
|
|
|
|
|
if (isDraft) { |
|
|
|
|
|
handleDelete(rowId)(); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
await deleteStockInLine(rowId); |
|
|
|
|
|
await fetchPoDetail( |
|
|
|
|
|
String(itemDetail.purchaseOrderId), |
|
|
|
|
|
true, |
|
|
|
|
|
itemDetail.id, |
|
|
|
|
|
); |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error("Failed to delete stock in line:", error); |
|
|
|
|
|
alert(t("Cannot delete put away record")); |
|
|
|
|
|
} finally { |
|
|
|
|
|
setIsDeleting(false); |
|
|
|
|
|
deleteInFlightRef.current = false; |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
void deleteDialog(doDelete, t); |
|
|
|
|
|
}, |
|
|
|
|
|
[fetchPoDetail, handleDelete, isDeleting, itemDetail.id, itemDetail.purchaseOrderId, t], |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
const closeQcModal = useCallback(() => { |
|
|
const closeQcModal = useCallback(() => { |
|
|
setQcOpen(false); |
|
|
setQcOpen(false); |
|
|
@@ -445,20 +545,37 @@ function PoInputGrid({ |
|
|
|
|
|
|
|
|
const getButtonSx = (sil : StockInLine) => { |
|
|
const getButtonSx = (sil : StockInLine) => { |
|
|
const status = sil?.status?.toLowerCase(); |
|
|
const status = sil?.status?.toLowerCase(); |
|
|
let btnSx = {label:"", color:""}; |
|
|
|
|
|
|
|
|
let btnSx = { label: "", color: "" }; |
|
|
switch (status) { |
|
|
switch (status) { |
|
|
case "received": btnSx = {label: t("view putaway"), color:"secondary.main"}; break; |
|
|
|
|
|
case "escalated": if (sessionToken?.id == sil?.handlerId) { |
|
|
|
|
|
btnSx = {label: t("escalation processing"), color:"warning.main"}; |
|
|
|
|
|
break;} |
|
|
|
|
|
case "rejected": |
|
|
|
|
|
|
|
|
case "received": |
|
|
|
|
|
btnSx = { label: t("view putaway"), color: "secondary.main" }; |
|
|
|
|
|
break; |
|
|
|
|
|
case "escalated": |
|
|
|
|
|
if (sessionToken?.id == sil?.handlerId) { |
|
|
|
|
|
btnSx = { label: t("escalation processing"), color: "warning.main" }; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
btnSx = { label: t("qc processing"), color: "success.main" }; |
|
|
|
|
|
break; |
|
|
|
|
|
case "rejected": |
|
|
case "partially_completed": |
|
|
case "partially_completed": |
|
|
case "completed": btnSx = {label: t("view stockin"), color:"info.main"}; break; |
|
|
|
|
|
default: btnSx = {label: t("qc processing"), color:"success.main"}; |
|
|
|
|
|
|
|
|
case "completed": |
|
|
|
|
|
btnSx = { label: t("view stockin"), color: "info.main" }; |
|
|
|
|
|
break; |
|
|
|
|
|
default: |
|
|
|
|
|
btnSx = { label: t("qc processing"), color: "success.main" }; |
|
|
} |
|
|
} |
|
|
return btnSx |
|
|
|
|
|
|
|
|
return btnSx; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const columnVisibilityModel = useMemo( |
|
|
|
|
|
() => ({ |
|
|
|
|
|
uom: !isCompact, |
|
|
|
|
|
stockUom: !isCompact, |
|
|
|
|
|
}), |
|
|
|
|
|
[isCompact], |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
// const handleQrCode = useCallback( |
|
|
// const handleQrCode = useCallback( |
|
|
// (id: GridRowId, params: any) => () => { |
|
|
// (id: GridRowId, params: any) => () => { |
|
|
// setRowModesModel((prev) => ({ |
|
|
// setRowModesModel((prev) => ({ |
|
|
@@ -478,333 +595,172 @@ function PoInputGrid({ |
|
|
// [printQrcode], |
|
|
// [printQrcode], |
|
|
// ); |
|
|
// ); |
|
|
|
|
|
|
|
|
const columns = useMemo<GridColDef[]>( |
|
|
|
|
|
() => [ |
|
|
|
|
|
// { |
|
|
|
|
|
// field: "itemNo", |
|
|
|
|
|
// headerName: t("itemNo"), |
|
|
|
|
|
// width: 100, |
|
|
|
|
|
// // flex: 0.4, |
|
|
|
|
|
// }, |
|
|
|
|
|
|
|
|
const columns = useMemo<GridColDef[]>(() => { |
|
|
|
|
|
const baseColumns: GridColDef[] = [ |
|
|
{ |
|
|
{ |
|
|
field: "dnNo", |
|
|
field: "dnNo", |
|
|
headerName: t("dnNo"), |
|
|
headerName: t("dnNo"), |
|
|
width: 125, |
|
|
|
|
|
// renderCell: () => { |
|
|
|
|
|
// return <>DN0000001</> |
|
|
|
|
|
// } |
|
|
|
|
|
// flex: 0.4, |
|
|
|
|
|
|
|
|
width: 92, |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
field: "receiptDate", |
|
|
field: "receiptDate", |
|
|
headerName: t("receiptDate"), |
|
|
headerName: t("receiptDate"), |
|
|
width: 125, |
|
|
width: 125, |
|
|
renderCell: (params) => { |
|
|
|
|
|
// console.log(params.row) |
|
|
|
|
|
// return <>07/08/2025</> |
|
|
|
|
|
return arrayToDateString(params.value) |
|
|
|
|
|
} |
|
|
|
|
|
// flex: 0.4, |
|
|
|
|
|
|
|
|
renderCell: (params) => arrayToDateString(params.value), |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
field: "productLotNo", |
|
|
field: "productLotNo", |
|
|
headerName: t("productLotNo"), |
|
|
headerName: t("productLotNo"), |
|
|
width: 125, |
|
|
|
|
|
|
|
|
width: 100, |
|
|
}, |
|
|
}, |
|
|
// { |
|
|
|
|
|
// field: "itemName", |
|
|
|
|
|
// headerName: t("itemName"), |
|
|
|
|
|
// width: 100, |
|
|
|
|
|
// // flex: 0.6, |
|
|
|
|
|
// }, |
|
|
|
|
|
{ |
|
|
{ |
|
|
field: "purchaseAcceptedQty", |
|
|
field: "purchaseAcceptedQty", |
|
|
headerName: t("acceptedQty"), |
|
|
headerName: t("acceptedQty"), |
|
|
// flex: 0.5, |
|
|
|
|
|
width: 125, |
|
|
|
|
|
|
|
|
width: 84, |
|
|
|
|
|
align: "right", |
|
|
|
|
|
headerAlign: "right", |
|
|
type: "number", |
|
|
type: "number", |
|
|
// editable: true, |
|
|
|
|
|
// replace with tooltip + content |
|
|
|
|
|
renderCell: (params) => { |
|
|
renderCell: (params) => { |
|
|
const qty = params.row.purchaseAcceptedQty ?? 0; |
|
|
const qty = params.row.purchaseAcceptedQty ?? 0; |
|
|
return integerFormatter.format(qty); |
|
|
return integerFormatter.format(qty); |
|
|
} |
|
|
|
|
|
|
|
|
}, |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
field: "uom", |
|
|
field: "uom", |
|
|
headerName: t("uom"), |
|
|
headerName: t("uom"), |
|
|
width: 150, |
|
|
|
|
|
// flex: 0.5, |
|
|
|
|
|
renderCell: (params) => { |
|
|
|
|
|
return itemDetail.uom?.udfudesc; |
|
|
|
|
|
|
|
|
width: 156, |
|
|
|
|
|
renderCell: () => { |
|
|
|
|
|
const text = itemDetail.uom?.udfudesc ?? "-"; |
|
|
|
|
|
return ( |
|
|
|
|
|
<Box |
|
|
|
|
|
sx={{ |
|
|
|
|
|
overflow: "hidden", |
|
|
|
|
|
textOverflow: "ellipsis", |
|
|
|
|
|
whiteSpace: "nowrap", |
|
|
|
|
|
width: "100%", |
|
|
|
|
|
}} |
|
|
|
|
|
title={text} |
|
|
|
|
|
> |
|
|
|
|
|
{text} |
|
|
|
|
|
</Box> |
|
|
|
|
|
); |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
field: "stockQty", |
|
|
field: "stockQty", |
|
|
headerName: t("Stock In Qty"), |
|
|
headerName: t("Stock In Qty"), |
|
|
// flex: 0.5, |
|
|
|
|
|
width: 125, |
|
|
width: 125, |
|
|
type: "number", |
|
|
type: "number", |
|
|
// editable: true, |
|
|
|
|
|
// replace with tooltip + content |
|
|
|
|
|
renderCell: (params) => { |
|
|
renderCell: (params) => { |
|
|
// acceptedQty 現在就是庫存單位數量 |
|
|
|
|
|
const stockQty = params.row.acceptedQty ?? 0; |
|
|
const stockQty = params.row.acceptedQty ?? 0; |
|
|
return decimalFormatter.format(stockQty); |
|
|
return decimalFormatter.format(stockQty); |
|
|
} |
|
|
|
|
|
|
|
|
}, |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
field: "stockUom", |
|
|
field: "stockUom", |
|
|
headerName: t("Stock UoM"), |
|
|
headerName: t("Stock UoM"), |
|
|
width: 150, |
|
|
|
|
|
// flex: 0.5, |
|
|
|
|
|
renderCell: (params) => { |
|
|
|
|
|
return itemDetail.stockUom.stockUomDesc; |
|
|
|
|
|
|
|
|
width: 124, |
|
|
|
|
|
renderCell: () => { |
|
|
|
|
|
const text = itemDetail.stockUom.stockUomDesc ?? "-"; |
|
|
|
|
|
return ( |
|
|
|
|
|
<Box |
|
|
|
|
|
sx={{ |
|
|
|
|
|
overflow: "hidden", |
|
|
|
|
|
textOverflow: "ellipsis", |
|
|
|
|
|
whiteSpace: "nowrap", |
|
|
|
|
|
width: "100%", |
|
|
|
|
|
}} |
|
|
|
|
|
title={text} |
|
|
|
|
|
> |
|
|
|
|
|
{text} |
|
|
|
|
|
</Box> |
|
|
|
|
|
); |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
// { |
|
|
|
|
|
// field: "weight", |
|
|
|
|
|
// headerName: t("weight"), |
|
|
|
|
|
// width: 120, |
|
|
|
|
|
// // flex: 0.5, |
|
|
|
|
|
// renderCell: (params) => { |
|
|
|
|
|
// const weight = calculateWeight( |
|
|
|
|
|
// params.row.acceptedQty, |
|
|
|
|
|
// params.row.uom, |
|
|
|
|
|
// ); |
|
|
|
|
|
// const weightUnit = returnWeightUnit(params.row.uom); |
|
|
|
|
|
// return `${decimalFormatter.format(weight)} ${weightUnit}`; |
|
|
|
|
|
// }, |
|
|
|
|
|
// }, |
|
|
|
|
|
{ |
|
|
{ |
|
|
field: "status", |
|
|
field: "status", |
|
|
headerName: t("Status"), |
|
|
headerName: t("Status"), |
|
|
width: 140, |
|
|
|
|
|
// flex: 0.5, |
|
|
|
|
|
|
|
|
width: 88, |
|
|
renderCell: (params) => { |
|
|
renderCell: (params) => { |
|
|
const handlerId = params.row.handlerId |
|
|
|
|
|
const status = params.row.status |
|
|
|
|
|
return (<span style={{ |
|
|
|
|
|
color: |
|
|
|
|
|
(status == "escalated")? "red": |
|
|
|
|
|
(status == "rejected" || status == "partially_completed") ? "orange" : "inherit"}} |
|
|
|
|
|
|
|
|
const status = params.row.status; |
|
|
|
|
|
return ( |
|
|
|
|
|
<span |
|
|
|
|
|
style={{ |
|
|
|
|
|
color: |
|
|
|
|
|
status == "escalated" |
|
|
|
|
|
? "red" |
|
|
|
|
|
: status == "rejected" || status == "partially_completed" |
|
|
|
|
|
? "orange" |
|
|
|
|
|
: "inherit", |
|
|
|
|
|
}} |
|
|
> |
|
|
> |
|
|
{t(`${params.row.status}`)} |
|
|
{t(`${params.row.status}`)} |
|
|
</span>); |
|
|
|
|
|
|
|
|
</span> |
|
|
|
|
|
); |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
field: "actions", |
|
|
field: "actions", |
|
|
type: "actions", |
|
|
|
|
|
// headerName: `${t("start")} | ${t("qc")} | ${t("escalation")} | ${t( |
|
|
|
|
|
// "stock in", |
|
|
|
|
|
// )} | ${t("putaway")} | ${t("delete")}`, |
|
|
|
|
|
headerName: "操作", |
|
|
headerName: "操作", |
|
|
// headerName: "start | qc | escalation | stock in | putaway | delete", |
|
|
|
|
|
width: 350, //200 |
|
|
|
|
|
// flex: 2, |
|
|
|
|
|
|
|
|
width: ACTIONS_COLUMN_WIDTH, |
|
|
|
|
|
sortable: false, |
|
|
|
|
|
filterable: false, |
|
|
|
|
|
disableColumnMenu: true, |
|
|
cellClassName: "actions", |
|
|
cellClassName: "actions", |
|
|
getActions: (params) => { |
|
|
|
|
|
const data = params.row; |
|
|
|
|
|
// console.log(params.row.status); |
|
|
|
|
|
|
|
|
renderCell: (params) => { |
|
|
|
|
|
const data = params.row as StockInLineRow; |
|
|
const btnSx = getButtonSx(data); |
|
|
const btnSx = getButtonSx(data); |
|
|
// console.log(stockInLineStatusMap[status]); |
|
|
|
|
|
// console.log(session?.user?.abilities?.includes("APPROVAL")); |
|
|
|
|
|
return [ |
|
|
|
|
|
<GridActionsCellItem |
|
|
|
|
|
icon={<Button variant="contained" sx={{ width: '150px', backgroundColor: btnSx.color }}> |
|
|
|
|
|
{btnSx.label}</Button>} |
|
|
|
|
|
label="start" |
|
|
|
|
|
sx={{ |
|
|
|
|
|
// color: "primary.main", |
|
|
|
|
|
// marginRight: 1, |
|
|
|
|
|
|
|
|
const status = (data.status ?? "").toLowerCase(); |
|
|
|
|
|
const canEmail = |
|
|
|
|
|
status === "rejected" || status === "partially_completed"; |
|
|
|
|
|
const canPrint = status === "received"; |
|
|
|
|
|
const canDelete = canDeleteStockInLine(data); |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
|
<StockInLineRowActions |
|
|
|
|
|
btnSx={btnSx} |
|
|
|
|
|
onPrimaryClick={() => { |
|
|
|
|
|
void handleNewQC(params.row.id, params)(); |
|
|
}} |
|
|
}} |
|
|
onClick={handleNewQC(params.row.id, params)} |
|
|
|
|
|
color="inherit" |
|
|
|
|
|
key={`edit`} |
|
|
|
|
|
/>, |
|
|
|
|
|
<GridActionsCellItem |
|
|
|
|
|
icon={ |
|
|
|
|
|
data.status !== "received" ? |
|
|
|
|
|
(<Button |
|
|
|
|
|
id="emailSupplier" |
|
|
|
|
|
type="button" |
|
|
|
|
|
variant="contained" |
|
|
|
|
|
color="primary" |
|
|
|
|
|
sx={{ width: '150px' }} |
|
|
|
|
|
disabled={params.row.status != "rejected" && params.row.status != "partially_completed"} |
|
|
|
|
|
onClick={() => handleMailTemplateForStockInLine(params.row.id as number)} |
|
|
|
|
|
> |
|
|
|
|
|
{t("email supplier")} |
|
|
|
|
|
</Button>) : ( |
|
|
|
|
|
<Button |
|
|
|
|
|
id="printQrCode" |
|
|
|
|
|
type="button" |
|
|
|
|
|
variant="contained" |
|
|
|
|
|
// color="red" |
|
|
|
|
|
sx={{ width: '150px', backgroundColor: '#7f434a' }} |
|
|
|
|
|
disabled={btnIsLoading || data.status != "received"} |
|
|
|
|
|
onClick={() => printQrcode(params.row)} |
|
|
|
|
|
> |
|
|
|
|
|
{t("printQrCode")} |
|
|
|
|
|
</Button> |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
canEmail={canEmail} |
|
|
|
|
|
canPrint={canPrint} |
|
|
|
|
|
canDelete={canDelete} |
|
|
|
|
|
onEmail={() => |
|
|
|
|
|
handleMailTemplateForStockInLine(params.row.id as number) |
|
|
} |
|
|
} |
|
|
label="start" |
|
|
|
|
|
sx={{ |
|
|
|
|
|
// color: "primary.main", |
|
|
|
|
|
// marginRight: 1, |
|
|
|
|
|
}} |
|
|
|
|
|
// onClick={handleNewQC(params.row.id, params)} |
|
|
|
|
|
color="inherit" |
|
|
|
|
|
key="edit" |
|
|
|
|
|
/>, |
|
|
|
|
|
// <GridActionsCellItem |
|
|
|
|
|
// icon={<Button variant="contained">{t("putawayBtn")}</Button>} |
|
|
|
|
|
// label="start" |
|
|
|
|
|
// sx={{ |
|
|
|
|
|
// color: "primary.main", |
|
|
|
|
|
// // marginRight: 1, |
|
|
|
|
|
// }} |
|
|
|
|
|
// // disabled={!(stockInLineStatusMap[status] === 0)} |
|
|
|
|
|
// // set _isNew to false after posting |
|
|
|
|
|
// // or check status |
|
|
|
|
|
// onClick={handleStart(params.row.id, params)} |
|
|
|
|
|
// color="inherit" |
|
|
|
|
|
// key="edit" |
|
|
|
|
|
// />, |
|
|
|
|
|
|
|
|
|
|
|
// <GridActionsCellItem |
|
|
|
|
|
// icon={<Button variant="contained">{t("qc processing")}</Button>} |
|
|
|
|
|
// label="start" |
|
|
|
|
|
// sx={{ |
|
|
|
|
|
// color: "primary.main", |
|
|
|
|
|
// // marginRight: 1, |
|
|
|
|
|
// }} |
|
|
|
|
|
// disabled={!(stockInLineStatusMap[status] === 0)} |
|
|
|
|
|
// // set _isNew to false after posting |
|
|
|
|
|
// // or check status |
|
|
|
|
|
// onClick={handleStart(params.row.id, params)} |
|
|
|
|
|
// color="inherit" |
|
|
|
|
|
// key="edit" |
|
|
|
|
|
// />, |
|
|
|
|
|
// <GridActionsCellItem |
|
|
|
|
|
// icon={<FactCheckIcon />} |
|
|
|
|
|
// label="qc" |
|
|
|
|
|
// sx={{ |
|
|
|
|
|
// color: "primary.main", |
|
|
|
|
|
// // marginRight: 1, |
|
|
|
|
|
// }} |
|
|
|
|
|
// disabled={ |
|
|
|
|
|
// // stockInLineStatusMap[status] === 9 || |
|
|
|
|
|
// stockInLineStatusMap[status] < 1 |
|
|
|
|
|
// } |
|
|
|
|
|
// // set _isNew to false after posting |
|
|
|
|
|
// // or check status |
|
|
|
|
|
// onClick={handleQC(params.row.id, params)} |
|
|
|
|
|
// color="inherit" |
|
|
|
|
|
// key="edit" |
|
|
|
|
|
// />, |
|
|
|
|
|
// <GridActionsCellItem |
|
|
|
|
|
// icon={<NotificationImportantIcon />} |
|
|
|
|
|
// label="escalation" |
|
|
|
|
|
// sx={{ |
|
|
|
|
|
// color: "primary.main", |
|
|
|
|
|
// // marginRight: 1, |
|
|
|
|
|
// }} |
|
|
|
|
|
// disabled={ |
|
|
|
|
|
// stockInLineStatusMap[status] === 9 || |
|
|
|
|
|
// stockInLineStatusMap[status] <= 0 || |
|
|
|
|
|
// stockInLineStatusMap[status] >= 5 |
|
|
|
|
|
// } |
|
|
|
|
|
// // set _isNew to false after posting |
|
|
|
|
|
// // or check status |
|
|
|
|
|
// onClick={handleEscalation(params.row.id, params)} |
|
|
|
|
|
// color="inherit" |
|
|
|
|
|
// key="edit" |
|
|
|
|
|
// />, |
|
|
|
|
|
// <GridActionsCellItem |
|
|
|
|
|
// icon={<ShoppingCartIcon />} |
|
|
|
|
|
// label="stockin" |
|
|
|
|
|
// sx={{ |
|
|
|
|
|
// color: "primary.main", |
|
|
|
|
|
// // marginRight: 1, |
|
|
|
|
|
// }} |
|
|
|
|
|
// disabled={ |
|
|
|
|
|
// stockInLineStatusMap[status] === 9 || |
|
|
|
|
|
// stockInLineStatusMap[status] <= 2 || |
|
|
|
|
|
// stockInLineStatusMap[status] >= 7 || |
|
|
|
|
|
// (stockInLineStatusMap[status] >= 3 && |
|
|
|
|
|
// stockInLineStatusMap[status] <= 5 && |
|
|
|
|
|
// !session?.user?.abilities?.includes("APPROVAL")) |
|
|
|
|
|
// } |
|
|
|
|
|
// // set _isNew to false after posting |
|
|
|
|
|
// // or check status |
|
|
|
|
|
// onClick={handleStockIn(params.row.id, params)} |
|
|
|
|
|
// color="inherit" |
|
|
|
|
|
// key="edit" |
|
|
|
|
|
// />, |
|
|
|
|
|
// <GridActionsCellItem |
|
|
|
|
|
// icon={<ShoppingCartIcon />} |
|
|
|
|
|
// label="putaway" |
|
|
|
|
|
// sx={{ |
|
|
|
|
|
// color: "primary.main", |
|
|
|
|
|
// // marginRight: 1, |
|
|
|
|
|
// }} |
|
|
|
|
|
// disabled={ |
|
|
|
|
|
// stockInLineStatusMap[status] === 9 || |
|
|
|
|
|
// stockInLineStatusMap[status] < 7 |
|
|
|
|
|
// } |
|
|
|
|
|
// // set _isNew to false after posting |
|
|
|
|
|
// // or check status |
|
|
|
|
|
// onClick={handlePutAway(params.row.id, params)} |
|
|
|
|
|
// color="inherit" |
|
|
|
|
|
// key="edit" |
|
|
|
|
|
// />, |
|
|
|
|
|
// // <GridActionsCellItem |
|
|
|
|
|
// // icon={<QrCodeIcon />} |
|
|
|
|
|
// // label="putaway" |
|
|
|
|
|
// // sx={{ |
|
|
|
|
|
// // color: "primary.main", |
|
|
|
|
|
// // // marginRight: 1, |
|
|
|
|
|
// // }} |
|
|
|
|
|
// // disabled={stockInLineStatusMap[status] === 9 || stockInLineStatusMap[status] !== 8} |
|
|
|
|
|
// // // set _isNew to false after posting |
|
|
|
|
|
// // // or check status |
|
|
|
|
|
// // onClick={handleQrCode(params.row.id, params)} |
|
|
|
|
|
// // color="inherit" |
|
|
|
|
|
// // key="edit" |
|
|
|
|
|
// // />, |
|
|
|
|
|
// <GridActionsCellItem |
|
|
|
|
|
// icon={ |
|
|
|
|
|
// stockInLineStatusMap[status] >= 1 ? ( |
|
|
|
|
|
// <DoDisturbIcon /> |
|
|
|
|
|
// ) : ( |
|
|
|
|
|
// <DeleteIcon /> |
|
|
|
|
|
// ) |
|
|
|
|
|
// } |
|
|
|
|
|
// label="Delete" |
|
|
|
|
|
// sx={{ |
|
|
|
|
|
// color: "error.main", |
|
|
|
|
|
// }} |
|
|
|
|
|
// disabled={ |
|
|
|
|
|
// stockInLineStatusMap[status] >= 7 && |
|
|
|
|
|
// stockInLineStatusMap[status] <= 9 |
|
|
|
|
|
// } |
|
|
|
|
|
// onClick={ |
|
|
|
|
|
// stockInLineStatusMap[status] === 0 |
|
|
|
|
|
// ? handleDelete(params.row.id) |
|
|
|
|
|
// : handleReject(params.row.id, params) |
|
|
|
|
|
// } |
|
|
|
|
|
// color="inherit" |
|
|
|
|
|
// key="edit" |
|
|
|
|
|
// />, |
|
|
|
|
|
]; |
|
|
|
|
|
|
|
|
onPrint={() => printQrcode(params.row)} |
|
|
|
|
|
onDelete={() => handleSoftDelete(data)} |
|
|
|
|
|
btnIsLoading={btnIsLoading} |
|
|
|
|
|
isDeleting={isDeleting} |
|
|
|
|
|
/> |
|
|
|
|
|
); |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
], |
|
|
|
|
|
[t, handleStart, handleEscalation, handleStockIn, handlePutAway, handleDelete, handleReject, itemDetail], |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
if (!isTablet) { |
|
|
|
|
|
return baseColumns; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return baseColumns.map((col) => { |
|
|
|
|
|
if (col.field === "actions") { |
|
|
|
|
|
return { ...col, flex: 0, width: ACTIONS_COLUMN_WIDTH }; |
|
|
|
|
|
} |
|
|
|
|
|
const minWidth = col.width ?? 80; |
|
|
|
|
|
return { ...col, flex: 1, minWidth, width: undefined }; |
|
|
|
|
|
}); |
|
|
|
|
|
}, [ |
|
|
|
|
|
t, |
|
|
|
|
|
isTablet, |
|
|
|
|
|
itemDetail, |
|
|
|
|
|
handleNewQC, |
|
|
|
|
|
handleMailTemplateForStockInLine, |
|
|
|
|
|
printQrcode, |
|
|
|
|
|
handleSoftDelete, |
|
|
|
|
|
btnIsLoading, |
|
|
|
|
|
isDeleting, |
|
|
|
|
|
sessionToken?.id, |
|
|
|
|
|
]); |
|
|
|
|
|
|
|
|
const unsortableColumns = useMemo(() => |
|
|
const unsortableColumns = useMemo(() => |
|
|
columns.map(column => ({ ...column, sortable: false })) |
|
|
columns.map(column => ({ ...column, sortable: false })) |
|
|
@@ -915,14 +871,36 @@ function PoInputGrid({ |
|
|
</Box> */} |
|
|
</Box> */} |
|
|
</> |
|
|
</> |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
const getRowHeight = useCallback( |
|
|
|
|
|
(params: { model: StockInLineRow }) => { |
|
|
|
|
|
const count = countActionButtonsForRow(params.model); |
|
|
|
|
|
return getActionRowHeight(count); |
|
|
|
|
|
}, |
|
|
|
|
|
[], |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
return ( |
|
|
return ( |
|
|
<> |
|
|
<> |
|
|
|
|
|
<Box |
|
|
|
|
|
sx={{ |
|
|
|
|
|
width: "100%", |
|
|
|
|
|
maxWidth: "100%", |
|
|
|
|
|
minWidth: 0, |
|
|
|
|
|
overflowX: "auto", |
|
|
|
|
|
WebkitOverflowScrolling: "touch", |
|
|
|
|
|
pb: 1, |
|
|
|
|
|
}} |
|
|
|
|
|
> |
|
|
<StyledDataGrid |
|
|
<StyledDataGrid |
|
|
getRowId={getRowId} |
|
|
getRowId={getRowId} |
|
|
apiRef={apiRef} |
|
|
apiRef={apiRef} |
|
|
autoHeight |
|
|
autoHeight |
|
|
|
|
|
getRowHeight={getRowHeight} |
|
|
|
|
|
columnVisibilityModel={columnVisibilityModel} |
|
|
sx={{ |
|
|
sx={{ |
|
|
|
|
|
width: "100%", |
|
|
|
|
|
minWidth: isTablet ? undefined : STOCK_IN_GRID_MIN_WIDTH_DESKTOP, |
|
|
"--DataGrid-overlayHeight": "100px", |
|
|
"--DataGrid-overlayHeight": "100px", |
|
|
".MuiDataGrid-row .MuiDataGrid-cell.hasError": { |
|
|
".MuiDataGrid-row .MuiDataGrid-cell.hasError": { |
|
|
border: "1px solid", |
|
|
border: "1px solid", |
|
|
@@ -932,6 +910,28 @@ function PoInputGrid({ |
|
|
border: "1px solid", |
|
|
border: "1px solid", |
|
|
borderColor: "warning.main", |
|
|
borderColor: "warning.main", |
|
|
}, |
|
|
}, |
|
|
|
|
|
"& .MuiDataGrid-cell.actions": { |
|
|
|
|
|
overflow: "visible", |
|
|
|
|
|
alignItems: "flex-start", |
|
|
|
|
|
py: 0.75, |
|
|
|
|
|
lineHeight: "normal", |
|
|
|
|
|
}, |
|
|
|
|
|
"& .MuiDataGrid-cell[data-field='actions']": { |
|
|
|
|
|
py: 0.75, |
|
|
|
|
|
px: 1, |
|
|
|
|
|
}, |
|
|
|
|
|
...Object.fromEntries( |
|
|
|
|
|
COMPACT_STOCK_IN_CELL_FIELDS.flatMap((field) => [ |
|
|
|
|
|
[ |
|
|
|
|
|
`& .MuiDataGrid-cell[data-field="${field}"]`, |
|
|
|
|
|
{ px: 1 }, |
|
|
|
|
|
], |
|
|
|
|
|
[ |
|
|
|
|
|
`& .MuiDataGrid-columnHeader[data-field="${field}"]`, |
|
|
|
|
|
{ px: 1 }, |
|
|
|
|
|
], |
|
|
|
|
|
]), |
|
|
|
|
|
), |
|
|
}} |
|
|
}} |
|
|
disableColumnMenu |
|
|
disableColumnMenu |
|
|
editMode="row" |
|
|
editMode="row" |
|
|
@@ -963,6 +963,7 @@ function PoInputGrid({ |
|
|
footer: { child: footer }, |
|
|
footer: { child: footer }, |
|
|
}} |
|
|
}} |
|
|
/> |
|
|
/> |
|
|
|
|
|
</Box> |
|
|
{/* {modalInfo !== undefined && ( */} |
|
|
{/* {modalInfo !== undefined && ( */} |
|
|
<> |
|
|
<> |
|
|
<QcStockInModal |
|
|
<QcStockInModal |
|
|
|