CANCERYS\kw093 2 месяцев назад
Родитель
Сommit
fe17377941
7 измененных файлов: 129 добавлений и 44 удалений
  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 Просмотреть файл

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

export interface DoSearchAll {


+ 1
- 0
src/app/api/do/index.tsx Просмотреть файл

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




+ 2
- 9
src/components/DoDetail/DoInfoCard.tsx Просмотреть файл

@@ -21,15 +21,8 @@ const DoInfoCard: React.FC<Props> = ({
<CardContent component={Stack} spacing={4}>
<Box>
<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}>
<TextField
{...register("code")}


+ 102
- 30
src/components/DoDetail/DoLineTable.tsx Просмотреть файл

@@ -1,11 +1,27 @@
import { DoDetail } from "@/app/api/do/actions";
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 { useMemo } from "react";
import { useMemo, useEffect, useState } from "react";
import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
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 = {

@@ -19,59 +35,114 @@ const DoLineTable: React.FC<Props> = ({
watch
} = 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[]>(() => [
{
field: "itemNo",
headerName: t("Item No."),
flex: 1,
flex: 0.6,
},
{
field: "itemName",
headerName: t("Item Name"),
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",
headerName: t("Quantity"),
flex: 1,
flex: 0.7,
align: "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",
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 (
<>
@@ -88,8 +159,9 @@ const DoLineTable: React.FC<Props> = ({
},
}}
disableColumnMenu
rows={watch("deliveryOrderLines")}
rows={rowsWithCalculatedFields}
columns={columns}
getRowHeight={() => 'auto'}
/>
</>
)


+ 3
- 3
src/components/FinishedGoodSearch/FinishedGoodSearch.tsx Просмотреть файл

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


+ 18
- 1
src/components/Jodetail/JobPickExecution.tsx Просмотреть файл

@@ -1295,7 +1295,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
const stockOutLineQty = lot.stockOutLineQty || 0;
return Math.max(0, requiredQty - stockOutLineQty);
}, []);
// Search criteria
const searchCriteria: Criterion<any>[] = [
{
@@ -1423,7 +1423,24 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
}
};
}, [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) => {
switch (lot.stockOutLineStatus?.toLowerCase()) {
case 'pending':


+ 2
- 1
src/i18n/zh/do.json Просмотреть файл

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

Загрузка…
Отмена
Сохранить