瀏覽代碼

update

master
CANCERYS\kw093 2 月之前
父節點
當前提交
47b29d5d4d
共有 6 個檔案被更改,包括 452 行新增96 行删除
  1. +28
    -2
      src/app/api/inventory/actions.ts
  2. +19
    -1
      src/app/api/pickOrder/actions.ts
  3. +8
    -0
      src/components/FinishedGoodSearch/FGPickOrderCard.tsx
  4. +267
    -92
      src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx
  5. +126
    -0
      src/components/FinishedGoodSearch/LotConfirmationModal.tsx
  6. +4
    -1
      src/i18n/zh/pickOrder.json

+ 28
- 2
src/app/api/inventory/actions.ts 查看文件

@@ -52,8 +52,34 @@ export interface PostInventoryLotLineResponse<T = null> {
entity?: T | T[];
consoCode?: string;
}


export interface QrCodeAnalysisResponse {
itemId: number;
itemCode: string;
itemName: string;
scanned: ScannedLotInfo;
sameItemLots: SameItemLotInfo[];
}
export interface ScannedLotInfo {
stockInLineId: number;
lotNo: string;
inventoryLotLineId: number;
}
export interface SameItemLotInfo {
lotNo: string;
inventoryLotLineId: number;
availableQty: number;
uom: string;
}
export const analyzeQrCode = async (data: {
itemId: number;
stockInLineId: number;
}) => {
return serverFetchJson<QrCodeAnalysisResponse>(`${BASE_API_URL}/inventoryLotLine/analyze-qr-code`, {
method: 'POST',
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
});
};
export const updateInventoryStatus = async (data: {
itemId: number;
lotId: number;


+ 19
- 1
src/app/api/pickOrder/actions.ts 查看文件

@@ -260,6 +260,7 @@ export interface FGPickOrderResponse {
numberOfCartons: number;
DepartureTime: string;
truckNo: string;
storeId: string;
qrCodeData: number;
}
export interface AutoAssignReleaseByStoreRequest {
@@ -526,7 +527,24 @@ export interface CheckCompleteResponse {
message: string | null;
errorPosition: string;
}

export interface LotSubstitutionConfirmRequest {
pickOrderLineId: number;
stockOutLineId: number;
originalSuggestedPickLotId: number;
newInventoryLotLineId: number;
}
export const confirmLotSubstitution = async (data: LotSubstitutionConfirmRequest) => {
const response = await serverFetchJson<PostPickOrderResponse>(
`${BASE_API_URL}/pickOrder/lot-substitution/confirm`,
{
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
},
);
revalidateTag("pickorder");
return response;
};
export const checkAndCompletePickOrderByConsoCode = async (consoCode: string): Promise<CheckCompleteResponse> => {
const response = await serverFetchJson<CheckCompleteResponse>(
`${BASE_API_URL}/pickOrder/check-complete/${consoCode}`,


+ 8
- 0
src/components/FinishedGoodSearch/FGPickOrderCard.tsx 查看文件

@@ -43,6 +43,14 @@ const FGPickOrderCard: React.FC<Props> = ({ fgOrder, onQrCodeClick }) => {
value={fgOrder.shopPoNo}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("Store ID")}
fullWidth
disabled={true}
value={fgOrder.storeId}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("Shop ID")}


+ 267
- 92
src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx 查看文件

@@ -19,6 +19,7 @@ import {
TablePagination,
Modal,
} from "@mui/material";
import { fetchLotDetail } from "@/app/api/inventory/actions";
import { useCallback, useEffect, useState, useRef, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { useRouter } from "next/navigation";
@@ -26,6 +27,7 @@ import {
fetchALLPickOrderLineLotDetails,
updateStockOutLineStatus,
createStockOutLine,
updateStockOutLine,
recordPickExecutionIssue,
fetchFGPickOrders, // ✅ Add this import
FGPickOrderResponse,
@@ -33,8 +35,14 @@ import {
AutoAssignReleaseResponse,
checkPickOrderCompletion,
PickOrderCompletionResponse,
checkAndCompletePickOrderByConsoCode
checkAndCompletePickOrderByConsoCode,
updateSuggestedLotLineId,
confirmLotSubstitution
} from "@/app/api/pickOrder/actions";

import LotConfirmationModal from "./LotConfirmationModal";
//import { fetchItem } from "@/app/api/settings/item";
import { updateInventoryLotLineStatus, analyzeQrCode } from "@/app/api/inventory/actions";
import { fetchNameList, NameList } from "@/app/api/user/actions";
import {
FormProvider,
@@ -344,7 +352,10 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
// ✅ Add QR modal states
const [qrModalOpen, setQrModalOpen] = useState(false);
const [selectedLotForQr, setSelectedLotForQr] = useState<any | null>(null);

const [lotConfirmationOpen, setLotConfirmationOpen] = useState(false);
const [expectedLotData, setExpectedLotData] = useState<any>(null);
const [scannedLotData, setScannedLotData] = useState<any>(null);
const [isConfirmingLot, setIsConfirmingLot] = useState(false);
// ✅ Add GoodPickExecutionForm states
const [pickExecutionFormOpen, setPickExecutionFormOpen] = useState(false);
const [selectedLotForExecutionForm, setSelectedLotForExecutionForm] = useState<any | null>(null);
@@ -399,6 +410,12 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
// TODO: Implement QR code functionality
};

const handleLotMismatch = useCallback((expectedLot: any, scannedLot: any) => {
console.log("Lot mismatch detected:", { expectedLot, scannedLot });
setExpectedLotData(expectedLot);
setScannedLotData(scannedLot);
setLotConfirmationOpen(true);
}, []);

const fetchAllCombinedLotData = useCallback(async (userId?: number) => {
setCombinedDataLoading(true);
@@ -427,32 +444,38 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
setCombinedDataLoading(false);
}
}, [currentUserId]);

// ✅ Only fetch existing data when session is ready, no auto-assignment
useEffect(() => {
if (session && currentUserId && !initializationRef.current) {
console.log("✅ Session loaded, initializing pick order...");
initializationRef.current = true;
// ✅ Only fetch existing data, no auto-assignment
fetchAllCombinedLotData();
const handleLotConfirmation = useCallback(async () => {
if (!expectedLotData || !scannedLotData || !selectedLotForQr) return;
setIsConfirmingLot(true);
try {
let newLotLineId = scannedLotData?.inventoryLotLineId;
if (!newLotLineId && scannedLotData?.stockInLineId) {
const ld = await fetchLotDetail(scannedLotData.stockInLineId);
newLotLineId = ld.inventoryLotLineId;
}
if (!newLotLineId) {
console.error("No inventory lot line id for scanned lot");
return;
}
await confirmLotSubstitution({
pickOrderLineId: selectedLotForQr.pickOrderLineId,
stockOutLineId: selectedLotForQr.stockOutLineId,
originalSuggestedPickLotId: selectedLotForQr.suggestedPickLotId,
newInventoryLotLineId: newLotLineId
});
setLotConfirmationOpen(false);
setExpectedLotData(null);
setScannedLotData(null);
setSelectedLotForQr(null);
await fetchAllCombinedLotData();
} catch (error) {
console.error("Error confirming lot substitution:", error);
} finally {
setIsConfirmingLot(false);
}
}, [session, currentUserId, fetchAllCombinedLotData]);

// ✅ Add event listener for manual assignment
useEffect(() => {
const handlePickOrderAssigned = () => {
console.log("🔄 Pick order assigned event received, refreshing data...");
fetchAllCombinedLotData();
};

window.addEventListener('pickOrderAssigned', handlePickOrderAssigned);
return () => {
window.removeEventListener('pickOrderAssigned', handlePickOrderAssigned);
};
}, [fetchAllCombinedLotData]);

}, [expectedLotData, scannedLotData, selectedLotForQr, fetchAllCombinedLotData]);
const handleQrCodeSubmit = useCallback(async (lotNo: string) => {
console.log(`✅ Processing QR Code for lot: ${lotNo}`);
@@ -469,6 +492,8 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
console.error(`❌ Lot not found: ${lotNo}`);
setQrScanError(true);
setQrScanSuccess(false);
const availableLotNos = currentLotData.map(lot => lot.lotNo).join(', ');
console.log(`❌ QR Code "${lotNo}" does not match any expected lots. Available lots: ${availableLotNos}`);
return;
}
@@ -483,38 +508,55 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
console.log(`🔄 Processing pick order line ${matchingLot.pickOrderLineId} for lot ${lotNo}`);
if (matchingLot.stockOutLineId) {
// ✅ FIXED: Only update status to 'checked', keep qty at 0
const stockOutLineUpdate = await updateStockOutLineStatus({
id: matchingLot.stockOutLineId,
status: 'checked',
qty: 0 // ✅ Keep qty at 0 until user actually submits
qty: 0
});
console.log(`Update stock out line result for line ${matchingLot.pickOrderLineId}:`, stockOutLineUpdate);
if (stockOutLineUpdate && stockOutLineUpdate.code === "SUCCESS") {
console.log(`✅ Stock out line updated successfully for line ${matchingLot.pickOrderLineId}`);
successCount++;
} else {
console.error(`❌ Failed to update stock out line for line ${matchingLot.pickOrderLineId}:`, stockOutLineUpdate);
errorCount++;
}
} else {
// ✅ If no stock out line exists, create one with qty = 0
const createStockOutLineData = {
consoCode: matchingLot.pickOrderConsoCode,
pickOrderLineId: matchingLot.pickOrderLineId,
inventoryLotLineId: matchingLot.lotId,
qty: 0 // ✅ Create with qty = 0
qty: 0
};
const createResult = await createStockOutLine(createStockOutLineData);
console.log(`Create stock out line result for line ${matchingLot.pickOrderLineId}:`, createResult);
if (createResult && createResult.code === "SUCCESS") {
console.log(`✅ Stock out line created successfully for line ${matchingLot.pickOrderLineId}`);
successCount++;
// Immediately set status to checked for new line
let newSolId: number | undefined;
const anyRes: any = createResult as any;
if (typeof anyRes?.id === 'number') {
newSolId = anyRes.id;
} else if (anyRes?.entity) {
newSolId = Array.isArray(anyRes.entity) ? anyRes.entity[0]?.id : anyRes.entity?.id;
}

if (newSolId) {
const setChecked = await updateStockOutLineStatus({
id: newSolId,
status: 'checked',
qty: 0
});
if (setChecked && setChecked.code === "SUCCESS") {
successCount++;
} else {
errorCount++;
}
} else {
console.warn("Created stock out line but no ID returned; cannot set to checked");
errorCount++;
}
} else {
console.error(`❌ Failed to create stock out line for line ${matchingLot.pickOrderLineId}:`, createResult);
errorCount++;
}
}
@@ -531,18 +573,19 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
setQrScanInput(''); // Clear input after successful processing
// ✅ Clear success state after a delay
setTimeout(() => {
setQrScanSuccess(false);
}, 2000);
//setTimeout(() => {
//setQrScanSuccess(false);
//}, 2000);
} else {
console.error(`❌ QR Code processing failed: ${errorCount} errors`);
setQrScanError(true);
setQrScanSuccess(false);
// ✅ Clear error state after a delay
setTimeout(() => {
setQrScanError(false);
}, 3000);
// setTimeout(() => {
// setQrScanError(false);
//}, 3000);
}
} catch (error) {
console.error("❌ Error processing QR code:", error);
@@ -564,6 +607,152 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
}, 1000);
}
}, [combinedLotData, fetchAllCombinedLotData]);
const processOutsideQrCode = useCallback(async (latestQr: string) => {
// 1) Parse JSON safely
let qrData: any = null;
try {
qrData = JSON.parse(latestQr);
} catch {
console.log("QR content is not JSON; skipping lotNo direct submit to avoid false matches.");
setQrScanError(true);
setQrScanSuccess(false);
return;
}
try {
// Only use the new API when we have JSON with stockInLineId + itemId
if (!(qrData?.stockInLineId && qrData?.itemId)) {
console.log("QR JSON missing required fields (itemId, stockInLineId).");
setQrScanError(true);
setQrScanSuccess(false);
return;
}
// Call new analyze-qr-code API
const analysis = await analyzeQrCode({
itemId: qrData.itemId,
stockInLineId: qrData.stockInLineId
});
if (!analysis) {
console.error("analyzeQrCode returned no data");
setQrScanError(true);
setQrScanSuccess(false);
return;
}
const {
itemId: analyzedItemId,
itemCode: analyzedItemCode,
itemName: analyzedItemName,
scanned,
} = analysis || {};
// 1) Find all lots for the same item from current expected list
const sameItemLotsInExpected = combinedLotData.filter(l =>
(l.itemId && analyzedItemId && l.itemId === analyzedItemId) ||
(l.itemCode && analyzedItemCode && l.itemCode === analyzedItemCode)
);
if (!sameItemLotsInExpected || sameItemLotsInExpected.length === 0) {
// Case 3: No item code match
console.error("No item match in expected lots for scanned code");
setQrScanError(true);
setQrScanSuccess(false);

return;
}
// 2) Check if scanned lot is exactly in expected lots
const exactLotMatch = sameItemLotsInExpected.find(l =>
(scanned?.inventoryLotLineId && l.lotId === scanned.inventoryLotLineId) ||
(scanned?.lotNo && l.lotNo === scanned.lotNo)
);
if (exactLotMatch && scanned?.lotNo) {
// Case 1: Normal case - item matches AND lot matches -> proceed
console.log(`Exact lot match found for ${scanned.lotNo}, submitting QR`);
handleQrCodeSubmit(scanned.lotNo);
return;
}
// Case 2: Item matches but lot number differs -> open confirmation modal
const expectedLot = sameItemLotsInExpected[0];
if (!expectedLot) {
console.error("Could not determine expected lot for confirmation");
setQrScanError(true);
setQrScanSuccess(false);
return;
}
setSelectedLotForQr(expectedLot);
handleLotMismatch(
{
lotNo: expectedLot.lotNo,
itemCode: analyzedItemCode || expectedLot.itemCode,
itemName: analyzedItemName || expectedLot.itemName
},
{
lotNo: scanned?.lotNo || '',
itemCode: analyzedItemCode || expectedLot.itemCode,
itemName: analyzedItemName || expectedLot.itemName,
inventoryLotLineId: scanned?.inventoryLotLineId,
stockInLineId: qrData.stockInLineId
}
);
} catch (error) {
console.error("Error during analyzeQrCode flow:", error);
setQrScanError(true);
setQrScanSuccess(false);
return;
}
}, [combinedLotData, handleQrCodeSubmit, handleLotMismatch]);
// ✅ Update the outside QR scanning effect to use enhanced processing
useEffect(() => {
if (!isManualScanning || qrValues.length === 0 || combinedLotData.length === 0 || isRefreshingData) {
return;
}
const latestQr = qrValues[qrValues.length - 1];
if (processedQrCodes.has(latestQr) || lastProcessedQr === latestQr) {
console.log("QR code already processed, skipping...");
return;
}
if (latestQr && latestQr !== lastProcessedQr) {
console.log(`🔍 Processing new QR code with enhanced validation: ${latestQr}`);
setLastProcessedQr(latestQr);
setProcessedQrCodes(prev => new Set(prev).add(latestQr));
processOutsideQrCode(latestQr);
}
}, [qrValues, isManualScanning, processedQrCodes, lastProcessedQr, isRefreshingData, processOutsideQrCode]);
// ✅ Only fetch existing data when session is ready, no auto-assignment
useEffect(() => {
if (session && currentUserId && !initializationRef.current) {
console.log("✅ Session loaded, initializing pick order...");
initializationRef.current = true;
// ✅ Only fetch existing data, no auto-assignment
fetchAllCombinedLotData();
}
}, [session, currentUserId, fetchAllCombinedLotData]);

// ✅ Add event listener for manual assignment
useEffect(() => {
const handlePickOrderAssigned = () => {
console.log("🔄 Pick order assigned event received, refreshing data...");
fetchAllCombinedLotData();
};

window.addEventListener('pickOrderAssigned', handlePickOrderAssigned);
return () => {
window.removeEventListener('pickOrderAssigned', handlePickOrderAssigned);
};
}, [fetchAllCombinedLotData]);



const handleManualInputSubmit = useCallback(() => {
if (qrScanInput.trim() !== '') {
@@ -612,55 +801,6 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
}
}, [selectedLotForQr, fetchAllCombinedLotData]);

// ✅ Outside QR scanning - process QR codes from outside the page automatically
useEffect(() => {
// ✅ Don't process QR codes when refreshing data or if not manually scanning
if (!isManualScanning || qrValues.length === 0 || combinedLotData.length === 0 || isRefreshingData) {
return;
}
const latestQr = qrValues[qrValues.length - 1];
// ✅ Prevent processing the same QR code multiple times
if (processedQrCodes.has(latestQr) || lastProcessedQr === latestQr) {
console.log(" QR code already processed, skipping...");
return;
}
if (latestQr && latestQr !== lastProcessedQr) {
console.log(` Processing new QR code: ${latestQr}`);
setLastProcessedQr(latestQr);
setProcessedQrCodes(prev => new Set(prev).add(latestQr));
let lotNo = '';
try {
const qrData = JSON.parse(latestQr);
if (qrData.stockInLineId && qrData.itemId) {
fetchStockInLineInfo(qrData.stockInLineId)
.then((stockInLineInfo) => {
console.log("Outside QR scan - Stock in line info:", stockInLineInfo);
const extractedLotNo = stockInLineInfo.lotNo;
if (extractedLotNo) {
console.log(`Outside QR scan detected (JSON): ${extractedLotNo}`);
handleQrCodeSubmit(extractedLotNo);
}
})
.catch((error) => {
console.error("Outside QR scan - Error fetching stock in line info:", error);
});
return;
}
} catch (error) {
lotNo = latestQr.replace(/[{}]/g, '');
}
if (lotNo) {
console.log(`Outside QR scan detected (direct): ${lotNo}`);
handleQrCodeSubmit(lotNo);
}
}
}, [qrValues, isManualScanning, processedQrCodes, lastProcessedQr, isRefreshingData, handleQrCodeSubmit]);


const handlePickQtyChange = useCallback((lotKey: string, value: number | string) => {
if (value === '' || value === null || value === undefined) {
@@ -1026,12 +1166,16 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe
setIsManualScanning(true);
setProcessedQrCodes(new Set());
setLastProcessedQr('');
setQrScanError(false);
setQrScanSuccess(false);
startScan();
}, [startScan]);

const handleStopScan = useCallback(() => {
console.log("⏹️ Stopping manual QR scan...");
setIsManualScanning(false);
setQrScanError(false);
setQrScanSuccess(false);
stopScan();
resetScan();
}, [stopScan, resetScan]);
@@ -1074,6 +1218,9 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe
<Typography variant="subtitle1">
<strong>{t("Pick Order Code")}:</strong>{fgPickOrders[0].pickOrderCode || '-'}
</Typography>
<Typography variant="subtitle1">
<strong>{t("Store ID")}:</strong> {fgPickOrders[0].storeId || '-'}
</Typography>
<Typography variant="subtitle1">
<strong>{t("Ticket No.")}:</strong> {fgPickOrders[0].ticketNo || '-'}
</Typography>
@@ -1125,8 +1272,20 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe
</Typography>
</Box>
)}
</Box>
</Box>

{qrScanError && (
<Alert severity="error" sx={{ mb: 2 }}>
{t("QR code does not match any item in current orders.")}
</Alert>
)}
{qrScanSuccess && (
<Alert severity="success" sx={{ mb: 2 }}>
{t("QR code verified.")}
</Alert>
)}
<TableContainer component={Paper}>
<Table>
@@ -1134,6 +1293,7 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe
<TableRow>
<TableCell>{t("Index")}</TableCell>
<TableCell>{t("Route")}</TableCell>
<TableCell>{t("Item Code")}</TableCell>
<TableCell>{t("Item Name")}</TableCell>
<TableCell>{t("Lot#")}</TableCell>
{/* <TableCell>{t("Target Date")}</TableCell> */}
@@ -1178,6 +1338,7 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe
{lot.routerRoute || '-'}
</Typography>
</TableCell>
<TableCell>{lot.itemCode}</TableCell>
<TableCell>{lot.itemName+'('+lot.stockUnit+')'}</TableCell>
<TableCell>
<Box>
@@ -1323,7 +1484,21 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe
combinedLotData={combinedLotData} // ✅ Add this prop
onQrCodeSubmit={handleQrCodeSubmitFromModal}
/>

{/* ✅ Lot Confirmation Modal */}
{lotConfirmationOpen && expectedLotData && scannedLotData && (
<LotConfirmationModal
open={lotConfirmationOpen}
onClose={() => {
setLotConfirmationOpen(false);
setExpectedLotData(null);
setScannedLotData(null);
}}
onConfirm={handleLotConfirmation}
expectedLot={expectedLotData}
scannedLot={scannedLotData}
isLoading={isConfirmingLot}
/>
)}
{/* ✅ Good Pick Execution Form Modal */}
{pickExecutionFormOpen && selectedLotForExecutionForm && (
<GoodPickExecutionForm


+ 126
- 0
src/components/FinishedGoodSearch/LotConfirmationModal.tsx 查看文件

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

import {
Box,
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Typography,
Alert,
Stack,
Divider,
} from "@mui/material";
import { useTranslation } from "react-i18next";

interface LotConfirmationModalProps {
open: boolean;
onClose: () => void;
onConfirm: () => void;
expectedLot: {
lotNo: string;
itemCode: string;
itemName: string;
};
scannedLot: {
lotNo: string;
itemCode: string;
itemName: string;
};
isLoading?: boolean;
}

const LotConfirmationModal: React.FC<LotConfirmationModalProps> = ({
open,
onClose,
onConfirm,
expectedLot,
scannedLot,
isLoading = false,
}) => {
const { t } = useTranslation("pickOrder");

return (
<Dialog open={open} onClose={onClose} maxWidth="md" fullWidth>
<DialogTitle>
<Typography variant="h6" component="div" color="warning.main">
{t("Lot Number Mismatch")}
</Typography>
</DialogTitle>
<DialogContent>
<Stack spacing={3}>
<Alert severity="warning">
{t("The scanned item matches the expected item, but the lot number is different. Do you want to proceed with this different lot?")}
</Alert>

<Box>
<Typography variant="subtitle1" gutterBottom color="primary">
{t("Expected Lot:")}
</Typography>
<Box sx={{ pl: 2, py: 1, backgroundColor: 'grey.50', borderRadius: 1 }}>
<Typography variant="body2">
<strong>{t("Item Code")}:</strong> {expectedLot.itemCode}
</Typography>
<Typography variant="body2">
<strong>{t("Item Name")}:</strong> {expectedLot.itemName}
</Typography>
<Typography variant="body2">
<strong>{t("Lot No")}:</strong> {expectedLot.lotNo}
</Typography>
</Box>
</Box>

<Divider />

<Box>
<Typography variant="subtitle1" gutterBottom color="warning.main">
{t("Scanned Lot:")}
</Typography>
<Box sx={{ pl: 2, py: 1, backgroundColor: 'warning.50', borderRadius: 1 }}>
<Typography variant="body2">
<strong>{t("Item Code")}:</strong> {scannedLot.itemCode}
</Typography>
<Typography variant="body2">
<strong>{t("Item Name")}:</strong> {scannedLot.itemName}
</Typography>
<Typography variant="body2">
<strong>{t("Lot No")}:</strong> {scannedLot.lotNo}
</Typography>
</Box>
</Box>

<Alert severity="info">
{t("If you proceed, the system will:")}
<ul style={{ margin: '8px 0 0 16px' }}>
<li>{t("Update the stock out line to use the scanned lot")}</li>
<li>{t("Put the original suggested lot on hold")}</li>
<li>{t("Update inventory lot line for the new lot")}</li>
</ul>
</Alert>
</Stack>
</DialogContent>

<DialogActions>
<Button
onClick={onClose}
variant="outlined"
disabled={isLoading}
>
{t("Cancel")}
</Button>
<Button
onClick={onConfirm}
variant="contained"
color="warning"
disabled={isLoading}
>
{isLoading ? t("Processing...") : t("Yes, Use This Lot")}
</Button>
</DialogActions>
</Dialog>
);
};

export default LotConfirmationModal;

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

@@ -261,5 +261,8 @@
"Start QR Scan":"開始QR掃描",
"Stop QR Scan":"停止QR掃描",
"Scanning...":"掃描中...",
"Print DN/Label":"列印送貨單/標籤"
"Print DN/Label":"列印送貨單/標籤",
"Store ID":"店鋪編號",
"QR code does not match any item in current orders.":"QR 碼不符合當前訂單中的任何貨品。"
}

Loading…
取消
儲存