瀏覽代碼

update PO flow

master
kelvin.yau 2 月之前
父節點
當前提交
2701b467bc
共有 9 個檔案被更改,包括 572 行新增35 行删除
  1. +42
    -7
      src/app/api/do/actions.tsx
  2. +1
    -0
      src/app/api/inventory/index.ts
  3. +271
    -6
      src/components/FinishedGoodSearch/FinishedGoodSearch.tsx
  4. +6
    -2
      src/components/FinishedGoodSearch/GoodPickExecution.tsx
  5. +21
    -2
      src/components/JoSave/InfoCard.tsx
  6. +103
    -0
      src/components/JoSave/JoRelease.tsx
  7. +2
    -0
      src/components/JoSave/JoSave.tsx
  8. +122
    -15
      src/components/JoSave/PickTable.tsx
  9. +4
    -3
      src/i18n/zh/pickOrder.json

+ 42
- 7
src/app/api/do/actions.tsx 查看文件

@@ -3,7 +3,7 @@ import { BASE_API_URL } from "@/config/api";
// import { ServerFetchError, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil";
import { revalidateTag } from "next/cache";
import { cache } from "react";
import { serverFetchJson } from "@/app/utils/fetchUtil";
import { serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil";
import { QcItemResult } from "../settings/qcItem";
import { RecordsRes } from "../utils";
import { DoResult } from ".";
@@ -87,6 +87,18 @@ export interface PrintDeliveryNoteResponse{
message?: string
}

export interface PrintDNLabelsRequest{
deliveryOrderId: number,
printerId: number,
printQty: number,
numOfCarton: number
}

export interface PrintDNLabelsRespone{
success: boolean;
message?: string
}

export const assignPickOrderByStore = cache(async (data: AssignByStoreRequest) => {
return await serverFetchJson<AssignByStoreResponse>(`${BASE_API_URL}/doPickOrder/assign-by-store`,
{
@@ -146,14 +158,37 @@ export const fetchDoSearch = cache(async (code: string, shopName: string, status
});

export async function printDN(request: PrintDeliveryNoteRequest){
const response = await serverFetchJson<PrintDeliveryNoteResponse>(`${BASE_API_URL}/do/print-DN`,{
const params = new URLSearchParams();
params.append('deliveryOrderId', request.deliveryOrderId.toString());
params.append('printerId', request.printerId.toString());
if (request.printQty !== null && request.printQty !== undefined) {
params.append('printQty', request.printQty.toString());
}
params.append('numOfCarton', request.numOfCarton.toString());
params.append('isDraft', request.isDraft.toString());
params.append('pickOrderId', request.pickOrderId.toString());

const response = await serverFetchWithNoContent(`${BASE_API_URL}/do/print-DN?${params.toString()}`,{
method: "GET",
body: JSON.stringify(request),
headers: {
'Content-type': 'application/json',
},
});
return response;
return { success: true, message: "Print job sent successfully (DN)" } as PrintDeliveryNoteResponse;
}

export async function printDNLabels(request: PrintDNLabelsRequest){
const params = new URLSearchParams();
params.append('deliveryOrderId', request.deliveryOrderId.toString());
params.append('printerId', request.printerId.toString());
if (request.printQty !== null && request.printQty !== undefined) {
params.append('printQty', request.printQty.toString());
}
params.append('numOfCarton', request.numOfCarton.toString());

const response = await serverFetchWithNoContent(`${BASE_API_URL}/do/print-DNLabels?${params.toString()}`,{
method: "GET"
});

return { success: true, message: "Print job sent successfully (labels)"} as PrintDeliveryNoteResponse
}



+ 1
- 0
src/app/api/inventory/index.ts 查看文件

@@ -16,6 +16,7 @@ export interface InventoryResult {
availableQty: number;
uomCode: string;
uomUdfudesc: string;
uomShortDesc: string;
// germPerSmallestUnit: number;
qtyPerSmallestUnit: number;
baseUom: string;


+ 271
- 6
src/components/FinishedGoodSearch/FinishedGoodSearch.tsx 查看文件

@@ -30,6 +30,11 @@ import { useSession } from "next-auth/react";
import { SessionWithTokens } from "@/config/authConfig";
import PickExecutionDetail from "./GoodPickExecutiondetail";
import GoodPickExecutionRecord from "./GoodPickExecutionRecord";
import Swal from "sweetalert2";
import { printDN, printDNLabels } from "@/app/api/do/actions";
import { FGPickOrderResponse } from "@/app/api/pickOrder/actions";
import FGPickOrderCard from "./FGPickOrderCard";

interface Props {
pickOrders: PickOrderResult[];
}
@@ -57,6 +62,263 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
const [hideCompletedUntilNext, setHideCompletedUntilNext] = useState<boolean>(
typeof window !== 'undefined' && localStorage.getItem('hideCompletedUntilNext') === 'true'
);

const [fgPickOrdersData, setFgPickOrdersData] = useState<FGPickOrderResponse[]>([]);

const handleDraft = useCallback(async () =>{
try{
if (fgPickOrdersData.length === 0) {
console.error("No FG Pick order data available");
return;
}

const currentFgOrder = fgPickOrdersData[0];

const printRequest = {
printerId: 2,
printQty: 1,
isDraft: true,
numOfCarton: 0,
deliveryOrderId: currentFgOrder.deliveryOrderId,
pickOrderId: currentFgOrder.pickOrderId
};

console.log("Printing draft with request: ", printRequest);

const response = await printDN(printRequest);

console.log("Print Draft response: ", response);

if(response.success){
Swal.fire({
position: "bottom-end",
icon: "info",
text: t("Printed Successfully."),
showConfirmButton: false,
timer: 1500
});
} else {
console.error("Print failed: ", response.message);
}
} catch(error){
console.error("error: ", error)
}
},[t, fgPickOrdersData]);

const handleDN = useCallback(async () =>{
const askNumofCarton = await Swal.fire({
title: t("Enter the number of cartons: "),
input: "number",
inputPlaceholder: t("Number of cartons"),
inputAttributes:{
min: "1",
step: "1"
},
inputValidator: (value) => {
if(!value){
return t("You need to enter a number")
}
if(parseInt(value) < 1){
return t("Number must be at least 1");
}
return null
},
showCancelButton: true,
confirmButtonText: t("Confirm"),
cancelButtonText: t("Cancel"),
showLoaderOnConfirm: true,
allowOutsideClick: () => !Swal.isLoading()
});

if (askNumofCarton.isConfirmed) {
const numOfCartons = askNumofCarton.value;
try{
if (fgPickOrdersData.length === 0) {
console.error("No FG Pick order data available");
return;
}

const currentFgOrder = fgPickOrdersData[0];

const printRequest = {
printerId: 2,
printQty: 1,
isDraft: false,
numOfCarton: numOfCartons,
deliveryOrderId: currentFgOrder.deliveryOrderId,
pickOrderId: currentFgOrder.pickOrderId
};

console.log("Printing Delivery Note with request: ", printRequest);

const response = await printDN(printRequest);

console.log("Print Delivery Note response: ", response);

if(response.success){
Swal.fire({
position: "bottom-end",
icon: "info",
text: t("Printed Successfully."),
showConfirmButton: false,
timer: 1500
});
} else {
console.error("Print failed: ", response.message);
}
} catch(error){
console.error("error: ", error)
}
}
},[t, fgPickOrdersData]);

const handleDNandLabel = useCallback(async () =>{
const askNumofCarton = await Swal.fire({
title: t("Enter the number of cartons: "),
input: "number",
inputPlaceholder: t("Number of cartons"),
inputAttributes:{
min: "1",
step: "1"
},
inputValidator: (value) => {
if(!value){
return t("You need to enter a number")
}
if(parseInt(value) < 1){
return t("Number must be at least 1");
}
return null
},
showCancelButton: true,
confirmButtonText: t("Confirm"),
cancelButtonText: t("Cancel"),
showLoaderOnConfirm: true,
allowOutsideClick: () => !Swal.isLoading()
});

if (askNumofCarton.isConfirmed) {
const numOfCartons = askNumofCarton.value;
try{
if (fgPickOrdersData.length === 0) {
console.error("No FG Pick order data available");
return;
}

const currentFgOrder = fgPickOrdersData[0];

const printDNRequest = {
printerId: 2,
printQty: 1,
isDraft: false,
numOfCarton: numOfCartons,
deliveryOrderId: currentFgOrder.deliveryOrderId,
pickOrderId: currentFgOrder.pickOrderId
};

const printDNLabelsRequest = {
printerId: 1,
printQty: 1,
numOfCarton: numOfCartons,
deliveryOrderId: currentFgOrder.deliveryOrderId
};
console.log("Printing Labels with request: ", printDNLabelsRequest);
console.log("Printing DN with request: ", printDNRequest);

const LabelsResponse = await printDNLabels(printDNLabelsRequest);
const DNResponse = await printDN(printDNRequest);
console.log("Print Labels response: ", LabelsResponse);
console.log("Print DN response: ", DNResponse);

if(LabelsResponse.success && DNResponse.success){
Swal.fire({
position: "bottom-end",
icon: "info",
text: t("Printed Successfully."),
showConfirmButton: false,
timer: 1500
});
} else {
if(!LabelsResponse.success){
console.error("Print failed: ", LabelsResponse.message);
}
else{
console.error("Print failed: ", DNResponse.message);
}
}
} catch(error){
console.error("error: ", error)
}
}
},[t, fgPickOrdersData]);

const handleLabel = useCallback(async () =>{
const askNumofCarton = await Swal.fire({
title: t("Enter the number of cartons: "),
input: "number",
inputPlaceholder: t("Number of cartons"),
inputAttributes:{
min: "1",
step: "1"
},
inputValidator: (value) => {
if(!value){
return t("You need to enter a number")
}
if(parseInt(value) < 1){
return t("Number must be at least 1");
}
return null
},
showCancelButton: true,
confirmButtonText: t("Confirm"),
cancelButtonText: t("Cancel"),
showLoaderOnConfirm: true,
allowOutsideClick: () => !Swal.isLoading()
});

if (askNumofCarton.isConfirmed) {
const numOfCartons = askNumofCarton.value;
try{
if (fgPickOrdersData.length === 0) {
console.error("No FG Pick order data available");
return;
}
const currentFgOrder = fgPickOrdersData[0];

const printRequest = {
printerId: 1,
printQty: 1,
numOfCarton: numOfCartons,
deliveryOrderId: currentFgOrder.deliveryOrderId,
};

console.log("Printing Labels with request: ", printRequest);

const response = await printDNLabels(printRequest);

console.log("Print Labels response: ", response);

if(response.success){
Swal.fire({
position: "bottom-end",
icon: "info",
text: t("Printed Successfully."),
showConfirmButton: false,
timer: 1500
});
} else {
console.error("Print failed: ", response.message);
}
} catch(error){
console.error("error: ", error)
}
}
},[t, fgPickOrdersData]);

useEffect(() => {
const onAssigned = () => {
localStorage.removeItem('hideCompletedUntilNext');
@@ -132,7 +394,6 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
};
// ✅ Manual assignment handler - uses the action function


const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
(_e, newValue) => {
setTabIndex(newValue);
@@ -385,29 +646,33 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
*/}
<Button
variant="contained"
disabled={!printButtonsEnabled}
// disabled={!printButtonsEnabled}
title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""}
onClick={handleDraft}
>
{t("Print Draft")}
</Button>
<Button
variant="contained"
disabled={!printButtonsEnabled}
// disabled={!printButtonsEnabled}
title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""}
onClick={handleDNandLabel}
>
{t("Print Pick Order and DN Label")}
</Button>
<Button
variant="contained"
disabled={!printButtonsEnabled}
// disabled={!printButtonsEnabled}
title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""}
onClick={handleDN}
>
{t("Print Pick Order")}
</Button>
<Button
variant="contained"
disabled={!printButtonsEnabled}
// disabled={!printButtonsEnabled}
title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""}
onClick={handleLabel}
>
{t("Print DN Label")}
</Button>
@@ -435,7 +700,7 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
<Box sx={{
p: 2
}}>
{tabIndex === 0 && <PickExecution filterArgs={filterArgs} />}
{tabIndex === 0 && <PickExecution filterArgs={filterArgs} onFgPickOrdersChange={setFgPickOrdersData}/>}
{tabIndex === 1 && <PickExecutionDetail filterArgs={filterArgs} />}
{tabIndex === 2 && <GoodPickExecutionRecord filterArgs={filterArgs} />}
</Box>


+ 6
- 2
src/components/FinishedGoodSearch/GoodPickExecution.tsx 查看文件

@@ -51,6 +51,7 @@ import GoodPickExecutionForm from "./GoodPickExecutionForm";
import FGPickOrderCard from "./FGPickOrderCard";
interface Props {
filterArgs: Record<string, any>;
onFgPickOrdersChange?: (fgPickOrders: FGPickOrderResponse[]) => void;
}

// ✅ QR Code Modal Component (from LotTable)
@@ -307,7 +308,7 @@ const QrCodeModal: React.FC<{
);
};

const PickExecution: React.FC<Props> = ({ filterArgs }) => {
const PickExecution: React.FC<Props> = ({ filterArgs, onFgPickOrdersChange }) => {
const { t } = useTranslation("pickOrder");
const router = useRouter();
const { data: session } = useSession() as { data: SessionWithTokens | null };
@@ -359,6 +360,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
if (pickOrderIds.length === 0) {
setFgPickOrders([]);
onFgPickOrdersChange?.([]);
return;
}
@@ -373,10 +375,12 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
const allFgPickOrders = fgPickOrdersResults.flat();
setFgPickOrders(allFgPickOrders);
onFgPickOrdersChange?.(allFgPickOrders);
console.log("✅ Fetched FG pick orders:", allFgPickOrders);
} catch (error) {
console.error("❌ Error fetching FG pick orders:", error);
setFgPickOrders([]);
onFgPickOrdersChange?.([]);
} finally {
setFgPickOrdersLoading(false);
}
@@ -385,7 +389,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
if (combinedLotData.length > 0) {
fetchFgPickOrdersData();
}
}, [combinedLotData, fetchFgPickOrdersData]);
}, [combinedLotData, fetchFgPickOrdersData, onFgPickOrdersChange]);

// ✅ Handle QR code button click
const handleQrCodeClick = (pickOrderId: number) => {


+ 21
- 2
src/components/JoSave/InfoCard.tsx 查看文件

@@ -4,6 +4,7 @@ import { Box, Card, CardContent, Grid, Stack, TextField } from "@mui/material";
import { upperFirst } from "lodash";
import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { arrayToDateString } from "@/app/utils/formatUtil";

type Props = {

@@ -21,7 +22,7 @@ const InfoCard: React.FC<Props> = ({
<CardContent component={Stack} spacing={4}>
<Box>
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
<Grid item xs={6}>
{/*<Grid item xs={6}>
<TextField
// {
// ...register("status")
@@ -32,7 +33,7 @@ const InfoCard: React.FC<Props> = ({
value={`${t(upperFirst(watch("status")))}`}
/>
</Grid>
<Grid item xs={6}/>
<Grid item xs={6}/>*/}
<Grid item xs={6}>
<TextField
{
@@ -74,6 +75,24 @@ const InfoCard: React.FC<Props> = ({
disabled={true}
/>
</Grid>
<Grid item xs={6}>
<TextField
value={arrayToDateString(watch("planStart"))}
label={t("Target Production Date")}
fullWidth
disabled={true}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("Production Priority")}
fullWidth
disabled={true}
{
...register("id")
}
/>
</Grid>
</Grid>
</Box>
</CardContent>


+ 103
- 0
src/components/JoSave/JoRelease.tsx 查看文件

@@ -0,0 +1,103 @@
import { Button, Card, CardContent, Stack, Typography } from "@mui/material";
import { useTranslation } from "react-i18next";
import { JoDetailPickLine } from "@/app/api/jo";
import { fetchInventories } from "@/app/api/inventory/actions";
import { InventoryResult } from "@/app/api/inventory";
import { useEffect, useState, useMemo } from "react";

type Props = {
onActionClick?: () => void;
pickLines: JoDetailPickLine[];
}

const JoRelease: React.FC<Props> = ({
onActionClick,
pickLines
}) => {
const { t } = useTranslation("jo");
const [inventoryData, setInventoryData] = useState<InventoryResult[]>([]);

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();
}, [pickLines]);

const getStockAvailable = (pickLine: JoDetailPickLine) => {
const inventory = inventoryData.find(inventory =>
inventory.itemCode === pickLine.code || inventory.itemName === pickLine.name
);
if (inventory) {
return inventory.availableQty || (inventory.onHandQty - inventory.onHoldQty - inventory.unavailableQty);
}
return 0;
};

const isStockSufficient = (pickLine: JoDetailPickLine) => {
const stockAvailable = getStockAvailable(pickLine);
return stockAvailable >= pickLine.reqQty;
};

const stockCounts = useMemo(() => {
const totalLines = pickLines.length;
const sufficientLines = pickLines.filter(pickLine => isStockSufficient(pickLine)).length;
const insufficientLines = totalLines - sufficientLines;

return {
total: totalLines,
sufficient: sufficientLines,
insufficient: insufficientLines
};
}, [pickLines, inventoryData]);

return (
<Card>
<CardContent>
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
spacing={2}
>
<Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
{t("Total lines: ")}<strong>{stockCounts.total}</strong>
</Typography>

<Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
{t("Lines with sufficient stock: ")}<strong style={{ color: 'green' }}>{stockCounts.sufficient}</strong>
</Typography>

<Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
{t("Lines with insufficient stock: ")}<strong style={{ color: 'red' }}>{stockCounts.insufficient}</strong>
</Typography>
<Button
variant="contained"
color="primary"
onClick={onActionClick}
>
{t("Release")}
</Button>
</Stack>

</CardContent>
</Card>
);
};

export default JoRelease;

+ 2
- 0
src/components/JoSave/JoSave.tsx 查看文件

@@ -14,6 +14,7 @@ import PickTable from "./PickTable";
import ActionButtons from "./ActionButtons";
import { useQrCodeScannerContext } from "../QrCodeScannerProvider/QrCodeScannerProvider";
import { fetchStockInLineInfo } from "@/app/api/po/actions";
import JoRelease from "./JoRelease";

type Props = {
id?: number;
@@ -163,6 +164,7 @@ const JoSave: React.FC<Props> = ({
)}
<ActionButtons handleRelease={handleRelease} handleStart={handleStart}/>
<InfoCard />
<JoRelease pickLines={pickLines}/>
<PickTable />
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Button


+ 122
- 15
src/components/JoSave/PickTable.tsx 查看文件

@@ -1,7 +1,7 @@
import { JoDetail, JoDetailPickLine } from "@/app/api/jo";
import { decimalFormatter } from "@/app/utils/formatUtil";
import { GridColDef, GridRenderCellParams, GridValidRowModel } from "@mui/x-data-grid";
import { isEmpty, upperFirst } from "lodash";
import { isEmpty, pick, upperFirst } from "lodash";
import { useCallback, useMemo } from "react";
import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
@@ -10,6 +10,15 @@ import { Box, Grid, Icon, IconButton, Stack, Typography } from "@mui/material";
import PendingOutlinedIcon from '@mui/icons-material/PendingOutlined';
import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined';
import HelpOutlineOutlinedIcon from '@mui/icons-material/HelpOutlineOutlined';
import { fetchInventories } from "@/app/api/inventory/actions";
import { InventoryResult } from "@/app/api/inventory";
import { useEffect, useState } from "react";
import DoDisturbAltRoundedIcon from '@mui/icons-material/DoDisturbAltRounded';

type JoDetailPickLineWithCalculations = JoDetailPickLine & {
stockAvailable: number;
isStockSufficient: boolean;
};

type Props = {

@@ -23,9 +32,74 @@ const PickTable: React.FC<Props> = ({
watch
} = useFormContext<JoDetail>()

const [inventoryData, setInventoryData] = useState<InventoryResult[]>([]);
const pickLines = watch("pickLines");
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();
}, [pickLines]);

const getStockAvailable = (pickLine: JoDetailPickLine) => {
const inventory = inventoryData.find(inventory =>
inventory.itemCode === pickLine.code || inventory.itemName === pickLine.name
);
if (inventory) {
return inventory.availableQty || (inventory.onHandQty - inventory.onHoldQty - inventory.unavailableQty);
}
return 0;
};

const getUomShortDesc = (pickLine: JoDetailPickLine) => {
const inventory = inventoryData.find(inventory =>
inventory.itemCode === pickLine.code || inventory.itemName === pickLine.name
);
return inventory?.uomShortDesc; // || pickLine.uom;
};

const isStockSufficient = (pickLine: JoDetailPickLine) => {
const stockAvailable = getStockAvailable(pickLine);
return stockAvailable >= pickLine.reqQty;
};

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

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

const rowsWithCalculatedFields = useMemo(() => {
return pickLines.map((pickLine, index) => ({
...pickLine,
id: pickLine.id || index,
sequence: index + 1,
stockAvailable: getStockAvailable(pickLine),
isStockSufficient: isStockSufficient(pickLine),
}));
}, [pickLines, inventoryData]);

const notPickedStatusColumn = useMemo(() => {
return (<HelpOutlineOutlinedIcon fontSize={"large"} color={"error"} />)
}, [])

const scanStatusColumn = useCallback((status: boolean) => {
return status ?
<CheckCircleOutlineOutlinedIcon fontSize={"large"} sx={{ ml: "5px" }} color="success" />
@@ -33,17 +107,31 @@ const PickTable: React.FC<Props> = ({
}, [])

const columns = useMemo<GridColDef[]>(() => [
{
field: "sequence",
headerName: t("Sequence"),
flex: 0.2,
align: "left",
headerAlign: "left",
type: "number",
renderCell: (params: GridRenderCellParams<JoDetailPickLine>) => {
return params.value;
},
},
{
field: "code",
headerName: t("Code"),
headerName: t("Item Code"),
flex: 0.6,
},
{
field: "name",
headerName: t("Name"),
headerName: t("Item Name"),
flex: 1,
renderCell: (params: GridRenderCellParams<JoDetailPickLine>) => {
return `${params.value} (${params.row.uom})`;
},
},
{
/*{
field: "scanStatus",
headerName: t("Scan Status"),
flex: 0.4,
@@ -56,7 +144,7 @@ const PickTable: React.FC<Props> = ({
const scanStatus = params.row.pickedLotNo.map((pln) => Boolean(pln.isScanned))
return isEmpty(scanStatus) ? notPickedStatusColumn : <Stack direction={"column"}>{scanStatus.map((status) => scanStatusColumn(status))}</Stack>
},
},
},
{
field: "lotNo",
headerName: t("Lot No."),
@@ -82,7 +170,7 @@ const PickTable: React.FC<Props> = ({
const qtys = params.row.pickedLotNo.map((pln) => pln.qty)
return isEmpty(qtys) ? t("Pending for pick") : qtys.map((qty) => <>{qty}<br /></>)
},
},
},*/
{
field: "reqQty",
headerName: t("Req. Qty"),
@@ -90,17 +178,35 @@ const PickTable: React.FC<Props> = ({
align: "right",
headerAlign: "right",
renderCell: (params: GridRenderCellParams<JoDetailPickLine>) => {
return decimalFormatter.format(params.value)
const uomShortDesc = getUomShortDesc(params.row);
return `${decimalFormatter.format(params.value)} ${uomShortDesc}`;
},
},
{
field: "uom",
headerName: t("UoM"),
flex: 1,
align: "left",
headerAlign: "left",
field: "stockAvailable",
headerName: t("Stock Available"),
flex: 0.7,
align: "right",
headerAlign: "right",
type: "number",
renderCell: (params: GridRenderCellParams<JoDetailPickLine>) => {
const uomShortDesc = getUomShortDesc(params.row);
return `${decimalFormatter.format(params.value)} ${uomShortDesc}`;
},
},
{
field: "stockStatus",
headerName: t("Stock Status"),
flex: 0.5,
align: "right",
headerAlign: "right",
type: "boolean",
renderCell: (params: GridRenderCellParams<JoDetailPickLineWithCalculations>) => {
return params.row.isStockSufficient ? sufficientStockIcon : insufficientStockIcon;
},
}

/*{
field: "status",
headerName: t("Status"),
flex: 1,
@@ -114,8 +220,9 @@ const PickTable: React.FC<Props> = ({
</>
)
},
},
], [])
},*/
], [t, inventoryData])

return (
<>
@@ -132,7 +239,7 @@ const PickTable: React.FC<Props> = ({
},
}}
disableColumnMenu
rows={watch("pickLines")}
rows={rowsWithCalculatedFields}
columns={columns}
getRowHeight={() => 'auto'}
/>


+ 4
- 3
src/i18n/zh/pickOrder.json 查看文件

@@ -288,11 +288,12 @@
"COMPLETED":"已完成",
"FG orders":"成品提料單",
"Back to List":"返回列表",
"No completed DO pick orders found":"沒有已完成送貨單提料單",

"Print DN Label":"列印送貨單標貼",
"Enter the number of cartons: ": "請輸入總箱數",
"Number of cartons": "箱數"
"Number of cartons": "箱數",
"You need to enter a number": "箱數不能為空",
"Number must be at least 1": "箱數最少為一",
"Printed Successfully.": "已成功列印"




Loading…
取消
儲存