Browse Source

update

master
CANCERYS\kw093 1 month ago
parent
commit
aba6774079
4 changed files with 178 additions and 186 deletions
  1. +22
    -7
      src/app/api/pickOrder/actions.ts
  2. +1
    -1
      src/components/FinishedGoodSearch/FinishedGoodFloorLanePanel.tsx
  3. +154
    -84
      src/components/FinishedGoodSearch/GoodPickExecutionRecord.tsx
  4. +1
    -94
      src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx

+ 22
- 7
src/app/api/pickOrder/actions.ts View File

@@ -320,12 +320,17 @@ export interface UpdateDoPickOrderHideStatusRequest {
}
export interface CompletedDoPickOrderResponse {
id: number;
doPickOrderRecordId: number; // ✅ 新增
pickOrderId: number;
pickOrderIds: number[]; // ✅ 新增:所有 pick order IDs
pickOrderCode: string;
pickOrderCodes: string; // ✅ 新增:所有 pick order codes (逗号分隔)
pickOrderConsoCode: string;
pickOrderStatus: string;
deliveryOrderId: number;
deliveryOrderIds: number[]; // ✅ 新增:所有 delivery order IDs
deliveryNo: string;
deliveryNos: string; // ✅ 新增:所有 delivery order codes (逗号分隔)
deliveryDate: string;
shopId: number;
shopCode: string;
@@ -335,6 +340,7 @@ export interface CompletedDoPickOrderResponse {
shopPoNo: string;
numberOfCartons: number;
truckLanceCode: string;
DepartureTime: string; // ✅ 新增
storeId: string;
completedDate: string;
fgPickOrders: FGPickOrderResponse[];
@@ -911,23 +917,32 @@ export const fetchAllPickOrderLotsHierarchical = cache(async (userId: number): P
};
}
});
export const fetchLotDetailsByPickOrderId = async (pickOrderId: number): Promise<any[]> => {
export const fetchLotDetailsByDoPickOrderRecordId = async (doPickOrderRecordId: number): Promise<{
fgInfo: any;
pickOrders: any[];
}> => {
try {
console.log("🔍 Fetching lot details for pickOrderId:", pickOrderId);
console.log("🔍 Fetching lot details for doPickOrderRecordId:", doPickOrderRecordId);
const data = await serverFetchJson<any[]>(
`${BASE_API_URL}/pickOrder/lot-details-by-pick-order/${pickOrderId}`,
const data = await serverFetchJson<{
fgInfo: any;
pickOrders: any[];
}>(
`${BASE_API_URL}/pickOrder/lot-details-by-do-pick-order-record/${doPickOrderRecordId}`,
{
method: 'GET',
next: { tags: ["pickorder"] },
}
);
console.log("✅ Fetched lot details for pickOrderId:", data);
console.log("✅ Fetched hierarchical lot details:", data);
return data;
} catch (error) {
console.error("❌ Error fetching lot details for pickOrderId:", error);
return [];
console.error("❌ Error fetching lot details:", error);
return {
fgInfo: null,
pickOrders: []
};
}
};
// Update the existing function to use the non-auto-assign endpoint


+ 1
- 1
src/components/FinishedGoodSearch/FinishedGoodFloorLanePanel.tsx View File

@@ -23,7 +23,7 @@ const FinishedGoodFloorLanePanel: React.FC<Props> = ({ onPickOrderAssigned }) =>
const [isLoadingSummary, setIsLoadingSummary] = useState(false);
const [isAssigning, setIsAssigning] = useState(false);
//const [selectedDate, setSelectedDate] = useState<string>("today");
const [selectedDate, setSelectedDate] = useState<string>("2025-09-27");
const [selectedDate, setSelectedDate] = useState<string>("today");

const loadData = async (dateValue: string) => {
setIsLoadingSummary(true);


+ 154
- 84
src/components/FinishedGoodSearch/GoodPickExecutionRecord.tsx View File

@@ -44,7 +44,7 @@ import {
fetchCompletedDoPickOrders,
CompletedDoPickOrderResponse,
CompletedDoPickOrderSearchParams,
fetchLotDetailsByPickOrderId
fetchLotDetailsByDoPickOrderRecordId
} from "@/app/api/pickOrder/actions";
import { fetchNameList, NameList } from "@/app/api/user/actions";
import {
@@ -407,30 +407,54 @@ const GoodPickExecutionRecord: React.FC<Props> = ({ filterArgs }) => {
setSelectedDoPickOrder(doPickOrder);
setShowDetailView(true);
// ✅ 修复:使用新的 API 根据 pickOrderId 获取 lot 详情
try {
const lotDetails = await fetchLotDetailsByPickOrderId(doPickOrder.pickOrderId);
setDetailLotData(lotDetails);
console.log("✅ Loaded detail lot data for pick order:", doPickOrder.pickOrderCode, lotDetails);
// ✅ 使用 doPickOrderRecordId 而不是 pickOrderId
const hierarchicalData = await fetchLotDetailsByDoPickOrderRecordId(doPickOrder.doPickOrderRecordId);
console.log("✅ Loaded hierarchical lot data:", hierarchicalData);
// ✅ 触发打印按钮状态更新 - 基于详情数据
const allCompleted = lotDetails.length > 0 && lotDetails.every(lot =>
// ✅ 转换为平铺格式
const flatLotData: any[] = [];
if (hierarchicalData.pickOrders && hierarchicalData.pickOrders.length > 0) {
hierarchicalData.pickOrders.forEach((po: any) => {
po.pickOrderLines?.forEach((line: any) => {
if (line.lots && line.lots.length > 0) {
line.lots.forEach((lot: any) => {
flatLotData.push({
pickOrderCode: po.pickOrderCode,
itemCode: line.item.code,
itemName: line.item.name,
lotNo: lot.lotNo,
location: lot.location,
requiredQty: lot.requiredQty,
actualPickQty: lot.actualPickQty,
processingStatus: lot.processingStatus,
stockOutLineStatus: lot.stockOutLineStatus
});
});
}
});
});
}
setDetailLotData(flatLotData);
// ✅ 计算完成状态
const allCompleted = flatLotData.length > 0 && flatLotData.every(lot =>
lot.processingStatus === 'completed'
);
// ✅ 发送事件,包含标签页信息
window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', {
detail: {
allLotsCompleted: allCompleted,
tabIndex: 2 // ✅ 明确指定这是来自标签页 2 的事件
tabIndex: 2
}
}));
} catch (error) {
} catch (error) { // ✅ 添加 catch 块
console.error("❌ Error loading detail lot data:", error);
setDetailLotData([]);
// ✅ 如果加载失败,禁用打印按钮
window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', {
detail: {
allLotsCompleted: false,
@@ -458,86 +482,132 @@ const GoodPickExecutionRecord: React.FC<Props> = ({ filterArgs }) => {


// ✅ 如果显示详情视图,渲染类似 GoodPickExecution 的表格
if (showDetailView && selectedDoPickOrder) {
return (
<FormProvider {...formProps}>
<Box>
{/* 返回按钮和标题 */}
<Box sx={{ mb: 2, display: 'flex', alignItems: 'center', gap: 2 }}>
<Button variant="outlined" onClick={handleBackToList}>
{t("Back to List")}
</Button>
<Typography variant="h6">
{t("Pick Order Details")}: {selectedDoPickOrder.pickOrderCode}
</Typography>
</Box>
// ✅ 如果显示详情视图,渲染层级结构
if (showDetailView && selectedDoPickOrder) {
return (
<FormProvider {...formProps}>
<Box>
{/* 返回按钮和标题 */}
<Box sx={{ mb: 2, display: 'flex', alignItems: 'center', gap: 2 }}>
<Button variant="outlined" onClick={handleBackToList}>
{t("Back to List")}
</Button>
<Typography variant="h6">
{t("Pick Order Details")}: {selectedDoPickOrder.ticketNo}
</Typography>
</Box>

{/* 订单基本信息 */}
<Box sx={{ mb: 2, p: 2, backgroundColor: '#f5f5f5', borderRadius: 1 }}>
<Typography variant="h6" gutterBottom>
{t("Order Information")}
</Typography>
<Typography variant="body2">
{/* FG 订单基本信息 */}
<Paper sx={{ mb: 2, p: 2 }}>
<Stack spacing={1}>
<Typography variant="subtitle1">
<strong>{t("Shop Name")}:</strong> {selectedDoPickOrder.shopName}
</Typography>
<Typography variant="body2">
<strong>{t("Delivery No")}:</strong> {selectedDoPickOrder.deliveryNo}
<Typography variant="subtitle1">
<strong>{t("Store ID")}:</strong> {selectedDoPickOrder.storeId}
</Typography>
<Typography variant="body2">
<Typography variant="subtitle1">
<strong>{t("Ticket No.")}:</strong> {selectedDoPickOrder.ticketNo}
</Typography>
<Typography variant="subtitle1">
<strong>{t("Truck Lance Code")}:</strong> {selectedDoPickOrder.truckLanceCode}
</Typography>
<Typography variant="subtitle1">
<strong>{t("Completed Date")}:</strong> {dayjs(selectedDoPickOrder.completedDate).format(OUTPUT_DATE_FORMAT)}
</Typography>
</Box>

{/* ✅ 添加数据检查 */}
{detailLotData.length === 0 ? (
<Box sx={{ p: 3, textAlign: 'center' }}>
<Typography variant="body2" color="text.secondary">
{t("No lot details found for this order")}
</Typography>
</Stack>
</Paper>

{/* ✅ 添加:多个 Pick Orders 信息(如果有) */}
{selectedDoPickOrder.pickOrderIds && selectedDoPickOrder.pickOrderIds.length > 1 && (
<Paper sx={{ mb: 2, p: 2, backgroundColor: '#f5f5f5' }}>
<Typography variant="subtitle2" sx={{ mb: 1, fontWeight: 'bold' }}>
{t("This ticket contains")} {selectedDoPickOrder.pickOrderIds.length} {t("pick orders")}:
</Typography>
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
{selectedDoPickOrder.pickOrderCodes?.split(', ').map((code, idx) => (
<Chip
key={idx}
label={code}
size="small"
variant="outlined"
/>
))}
</Box>
) : (
/* 显示完成数据的表格 */
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>{t("Pick Order Code")}</TableCell>
<TableCell>{t("Item Code")}</TableCell>
<TableCell>{t("Item Name")}</TableCell>
<TableCell>{t("Lot No")}</TableCell>
<TableCell>{t("Location")}</TableCell>
<TableCell>{t("Required Qty")}</TableCell>
<TableCell>{t("Actual Pick Qty")}</TableCell>
<TableCell>{t("Submitted Status")}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{detailLotData.map((lot, index) => (
<TableRow key={index}>
<TableCell>{lot.pickOrderCode || 'N/A'}</TableCell>
<TableCell>{lot.itemCode || 'N/A'}</TableCell>
<TableCell>{lot.itemName || 'N/A'}</TableCell>
<TableCell>{lot.lotNo || 'N/A'}</TableCell>
<TableCell>{lot.location || 'N/A'}</TableCell>
<TableCell>{lot.requiredQty || 0}</TableCell>
<TableCell>{lot.actualPickQty || 0}</TableCell>
<TableCell>
<Chip
label={t(lot.processingStatus || 'unknown')}
color={lot.processingStatus === 'completed' ? 'success' : 'default'}
size="small"
/>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)}
</Box>
</FormProvider>
);
}
</Paper>
)}

{/* ✅ 数据检查 */}
{detailLotData.length === 0 ? (
<Box sx={{ p: 3, textAlign: 'center' }}>
<Typography variant="body2" color="text.secondary">
{t("No lot details found for this order")}
</Typography>
</Box>
) : (
/* ✅ 按 Pick Order 分组显示 */
<Stack spacing={2}>
{/* ✅ 按 pickOrderCode 分组 */}
{Object.entries(
detailLotData.reduce((acc: any, lot: any) => {
const key = lot.pickOrderCode || 'Unknown';
if (!acc[key]) acc[key] = [];
acc[key].push(lot);
return acc;
}, {})
).map(([pickOrderCode, lots]: [string, any]) => (
<Accordion key={pickOrderCode} defaultExpanded={true}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography variant="subtitle1" fontWeight="bold">
{t("Pick Order")}: {pickOrderCode} ({(lots as any[]).length} {t("items")})
</Typography>
</AccordionSummary>
<AccordionDetails>
<TableContainer component={Paper}>
<Table size="small">
<TableHead>
<TableRow>
<TableCell>{t("Index")}</TableCell>
<TableCell>{t("Item Code")}</TableCell>
<TableCell>{t("Item Name")}</TableCell>
<TableCell>{t("Lot No")}</TableCell>
<TableCell>{t("Location")}</TableCell>
<TableCell align="right">{t("Required Qty")}</TableCell>
<TableCell align="right">{t("Actual Pick Qty")}</TableCell>
<TableCell align="center">{t("Status")}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{(lots as any[]).map((lot: any, index: number) => (
<TableRow key={index}>
<TableCell>{index + 1}</TableCell>
<TableCell>{lot.itemCode || 'N/A'}</TableCell>
<TableCell>{lot.itemName || 'N/A'}</TableCell>
<TableCell>{lot.lotNo || 'N/A'}</TableCell>
<TableCell>{lot.location || 'N/A'}</TableCell>
<TableCell align="right">{lot.requiredQty || 0}</TableCell>
<TableCell align="right">{lot.actualPickQty || 0}</TableCell>
<TableCell align="center">
<Chip
label={t(lot.processingStatus || 'unknown')}
color={lot.processingStatus === 'completed' ? 'success' : 'default'}
size="small"
/>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</AccordionDetails>
</Accordion>
))}
</Stack>
)}
</Box>
</FormProvider>
);
}

// ✅ 默认列表视图
return (


+ 1
- 94
src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx View File

@@ -663,100 +663,7 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false);
});
}
});
targetPickOrder.pickOrderLines.forEach((line: any) => {
if (line.lots && line.lots.length > 0) {
// ✅ 有 lots 的情况
line.lots.forEach((lot: any) => {
flatLotData.push({
pickOrderId: targetPickOrder.pickOrderId,
pickOrderCode: targetPickOrder.pickOrderCode,
pickOrderConsoCode: targetPickOrder.consoCode,
pickOrderTargetDate: targetPickOrder.targetDate,
pickOrderStatus: targetPickOrder.status,
pickOrderLineId: line.id,
pickOrderLineRequiredQty: line.requiredQty,
pickOrderLineStatus: line.status,
itemId: line.item.id,
itemCode: line.item.code,
itemName: line.item.name,
//uomCode: line.item.uomCode,
uomDesc: line.item.uomDesc,
uomShortDesc: line.item.uomShortDesc,
lotId: lot.id,
lotNo: lot.lotNo,
expiryDate: lot.expiryDate,
location: lot.location,
stockUnit: lot.stockUnit,
availableQty: lot.availableQty,
requiredQty: lot.requiredQty,
actualPickQty: lot.actualPickQty,
inQty: lot.inQty,
outQty: lot.outQty,
holdQty: lot.holdQty,
lotStatus: lot.lotStatus,
lotAvailability: lot.lotAvailability,
processingStatus: lot.processingStatus,
suggestedPickLotId: lot.suggestedPickLotId,
stockOutLineId: lot.stockOutLineId,
stockOutLineStatus: lot.stockOutLineStatus,
stockOutLineQty: lot.stockOutLineQty,
routerId: lot.router?.id,
routerIndex: lot.router?.index,
routerRoute: lot.router?.route,
routerArea: lot.router?.area,
});
});
} else {
// ✅ 没有 lots 的情况(null stock)- 也要显示
flatLotData.push({
pickOrderId: targetPickOrder.pickOrderId,
pickOrderCode: targetPickOrder.pickOrderCode,
pickOrderConsoCode: targetPickOrder.consoCode,
pickOrderTargetDate: targetPickOrder.targetDate,
pickOrderStatus: targetPickOrder.status,
pickOrderLineId: line.id,
pickOrderLineRequiredQty: line.requiredQty,
pickOrderLineStatus: line.status,
itemId: line.item.id,
itemCode: line.item.code,
itemName: line.item.name,
//uomCode: line.item.uomCode,
uomDesc: line.item.uomDesc,
// ✅ Null stock 字段
lotId: null,
lotNo: null,
expiryDate: null,
location: null,
stockUnit: line.item.uomDesc,
availableQty: 0,
requiredQty: line.requiredQty,
actualPickQty: 0,
inQty: 0,
outQty: 0,
holdQty: 0,
lotStatus: 'unavailable',
lotAvailability: 'insufficient_stock',
processingStatus: 'pending',
suggestedPickLotId: null,
stockOutLineId: null,
stockOutLineStatus: null,
stockOutLineQty: 0,
routerId: null,
routerIndex: 999999, // ✅ 放到最后
routerRoute: null,
routerArea: null,
uomShortDesc: line.item.uomShortDesc
});
}
});

console.log("✅ Transformed flat lot data:", flatLotData);
console.log("🔍 Total items (including null stock):", flatLotData.length);


Loading…
Cancel
Save