Browse Source

update

master
CANCERYS\kw093 1 month ago
parent
commit
38d8fe4c6a
3 changed files with 133 additions and 32 deletions
  1. +10
    -0
      src/app/api/pickOrder/actions.ts
  2. +121
    -31
      src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx
  3. +2
    -1
      src/components/QrCodeScannerProvider/TestQrCodeProvider.tsx

+ 10
- 0
src/app/api/pickOrder/actions.ts View File

@@ -245,6 +245,16 @@ export interface PickOrderCompletionResponse {
export interface UpdateSuggestedLotLineIdRequest {
newLotLineId: number;
}
export interface stockReponse{
id: number;
status: string;
qty: number;
lotId: number;
lotNo: string;
location: string;
availableQty: number;
noLot: boolean;
}
export interface FGPickOrderResponse {
// ✅ 新增:支持多个 pick orders
doPickOrderId: number;


+ 121
- 31
src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx View File

@@ -33,7 +33,7 @@ import {
recordPickExecutionIssue,
fetchFGPickOrders, // ✅ Add this import
FGPickOrderResponse,
stockReponse,

checkPickOrderCompletion,
fetchAllPickOrderLotsHierarchical,
@@ -369,6 +369,7 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false);
const [pickExecutionFormOpen, setPickExecutionFormOpen] = useState(false);
const [selectedLotForExecutionForm, setSelectedLotForExecutionForm] = useState<any | null>(null);
const [fgPickOrders, setFgPickOrders] = useState<FGPickOrderResponse[]>([]);

const [fgPickOrdersLoading, setFgPickOrdersLoading] = useState(false);
// ✅ Add these missing state variables after line 352
const [isManualScanning, setIsManualScanning] = useState<boolean>(false);
@@ -546,12 +547,17 @@ console.log("🔍 DEBUG fgOrder.deliveryNos:", fgOrder.deliveryNos);
routerIndex: lot.router?.index,
routerRoute: lot.router?.route,
routerArea: lot.router?.area,
noLot: false,
});
});
} else {
// ✅ 没有 lots 的情况(null stock)
// ✅ 没有 lots 的情况(null stock)- 从 stockouts 数组中获取 id
const firstStockout = line.stockouts && line.stockouts.length > 0
? line.stockouts[0]
: null;
flatLotData.push({
pickOrderConsoCode: mergedPickOrder.consoCode,
pickOrderConsoCode: mergedPickOrder.consoCodes?.[0] || "", // ✅ 修复:consoCodes 是数组
pickOrderTargetDate: mergedPickOrder.targetDate,
pickOrderStatus: mergedPickOrder.status,
@@ -565,30 +571,31 @@ console.log("🔍 DEBUG fgOrder.deliveryNos:", fgOrder.deliveryNos);
uomDesc: line.item.uomDesc,
uomShortDesc: line.item.uomShortDesc,
// ✅ Null stock 字段
lotId: null,
lotNo: null,
// ✅ Null stock 字段 - 从 stockouts 数组中获取
lotId: firstStockout?.lotId || null,
lotNo: firstStockout?.lotNo || null,
expiryDate: null,
location: null,
location: firstStockout?.location || null,
stockUnit: line.item.uomDesc,
availableQty: 0,
availableQty: firstStockout?.availableQty || 0,
requiredQty: line.requiredQty,
actualPickQty: 0,
actualPickQty: firstStockout?.qty || 0,
inQty: 0,
outQty: 0,
holdQty: 0,
lotStatus: 'unavailable',
lotAvailability: 'insufficient_stock',
processingStatus: 'pending',
processingStatus: firstStockout?.status || 'pending',
suggestedPickLotId: null,
stockOutLineId: null,
stockOutLineStatus: null,
stockOutLineQty: 0,
stockOutLineId: firstStockout?.id || null, // ✅ 使用 stockouts 数组中的 id
stockOutLineStatus: firstStockout?.status || null,
stockOutLineQty: firstStockout?.qty || 0,
routerId: null,
routerIndex: 999999,
routerRoute: null,
routerArea: null,
noLot: true,
});
}
});
@@ -684,14 +691,22 @@ console.log("🔍 DEBUG fgOrder.deliveryNos:", fgOrder.deliveryNos);
const handleQrCodeSubmit = useCallback(async (lotNo: string) => {
console.log(`✅ Processing QR Code for lot: ${lotNo}`);
// ✅ 检查 lotNo 是否为 null 或 undefined(包括字符串 "null")
if (!lotNo || lotNo === 'null' || lotNo.trim() === '') {
console.error("❌ Invalid lotNo: null, undefined, or empty");
return;
}
// ✅ Use current data without refreshing to avoid infinite loop
const currentLotData = combinedLotData;
console.log(` Available lots:`, currentLotData.map(lot => lot.lotNo));
const matchingLots = currentLotData.filter(lot =>
lot.lotNo === lotNo ||
lot.lotNo?.toLowerCase() === lotNo.toLowerCase()
);
// ✅ 修复:在比较前确保 lotNo 不为 null
const lotNoLower = lotNo.toLowerCase();
const matchingLots = currentLotData.filter(lot => {
if (!lot.lotNo) return false; // ✅ 跳过 null lotNo
return lot.lotNo === lotNo || lot.lotNo.toLowerCase() === lotNoLower;
});
if (matchingLots.length === 0) {
console.error(`❌ Lot not found: ${lotNo}`);
@@ -1451,10 +1466,34 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe
stopScan();
resetScan();
}, [stopScan, resetScan]);
const handlelotnull = useCallback(async (lot: any) => {
// ✅ 优先使用 stockouts 中的 id,如果没有则使用 stockOutLineId
const stockOutLineId = lot.stockOutLineId;
if (!stockOutLineId) {
console.error("❌ No stockOutLineId found for lot:", lot);
return;
}
await updateStockOutLineStatus({
id: stockOutLineId, // ✅ 现在这个值应该来自 stockouts 数组的 id
status: 'completed',
qty: 0
});
await fetchAllCombinedLotData();
}, [fetchAllCombinedLotData]);
const handleSubmitAllScanned = useCallback(async () => {
const scannedLots = combinedLotData.filter(lot =>
lot.stockOutLineStatus === 'checked' // Only submit items that are scanned but not yet submitted
);
const scannedLots = combinedLotData.filter(lot => {
// ✅ 如果是 noLot 情况,检查状态是否为 pending 或 partially_complete
if (lot.noLot === true) {
return lot.stockOutLineStatus === 'checked' ||
lot.stockOutLineStatus === 'pending' ||
lot.stockOutLineStatus === 'partially_completed' ||
lot.stockOutLineStatus === 'PARTIALLY_COMPLETE';
}
// ✅ 正常情况:只包含 checked 状态
return lot.stockOutLineStatus === 'checked';
});
if (scannedLots.length === 0) {
console.log("No scanned items to submit");
@@ -1467,6 +1506,20 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe
try {
// ✅ Submit all items in parallel using Promise.all
const submitPromises = scannedLots.map(async (lot) => {
// ✅ 检查是否是 noLot 情况
if (lot.noLot === true) {
// ✅ 使用 handlelotnull 处理无 lot 的情况
console.log(`Submitting no-lot item: ${lot.itemName || lot.itemCode}`);
await updateStockOutLineStatus({
id: lot.stockOutLineId,
status: 'completed',
qty: 0
});
console.log(`✅ No-lot item completed: ${lot.itemName || lot.itemCode}`);
return { success: true, lotNo: lot.lotNo || 'No Lot', isNoLot: true };
}
// ✅ 正常情况:有 lot 的处理逻辑
const submitQty = lot.requiredQty || lot.pickOrderLineRequiredQty;
const currentActualPickQty = lot.actualPickQty || 0;
const cumulativeQty = currentActualPickQty + submitQty;
@@ -1486,7 +1539,7 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe
});
// Update inventory
if (submitQty > 0) {
if (submitQty > 0 && lot.lotId) {
await updateInventoryLotLineQuantities({
inventoryLotLineId: lot.lotId,
qty: submitQty,
@@ -1526,12 +1579,37 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe
} finally {
setIsSubmittingAll(false);
}
}, [combinedLotData, fetchAllCombinedLotData, checkAndAutoAssignNext]);
}, [combinedLotData, fetchAllCombinedLotData, checkAndAutoAssignNext, handlelotnull]);

// ✅ Calculate scanned items count
const scannedItemsCount = useMemo(() => {
return combinedLotData.filter(lot => lot.stockOutLineStatus === 'checked').length;
}, [combinedLotData]);
// ✅ Calculate scanned items count (should match handleSubmitAllScanned filter logic)
const scannedItemsCount = useMemo(() => {
const filtered = combinedLotData.filter(lot => {
// ✅ 如果是 noLot 情况,只要状态不是 completed 或 rejected,就包含
if (lot.noLot === true) {
const status = lot.stockOutLineStatus?.toLowerCase();
const include = status !== 'completed' && status !== 'rejected';
if (include) {
console.log(`📊 Including noLot item: ${lot.itemName || lot.itemCode}, status: ${lot.stockOutLineStatus}`);
}
return include;
}
// ✅ 正常情况:只包含 checked 状态
return lot.stockOutLineStatus === 'checked';
});
// ✅ 添加调试日志
const noLotCount = filtered.filter(l => l.noLot === true).length;
const normalCount = filtered.filter(l => l.noLot !== true).length;
console.log(`📊 scannedItemsCount calculation: total=${filtered.length}, noLot=${noLotCount}, normal=${normalCount}`);
console.log(`📊 All items breakdown:`, {
total: combinedLotData.length,
noLot: combinedLotData.filter(l => l.noLot === true).length,
normal: combinedLotData.filter(l => l.noLot !== true).length
});
return filtered.length;
}, [combinedLotData]);

// ✅ ADD THIS: Auto-stop scan when no data available
useEffect(() => {
@@ -1782,7 +1860,8 @@ paginatedData.map((lot, index) => {
// color: isIssueLot ? 'warning.main' : lot.lotAvailability === 'rejected' ? 'text.disabled' : 'inherit',
}}
>
{lot.lotNo || t('⚠️ No Stock Available')}
{lot.lotNo ||
t('⚠️ No Stock Available')}
</Typography>
</Box>
</TableCell>
@@ -1809,8 +1888,20 @@ paginatedData.map((lot, index) => {
}}
/>
</Box>
) : isIssueLot ? (
null
) : isIssueLot&&lot.stockOutLineStatus?.toLowerCase() == 'partially_completed' ? (
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<Checkbox
checked={true}
disabled={true}
readOnly={true}
size="large"
sx={{
color: 'error.main',
'&.Mui-checked': { color: 'error.main' },
transform: 'scale(1.3)',
}}
/>
</Box>
) : null}
</TableCell>

@@ -1821,8 +1912,7 @@ paginatedData.map((lot, index) => {
<Button
variant="outlined"
size="small"
onClick={() => handlePickExecutionForm(lot)}
disabled={true}
onClick={() => handlelotnull(lot)}
sx={{
fontSize: '0.7rem',
py: 0.5,
@@ -1831,7 +1921,7 @@ paginatedData.map((lot, index) => {
borderColor: 'warning.main',
color: 'warning.main'
}}
title="Rejected lot - Issue only"
>
{t("Issue")}
</Button>


+ 2
- 1
src/components/QrCodeScannerProvider/TestQrCodeProvider.tsx View File

@@ -51,7 +51,8 @@ const TestQrCodeProvider: React.FC<TestQrCodeProviderProps> = ({
lot.stockOutLineStatus !== 'rejected' &&
lot.stockOutLineStatus !== 'completed' &&
lot.processingStatus !== 'completed' &&
lot.matchStatus !== 'completed'
lot.matchStatus !== 'completed' &&
lot.noLot !== true
);
}, []);



Loading…
Cancel
Save