Explorar el Código

update

MergeProblem1
CANCERYS\kw093 hace 1 día
padre
commit
00233d5353
Se han modificado 5 ficheros con 164 adiciones y 95 borrados
  1. +48
    -5
      src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx
  2. +8
    -0
      src/components/FinishedGoodSearch/LotConfirmationModal.tsx
  3. +8
    -0
      src/components/Jodetail/LotConfirmationModal.tsx
  4. +94
    -88
      src/components/Jodetail/newJobPickExecution.tsx
  5. +6
    -2
      src/i18n/zh/pickOrder.json

+ 48
- 5
src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx Ver fichero

@@ -522,6 +522,13 @@ function isLotAvailabilityExpired(lot: any): boolean {
return String(lot?.lotAvailability || "").toLowerCase() === "expired";
}

/** inventory_lot_line.status = unavailable(API 可能用 lotAvailability 或 lotStatus) */
function isInventoryLotLineUnavailable(lot: any): boolean {
if (!lot) return false;
if (lot.lotAvailability === "status_unavailable") return true;
return String(lot.lotStatus || "").toLowerCase() === "unavailable";
}

/** Issue「改數」未寫入 SOL,刷新/換頁後需靠 session 還原,否則 Qty will submit 會回到 req */
const FG_ISSUE_PICKED_KEY = (doPickOrderId: number) =>
`fpsms-fg-issuePickedQty:${doPickOrderId}`;
@@ -611,6 +618,7 @@ const [pickOrderSwitching, setPickOrderSwitching] = useState(false);
// QR scanner states (always-on, no modal)
const [selectedLotForQr, setSelectedLotForQr] = useState<any | null>(null);
const [lotConfirmationOpen, setLotConfirmationOpen] = useState(false);
const [lotConfirmationError, setLotConfirmationError] = useState<string | null>(null);
const [expectedLotData, setExpectedLotData] = useState<any>(null);
const [scannedLotData, setScannedLotData] = useState<any>(null);
const [isConfirmingLot, setIsConfirmingLot] = useState(false);
@@ -1250,6 +1258,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO

const clearLotConfirmationState = useCallback((clearProcessedRefs: boolean = false) => {
setLotConfirmationOpen(false);
setLotConfirmationError(null);
setExpectedLotData(null);
setScannedLotData(null);
setSelectedLotForQr(null);
@@ -1299,18 +1308,34 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO
const handleLotConfirmation = useCallback(async () => {
if (!expectedLotData || !scannedLotData || !selectedLotForQr) return;
setIsConfirmingLot(true);
setLotConfirmationError(null);
try {
const newLotNo = scannedLotData?.lotNo;
const newStockInLineId = scannedLotData?.stockInLineId;
await confirmLotSubstitution({
const substitutionResult = await confirmLotSubstitution({
pickOrderLineId: selectedLotForQr.pickOrderLineId,
stockOutLineId: selectedLotForQr.stockOutLineId,
originalSuggestedPickLotId: selectedLotForQr.suggestedPickLotId,
newInventoryLotNo: "",
newStockInLineId: newStockInLineId
});

if (!substitutionResult || substitutionResult.code !== "SUCCESS") {
const errMsg =
substitutionResult?.code === "LOT_UNAVAILABLE"
? t(
"The scanned lot inventory line is unavailable. Cannot switch or bind; pick line was not updated."
)
: substitutionResult?.message ||
t("Lot switch failed; pick line was not marked as checked.");
setLotConfirmationError(errMsg);
setQrScanError(true);
setQrScanSuccess(false);
setQrScanErrorMsg(errMsg);
return;
}
setQrScanError(false);
setQrScanSuccess(false);
@@ -1342,10 +1367,14 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO
setIsRefreshingData(false);
} catch (error) {
console.error("Error confirming lot substitution:", error);
const errMsg = t("Lot confirmation failed. Please try again.");
setLotConfirmationError(errMsg);
setQrScanError(true);
setQrScanErrorMsg(errMsg);
} finally {
setIsConfirmingLot(false);
}
}, [expectedLotData, scannedLotData, selectedLotForQr, fetchAllCombinedLotData, resetScan, clearLotConfirmationState]);
}, [expectedLotData, scannedLotData, selectedLotForQr, fetchAllCombinedLotData, resetScan, clearLotConfirmationState, t]);

const handleLotConfirmationByRescan = useCallback(async (rawQr: string): Promise<boolean> => {
if (!lotConfirmationOpen || !selectedLotForQr || !expectedLotData || !scannedLotData) {
@@ -1785,7 +1814,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO
const isRejected =
scannedLot.stockOutLineStatus?.toLowerCase() === 'rejected' ||
scannedLot.lotAvailability === 'rejected' ||
scannedLot.lotAvailability === 'status_unavailable';
isInventoryLotLineUnavailable(scannedLot);
if (isRejected) {
console.warn(`⚠️ [QR PROCESS] Scanned lot (stockInLineId: ${scannedStockInLineId}, lotNo: ${scannedLot.lotNo}) is rejected or unavailable`);
@@ -1849,7 +1878,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO
const rejectedLot = allLotsForItem.find((lot: any) =>
lot.stockOutLineStatus?.toLowerCase() === 'rejected' ||
lot.lotAvailability === 'rejected' ||
lot.lotAvailability === 'status_unavailable'
isInventoryLotLineUnavailable(lot)
);
const expectedLot =
rejectedLot ||
@@ -3700,10 +3729,13 @@ paginatedData.map((lot, index) => {
variant="outlined"
size="small"
onClick={() => handlelotnull(lot)}
/*
disabled={
status === 'completed' ||
(Number(lot.stockOutLineId) > 0 && actionBusyBySolId[Number(lot.stockOutLineId)] === true)
}
*/
disabled={true}
sx={{
fontSize: '0.7rem',
py: 0.5,
@@ -3729,14 +3761,18 @@ paginatedData.map((lot, index) => {
handlePickQtyChange(lotKey, submitQty);
handleSubmitPickQtyWithQty(lot, submitQty, 'singleSubmit');
}}
/*
disabled={
lot.lotAvailability === 'expired' ||
lot.lotAvailability === 'status_unavailable' ||
isInventoryLotLineUnavailable(lot) ||
lot.lotAvailability === 'rejected' ||
lot.stockOutLineStatus === 'completed' ||
lot.stockOutLineStatus === 'pending' ||
(Number(lot.stockOutLineId) > 0 && actionBusyBySolId[Number(lot.stockOutLineId)] === true)
}
*/
disabled={true}

sx={{ fontSize: '0.75rem', py: 0.5, minHeight: '28px', minWidth: '70px' }}
>
{t("Submit")}
@@ -3746,11 +3782,14 @@ paginatedData.map((lot, index) => {
variant="outlined"
size="small"
onClick={() => handlePickExecutionForm(lot)}
/*
disabled={
lot.lotAvailability === 'expired' ||
lot.stockOutLineStatus === 'completed' ||
(Number(lot.stockOutLineId) > 0 && actionBusyBySolId[Number(lot.stockOutLineId)] === true)
}
*/
disabled={true}
sx={{
fontSize: '0.7rem',
py: 0.5,
@@ -3767,6 +3806,7 @@ paginatedData.map((lot, index) => {
variant="outlined"
size="small"
onClick={() => handleSkip(lot)}
disabled={
lot.stockOutLineStatus === 'completed' ||
lot.stockOutLineStatus === 'checked' ||
@@ -3777,6 +3817,8 @@ paginatedData.map((lot, index) => {
(Number(lot.stockOutLineId) > 0 && issuePickedQtyBySolId[Number(lot.stockOutLineId)] !== undefined) ||
(Number(lot.stockOutLineId) > 0 && actionBusyBySolId[Number(lot.stockOutLineId)] === true)
}

sx={{ fontSize: '0.7rem', py: 0.5, minHeight: '28px', minWidth: '60px' }}
>
{t("Just Completed")}
@@ -3833,6 +3875,7 @@ paginatedData.map((lot, index) => {
expectedLot={expectedLotData}
scannedLot={scannedLotData}
isLoading={isConfirmingLot}
errorMessage={lotConfirmationError}
/>
)}


+ 8
- 0
src/components/FinishedGoodSearch/LotConfirmationModal.tsx Ver fichero

@@ -29,6 +29,8 @@ interface LotConfirmationModalProps {
itemName: string;
};
isLoading?: boolean;
/** Shown inside the dialog when confirm/switch API fails (e.g. LOT_UNAVAILABLE). */
errorMessage?: string | null;
}

const LotConfirmationModal: React.FC<LotConfirmationModalProps> = ({
@@ -38,6 +40,7 @@ const LotConfirmationModal: React.FC<LotConfirmationModalProps> = ({
expectedLot,
scannedLot,
isLoading = false,
errorMessage = null,
}) => {
const { t } = useTranslation("pickOrder");

@@ -60,6 +63,11 @@ const LotConfirmationModal: React.FC<LotConfirmationModalProps> = ({
<DialogContent>
<Stack spacing={3}>
{errorMessage ? (
<Alert severity="error">
{t(errorMessage)}
</Alert>
) : null}
<Alert severity="warning">
{t("The scanned item matches the expected item, but the lot number is different. Scan again to confirm: scan the expected lot QR to keep the suggested lot, or scan the other lot QR again to switch.")}
</Alert>


+ 8
- 0
src/components/Jodetail/LotConfirmationModal.tsx Ver fichero

@@ -29,6 +29,8 @@ interface LotConfirmationModalProps {
itemName: string;
};
isLoading?: boolean;
/** Shown inside the dialog when confirm/switch API fails (e.g. LOT_UNAVAILABLE). */
errorMessage?: string | null;
}

const LotConfirmationModal: React.FC<LotConfirmationModalProps> = ({
@@ -38,6 +40,7 @@ const LotConfirmationModal: React.FC<LotConfirmationModalProps> = ({
expectedLot,
scannedLot,
isLoading = false,
errorMessage = null,
}) => {
const { t } = useTranslation("pickOrder");

@@ -60,6 +63,11 @@ const LotConfirmationModal: React.FC<LotConfirmationModalProps> = ({
<DialogContent>
<Stack spacing={3}>
{errorMessage ? (
<Alert severity="error">
{t(errorMessage)}
</Alert>
) : null}
<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>


+ 94
- 88
src/components/Jodetail/newJobPickExecution.tsx Ver fichero

@@ -76,6 +76,13 @@ function isLotAvailabilityExpired(lot: any): boolean {
return String(lot?.lotAvailability || "").toLowerCase() === "expired";
}

/** inventory_lot_line.status = unavailable(API 可能用 lotAvailability 或 lotStatus) */
function isInventoryLotLineUnavailable(lot: any): boolean {
if (!lot) return false;
if (lot.lotAvailability === "status_unavailable") return true;
return String(lot.lotStatus || "").toLowerCase() === "unavailable";
}

const JO_ISSUE_PICKED_KEY = (pickOrderId: number) =>
`fpsms-jo-issuePickedQty:${pickOrderId}`;

@@ -471,6 +478,7 @@ const QrCodeModal: React.FC<{

const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
const { t } = useTranslation("jo");
const { t: tPick } = useTranslation("pickOrder");
const router = useRouter();
const { data: session } = useSession() as { data: SessionWithTokens | null };
@@ -486,6 +494,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
const { values: qrValues, isScanning, startScan, stopScan, resetScan } = useQrCodeScannerContext();
const [lotConfirmationOpen, setLotConfirmationOpen] = useState(false);
const [lotConfirmationError, setLotConfirmationError] = useState<string | null>(null);
const [expectedLotData, setExpectedLotData] = useState<any>(null);
const [scannedLotData, setScannedLotData] = useState<any>(null);
const [isConfirmingLot, setIsConfirmingLot] = useState(false);
@@ -1114,6 +1123,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
console.log("✅ [LOT CONFIRM] Selected lot for QR:", selectedLotForQr);
setIsConfirmingLot(true);
setLotConfirmationError(null);
try {
let newLotLineId = scannedLotData?.inventoryLotLineId;
if (!newLotLineId && scannedLotData?.stockInLineId) {
@@ -1171,9 +1181,16 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
console.log("✅ [LOT CONFIRM] updateStockOutLineStatusByQRCodeAndLotNo result:", res);
const ok = res?.code === "checked" || res?.code === "SUCCESS";
if (!ok) {
const errMsg =
res?.code === "LOT_UNAVAILABLE"
? tPick(
"The scanned lot inventory line is unavailable. Cannot switch or bind; pick line was not updated."
)
: res?.message || tPick("Lot switch failed; pick line was not marked as checked.");
setLotConfirmationError(errMsg);
setQrScanError(true);
setQrScanSuccess(false);
setQrScanErrorMsg(res?.message || "换批失败:无法更新 stock out line");
setQrScanErrorMsg(errMsg);
return;
}
} else {
@@ -1194,12 +1211,17 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
// Keep modal open so user can cancel/rescan.
if (!substitutionResult || substitutionResult.code !== "SUCCESS") {
console.error("❌ [LOT CONFIRM] Lot substitution failed. Will NOT update stockOutLine status.");
const errMsg =
substitutionResult?.code === "LOT_UNAVAILABLE"
? tPick(
"The scanned lot inventory line is unavailable. Cannot switch or bind; pick line was not updated."
)
: substitutionResult?.message ||
`换批失败:stockInLineId ${scannedLotData?.stockInLineId ?? ""} 不存在或无法匹配`;
setLotConfirmationError(errMsg);
setQrScanError(true);
setQrScanSuccess(false);
setQrScanErrorMsg(
substitutionResult?.message ||
`换批失败:stockInLineId ${scannedLotData?.stockInLineId ?? ""} 不存在或无法匹配`
);
setQrScanErrorMsg(errMsg);
return;
}
}
@@ -1262,15 +1284,17 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
} catch (error) {
console.error("Error confirming lot substitution:", error);
const errMsg = tPick("Lot confirmation failed. Please try again.");
setLotConfirmationError(errMsg);
setQrScanError(true);
setQrScanSuccess(false);
setQrScanErrorMsg('换批发生异常,请重试或联系管理员');
setQrScanErrorMsg(errMsg);
// Clear refresh flag on error
setIsRefreshingData(false);
} finally {
setIsConfirmingLot(false);
}
}, [expectedLotData, scannedLotData, selectedLotForQr, fetchJobOrderData,currentUserId, updateHandledBy ]);
}, [expectedLotData, scannedLotData, selectedLotForQr, fetchJobOrderData, currentUserId, updateHandledBy, tPick]);

const processOutsideQrCode = useCallback(async (latestQr: string) => {
// ✅ Only JSON QR supported for outside scanner (avoid false positive with lotNo)
@@ -1315,7 +1339,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
const isRejected =
scannedLot.stockOutLineStatus?.toLowerCase() === 'rejected' ||
scannedLot.lotAvailability === 'rejected' ||
scannedLot.lotAvailability === 'status_unavailable';
isInventoryLotLineUnavailable(scannedLot);
if (isRejected) {
console.warn(`⚠️ [QR PROCESS] Scanned lot (stockInLineId: ${scannedStockInLineId}, lotNo: ${scannedLot.lotNo}) is rejected or unavailable`);
@@ -1335,6 +1359,28 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
});
return;
}

const isExpired =
String(scannedLot.lotAvailability || "").toLowerCase() === "expired";
if (isExpired) {
console.warn(
`⚠️ [QR PROCESS] Scanned lot (stockInLineId: ${scannedStockInLineId}, lotNo: ${scannedLot.lotNo}) is expired`
);
startTransition(() => {
setQrScanError(true);
setQrScanSuccess(false);
setQrScanErrorMsg(
`此批次(${scannedLot.lotNo || scannedStockInLineId})已过期,无法使用。请扫描其他批次。`
);
});
setProcessedQrCombinations((prev) => {
const newMap = new Map(prev);
if (!newMap.has(scannedItemId)) newMap.set(scannedItemId, new Set());
newMap.get(scannedItemId)!.add(scannedStockInLineId);
return newMap;
});
return;
}
}
// ✅ If no active suggested lots, but scanned lot is not rejected, allow lot switching
@@ -1489,10 +1535,16 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
return;
}

// ✅ mismatch: validate scanned stockInLineId exists before opening confirmation modal
console.log(`⚠️ [QR PROCESS] No exact match found. Validating scanned stockInLineId ${scannedStockInLineId} for itemId ${scannedItemId}`);
console.log(`⚠️ [QR PROCESS] Active suggested lots for itemId ${scannedItemId}:`, activeSuggestedLots.map(l => ({ lotNo: l.lotNo, stockInLineId: l.stockInLineId })));
// ✅ mismatch: align with GoodPickExecutiondetail — open LotConfirmationModal first;
// handleLotMismatch loads scanned lotNo via fetchStockInLineInfoCached in the background when lotNo is null.
console.log(
`⚠️ [QR PROCESS] No exact match found (itemId ${scannedItemId}, scanned stockInLineId ${scannedStockInLineId})`
);
console.log(
`⚠️ [QR PROCESS] Active suggested lots for itemId ${scannedItemId}:`,
activeSuggestedLots.map((l) => ({ lotNo: l.lotNo, stockInLineId: l.stockInLineId }))
);

if (activeSuggestedLots.length === 0) {
console.error(`❌ [QR PROCESS] No active suggested lots found for itemId ${scannedItemId}`);
startTransition(() => {
@@ -1504,82 +1556,28 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
}

const expectedLot = activeSuggestedLots[0];
console.log(`⚠️ [QR PROCESS] Expected lot: ${expectedLot.lotNo} (stockInLineId: ${expectedLot.stockInLineId}), Scanned stockInLineId: ${scannedStockInLineId}`);
// ✅ Validate scanned stockInLineId exists before opening modal
// This ensures the backend can find the lot when user confirms
try {
console.log(`🔍 [QR PROCESS] Validating scanned stockInLineId ${scannedStockInLineId} exists...`);
const stockInLineInfo = await fetchStockInLineInfoCached(scannedStockInLineId);
console.log(`✅ [QR PROCESS] Scanned stockInLineId ${scannedStockInLineId} exists, lotNo: ${stockInLineInfo.lotNo}`);
// ✅ 检查扫描的批次是否已被拒绝
const scannedLot = combinedLotData.find(
(lot: any) => lot.stockInLineId === scannedStockInLineId && lot.itemId === scannedItemId
);
if (scannedLot) {
const isRejected =
scannedLot.stockOutLineStatus?.toLowerCase() === 'rejected' ||
scannedLot.lotAvailability === 'rejected' ||
scannedLot.lotAvailability === 'status_unavailable';
if (isRejected) {
console.warn(`⚠️ [QR PROCESS] Scanned lot ${stockInLineInfo.lotNo} (stockInLineId: ${scannedStockInLineId}) is rejected or unavailable`);
startTransition(() => {
setQrScanError(true);
setQrScanSuccess(false);
setQrScanErrorMsg(
`此批次(${stockInLineInfo.lotNo || scannedStockInLineId})已被拒绝,无法使用。请扫描其他批次。`
);
});
// Mark as processed to prevent re-processing
setProcessedQrCombinations(prev => {
const newMap = new Map(prev);
if (!newMap.has(scannedItemId)) newMap.set(scannedItemId, new Set());
newMap.get(scannedItemId)!.add(scannedStockInLineId);
return newMap;
});
return;
}
console.log(
`⚠️ [QR PROCESS] Expected lot: ${expectedLot.lotNo} (stockInLineId: ${expectedLot.stockInLineId}), Scanned stockInLineId: ${scannedStockInLineId}`
);
console.log(
`⚠️ [QR PROCESS] Opening confirmation modal - user must confirm before any lot is marked as scanned`
);
setSelectedLotForQr(expectedLot);
handleLotMismatch(
{
lotNo: expectedLot.lotNo,
itemCode: expectedLot.itemCode,
itemName: expectedLot.itemName,
},
{
lotNo: null,
itemCode: expectedLot.itemCode,
itemName: expectedLot.itemName,
inventoryLotLineId: null,
stockInLineId: scannedStockInLineId,
}
// ✅ stockInLineId exists and is not rejected, open confirmation modal
console.log(`⚠️ [QR PROCESS] Opening confirmation modal - user must confirm before any lot is marked as scanned`);
setSelectedLotForQr(expectedLot);
handleLotMismatch(
{
lotNo: expectedLot.lotNo,
itemCode: expectedLot.itemCode,
itemName: expectedLot.itemName
},
{
lotNo: stockInLineInfo.lotNo || null, // Use fetched lotNo for display
itemCode: expectedLot.itemCode,
itemName: expectedLot.itemName,
inventoryLotLineId: null,
stockInLineId: scannedStockInLineId
}
);
} catch (error) {
// ✅ stockInLineId does NOT exist, show error immediately (don't open modal)
console.error(`❌ [QR PROCESS] Scanned stockInLineId ${scannedStockInLineId} does NOT exist:`, error);
startTransition(() => {
setQrScanError(true);
setQrScanSuccess(false);
setQrScanErrorMsg(
`扫描的 stockInLineId ${scannedStockInLineId} 不存在。请检查 QR 码是否正确,或联系管理员。`
);
});
// Mark as processed to prevent re-processing
setProcessedQrCombinations(prev => {
const newMap = new Map(prev);
if (!newMap.has(scannedItemId)) newMap.set(scannedItemId, new Set());
newMap.get(scannedItemId)!.add(scannedStockInLineId);
return newMap;
});
}
}, [filterArgs?.pickOrderId, fetchJobOrderData, handleLotMismatch, lotDataIndexes, processedQrCombinations, combinedLotData, fetchStockInLineInfoCached,currentUserId, updateHandledBy ]);
);
}, [filterArgs?.pickOrderId, fetchJobOrderData, handleLotMismatch, lotDataIndexes, processedQrCombinations, combinedLotData, currentUserId, updateHandledBy]);

// Store in refs for immediate access in qrValues effect
processOutsideQrCodeRef.current = processOutsideQrCode;
@@ -2823,14 +2821,17 @@ const sortedData = [...sourceData].sort((a, b) => {
}
}
}}
/*
disabled={
(Number(lot.stockOutLineId) > 0 && actionBusyBySolId[Number(lot.stockOutLineId)] === true) ||
(lot.lotAvailability === 'expired' ||
lot.lotAvailability === 'status_unavailable' ||
isInventoryLotLineUnavailable(lot) ||
lot.lotAvailability === 'rejected') ||
lot.stockOutLineStatus === 'completed' ||
lot.stockOutLineStatus === 'pending'
}
*/
disabled={true}
sx={{
fontSize: '0.75rem',
py: 0.5,
@@ -2845,6 +2846,7 @@ const sortedData = [...sourceData].sort((a, b) => {
variant="outlined"
size="small"
onClick={() => handlePickExecutionForm(lot)}
/*
disabled={
lot.lotAvailability === "expired" ||
lot.stockOutLineStatus === "completed" ||
@@ -2853,6 +2855,8 @@ const sortedData = [...sourceData].sort((a, b) => {
(Number(lot.stockOutLineId) > 0 &&
actionBusyBySolId[Number(lot.stockOutLineId)] === true)
}
*/
disabled={true}
sx={{
fontSize: '0.7rem',
py: 0.5,
@@ -2954,6 +2958,7 @@ const sortedData = [...sourceData].sort((a, b) => {
onClose={() => {
console.log(`⏱️ [LOT CONFIRM MODAL] Closing modal, clearing state`);
setLotConfirmationOpen(false);
setLotConfirmationError(null);
setExpectedLotData(null);
setScannedLotData(null);
setSelectedLotForQr(null);
@@ -2986,6 +2991,7 @@ const sortedData = [...sourceData].sort((a, b) => {
expectedLot={expectedLotData}
scannedLot={scannedLotData}
isLoading={isConfirmingLot}
errorMessage={lotConfirmationError}
/>
)}



+ 6
- 2
src/i18n/zh/pickOrder.json Ver fichero

@@ -459,5 +459,9 @@
"Ticket Release Table": "查看提貨情況",
"Please take one pick order before printing the draft.": "請先從「撳單/提料單詳情」頁面下方選取提料單,再列印草稿。",
"No released pick order records found.": "目前沒有可用的提料單。",
"EDT - Lane Code (Unassigned/Total)": "預計出發時間 - 貨車班次(未撳數/總單數)"
}
"EDT - Lane Code (Unassigned/Total)": "預計出發時間 - 貨車班次(未撳數/總單數)",
"The scanned lot inventory line is unavailable. Cannot switch or bind; pick line was not updated.": "掃描的庫存批行為「不可用」,無法換批或綁定;揀貨行未更新。",
"Lot switch failed; pick line was not marked as checked.": "換批失敗;揀貨行未標為已核對。",
"Lot confirmation failed. Please try again.": "確認批號失敗,請重試。",
"Lot status is unavailable. Cannot switch or bind; pick line was not updated.": "批號狀態為「不可用」,無法換批或綁定;揀貨行未更新。"
}

Cargando…
Cancelar
Guardar