Sfoglia il codice sorgente

update reject handle correct

master
CANCERYS\kw093 3 mesi fa
parent
commit
46b313cb08
7 ha cambiato i file con 167 aggiunte e 56 eliminazioni
  1. +13
    -5
      src/app/api/inventory/actions.ts
  2. +88
    -18
      src/components/PickOrderSearch/LotTable.tsx
  3. +10
    -2
      src/components/PickOrderSearch/PickExecution.tsx
  4. +3
    -1
      src/components/PickOrderSearch/PickOrderSearch.tsx
  5. +37
    -25
      src/components/PickOrderSearch/PickQcStockInModalVer3.tsx
  6. +4
    -3
      src/components/PickOrderSearch/newcreatitem.tsx
  7. +12
    -2
      src/i18n/zh/pickOrder.json

+ 13
- 5
src/app/api/inventory/actions.ts Vedi File

@@ -77,13 +77,21 @@ export const fetchLotDetail = cache(async (stockInLineId: number) => {
export const updateInventoryLotLineStatus = async (data: {
inventoryLotLineId: number;
status: string;
qty: number;
operation?: string;
//qty: number;
//operation?: string;
}) => {
return serverFetchJson(`${BASE_API_URL}/inventory/lot-line/update-status`, {
method: 'PUT',
body: JSON.stringify(data)
// return await serverFetchJson(`${BASE_API_URL}/inventoryLotLine/updateStatus`, {
// next: { tags: ["inventoryLotLine"] },
// method: 'POST',
// body: JSON.stringify(data)
// });
return await serverFetchJson<PostInventoryLotLineResponse<InventoryLotLineResult>>(`${BASE_API_URL}/inventoryLotLine/updateStatus`, {
next: { tags: ["inventoryLotLine"] },
method: 'POST',
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
});
// revalidateTag("po");
};

export const fetchInventories = cache(async (data: SearchInventory) => {


+ 88
- 18
src/components/PickOrderSearch/LotTable.tsx Vedi File

@@ -22,7 +22,8 @@ import { useTranslation } from "react-i18next";
import QrCodeIcon from '@mui/icons-material/QrCode';
import { GetPickOrderLineInfo } from "@/app/api/pickOrder/actions";
import { useQrCodeScannerContext } from '../QrCodeScannerProvider/QrCodeScannerProvider';

import { updateInventoryLotLineStatus } from "@/app/api/inventory/actions";
import { updateStockOutLineStatus } from "@/app/api/pickOrder/actions";
interface LotPickData {
id: number;
lotId: number;
@@ -63,6 +64,7 @@ interface LotTableProps {
setShowInputBody: (show: boolean) => void;
selectedLotForInput: LotPickData | null;
generateInputBody: () => any;
onDataRefresh: () => Promise<void>;
}

// ✅ QR Code Modal Component
@@ -222,6 +224,23 @@ const handleManualSubmit = () => {
);
};
*/}
useEffect(() => {
if (manualInput.trim() === lot?.lotNo && manualInput.trim() !== '') {
// Auto-submit when manual input matches the expected lot number
console.log('🔄 Auto-submitting manual input:', manualInput.trim());
// Add a small delay to ensure proper execution order
const timer = setTimeout(() => {
onQrCodeSubmit(lot.lotNo);
onClose();
setManualInput('');
setManualInputError(false);
setManualInputSubmitted(false);
}, 200); // 200ms delay
return () => clearTimeout(timer);
}
}, [manualInput, lot, onQrCodeSubmit, onClose]);
const handleManualSubmit = () => {
if (manualInput.trim() === lot?.lotNo) {
// ✅ Success - no error helper text needed
@@ -327,6 +346,7 @@ const LotTable: React.FC<LotTableProps> = ({
setShowInputBody,
selectedLotForInput,
generateInputBody,
onDataRefresh,
}) => {
const { t } = useTranslation("pickOrder");
@@ -347,24 +367,24 @@ const LotTable: React.FC<LotTableProps> = ({
// ✅ 添加状态消息生成函数
const getStatusMessage = useCallback((lot: LotPickData) => {
if (!lot.stockOutLineId) {
return "Please finish QR code scan, QC check and pick order.";
return t("Please finish QR code scan, QC check and pick order.");
}
switch (lot.stockOutLineStatus?.toLowerCase()) {
case 'pending':
return "Please finish QC check and pick order.";
return t("Please finish QC check and pick order.");
case 'checked':
return "Please submit the pick order.";
return t("Please submit the pick order.");
case 'partially_completed':
return "Partial quantity submitted. You can submit more or complete the order.";
return t("Partial quantity submitted. Please submit more or complete the order.") ;
case 'completed':
return "Pick order completed successfully!";
return t("Pick order completed successfully!");
case 'rejected':
return "QC check failed. Lot has been rejected and marked as unavailable.";
return t("QC check failed. Lot has been rejected and marked as unavailable.");
case 'unavailable':
return "This order is insufficient, please pick another lot.";
return t("This order is insufficient, please pick another lot.");
default:
return "Please finish QR code scan, QC check and pick order.";
return t("Please finish QR code scan, QC check and pick order.");
}
}, []);

@@ -399,21 +419,34 @@ const LotTable: React.FC<LotTableProps> = ({
}, []);

// ✅ Handle QR code submission
const handleQrCodeSubmit = useCallback((lotNo: string) => {
const handleQrCodeSubmit = useCallback(async (lotNo: string) => {
if (selectedLotForQr && selectedLotForQr.lotNo === lotNo) {
console.log(`✅ QR Code verified for lot: ${lotNo}`);
// ✅ Create stock out line
onCreateStockOutLine(selectedLotForQr.lotId);
// ✅ Store the required quantity before creating stock out line
const requiredQty = selectedLotForQr.requiredQty;
const lotId = selectedLotForQr.lotId;
// ✅ Show success message
console.log("Stock out line created successfully!");
// ✅ Create stock out line and wait for it to complete
await onCreateStockOutLine(selectedLotForQr.lotId);
// ✅ Close modal
setQrModalOpen(false);
setSelectedLotForQr(null);
// ✅ Set pick quantity AFTER stock out line creation and refresh is complete
if (selectedRowId) {
// Add a small delay to ensure the data refresh from onCreateStockOutLine is complete
setTimeout(() => {
onPickQtyChange(selectedRowId, lotId, requiredQty);
console.log(`✅ Auto-set pick quantity to ${requiredQty} for lot ${lotNo}`);
}, 500); // 500ms delay to ensure refresh is complete
}
// ✅ Show success message
console.log("Stock out line created successfully!");
}
}, [selectedLotForQr, onCreateStockOutLine]);
}, [selectedLotForQr, onCreateStockOutLine, selectedRowId, onPickQtyChange]);

return (
<>
@@ -520,6 +553,7 @@ const LotTable: React.FC<LotTableProps> = ({
</TableCell>
{/* QC Check Button */}
{/*
<TableCell align="center">
<Button
variant="outlined"
@@ -541,7 +575,8 @@ const LotTable: React.FC<LotTableProps> = ({
>
{t("QC")}
</Button>
</TableCell>
*/}
{/* Lot Actual Pick Qty */}
<TableCell align="right">
@@ -585,7 +620,42 @@ const LotTable: React.FC<LotTableProps> = ({
placeholder="0" // Show placeholder instead of default value
/>
</TableCell>
<TableCell align="center">
<Button
variant="outlined"
size="small"
onClick={async () => {
if (selectedRowId && selectedRow && lot.stockOutLineId) {
try {
// ✅ Call updateStockOutLineStatus to reject the stock out line
await updateStockOutLineStatus({
id: lot.stockOutLineId,
status: 'rejected',
qty: 0
});
// ✅ Refresh data after rejection
if (onDataRefresh) {
await onDataRefresh();
}
} catch (error) {
console.error("Error rejecting lot:", error);
}
}
}}
// ✅ Only enable if stock out line exists
disabled={!lot.stockOutLineId}
sx={{
fontSize: '0.7rem',
py: 0.5,
minHeight: '28px',
whiteSpace: 'nowrap',
minWidth: '40px'
}}
>
{t("Reject")}
</Button>
</TableCell>
{/* Submit Button */}
<TableCell align="center">
<Button
@@ -600,7 +670,7 @@ const LotTable: React.FC<LotTableProps> = ({
(lot.lotAvailability === 'expired' || lot.lotAvailability === 'status_unavailable') ||
!pickQtyData[selectedRowId!]?.[lot.lotId] ||
!lot.stockOutLineStatus || // Must have stock out line
!['checked', 'partially_completed'].includes(lot.stockOutLineStatus.toLowerCase()) // Only these statuses
!['pending','checked', 'partially_completed'].includes(lot.stockOutLineStatus.toLowerCase()) // Only these statuses
}
// ✅ Allow submission for available AND insufficient_stock lots
sx={{


+ 10
- 2
src/components/PickOrderSearch/PickExecution.tsx Vedi File

@@ -515,6 +515,13 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {

// ✅ Fix lot selection logic
const handleLotSelection = useCallback((uniqueLotId: string, lotId: number) => {
console.log("=== DEBUG: Lot Selection ===");
console.log("uniqueLotId:", uniqueLotId);
console.log("lotId (inventory lot line ID):", lotId);
// Find the selected lot data
const selectedLot = lotData.find(lot => lot.lotId === lotId);
console.log("Selected lot data:", selectedLot);
// If clicking the same lot, unselect it
if (selectedLotRowId === uniqueLotId) {
setSelectedLotRowId(null);
@@ -1022,7 +1029,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
{selectedRow && (
<Box>
<Typography variant="h6" gutterBottom>
Item lot to be Pick: {selectedRow.pickOrderCode} - {selectedRow.itemName}
{t("Item lot to be Pick:")} {selectedRow.pickOrderCode} - {selectedRow.itemName}
</Typography>
{/* 检查是否有可用的批次数据 */}
@@ -1039,6 +1046,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
onSubmitPickQty={handleSubmitPickQty}
onCreateStockOutLine={handleCreateStockOutLine}
onQcCheck={handleQcCheck}
onDataRefresh={handleFetchAllPickOrderDetails}
onLotSelectForInput={handleLotSelectForInput}
showInputBody={showInputBody}
setShowInputBody={setShowInputBody}
@@ -1079,7 +1087,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
onClick={() => handleInsufficientStock()}
sx={{ whiteSpace: 'nowrap' }}
>
{t("Insufficient Stock & Pick Another Lot")}
{t("Pick Another Lot")}
</Button>
</Stack>
</Box>


+ 3
- 1
src/components/PickOrderSearch/PickOrderSearch.tsx Vedi File

@@ -228,7 +228,7 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
// 添加处理提料单创建成功的函数
const handlePickOrderCreated = useCallback(() => {
// 切换到 Assign & Release 标签页 (tabIndex = 1)
setTabIndex(1);
setTabIndex(2);
}, []);

return (
@@ -248,6 +248,7 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
{t("Pick Order")}
</Typography>
</Grid>
{/*
<Grid item xs={4} display="flex" justifyContent="end" alignItems="end">
<Button onClick={openCreateModal}>
{t("create")}
@@ -260,6 +261,7 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
/>
}
</Grid>
*/}
</Grid>
</Stack>
</Box>


+ 37
- 25
src/components/PickOrderSearch/PickQcStockInModalVer3.tsx Vedi File

@@ -322,21 +322,41 @@ const PickQcStockInModalVer3: React.FC<Props> = ({
// ✅ QC Decision 2: Report and Re-pick
console.log("QC Decision 2 - Report and Re-pick: Rejecting lot and marking as unavailable");
// ✅ Stock out line status: rejected
await updateStockOutLineStatus({
id: selectedLotId,
status: 'rejected',
qty: acceptQty || 0
});
// ✅ Inventory lot line status: unavailable
if (selectedLot) {
await updateInventoryLotLineStatus({
inventoryLotLineId: selectedLot.lotId,
status: 'unavailable',
qty: acceptQty || 0,
operation: 'reject'
});
try {
console.log("=== DEBUG: Updating inventory lot line status ===");
console.log("Selected lot:", selectedLot);
console.log("Selected lot ID:", selectedLotId);
// ✅ FIX: Only send the fields that the backend expects
const updateData = {
inventoryLotLineId: selectedLot.lotId,
status: 'unavailable'
// ❌ Remove qty and operation - backend doesn't expect these
};
console.log("Update data:", updateData);
const result = await updateInventoryLotLineStatus(updateData);
console.log("✅ Inventory lot line status updated successfully:", result);
} catch (error) {
console.error("❌ Error updating inventory lot line status:", error);
console.error("Error details:", {
selectedLot,
selectedLotId,
acceptQty
});
// Show user-friendly error message

return; // Stop execution if this fails
}
} else {
console.error("❌ Selected lot not found for inventory update");
alert("Selected lot not found. Cannot update inventory status.");
return;
}
// ✅ Close modal and refresh data
@@ -612,16 +632,16 @@ const PickQcStockInModalVer3: React.FC<Props> = ({
<FormControlLabel
value="1"
control={<Radio />}
label="接受出庫"
label={t("Accept Stock Out")}
/>

{/* ✅ Combine options 2 & 3 into one */}
{/* ✅ Combirne options 2 & 3 into one */}
<FormControlLabel
value="2"
control={<Radio />}
sx={{"& .Mui-checked": {color: "blue"}}}
label="上報品檢結果並重新揀貨"
label={t("Report and Pick another lot")}
/>
</RadioGroup>
)}
@@ -630,15 +650,7 @@ const PickQcStockInModalVer3: React.FC<Props> = ({
</Grid>

{/* ✅ Show escalation component when QC Decision = 2 (Report and Re-pick) */}
{qcDecision == 2 && (
<Grid item xs={12}>
<EscalationComponent
forSupervisor={false}
isCollapsed={isCollapsed}
setIsCollapsed={setIsCollapsed}
/>
</Grid>
)}

<Grid item xs={12} sx={{ mt: 2 }}>
<Stack direction="row" justifyContent="flex-start" gap={1}>


+ 4
- 3
src/components/PickOrderSearch/newcreatitem.tsx Vedi File

@@ -1976,8 +1976,8 @@ const CustomSearchResultsTable = () => {
)}

{/* Add Submit Button between tables */}
{/* Search Results with SearchResults component */}
{/*
{hasSearchedSecond && secondSearchResults.length > 0 && selectedSecondSearchItemIds.length > 0 && (
<Box sx={{ mt: 2, mb: 2 }}>
<Box sx={{ display: 'flex', justifyContent: 'left', alignItems: 'center', gap: 2 }}>
@@ -2001,9 +2001,10 @@ const CustomSearchResultsTable = () => {
</Typography>
)}
</Box>
</Box>
)}
*/}

{/* 创建项目区域 - 修改Group列为可选择的 */}
{createdItems.length > 0 && (


+ 12
- 2
src/i18n/zh/pickOrder.json Vedi File

@@ -172,6 +172,16 @@
"Job Order Code": "工單編號",
"QC Check": "QC 檢查",
"QR Code Scan": "QR Code掃描",
"Pick Order Details": "提料單詳情"

"Pick Order Details": "提料單詳情",
"Partial quantity submitted. Please submit more or complete the order.": "已提料部分數量。請提交更多或完成訂單。",
"Pick order completed successfully!": "提料單完成成功!",
"QC check failed. Lot has been rejected and marked as unavailable.": "QC 檢查失敗。批號已拒絕並標記為不可用。",
"This order is insufficient, please pick another lot.": "此訂單不足,請選擇其他批號。",
"Please finish QR code scan, QC check and pick order.": "請完成 QR 碼掃描、QC 檢查和提料。",
"No data available": "沒有資料",
"Please submit the pick order.": "請提交提料單。",
"Item lot to be Pick:": "批次貨品提料:",
"Report and Pick another lot": "上報並需重新選擇批號",
"Accept Stock Out": "接受出庫",
"Pick Another Lot": "重新選擇批號"
}

Caricamento…
Annulla
Salva