kelvin.yau před 1 měsícem
rodič
revize
16be668c6f
4 změnil soubory, kde provedl 335 přidání a 295 odebrání
  1. +91
    -0
      src/components/FinishedGoodSearch/FGPickOrderTicketReleaseTable.tsx
  2. +3
    -289
      src/components/FinishedGoodSearch/FinishedGoodSearch.tsx
  3. +235
    -2
      src/components/FinishedGoodSearch/GoodPickExecutionRecord.tsx
  4. +6
    -4
      src/i18n/zh/pickOrder.json

+ 91
- 0
src/components/FinishedGoodSearch/FGPickOrderTicketReleaseTable.tsx Zobrazit soubor

@@ -0,0 +1,91 @@
"use client";

import React, { useState } from 'react';
import {
Box,
Typography,
FormControl,
InputLabel,
Select,
MenuItem,
Card,
CardContent,
Stack,
} from '@mui/material';
import { useTranslation } from 'react-i18next';
import dayjs from 'dayjs';

const FGPickOrderTicketReleaseTable: React.FC = () => {
const { t } = useTranslation("pickOrder");
const [selectedDate, setSelectedDate] = useState<string>("today");
const [selectedFloor, setSelectedFloor] = useState<string>("");

const getDateLabel = (offset: number) => {
return dayjs().add(offset, 'day').format('YYYY-MM-DD');
};

return (
<Card sx={{ mb: 2 }}>
<CardContent>
{/* Title */}
<Typography variant="h5" sx={{ mb: 3, fontWeight: 600 }}>
Ticket Release Table
</Typography>

{/* Dropdown Menus */}
<Stack direction="row" spacing={2} sx={{ mb: 3 }}>
{/* Date Selection Dropdown */}
<FormControl sx={{ minWidth: 250 }} size="small">
<InputLabel id="date-select-label">{t("Select Date")}</InputLabel>
<Select
labelId="date-select-label"
id="date-select"
value={selectedDate}
label={t("Select Date")}
onChange={(e) => setSelectedDate(e.target.value)}
>
<MenuItem value="today">
{t("Today")} ({getDateLabel(0)})
</MenuItem>
<MenuItem value="tomorrow">
{t("Tomorrow")} ({getDateLabel(1)})
</MenuItem>
<MenuItem value="dayAfterTomorrow">
{t("Day After Tomorrow")} ({getDateLabel(2)})
</MenuItem>
</Select>
</FormControl>

{/* Floor Selection Dropdown */}
<FormControl sx={{ minWidth: 150 }} size="small">
<InputLabel id="floor-select-label">{t("Floor")}</InputLabel>
<Select
labelId="floor-select-label"
id="floor-select"
value={selectedFloor}
label={t("Floor")}
onChange={(e) => setSelectedFloor(e.target.value)}
displayEmpty
>
<MenuItem value="">
<em>{t("All Floors")}</em>
</MenuItem>
<MenuItem value="2F">2/F</MenuItem>
<MenuItem value="4F">4/F</MenuItem>
</Select>
</FormControl>
</Stack>

{/* Table content will go here */}
<Box sx={{ mt: 2 }}>
<Typography variant="body2" color="text.secondary">
{/* Add your table component here */}
Table content goes here...
</Typography>
</Box>
</CardContent>
</Card>
);
};

export default FGPickOrderTicketReleaseTable;

+ 3
- 289
src/components/FinishedGoodSearch/FinishedGoodSearch.tsx Zobrazit soubor

@@ -38,6 +38,7 @@ import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import dayjs, { Dayjs } from 'dayjs';
import FGPickOrderTicketReleaseTable from "./FGPickOrderTicketReleaseTable";

interface Props {
pickOrders: PickOrderResult[];
@@ -217,228 +218,7 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {

},[t]);

const handleDN = useCallback(async () =>{
const askNumofCarton = await Swal.fire({
title: t("Enter the number of cartons: "),
icon: "info",
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"),
confirmButtonColor: "#8dba00",
cancelButtonColor: "#F04438",
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,
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: "success",
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: "),
icon: "info",
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"),
confirmButtonColor: "#8dba00",
cancelButtonColor: "#F04438",
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: 1,
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: "success",
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: "),
icon: "info",
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"),
confirmButtonColor: "#8dba00",
cancelButtonColor: "#F04438",
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: "success",
text: t("Printed Successfully."),
showConfirmButton: false,
timer: 1500
});
} else {
console.error("Print failed: ", response.message);
}
} catch(error){
console.error("error: ", error)
}
}
},[t, fgPickOrdersData]);

useEffect(() => {
fetchReleasedOrderCount();
@@ -776,7 +556,6 @@ const handleAssignByLane = useCallback(async (
>
<Button
variant="contained"
size="small"
sx={{
py: 0.5, // 增加垂直padding
px: 1.25, // 增加水平padding
@@ -796,7 +575,6 @@ const handleAssignByLane = useCallback(async (
</Button>
<Button
variant="contained"
size="small"
sx={{
py: 0.5,
px: 1.25,
@@ -815,72 +593,6 @@ const handleAssignByLane = useCallback(async (
>
{t("Print Draft")}
</Button>
<Button
variant="contained"
disabled={!printButtonsEnabled}
size="small"
sx={{
py: 0.5,
px: 1.25,
height: '40px',
fontSize: '0.75rem',
lineHeight: 1.2,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
'&.Mui-disabled': {
height: '40px'
}
}}
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}
size="small"
sx={{
py: 0.5,
px: 1.25,
height: '40px',
fontSize: '0.75rem',
lineHeight: 1.2,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
'&.Mui-disabled': {
height: '40px'
}
}}
title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""}
onClick={handleDN}
>
{t("Print Pick Order")}
</Button>
<Button
variant="contained"
disabled={!printButtonsEnabled}
size="small"
sx={{
py: 0.5,
px: 1.25,
height: '40px',
fontSize: '0.75rem',
lineHeight: 1.2,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
'&.Mui-disabled': {
height: '40px'
}
}}
title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""}
onClick={handleLabel}
>
{t("Print DN Label")}
</Button>
</Stack>
</Box>
</Grid>
@@ -895,6 +607,7 @@ const handleAssignByLane = useCallback(async (
<Tab label={t("Pick Order Detail")} iconPosition="end" />
<Tab label={t("Finished Good Detail")} iconPosition="end" />
<Tab label={t("Finished Good Record")} iconPosition="end" />
<Tab label={t("Ticket Release Table")} iconPosition="end" />
</Tabs>
</Box>
@@ -906,6 +619,7 @@ const handleAssignByLane = useCallback(async (
{tabIndex === 0 && <PickExecution filterArgs={filterArgs} onFgPickOrdersChange={setFgPickOrdersData}/>}
{tabIndex === 1 && <PickExecutionDetail filterArgs={filterArgs} />}
{tabIndex === 2 && <GoodPickExecutionRecord filterArgs={filterArgs} />}
{tabIndex === 3 && <FGPickOrderTicketReleaseTable/>}
</Box>
</Box>
);


+ 235
- 2
src/components/FinishedGoodSearch/GoodPickExecutionRecord.tsx Zobrazit soubor

@@ -41,10 +41,10 @@ import {
checkPickOrderCompletion,
PickOrderCompletionResponse,
checkAndCompletePickOrderByConsoCode,
fetchCompletedDoPickOrders, // ✅ 新增:使用新的 API
fetchCompletedDoPickOrders,
CompletedDoPickOrderResponse,
CompletedDoPickOrderSearchParams,
fetchLotDetailsByPickOrderId // ✅ 修复:导入类型
fetchLotDetailsByPickOrderId
} from "@/app/api/pickOrder/actions";
import { fetchNameList, NameList } from "@/app/api/user/actions";
import {
@@ -63,6 +63,9 @@ import GoodPickExecutionForm from "./GoodPickExecutionForm";
import FGPickOrderCard from "./FGPickOrderCard";
import dayjs from "dayjs";
import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
import { printDN, printDNLabels } from "@/app/api/do/actions";
import Swal from "sweetalert2";


interface Props {
filterArgs: Record<string, any>;
@@ -108,6 +111,205 @@ const GoodPickExecutionRecord: React.FC<Props> = ({ filterArgs }) => {
const formProps = useForm();
const errors = formProps.formState.errors;

// ✅ Print handler functions
const handleDN = useCallback(async (deliveryOrderId: number, pickOrderId: number) => {
const askNumofCarton = await Swal.fire({
title: t("Enter the number of cartons: "),
icon: "info",
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"),
confirmButtonColor: "#8dba00",
cancelButtonColor: "#F04438",
showLoaderOnConfirm: true,
allowOutsideClick: () => !Swal.isLoading()
});

if (askNumofCarton.isConfirmed) {
const numOfCartons = askNumofCarton.value;
try{
const printRequest = {
printerId: 1,
printQty: 1,
isDraft: false,
numOfCarton: numOfCartons,
deliveryOrderId: deliveryOrderId,
pickOrderId: 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: "success",
text: t("Printed Successfully."),
showConfirmButton: false,
timer: 1500
});
} else {
console.error("Print failed: ", response.message);
}
} catch(error){
console.error("error: ", error)
}
}
}, [t]);

const handleDNandLabel = useCallback(async (deliveryOrderId: number, pickOrderId: number) => {
const askNumofCarton = await Swal.fire({
title: t("Enter the number of cartons: "),
icon: "info",
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"),
confirmButtonColor: "#8dba00",
cancelButtonColor: "#F04438",
showLoaderOnConfirm: true,
allowOutsideClick: () => !Swal.isLoading()
});

if (askNumofCarton.isConfirmed) {
const numOfCartons = askNumofCarton.value;
try{
const printDNRequest = {
printerId: 1,
printQty: 1,
isDraft: false,
numOfCarton: numOfCartons,
deliveryOrderId: deliveryOrderId,
pickOrderId: pickOrderId
};

const printDNLabelsRequest = {
printerId: 1,
printQty: 1,
numOfCarton: numOfCartons,
deliveryOrderId: 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: "success",
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]);

const handleLabel = useCallback(async (deliveryOrderId: number) => {
const askNumofCarton = await Swal.fire({
title: t("Enter the number of cartons: "),
icon: "info",
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"),
confirmButtonColor: "#8dba00",
cancelButtonColor: "#F04438",
showLoaderOnConfirm: true,
allowOutsideClick: () => !Swal.isLoading()
});

if (askNumofCarton.isConfirmed) {
const numOfCartons = askNumofCarton.value;
try{
const printRequest = {
printerId: 1,
printQty: 1,
numOfCarton: numOfCartons,
deliveryOrderId: 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: "success",
text: t("Printed Successfully."),
showConfirmButton: false,
timer: 1500
});
} else {
console.error("Print failed: ", response.message);
}
} catch(error){
console.error("error: ", error)
}
}
}, [t]);

// ✅ 修改:使用新的 API 获取已完成的 DO Pick Orders
const fetchCompletedDoPickOrdersData = useCallback(async (searchParams?: CompletedDoPickOrderSearchParams) => {
if (!currentUserId) return;
@@ -406,6 +608,37 @@ const GoodPickExecutionRecord: React.FC<Props> = ({ filterArgs }) => {
>
{t("View Details")}
</Button>
{doPickOrder.fgPickOrders && doPickOrder.fgPickOrders.length > 0 && (
<>
<Button
variant="contained"
onClick={() => handleDN(
doPickOrder.fgPickOrders[0].deliveryOrderId,
doPickOrder.fgPickOrders[0].pickOrderId
)}
>
{t("Print Pick Order")}
</Button>
<Button
variant="contained"
onClick={() => handleDNandLabel(
doPickOrder.fgPickOrders[0].deliveryOrderId,
doPickOrder.fgPickOrders[0].pickOrderId
)}
>
{t("Print DN & Label")}
</Button>
<Button
variant="contained"
onClick={() => handleLabel(
doPickOrder.fgPickOrders[0].deliveryOrderId
)}
>
{t("Print Label")}
</Button>
</>
)}
</CardActions>
</Card>
))}


+ 6
- 4
src/i18n/zh/pickOrder.json Zobrazit soubor

@@ -275,9 +275,9 @@
"Confirm":"確認",
"Update your suggested lot to the this scanned lot":"更新您的建議批次為此掃描的批次",
"Print Draft":"列印草稿",
"Print Pick Order and DN Label":"列印提料單和送貨單標",
"Print Pick Order and DN Label":"列印提料單和送貨單標",
"Print Pick Order":"列印提料單",
"Print DN Label":"列印送貨單標",
"Print DN Label":"列印送貨單標",
"Print All Draft" : "列印全部草稿",
"If you confirm, the system will:":"如果您確認,系統將:",
"QR code verified.":"QR 碼驗證成功。",
@@ -289,7 +289,6 @@
"Completed DO pick orders: ":"已完成送貨單提料單:",
"No completed DO pick orders found":"沒有已完成送貨單提料單",

"Print DN Label":"列印送貨單標貼",
"Enter the number of cartons: ": "請輸入總箱數",
"Number of cartons": "箱數",
"Select an action for the assigned pick orders.": "選擇分配提料單的動作。",
@@ -387,5 +386,8 @@
"Today": "是日",
"Tomorrow": "翌日",
"Day After Tomorrow": "後日",
"Select Date": "請選擇日期"
"Select Date": "請選擇日期",
"Print DN & Label": "列印提料單和送貨單標籤",
"Print Label": "列印送貨單標籤",
"Ticket Release Table": "查看提貨情況"
}

Načítá se…
Zrušit
Uložit