Przeglądaj źródła

update pucahseorder speed

reset-do-picking-order
CANCERYS\kw093 2 tygodni temu
rodzic
commit
09e8bdff0d
6 zmienionych plików z 115 dodań i 24 usunięć
  1. +15
    -0
      src/app/api/po/actions.ts
  2. +51
    -16
      src/components/PoDetail/PoDetail.tsx
  3. +32
    -6
      src/components/PoSearch/PoSearch.tsx
  4. +1
    -0
      src/i18n/zh/inventory.json
  5. +1
    -0
      src/i18n/zh/items.json
  6. +15
    -2
      src/i18n/zh/pickOrder.json

+ 15
- 0
src/app/api/po/actions.ts Wyświetl plik

@@ -200,6 +200,21 @@ export const fetchPoInClient = cache(async (id: number) => {
});
});

export interface PurchaseOrderSummary {
id: number;
code: string;
status: string;
orderDate: string;
estimatedArrivalDate: string;
supplierName: string;
escalated: boolean;
}
export const fetchPoSummariesClient = cache(async (ids: number[]) => {
return serverFetchJson<PurchaseOrderSummary[]>(`${BASE_API_URL}/po/summary`, {
next: { tags: ["po"] },
});
});

export const fetchPoListClient = cache(
async (queryParams?: Record<string, any>) => {
if (queryParams) {


+ 51
- 16
src/components/PoDetail/PoDetail.tsx Wyświetl plik

@@ -43,6 +43,7 @@ import {
checkPolAndCompletePo,
fetchPoInClient,
fetchPoListClient,
fetchPoSummariesClient,
startPo,
} from "@/app/api/po/actions";
import {
@@ -201,6 +202,7 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
purchaseOrder.pol || [],
);
const [polInputList, setPolInputList] = useState<PolInputResult[]>([])
const PO_DETAIL_SELECTION_KEY = "po-detail-selection";
useEffect(() => {
setPolInputList(
(purchaseOrder.pol ?? []).map(() => ({
@@ -209,7 +211,21 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
} as PolInputResult))
);
}, [purchaseOrder.pol]);

useEffect(() => {
try {
const raw = sessionStorage.getItem("po-detail-selection");
if (raw) {
const parsed = JSON.parse(raw) as { id: number; code: string; status: string; supplier: string | null }[];
if (Array.isArray(parsed) && parsed.length > 0) {
setPoList(parsed as PoResult[]);
sessionStorage.removeItem("po-detail-selection"); // 可选:用一次就删,避免下次从别处进还看到旧数据
}
}
} catch (e) {
console.warn("sessionStorage getItem/parse failed", e);
}
}, []);
const pathname = usePathname()
const searchParams = useSearchParams();
@@ -227,6 +243,7 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {

const router = useRouter();
const [poList, setPoList] = useState<PoResult[]>([]);
const [isPoListLoading, setIsPoListLoading] = useState(false);
const [selectedPoId, setSelectedPoId] = useState(po.id);
const [focusField, setFocusField] = useState<HTMLInputElement>();

@@ -240,15 +257,26 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
}
})
const fetchPoList = useCallback(async () => {
setIsPoListLoading(true);
try {
if (selectedIdsParam) {

const selectedIds = selectedIdsParam.split(',').map(id => parseInt(id));
const promises = selectedIds.map(id => fetchPoInClient(id));
const results = await Promise.all(promises);
setPoList(results.filter(Boolean));
const MAX_IDS = 20; // 一次最多加载 20 个,防止卡死
const allIds = selectedIdsParam
.split(',')
.map(id => parseInt(id))
.filter(id => !Number.isNaN(id));
const limitedIds = allIds.slice(0, MAX_IDS);
if (allIds.length > MAX_IDS) {
console.warn(
`selectedIds too many (${allIds.length}), only loading first ${MAX_IDS}.`
);
}
const result = await fetchPoSummariesClient(limitedIds);
setPoList(result as any);
} else {

const result = await fetchPoListClient({ limit: 20, offset: 0 });
if (result && result.records) {
setPoList(result.records);
@@ -256,6 +284,8 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
}
} catch (error) {
console.error("Failed to fetch PO list:", error);
} finally {
setIsPoListLoading(false);
}
}, [selectedIdsParam]);

@@ -311,11 +341,11 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
fetchPoDetail(currentPoId);
}
}, [currentPoId, fetchPoDetail]);
/*
useEffect(() => {
fetchPoList();
}, [fetchPoList]);
*/
useEffect(() => {
if (currentPoId) {
setSelectedPoId(parseInt(currentPoId));
@@ -490,12 +520,15 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
const highlightColor = (Number(receivedTotal.replace(/,/g, '')) <= 0) ? "red" : "inherit";
return (
<>

<TableRow
sx={{ "& > *": { borderBottom: "unset" },
color: "black"
}}
onClick={() => changeStockInLines(row.id)}
>
{/* <TableCell>
<IconButton
disabled={purchaseOrder.status.toLowerCase() === "pending"}
@@ -729,13 +762,15 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
<Grid container spacing={3} sx={{ maxWidth: 'fit-content' }}>
{/* left side select po */}
<Grid item xs={4}>
<PoSearchList
poList={poList}
selectedPoId={selectedPoId}
onSelect={handlePoSelect}
/>
</Grid>
<Stack spacing={1}>

<PoSearchList
poList={poList}
selectedPoId={selectedPoId}
onSelect={handlePoSelect}
/>
</Stack>
</Grid>

{/* right side po info */}
<Grid item xs={8}>


+ 32
- 6
src/components/PoSearch/PoSearch.tsx Wyświetl plik

@@ -41,6 +41,7 @@ const PoSearch: React.FC<Props> = ({
const [filterArgs, setFilterArgs] = useState<Record<string, any>>({estimatedArrivalDate : dayjsToDateString(dayjs(), "input")});
const { t } = useTranslation(["purchaseOrder", "dashboard"]);
const router = useRouter();
const PO_DETAIL_SELECTION_KEY = "po-detail-selection";
const [pagingController, setPagingController] = useState(
defaultPagingController,
);
@@ -83,7 +84,18 @@ const PoSearch: React.FC<Props> = ({
(po: PoResult) => {
setSelectedPoIds([]);
setSelectAll(false);
router.push(`/po/edit?id=${po.id}&start=true&selectedIds=${po.id}`);
const listForDetail = [
{ id: po.id, code: po.code, status: po.status, supplier: po.supplier ?? null },
];
try {
sessionStorage.setItem(
PO_DETAIL_SELECTION_KEY,
JSON.stringify(listForDetail),
);
} catch (e) {
console.warn("sessionStorage setItem failed", e);
}
router.push(`/po/edit?id=${po.id}&start=true`);
},
[router],
);
@@ -111,12 +123,26 @@ const PoSearch: React.FC<Props> = ({

// navigate to PoDetail page
const handleGoToPoDetail = useCallback(() => {
if (selectedPoIds.length > 0) {
const selectedIdsParam = selectedPoIds.join(',');
const firstPoId = selectedPoIds[0];
router.push(`/po/edit?id=${firstPoId}&start=true&selectedIds=${selectedIdsParam}`);
if (selectedPoIds.length === 0) return;
const selectedList = filteredPo.filter((p) => selectedPoIds.includes(p.id));
const listForDetail = selectedList.map((p) => ({
id: p.id,
code: p.code,
status: p.status,
supplier: p.supplier ?? null,
}));
try {
sessionStorage.setItem("po-detail-selection", JSON.stringify(listForDetail));
} catch (e) {
console.warn("sessionStorage setItem failed", e);
}
}, [selectedPoIds, router]);
const selectedIdsParam = selectedPoIds.join(",");
const firstPoId = selectedPoIds[0];
router.push(`/po/edit?id=${firstPoId}&start=true&selectedIds=${selectedIdsParam}`);
}, [selectedPoIds, filteredPo, router]);

const itemColumn = useCallback((value: string | undefined) => {
if (!value) {


+ 1
- 0
src/i18n/zh/inventory.json Wyświetl plik

@@ -11,6 +11,7 @@
"Variance %": "差異百分比",
"fg": "成品",
"Back to List": "返回列表",
"Start Stock Take Date": "盤點日期",
"Record Status": "記錄狀態",
"Stock take record status updated to not match": "盤點記錄狀態更新為數值不符",
"available": "可用",


+ 1
- 0
src/i18n/zh/items.json Wyświetl plik

@@ -9,6 +9,7 @@
"Edit Product / Material": "編輯產品 / 材料",
"Product / Material": "產品 / 材料",
"Product / Material Details": "產品 / 材料詳情",

"Qc items": "QC 項目",
"Qc Category": "質檢模板",
"Name": "名稱",


+ 15
- 2
src/i18n/zh/pickOrder.json Wyświetl plik

@@ -29,12 +29,12 @@
"Do you want to start?": "確定開始嗎?",
"Start": "開始",
"Pick Order Code(s)": "提料單編號",
"Delivery Order Code(s)": "送貨單編號",
"Delivery Order Code(s)": "提料單編號",
"Start Success": "開始成功",
"Truck Lance Code": "車牌號碼",
"Pick Order Codes": "提料單編號",
"Pick Order Lines": "提料單行數",
"Delivery Order Codes": "送貨單編號",
"Delivery Order Codes": "提料單編號",
"Delivery Order Lines": "送貨單行數",
"Lines Per Pick Order": "每提料單行數",
"Pick Orders Details": "提料單詳情",
@@ -394,9 +394,15 @@
"submitStockIn": "提交入庫",
"not default warehosue": "不是默認倉庫",
"printQty": "打印數量",
"Shop": "商店名稱",
"warehouse": "倉庫",
"Add Record": "添加記錄",
"Clean Record": "清空記錄",
"Select": "選擇",
"Close": "關閉",
"Truck": "車輛",
"Date": "日期",
"Delivery Order Code": "送貨單編號",
"Escalation Info": "升級信息",
"Escalation Result": "升級結果",
"acceptQty must not greater than": "接受數量不能大於",
@@ -426,10 +432,17 @@
"No entries available": "該樓層未有需處理訂單",
"Today": "是日",
"Tomorrow": "翌日",
"No Stock Available": "沒有庫存可用",
"This lot is not available, please scan another lot.": "此批號不可用,請掃描其他批號。",
"Day After Tomorrow": "後日",
"Select Date": "請選擇日期",
"Search by Shop": "搜尋商店",
"Search by Truck": "搜尋貨車",
"Print DN & Label": "列印提料單和送貨單標籤",
"Print Label": "列印送貨單標籤",
"Not Yet Finished Released Do Pick Orders": "未完成提料單",
"Not yet finished released do pick orders": "未完成提料單",
"Released orders not yet completed - click lane to select and assign": "未完成提料單- 點擊貨車班次選擇並分配",
"Ticket Release Table": "查看提貨情況",
"Please take one pick order before printing the draft.": "請先從「撳單/提料單詳情」頁面下方選取提料單,再列印草稿。",
"No released pick order records found.": "目前沒有可用的提料單。",


Ładowanie…
Anuluj
Zapisz