Browse Source

update

master
CANCERYS\kw093 2 months ago
parent
commit
fe17377941
7 changed files with 129 additions and 44 deletions
  1. +1
    -0
      src/app/api/do/actions.tsx
  2. +1
    -0
      src/app/api/do/index.tsx
  3. +2
    -9
      src/components/DoDetail/DoInfoCard.tsx
  4. +102
    -30
      src/components/DoDetail/DoLineTable.tsx
  5. +3
    -3
      src/components/FinishedGoodSearch/FinishedGoodSearch.tsx
  6. +18
    -1
      src/components/Jodetail/JobPickExecution.tsx
  7. +2
    -1
      src/i18n/zh/do.json

+ 1
- 0
src/app/api/do/actions.tsx View File

@@ -35,6 +35,7 @@ export interface DoDetailLine {
status: string; status: string;
itemName?: string; itemName?: string;
uomCode?: string; uomCode?: string;
shortUom?: string;
} }


export interface DoSearchAll { export interface DoSearchAll {


+ 1
- 0
src/app/api/do/index.tsx View File

@@ -21,6 +21,7 @@ export interface DoDetailLine {
status: string; status: string;
itemName?: string; itemName?: string;
uomCode?: string; uomCode?: string;
shortUom?: string;
} }






+ 2
- 9
src/components/DoDetail/DoInfoCard.tsx View File

@@ -21,15 +21,8 @@ const DoInfoCard: React.FC<Props> = ({
<CardContent component={Stack} spacing={4}> <CardContent component={Stack} spacing={4}>
<Box> <Box>
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}> <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
<Grid item xs={6}>
<TextField
label={t("Status")}
fullWidth
disabled={true}
value={`${t(upperFirst(watch("status")))}`}
/>
</Grid>
<Grid item xs={6}/>
<Grid item xs={6}> <Grid item xs={6}>
<TextField <TextField
{...register("code")} {...register("code")}


+ 102
- 30
src/components/DoDetail/DoLineTable.tsx View File

@@ -1,11 +1,27 @@
import { DoDetail } from "@/app/api/do/actions"; import { DoDetail } from "@/app/api/do/actions";
import { decimalFormatter } from "@/app/utils/formatUtil"; import { decimalFormatter } from "@/app/utils/formatUtil";
import { GridColDef } from "@mui/x-data-grid";
import { GridColDef, GridRenderCellParams } from "@mui/x-data-grid";
import { isEmpty, upperFirst } from "lodash"; import { isEmpty, upperFirst } from "lodash";
import { useMemo } from "react";
import { useMemo, useEffect, useState } from "react";
import { useFormContext } from "react-hook-form"; import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import StyledDataGrid from "../StyledDataGrid/StyledDataGrid"; import StyledDataGrid from "../StyledDataGrid/StyledDataGrid";
import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined';
import DoDisturbAltRoundedIcon from '@mui/icons-material/DoDisturbAltRounded';
import { fetchInventories } from "@/app/api/inventory/actions";
import { InventoryResult } from "@/app/api/inventory";

type DoLineWithCalculations = {
id?: number;
itemNo: string;
itemName: string;
qty: number;
uomCode: string;
shortUom?: string;
status: string;
stockAvailable: number;
isStockSufficient: boolean;
};


type Props = { type Props = {


@@ -19,59 +35,114 @@ const DoLineTable: React.FC<Props> = ({
watch watch
} = useFormContext<DoDetail>() } = useFormContext<DoDetail>()


const [inventoryData, setInventoryData] = useState<InventoryResult[]>([]);
const deliveryOrderLines = watch("deliveryOrderLines");

useEffect(() => {
const fetchInventoryData = async () => {
try {
const inventoryResponse = await fetchInventories({
code: "",
name: "",
type: "",
pageNum: 0,
pageSize: 1000
});
setInventoryData(inventoryResponse.records);
} catch (error) {
console.error("Error fetching inventory data:", error);
}
};

fetchInventoryData();
}, [deliveryOrderLines]);

const getStockAvailable = (line: any) => {
const inventory = inventoryData.find(inventory =>
inventory.itemCode === line.itemNo || inventory.itemName === line.itemName
);
if (inventory) {
return inventory.availableQty || (inventory.onHandQty - inventory.onHoldQty - inventory.unavailableQty);
}
return 0;
};

const isStockSufficient = (line: any) => {
const stockAvailable = getStockAvailable(line);
return stockAvailable >= line.qty;
};

const sufficientStockIcon = useMemo(() => {
return <CheckCircleOutlineOutlinedIcon fontSize={"large"} color="success" />
}, []);

const insufficientStockIcon = useMemo(() => {
return <DoDisturbAltRoundedIcon fontSize={"large"} color="error" />
}, []);

const rowsWithCalculatedFields = useMemo(() => {
return deliveryOrderLines.map((line, index) => ({
...line,
id: line.id || index,
shortUom: line.shortUom, // 假设 shortUom 就是 uomCode,如果有其他字段请修改
stockAvailable: getStockAvailable(line),
isStockSufficient: isStockSufficient(line),
}));
}, [deliveryOrderLines, inventoryData]);

const columns = useMemo<GridColDef[]>(() => [ const columns = useMemo<GridColDef[]>(() => [
{ {
field: "itemNo", field: "itemNo",
headerName: t("Item No."), headerName: t("Item No."),
flex: 1,
flex: 0.6,
}, },
{ {
field: "itemName", field: "itemName",
headerName: t("Item Name"), headerName: t("Item Name"),
flex: 1, flex: 1,
renderCell: (row) => {
return isEmpty(row.value) ? "N/A" : row.value
renderCell: (params: GridRenderCellParams<DoLineWithCalculations>) => {
const name = isEmpty(params.value) ? "N/A" : params.value;
const uom = params.row.uomCode || "";
return `${name} (${uom})`;
}, },
}, },
{ {
field: "qty", field: "qty",
headerName: t("Quantity"), headerName: t("Quantity"),
flex: 1,
flex: 0.7,
align: "right", align: "right",
headerAlign: "right", headerAlign: "right",
renderCell: (row) => {
return decimalFormatter.format(row.value)
renderCell: (params: GridRenderCellParams<DoLineWithCalculations>) => {
return `${decimalFormatter.format(params.value)} (${params.row.shortUom})`;
}, },
}, },
{ {
field: "price",
headerName: t("Price"),
flex: 1,
field: "stockAvailable",
headerName: t("Stock Available"),
flex: 0.7,
align: "right", align: "right",
headerAlign: "right", headerAlign: "right",
renderCell: (row) => {
return decimalFormatter.format(row.value)
},
},
{
field: "uomCode",
headerName: t("UoM"),
flex: 1,
align: "left",
headerAlign: "left",
renderCell: (row) => {
return isEmpty(row.value) ? "N/A" : row.value
type: "number",
renderCell: (params: GridRenderCellParams<DoLineWithCalculations>) => {
return `${decimalFormatter.format(params.value)} (${params.row.shortUom})`;
}, },
}, },
{ {
field: "status",
headerName: t("Status"),
flex: 1,
renderCell: (row) => {
return t(upperFirst(row.value))
field: "stockStatus",
headerName: t("Stock Status"),
flex: 0.5,
align: "right",
headerAlign: "right",
type: "boolean",
renderCell: (params: GridRenderCellParams<DoLineWithCalculations>) => {
return params.row.isStockSufficient ? sufficientStockIcon : insufficientStockIcon;
}, },
}, },
], [t])

], [t, inventoryData])


return ( return (
<> <>
@@ -88,8 +159,9 @@ const DoLineTable: React.FC<Props> = ({
}, },
}} }}
disableColumnMenu disableColumnMenu
rows={watch("deliveryOrderLines")}
rows={rowsWithCalculatedFields}
columns={columns} columns={columns}
getRowHeight={() => 'auto'}
/> />
</> </>
) )


+ 3
- 3
src/components/FinishedGoodSearch/FinishedGoodSearch.tsx View File

@@ -640,7 +640,7 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
</Button> </Button>
<Button <Button
variant="contained" variant="contained"
// disabled={!printButtonsEnabled}
disabled={!printButtonsEnabled}
title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""} title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""}
onClick={handleDNandLabel} onClick={handleDNandLabel}
> >
@@ -648,7 +648,7 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
</Button> </Button>
<Button <Button
variant="contained" variant="contained"
// disabled={!printButtonsEnabled}
disabled={!printButtonsEnabled}
title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""} title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""}
onClick={handleDN} onClick={handleDN}
> >
@@ -656,7 +656,7 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
</Button> </Button>
<Button <Button
variant="contained" variant="contained"
// disabled={!printButtonsEnabled}
disabled={!printButtonsEnabled}
title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""} title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""}
onClick={handleLabel} onClick={handleLabel}
> >


+ 18
- 1
src/components/Jodetail/JobPickExecution.tsx View File

@@ -1295,7 +1295,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
const stockOutLineQty = lot.stockOutLineQty || 0; const stockOutLineQty = lot.stockOutLineQty || 0;
return Math.max(0, requiredQty - stockOutLineQty); return Math.max(0, requiredQty - stockOutLineQty);
}, []); }, []);
// Search criteria // Search criteria
const searchCriteria: Criterion<any>[] = [ const searchCriteria: Criterion<any>[] = [
{ {
@@ -1423,7 +1423,24 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
} }
}; };
}, [isManualScanning, stopScan, resetScan]); }, [isManualScanning, stopScan, resetScan]);
useEffect(() => {
if (isManualScanning && combinedLotData.length === 0) {
console.log("⏹️ No data available, auto-stopping QR scan...");
handleStopScan();
}
}, [combinedLotData.length, isManualScanning, handleStopScan]);


// ✅ Cleanup effect
useEffect(() => {
return () => {
// Cleanup when component unmounts (e.g., when switching tabs)
if (isManualScanning) {
console.log("🧹 Component unmounting, stopping QR scanner...");
stopScan();
resetScan();
}
};
}, [isManualScanning, stopScan, resetScan]);
const getStatusMessage = useCallback((lot: any) => { const getStatusMessage = useCallback((lot: any) => {
switch (lot.stockOutLineStatus?.toLowerCase()) { switch (lot.stockOutLineStatus?.toLowerCase()) {
case 'pending': case 'pending':


+ 2
- 1
src/i18n/zh/do.json View File

@@ -44,5 +44,6 @@
"Batch Release": "批量放單", "Batch Release": "批量放單",
"Batch release completed successfully.": "已完成批量放單", "Batch release completed successfully.": "已完成批量放單",
"Edit Delivery Order Detail": "編輯交貨單詳情", "Edit Delivery Order Detail": "編輯交貨單詳情",
"DO released successfully! Pick orders created.": "交貨單放單成功!提料單已建立。"
"DO released successfully! Pick orders created.": "交貨單放單成功!提料單已建立。",
"Stock Available": "庫存可用"
} }

Loading…
Cancel
Save