kelvin.yau преди 2 месеца
родител
ревизия
dda58679cc
променени са 5 файла, в които са добавени 378 реда и са изтрити 129 реда
  1. +276
    -43
      src/components/Jodetail/JobPickExecution.tsx
  2. +83
    -84
      src/components/Jodetail/JobPickExecutionsecondscan.tsx
  3. +1
    -1
      src/components/Jodetail/LotConfirmationModal.tsx
  4. +16
    -1
      src/i18n/zh/jo.json
  5. +2
    -0
      src/i18n/zh/pickOrder.json

+ 276
- 43
src/components/Jodetail/JobPickExecution.tsx Целия файл

@@ -32,7 +32,8 @@ import {
AutoAssignReleaseResponse,
checkPickOrderCompletion,
PickOrderCompletionResponse,
checkAndCompletePickOrderByConsoCode
checkAndCompletePickOrderByConsoCode,
confirmLotSubstitution
} from "@/app/api/pickOrder/actions";
// ✅ 修改:使用 Job Order API
import {
@@ -47,7 +48,7 @@ import {
} from "react-hook-form";
import SearchBox, { Criterion } from "../SearchBox";
import { CreateStockOutLine } from "@/app/api/pickOrder/actions";
import { updateInventoryLotLineQuantities } from "@/app/api/inventory/actions";
import { updateInventoryLotLineQuantities, analyzeQrCode, fetchLotDetail } from "@/app/api/inventory/actions";
import QrCodeIcon from '@mui/icons-material/QrCode';
import { useQrCodeScannerContext } from '../QrCodeScannerProvider/QrCodeScannerProvider';
import { useSession } from "next-auth/react";
@@ -55,7 +56,7 @@ import { SessionWithTokens } from "@/config/authConfig";
import { fetchStockInLineInfo } from "@/app/api/po/actions";
import GoodPickExecutionForm from "./JobPickExecutionForm";
import FGPickOrderCard from "./FGPickOrderCard";
import LotConfirmationModal from "./LotConfirmationModal";
interface Props {
filterArgs: Record<string, any>;
}
@@ -332,7 +333,10 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
const [isLoadingUnassigned, setIsLoadingUnassigned] = useState(false);
const { values: qrValues, isScanning, startScan, stopScan, resetScan } = useQrCodeScannerContext();
const [lotConfirmationOpen, setLotConfirmationOpen] = useState(false);
const [expectedLotData, setExpectedLotData] = useState<any>(null);
const [scannedLotData, setScannedLotData] = useState<any>(null);
const [isConfirmingLot, setIsConfirmingLot] = useState(false);
const [qrScanInput, setQrScanInput] = useState<string>('');
const [qrScanError, setQrScanError] = useState<boolean>(false);
const [qrScanSuccess, setQrScanSuccess] = useState<boolean>(false);
@@ -522,7 +526,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
stockOutLineId: lot.stockOutLineId,
stockOutLineStatus: lot.stockOutLineStatus,
stockOutLineQty: lot.stockOutLineQty,
suggestedPickLotId: lot.suggestedPickLotId,
// Router info
routerIndex: lot.routerIndex,
secondQrScanStatus: lot.secondQrScanStatus,
@@ -710,9 +714,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
setQrScanSuccess(true);
setQrScanError(false);
setQrScanInput(''); // Clear input after successful processing
setIsManualScanning(false);
stopScan();
resetScan();
} else {
console.error(`❌ QR Code processing failed: ${errorCount} errors`);
setQrScanError(true);
@@ -733,6 +735,224 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
}, 1000);
}
}, [combinedLotData, fetchJobOrderData]);
const handleLotMismatch = useCallback((expectedLot: any, scannedLot: any) => {
console.log("Lot mismatch detected:", { expectedLot, scannedLot });
setExpectedLotData(expectedLot);
setScannedLotData(scannedLot);
setLotConfirmationOpen(true);
}, []);

// ✅ Add handleLotConfirmation function
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;
}
console.log("=== Lot Confirmation Debug ===");
console.log("Selected Lot:", selectedLotForQr);
console.log("Pick Order Line ID:", selectedLotForQr.pickOrderLineId);
console.log("Stock Out Line ID:", selectedLotForQr.stockOutLineId);
console.log("Suggested Pick Lot ID:", selectedLotForQr.suggestedPickLotId);
console.log("Lot ID (fallback):", selectedLotForQr.lotId);
console.log("New Inventory Lot Line ID:", newLotLineId);

// ✅ Call confirmLotSubstitution to update the suggested lot
const substitutionResult = await confirmLotSubstitution({
pickOrderLineId: selectedLotForQr.pickOrderLineId,
stockOutLineId: selectedLotForQr.stockOutLineId,
originalSuggestedPickLotId: selectedLotForQr.suggestedPickLotId || selectedLotForQr.lotId,
newInventoryLotLineId: newLotLineId
});
console.log("✅ Lot substitution result:", substitutionResult);
// ✅ Update stock out line status to 'checked' after substitution
if(selectedLotForQr?.stockOutLineId){
await updateStockOutLineStatus({
id: selectedLotForQr.stockOutLineId,
status: 'checked',
qty: 0
});
console.log("✅ Stock out line status updated to 'checked'");
}
// ✅ Close modal and clean up state BEFORE refreshing
setLotConfirmationOpen(false);
setExpectedLotData(null);
setScannedLotData(null);
setSelectedLotForQr(null);
// ✅ Clear QR processing state but DON'T clear processedQrCodes yet
setQrScanError(false);
setQrScanSuccess(true);
setQrScanInput('');
// ✅ Set refreshing flag to prevent QR processing during refresh
setIsRefreshingData(true);
// ✅ Refresh data to show updated lot
console.log("🔄 Refreshing job order data...");
await fetchJobOrderData();
console.log("✅ Lot substitution confirmed and data refreshed");
// ✅ Clear processed QR codes and flags immediately after refresh
// This allows new QR codes to be processed right away
setTimeout(() => {
console.log("✅ Clearing processed QR codes and resuming scan");
setProcessedQrCodes(new Set());
setLastProcessedQr('');
setQrScanSuccess(false);
setIsRefreshingData(false);
}, 500); // ✅ Reduced from 3000ms to 500ms - just enough for UI update
} catch (error) {
console.error("Error confirming lot substitution:", error);
setQrScanError(true);
setQrScanSuccess(false);
// ✅ Clear refresh flag on error
setIsRefreshingData(false);
} finally {
setIsConfirmingLot(false);
}
}, [expectedLotData, scannedLotData, selectedLotForQr, fetchJobOrderData]);

const processOutsideQrCode = useCallback(async (latestQr: string) => {
// ✅ Don't process if confirmation modal is open
if (lotConfirmationOpen) {
console.log("⏸️ Confirmation modal is open, skipping QR processing");
return;
}

let qrData: any = null;
try {
qrData = JSON.parse(latestQr);
} catch {
console.log("QR is not JSON format");
// Handle non-JSON QR codes as direct lot numbers
const directLotNo = latestQr.replace(/[{}]/g, '');
if (directLotNo) {
console.log(`Processing direct lot number: ${directLotNo}`);
await handleQrCodeSubmit(directLotNo);
}
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;
}
// ✅ First, fetch stock in line info to get the lot number
let stockInLineInfo: any;
try {
stockInLineInfo = await fetchStockInLineInfo(qrData.stockInLineId);
console.log("Stock in line info:", stockInLineInfo);
} catch (error) {
console.error("Error fetching stock in line info:", error);
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;
}
// Find the ACTIVE suggested lot (not rejected lots)
const activeSuggestedLots = sameItemLotsInExpected.filter(lot =>
lot.lotAvailability !== 'rejected' &&
lot.stockOutLineStatus !== 'rejected' &&
lot.stockOutLineStatus !== 'completed'
);
if (activeSuggestedLots.length === 0) {
console.warn("All lots for this item are rejected or completed");
setQrScanError(true);
setQrScanSuccess(false);
return;
}
// Use the first active suggested lot as the "expected" lot
const expectedLot = activeSuggestedLots[0];
// 2) Check if the scanned lot matches exactly
if (scanned?.lotNo === expectedLot.lotNo) {
// Case 1: Exact match - process normally
console.log(`✅ Exact lot match: ${scanned.lotNo}`);
await handleQrCodeSubmit(scanned.lotNo);
return;
}
// Case 2: Same item, different lot - show confirmation modal
console.log(`🔍 Lot mismatch: Expected ${expectedLot.lotNo}, Scanned ${scanned?.lotNo}`);
// ✅ DON'T stop scanning - just pause QR processing by showing modal
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, lotConfirmationOpen]);


const handleManualInputSubmit = useCallback(() => {
if (qrScanInput.trim() !== '') {
@@ -782,43 +1002,28 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
}
}, [selectedLotForQr, fetchJobOrderData]);

// ✅ Outside QR scanning - process QR codes from outside the page automatically
useEffect(() => {
if (qrValues.length > 0 && combinedLotData.length > 0) {
const latestQr = qrValues[qrValues.length - 1];
// Extract lot number from QR code
let lotNo = '';
try {
const qrData = JSON.parse(latestQr);
if (qrData.stockInLineId && qrData.itemId) {
// For JSON QR codes, we need to fetch the lot number
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; // Exit early for JSON QR codes
}
} catch (error) {
// Not JSON format, treat as direct lot number
lotNo = latestQr.replace(/[{}]/g, '');
}
// ✅ Add isManualScanning check
if (!isManualScanning || qrValues.length === 0 || combinedLotData.length === 0 || isRefreshingData || lotConfirmationOpen) {
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));
// For direct lot number QR codes
if (lotNo) {
console.log(`Outside QR scan detected (direct): ${lotNo}`);
handleQrCodeSubmit(lotNo);
}
processOutsideQrCode(latestQr);
}
}, [qrValues, combinedLotData, handleQrCodeSubmit]);
}, [qrValues, processedQrCodes, lastProcessedQr, isRefreshingData, processOutsideQrCode, combinedLotData, isManualScanning, lotConfirmationOpen]);

const handlePickQtyChange = useCallback((lotKey: string, value: number | string) => {
if (value === '' || value === null || value === undefined) {
@@ -1208,6 +1413,16 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
stopScan();
resetScan();
}, [stopScan, resetScan]);
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()) {
@@ -1475,6 +1690,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
</Stack>

{/* ✅ QR Code Modal */}
{!lotConfirmationOpen && (
<QrCodeModal
open={qrModalOpen}
onClose={() => {
@@ -1487,7 +1703,24 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
combinedLotData={combinedLotData}
onQrCodeSubmit={handleQrCodeSubmitFromModal}
/>

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


+ 83
- 84
src/components/Jodetail/JobPickExecutionsecondscan.tsx Целия файл

@@ -614,7 +614,16 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
if (successCount > 0) {
setQrScanSuccess(true);
setQrScanError(false);
// ✅ Set refreshing flag briefly to prevent duplicate processing
setIsRefreshingData(true);
await fetchJobOrderData(); // Refresh data
// ✅ Clear refresh flag and success message after a short delay
setTimeout(() => {
setQrScanSuccess(false);
setIsRefreshingData(false);
}, 500);
} else {
setQrScanError(true);
setQrScanSuccess(false);
@@ -626,52 +635,68 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
}
}, [combinedLotData, fetchJobOrderData, processedQrCodes]);
useEffect(() => {
if (qrValues.length > 0 && combinedLotData.length > 0) {
const latestQr = qrValues[qrValues.length - 1];
// ✅ Check if this QR was already processed recently
if (processedQrCodes.has(latestQr) || lastProcessedQr === latestQr) {
console.log("⏭️ QR code already processed, skipping...");
return;
}
// ✅ Mark as processed
setProcessedQrCodes(prev => new Set(prev).add(latestQr));
setLastProcessedQr(latestQr);
// Extract lot number from QR code
let lotNo = '';
try {
const qrData = JSON.parse(latestQr);
if (qrData.stockInLineId && qrData.itemId) {
// For JSON QR codes, we need to fetch the lot number
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; // Exit early for JSON QR codes
}
} catch (error) {
// Not JSON format, treat as direct lot number
lotNo = latestQr.replace(/[{}]/g, '');
}
// For direct lot number QR codes
if (lotNo) {
console.log(`Outside QR scan detected (direct): ${lotNo}`);
handleQrCodeSubmit(lotNo);
// ✅ Add isManualScanning and isRefreshingData checks
if (!isManualScanning || qrValues.length === 0 || combinedLotData.length === 0 || isRefreshingData) {
return;
}
const latestQr = qrValues[qrValues.length - 1];
// ✅ Check if this QR was already processed recently
if (processedQrCodes.has(latestQr) || lastProcessedQr === latestQr) {
console.log("⏭️ QR code already processed, skipping...");
return;
}
// ✅ Mark as processed
setProcessedQrCodes(prev => new Set(prev).add(latestQr));
setLastProcessedQr(latestQr);
// Extract lot number from QR code
let lotNo = '';
try {
const qrData = JSON.parse(latestQr);
if (qrData.stockInLineId && qrData.itemId) {
// For JSON QR codes, we need to fetch the lot number
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; // Exit early for JSON QR codes
}
} catch (error) {
// Not JSON format, treat as direct lot number
lotNo = latestQr.replace(/[{}]/g, '');
}
// For direct lot number QR codes
if (lotNo) {
console.log(`Outside QR scan detected (direct): ${lotNo}`);
handleQrCodeSubmit(lotNo);
}
}, [qrValues, combinedLotData, handleQrCodeSubmit, processedQrCodes, lastProcessedQr]);
}, [qrValues, combinedLotData, handleQrCodeSubmit, processedQrCodes, lastProcessedQr, isManualScanning, isRefreshingData]);

// ✅ ADD THIS: Cleanup effect
useEffect(() => {
return () => {
// Cleanup when component unmounts (e.g., when switching tabs)
if (isManualScanning) {
console.log("🧹 Second scan component unmounting, stopping QR scanner...");
stopScan();
resetScan();
}
};
}, [isManualScanning, stopScan, resetScan]);
const handleManualInputSubmit = useCallback(() => {
if (qrScanInput.trim() !== '') {
handleQrCodeSubmit(qrScanInput.trim());
@@ -721,42 +746,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
}, [selectedLotForQr, fetchJobOrderData]);

// ✅ Outside QR scanning - process QR codes from outside the page automatically
useEffect(() => {
if (qrValues.length > 0 && combinedLotData.length > 0) {
const latestQr = qrValues[qrValues.length - 1];
// Extract lot number from QR code
let lotNo = '';
try {
const qrData = JSON.parse(latestQr);
if (qrData.stockInLineId && qrData.itemId) {
// For JSON QR codes, we need to fetch the lot number
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; // Exit early for JSON QR codes
}
} catch (error) {
// Not JSON format, treat as direct lot number
lotNo = latestQr.replace(/[{}]/g, '');
}
// For direct lot number QR codes
if (lotNo) {
console.log(`Outside QR scan detected (direct): ${lotNo}`);
handleQrCodeSubmit(lotNo);
}
}
}, [qrValues, combinedLotData, handleQrCodeSubmit]);

const handlePickQtyChange = useCallback((lotKey: string, value: number | string) => {
if (value === '' || value === null || value === undefined) {
@@ -993,7 +983,16 @@ const paginatedData = useMemo(() => {
stopScan();
resetScan();
}, [stopScan, resetScan]);

useEffect(() => {
return () => {
// Cleanup when component unmounts (e.g., when switching tabs)
if (isManualScanning) {
console.log("🧹 Second scan component unmounting, stopping QR scanner...");
stopScan();
resetScan();
}
};
}, [isManualScanning, stopScan, resetScan]);
const getStatusMessage = useCallback((lot: any) => {
switch (lot.stockOutLineStatus?.toLowerCase()) {
case 'pending':
@@ -1152,8 +1151,8 @@ const paginatedData = useMemo(() => {
})()}
</TableCell>
<TableCell align="center">
{lot.stockOutLineStatus?.toLowerCase() !== 'pending' ? (
<TableCell align="center">
{lot.secondQrScanStatus?.toLowerCase() !== 'pending' ? (
<Box sx={{
display: 'flex',
justifyContent: 'center',
@@ -1162,12 +1161,12 @@ const paginatedData = useMemo(() => {
height: '100%'
}}>
<Checkbox
checked={lot.secondQrScanStatus?.toLowerCase() !== 'pending'}
checked={lot.secondQrScanStatus?.toLowerCase() !== 'pending'}
disabled={true}
readOnly={true}
size="large"
sx={{
color: lot.secondQrScanStatus?.toLowerCase() !== 'pending' ? 'success.main' : 'grey.400',
color: lot.secondQrScanStatus?.toLowerCase() !== 'pending' ? 'success.main' : 'grey.400',
'&.Mui-checked': {
color: 'success.main',
},


+ 1
- 1
src/components/Jodetail/LotConfirmationModal.tsx Целия файл

@@ -39,7 +39,7 @@ const LotConfirmationModal: React.FC<LotConfirmationModalProps> = ({
scannedLot,
isLoading = false,
}) => {
const { t } = useTranslation("jo");
const { t } = useTranslation("pickOrder");

return (
<Dialog open={open} onClose={onClose} maxWidth="md" fullWidth>


+ 16
- 1
src/i18n/zh/jo.json Целия файл

@@ -40,6 +40,7 @@
"Route": "路線",
"Qty": "數量",
"Unit": "單位",
"Issue": "問題",
"Location": "位置",
"Scan Result": "掃碼結果",
"Expiry Date": "有效期",
@@ -88,6 +89,20 @@
"qty is required": "數量是必需的",
"qty is not allowed to be greater than remaining available qty": "數量不能大於剩餘可用數量",
"qty is not allowed to be greater than required qty": "數量不能大於需求數量",
"qty is not allowed to be greater than picked qty": "數量不能大於已提料數量"
"qty is not allowed to be greater than picked qty": "數量不能大於已提料數量",
"QR code verified.": "QR碼驗證成功。",
"QR code does not match any item in current orders.": "QR碼不匹配當前工單的物料。",
"This form is for reporting issues only. You must report either missing items or bad items.": "此表單僅用於報告問題。您必須報告缺失的物料或不良的物料。",
"Pick Execution Issue Form": "提料執行問題表單",
"Verified Qty": "驗證數量",
"Missing item Qty": "缺失的物料數量",
"Bad Item Qty": "不良的物料數量",
"submit": "提交",
"Issue Remark": "問題描述",
"Received Qty": "接收數量",
"Create": "創建",
"Confirm Lot Substitution": "確認批號替換",
"Processing...": "處理中",
"Processing...": "處理中"

}

+ 2
- 0
src/i18n/zh/pickOrder.json Целия файл

@@ -224,6 +224,8 @@
"Selected items will join above created group": "已選擇的貨品將加入以上建立的分組",
"Issue":"問題",
"issue":"問題",
"Processing...":"處理中",
"Assigning pick order...":"分派提料單中...",
"Pick Execution Issue Form":"提料問題表單",
"This form is for reporting issues only. You must report either missing items or bad items.":"此表單僅用於報告問題。您必須報告缺少的貨品或不良貨品。",
"Bad item Qty":"不良貨品數量",


Зареждане…
Отказ
Запис