Browse Source

update

master
CANCERYS\kw093 1 month ago
parent
commit
7ffe0fb889
3 changed files with 205 additions and 25 deletions
  1. +129
    -0
      src/app/api/jo/actions.ts
  2. +2
    -2
      src/app/api/pickOrder/actions.ts
  3. +74
    -23
      src/components/FinishedGoodSearch/GoodPickExecutionRecord.tsx

+ 129
- 0
src/app/api/jo/actions.ts View File

@@ -149,6 +149,135 @@ export const recordSecondScanIssue = cache(async (
},
);
});


export interface ProductProcessResponse {
id: number;
productProcessCode: string;
status: string;
startTime?: string;
endTime?: string;
date: string;
bomId?: number;
jobOrderId?: number;
}

export interface ProductProcessLineResponse {
id: number;
seqNo: number;
name: string;
description?: string;
equipmentType?: string;
startTime?: string;
endTime?: string;
outputFromProcessQty?: number;
outputFromProcessUom?: string;
defectQty?: number;
scrapQty?: number;
byproductName?: string;
byproductQty?: number;
handlerId?: number;
}

export interface ProductProcessWithLinesResponse {
id: number;
productProcessCode: string;
status: string;
startTime?: string;
endTime?: string;
date: string;
lines: ProductProcessLineResponse[];
}
export const startProductProcessLine = async (lineId: number, userId: number) => {
return serverFetchJson<ProductProcessLineResponse>(
`${BASE_API_URL}/product-process/lines/${lineId}/start?userId=${userId}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
}
);
};
// 查询所有 production processes
export const fetchProductProcesses = cache(async () => {
return serverFetchJson<{ content: ProductProcessResponse[] }>(
`${BASE_API_URL}/product-process`,
{
method: "GET",
next: { tags: ["productProcess"] },
}
);
});

// 根据 ID 查询
export const fetchProductProcessById = cache(async (id: number) => {
return serverFetchJson<ProductProcessResponse>(
`${BASE_API_URL}/product-process/${id}`,
{
method: "GET",
next: { tags: ["productProcess"] },
}
);
});

// 根据 Job Order ID 查询
export const fetchProductProcessesByJobOrderId = cache(async (jobOrderId: number) => {
return serverFetchJson<ProductProcessWithLinesResponse[]>(
`${BASE_API_URL}/product-process/by-job-order/${jobOrderId}`,
{
method: "GET",
next: { tags: ["productProcess"] },
}
);
});

// 获取 process 的所有 lines
export const fetchProductProcessLines = cache(async (processId: number) => {
return serverFetchJson<ProductProcessLineResponse[]>(
`${BASE_API_URL}/product-process/${processId}/lines`,
{
method: "GET",
next: { tags: ["productProcessLines"] },
}
);
});

// 创建 production process
export const createProductProcess = async (data: {
bomId: number;
jobOrderId?: number;
date?: string;
}) => {
return serverFetchJson<{ id: number; productProcessCode: string; linesCreated: number }>(
`${BASE_API_URL}/product-process`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
}
);
};

// 更新 line 产出数据
export const updateLineOutput = async (lineId: number, data: {
outputQty?: number;
outputUom?: string;
defectQty?: number;
defectUom?: string;
scrapQty?: number;
scrapUom?: string;
byproductName?: string;
byproductQty?: number;
byproductUom?: string;
}) => {
return serverFetchJson<ProductProcessLineResponse>(
`${BASE_API_URL}/product-process/lines/${lineId}/output`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
}
);
};
export const updateSecondQrScanStatus = cache(async (pickOrderId: number, itemId: number, userId: number, qty: number) => {
return serverFetchJson<any>(
`${BASE_API_URL}/jo/update-match-status`,


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

@@ -193,8 +193,8 @@ export interface PickExecutionIssueData {
itemId: number;
itemCode: string;
itemDescription: string;
lotId: number;
lotNo: string;
lotId: number|null;
lotNo: string|null;
storeLocation: string;
requiredQty: number;
actualPickQty: number;


+ 74
- 23
src/components/FinishedGoodSearch/GoodPickExecutionRecord.tsx View File

@@ -411,31 +411,82 @@ const GoodPickExecutionRecord: React.FC<Props> = ({ filterArgs }) => {
// ✅ 转换为平铺格式
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,
deliveryOrderCode: po.deliveryOrderCode,
requiredQty: lot.requiredQty,
actualPickQty: lot.actualPickQty,
processingStatus: lot.processingStatus,
stockOutLineStatus: lot.stockOutLineStatus
});

if (hierarchicalData.pickOrders && hierarchicalData.pickOrders.length > 0) {
const toProc = (s?: string) => {
if (!s) return 'pending';
const v = s.toLowerCase();
if (v === 'completed' || v === 'complete') return 'completed';
if (v === 'rejected') return 'rejected';
if (v === 'partially_completed') return 'pending';
return 'pending';
};

hierarchicalData.pickOrders.forEach((po: any) => {
po.pickOrderLines?.forEach((line: any) => {
const lineStockouts = line.stockouts || []; // ← 用“行级” stockouts
const lots = line.lots || [];

if (lots.length > 0) {
lots.forEach((lot: any) => {
// 先按该 lot 过滤出匹配的 stockouts(同 lotId)
const sos = lineStockouts.filter((so: any) => (so.lotId ?? null) === (lot.id ?? null));
if (sos.length > 0) {
sos.forEach((so: any) => {
flatLotData.push({
pickOrderCode: po.pickOrderCode,
itemCode: line.item?.code,
itemName: line.item?.name,
lotNo: so.lotNo || lot.lotNo,
location: so.location || lot.location,
deliveryOrderCode: po.deliveryOrderCode,
requiredQty: lot.requiredQty,
actualPickQty: (so.qty ?? lot.actualPickQty ?? 0),
processingStatus: toProc(so.status), // ← 用 stockouts.status
stockOutLineStatus: so.status, // 可选保留
noLot: so.noLot === true
});
}
});
} else {
// 没有匹配的 stockouts,也要显示 lot
flatLotData.push({
pickOrderCode: po.pickOrderCode,
itemCode: line.item?.code,
itemName: line.item?.name,
lotNo: lot.lotNo,
location: lot.location,
deliveryOrderCode: po.deliveryOrderCode,
requiredQty: lot.requiredQty,
actualPickQty: lot.actualPickQty ?? 0,
processingStatus: lot.processingStatus || 'pending',
stockOutLineStatus: lot.stockOutLineStatus || 'pending',
noLot: false
});
}
});
} else if (lineStockouts.length > 0) {
// ✅ lots 为空但有 stockouts(如「雞絲碗仔翅」),也要显示
lineStockouts.forEach((so: any) => {
flatLotData.push({
pickOrderCode: po.pickOrderCode,
itemCode: line.item?.code,
itemName: line.item?.name,
lotNo: so.lotNo || '',
location: so.location || '',
deliveryOrderCode: po.deliveryOrderCode,
requiredQty: line.requiredQty ?? 0, // 行级没有 lot 时,用行的 requiredQty
actualPickQty: (so.qty ?? 0),
processingStatus: toProc(so.status),
stockOutLineStatus: so.status,
noLot: so.noLot === true
});
});
}
setDetailLotData(flatLotData);
});
});
}

setDetailLotData(flatLotData);
// ✅ 计算完成状态
const allCompleted = flatLotData.length > 0 && flatLotData.every(lot =>
@@ -593,8 +644,8 @@ if (showDetailView && selectedDoPickOrder) {
<TableCell align="right">{lot.requiredQty || 0}</TableCell>
<TableCell align="right">{lot.actualPickQty || 0}</TableCell>
<TableCell align="center">
<Chip
label={t(lot.processingStatus || 'unknown')}
<Chip
label={t(lot.processingStatus || 'unknown')}
color={lot.processingStatus === 'completed' ? 'success' : 'default'}
size="small"
/>


Loading…
Cancel
Save