CANCERYS\kw093 před 3 měsíci
rodič
revize
43f1c9a726
4 změnil soubory, kde provedl 261 přidání a 33 odebrání
  1. +28
    -0
      src/app/api/pickOrder/actions.ts
  2. +113
    -0
      src/components/FinishedGoodSearch/FGPickOrderCard.tsx
  3. +116
    -31
      src/components/FinishedGoodSearch/GoodPickExecution.tsx
  4. +4
    -2
      src/i18n/zh/common.json

+ 28
- 0
src/app/api/pickOrder/actions.ts Zobrazit soubor

@@ -247,6 +247,34 @@ export interface PickOrderCompletionResponse {
export interface UpdateSuggestedLotLineIdRequest {
newLotLineId: number;
}
export interface FGPickOrderResponse {
pickOrderId: number;
pickOrderCode: string;
pickOrderConsoCode: string;
pickOrderTargetDate: string;
pickOrderStatus: string;
deliveryOrderId: number;
deliveryNo: string;
deliveryDate: string;
shopId: number;
shopCode: string;
shopName: string;
shopAddress: string;
shopPoNo: string;
numberOfCartons: number;
DepartureTime: string;
truckNo: string;
qrCodeData: number;
}
export const fetchFGPickOrders = async (pickOrderId: number) => {
const response = await serverFetchJson<FGPickOrderResponse>(
`${BASE_API_URL}/pickOrder/fg-pick-orders/${pickOrderId}`,
{
method: "GET",
},
);
return response;
};
export const updateSuggestedLotLineId = async (suggestedPickLotId: number, newLotLineId: number) => {
const response = await serverFetchJson<PostPickOrderResponse<UpdateSuggestedLotLineIdRequest>>(
`${BASE_API_URL}/suggestedPickLot/update-suggested-lot/${suggestedPickLotId}`,


+ 113
- 0
src/components/FinishedGoodSearch/FGPickOrderCard.tsx Zobrazit soubor

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

import { FGPickOrderResponse } from "@/app/api/pickOrder/actions";
import { Box, Card, CardContent, Grid, Stack, TextField, Button } from "@mui/material";
import { useTranslation } from "react-i18next";
import QrCodeIcon from '@mui/icons-material/QrCode';

type Props = {
fgOrder: FGPickOrderResponse;
onQrCodeClick: (pickOrderId: number) => void;
};

const FGPickOrderCard: React.FC<Props> = ({ fgOrder, onQrCodeClick }) => {
const { t } = useTranslation("pickOrder");

return (
<Card sx={{ display: "block" }}>
<CardContent component={Stack} spacing={4}>
<Box>
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
<Grid item xs={6}>
<TextField
label={t("Delivery No.")}
fullWidth
disabled={true}
value={fgOrder.deliveryNo}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("FG Pick Order No.")}
fullWidth
disabled={true}
value={fgOrder.pickOrderCode}
//helperText={fgOrder.pickOrderConsoCode}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("Shop PO No.")}
fullWidth
disabled={true}
value={fgOrder.shopPoNo}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("Shop ID")}
fullWidth
disabled={true}
value={fgOrder.shopCode}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("Shop Name")}
fullWidth
disabled={true}
value={fgOrder.shopName}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("Delivery Date")}
fullWidth
disabled={true}
value={new Date(fgOrder.deliveryDate).toLocaleDateString()}
/>
</Grid>
<Grid item xs={12}>
<TextField
label={t("Shop Address")}
fullWidth
disabled={true}
value={fgOrder.shopAddress}
multiline
rows={2}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("Departure Time")}
fullWidth
disabled={true}
value={fgOrder.DepartureTime}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("Truck No.")}
fullWidth
disabled={true}
value={fgOrder.truckNo}
/>
</Grid>
<Grid item xs={12} sx={{ display: 'flex', justifyContent: 'flex-end', mt: 2 }}>
<Button
variant="contained"
startIcon={<QrCodeIcon />}
onClick={() => onQrCodeClick(fgOrder.pickOrderId)}
sx={{ minWidth: 120 }}
>
{t("View QR Code")}
</Button>
</Grid>
</Grid>
</Box>
</CardContent>
</Card>
);
};

export default FGPickOrderCard;

+ 116
- 31
src/components/FinishedGoodSearch/GoodPickExecution.tsx Zobrazit soubor

@@ -26,6 +26,13 @@ import {
updateStockOutLineStatus,
createStockOutLine,
recordPickExecutionIssue,
fetchFGPickOrders, // ✅ Add this import
FGPickOrderResponse,
autoAssignAndReleasePickOrder,
AutoAssignReleaseResponse,
checkPickOrderCompletion,
PickOrderCompletionResponse,
checkAndCompletePickOrderByConsoCode
} from "@/app/api/pickOrder/actions";
import { fetchNameList, NameList } from "@/app/api/user/actions";
import {
@@ -39,16 +46,9 @@ import QrCodeIcon from '@mui/icons-material/QrCode';
import { useQrCodeScannerContext } from '../QrCodeScannerProvider/QrCodeScannerProvider';
import { useSession } from "next-auth/react";
import { SessionWithTokens } from "@/config/authConfig";
import {
autoAssignAndReleasePickOrder,
AutoAssignReleaseResponse,
checkPickOrderCompletion,
PickOrderCompletionResponse,
checkAndCompletePickOrderByConsoCode
} from "@/app/api/pickOrder/actions";
import { fetchStockInLineInfo } from "@/app/api/po/actions";
import GoodPickExecutionForm from "./GoodPickExecutionForm";
import FGPickOrderCard from "./FGPickOrderCard";
interface Props {
filterArgs: Record<string, any>;
}
@@ -73,7 +73,7 @@ const QrCodeModal: React.FC<{
const [processedQrCodes, setProcessedQrCodes] = useState<Set<string>>(new Set());
const [scannedQrResult, setScannedQrResult] = useState<string>('');
const [fgPickOrder, setFgPickOrder] = useState<FGPickOrderResponse | null>(null);
// Process scanned QR codes
useEffect(() => {
if (qrValues.length > 0 && lot && !isProcessingQr && !qrScanSuccess) {
@@ -347,6 +347,51 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
// ✅ Add GoodPickExecutionForm states
const [pickExecutionFormOpen, setPickExecutionFormOpen] = useState(false);
const [selectedLotForExecutionForm, setSelectedLotForExecutionForm] = useState<any | null>(null);
const [fgPickOrders, setFgPickOrders] = useState<FGPickOrderResponse[]>([]);
const [fgPickOrdersLoading, setFgPickOrdersLoading] = useState(false);
const fetchFgPickOrdersData = useCallback(async () => {
if (!currentUserId) return;
setFgPickOrdersLoading(true);
try {
// Get all pick order IDs from combinedLotData
const pickOrderIds = Array.from(new Set(combinedLotData.map(lot => lot.pickOrderId)));
if (pickOrderIds.length === 0) {
setFgPickOrders([]);
return;
}
// Fetch FG pick orders for each pick order ID
const fgPickOrdersPromises = pickOrderIds.map(pickOrderId =>
fetchFGPickOrders(pickOrderId)
);
const fgPickOrdersResults = await Promise.all(fgPickOrdersPromises);
// Flatten the results (each fetchFGPickOrders returns an array)
const allFgPickOrders = fgPickOrdersResults.flat();
setFgPickOrders(allFgPickOrders);
console.log("✅ Fetched FG pick orders:", allFgPickOrders);
} catch (error) {
console.error("❌ Error fetching FG pick orders:", error);
setFgPickOrders([]);
} finally {
setFgPickOrdersLoading(false);
}
}, [currentUserId, combinedLotData]);
useEffect(() => {
if (combinedLotData.length > 0) {
fetchFgPickOrdersData();
}
}, [combinedLotData, fetchFgPickOrdersData]);

// ✅ Handle QR code button click
const handleQrCodeClick = (pickOrderId: number) => {
console.log(`QR Code clicked for pick order ID: ${pickOrderId}`);
// TODO: Implement QR code functionality
};

useEffect(() => {
startScan();
@@ -850,11 +895,30 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
});
}, []);

// Pagination data
// Pagination data with sorting by routerIndex
const paginatedData = useMemo(() => {
// ✅ Sort by routerIndex first, then by other criteria
const sortedData = [...combinedLotData].sort((a, b) => {
const aIndex = a.routerIndex || 0;
const bIndex = b.routerIndex || 0;
// Primary sort: by routerIndex
if (aIndex !== bIndex) {
return aIndex - bIndex;
}
// Secondary sort: by pickOrderCode if routerIndex is the same
if (a.pickOrderCode !== b.pickOrderCode) {
return a.pickOrderCode.localeCompare(b.pickOrderCode);
}
// Tertiary sort: by lotNo if everything else is the same
return (a.lotNo || '').localeCompare(b.lotNo || '');
});
const startIndex = paginationController.pageNum * paginationController.pageSize;
const endIndex = startIndex + paginationController.pageSize;
return combinedLotData.slice(startIndex, endIndex);
return sortedData.slice(startIndex, endIndex);
}, [combinedLotData, paginationController]);

return (
@@ -862,11 +926,35 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
<Stack spacing={2}>
{/* Search Box */}
<Box>
<SearchBox
criteria={searchCriteria}
onSearch={handleSearch}
onReset={handleReset}
/>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h6" gutterBottom sx={{ mb: 0 }}>
{t("FG Pick Orders")}
</Typography>
</Box>
{fgPickOrdersLoading ? (
<Box sx={{ display: 'flex', justifyContent: 'center', p: 3 }}>
<CircularProgress />
</Box>
) : (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
{fgPickOrders.length === 0 ? (
<Box sx={{ p: 3, textAlign: 'center' }}>
<Typography variant="body2" color="text.secondary">
{t("No FG pick orders found")}
</Typography>
</Box>
) : (
fgPickOrders.map((fgOrder) => (
<FGPickOrderCard
key={fgOrder.pickOrderId}
fgOrder={fgOrder}
onQrCodeClick={handleQrCodeClick}
/>
))
)}
</Box>
)}
</Box>


@@ -887,8 +975,8 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
<Table>
<TableHead>
<TableRow>
<TableCell>{t("Pick Order Code")}</TableCell>
<TableCell>{t("Item Code")}</TableCell>
<TableCell>{t("Index")}</TableCell>
<TableCell>{t("Route")}</TableCell>
<TableCell>{t("Item Name")}</TableCell>
<TableCell>{t("Lot#")}</TableCell>
<TableCell>{t("Target Date")}</TableCell>
@@ -921,8 +1009,16 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
}
}}
>
<TableCell>{lot.pickOrderCode}</TableCell>
<TableCell>{lot.itemCode}</TableCell>
<TableCell>
<Typography variant="body2" fontWeight="bold">
{lot.routerIndex || index + 1}
</Typography>
</TableCell>
<TableCell>
<Typography variant="body2">
{lot.routerRoute || '-'}
</Typography>
</TableCell>
<TableCell>{lot.itemName}</TableCell>
<TableCell>
<Box>
@@ -933,18 +1029,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
}}
>
{lot.lotNo}
</Typography>
{/*
{lot.lotAvailability !== 'available' && (
<Typography variant="caption" color="error" display="block">
({lot.lotAvailability === 'expired' ? 'Expired' :
lot.lotAvailability === 'insufficient_stock' ? 'Insufficient' :
lot.lotAvailability === 'rejected' ? 'Rejected' :
'Unavailable'})
</Typography>
)*/}
</Box>
</TableCell>
<TableCell>{lot.pickOrderTargetDate}</TableCell>


+ 4
- 2
src/i18n/zh/common.json Zobrazit soubor

@@ -86,6 +86,8 @@
"Production": "生產流程",
"Put Away": "上架",
"Put Away Scan": "上架掃碼",
"Finished Good Order": "成品訂單",
"finishedGood": "成品"
"Finished Good Order": "成品出倉",
"finishedGood": "成品",
"Router": "執貨路線"

}

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