Kaynağa Gözat

no message

production
[email protected] 7 saat önce
ebeveyn
işleme
f6d7c3ac7c
1 değiştirilmiş dosya ile 126 ekleme ve 36 silme
  1. +126
    -36
      src/app/(main)/m18Syn/page.tsx

+ 126
- 36
src/app/(main)/m18Syn/page.tsx Dosyayı Görüntüle

@@ -29,6 +29,62 @@ function TabPanel(props: TabPanelProps) {
export default function M18SynPage() {
const [tabValue, setTabValue] = useState(0);

type SyncResultDto = {
totalProcessed: number;
totalSuccess: number;
totalFail: number;
query?: string | null;
};

type ParsedSyncResult =
| { kind: "exists"; message: string }
| { kind: "not_found"; message: string }
| { kind: "success"; message: string; totalSuccess: number }
| { kind: "fail"; message: string; totalFail: number }
| { kind: "mixed"; message: string; totalSuccess: number; totalFail: number }
| { kind: "raw"; message: string };

const parseSyncResult = (docName: string, rawText: string): ParsedSyncResult => {
const text = rawText?.trim() ?? "";
if (!text) return { kind: "raw", message: "未收到回應" };
try {
const obj = JSON.parse(text) as Partial<SyncResultDto>;
const totalSuccess = Number(obj.totalSuccess ?? 0);
const totalFail = Number(obj.totalFail ?? 0);
const totalProcessed = Number(obj.totalProcessed ?? 0);
const query = (obj.query ?? "").toString();

// newOnly skip message from backend
if (query.includes("skipped") && query.includes("already exists")) {
return { kind: "exists", message: `${docName}已存在系統` };
}

// "not found in M18" (sync-by-code returns processed=1, fail=1)
if (
docName === "送貨訂單" &&
totalProcessed === 1 &&
totalSuccess === 0 &&
totalFail === 1 &&
query.includes("code=equal=")
) {
return { kind: "not_found", message: `在M18找不到${docName}` };
}

if (totalSuccess > 0 && totalFail === 0) {
return { kind: "success", totalSuccess, message: `成功同步: ${totalSuccess}張${docName}` };
}
if (totalSuccess === 0 && totalFail > 0) {
return { kind: "fail", totalFail, message: `失敗: 無法同步${docName}` };
}
return { kind: "mixed", totalSuccess, totalFail, message: `完成: 成功 ${totalSuccess} / 失敗 ${totalFail} (${docName})` };
} catch {
// Non-JSON error body or plain text
return { kind: "raw", message: text };
}
};

const formatSyncResultText = (docName: string, rawText: string): string => parseSyncResult(docName, rawText).message;

const [m18PoCode, setM18PoCode] = useState("");
const [isSyncingM18Po, setIsSyncingM18Po] = useState(false);
const [m18PoSyncResult, setM18PoSyncResult] = useState<string>("");
@@ -65,7 +121,7 @@ export default function M18SynPage() {
);
if (response.status === 401 || response.status === 403) return;
const text = await response.text();
setM18PoSyncResult(text);
setM18PoSyncResult(formatSyncResultText("採購單", text));
if (!response.ok) {
alert(`Sync failed: ${response.status}`);
}
@@ -94,7 +150,7 @@ export default function M18SynPage() {
);
if (response.status === 401 || response.status === 403) return;
const text = await response.text();
setM18DoSyncResult(text);
setM18DoSyncResult(formatSyncResultText("送貨訂單", text));
if (!response.ok) {
alert(`Sync failed: ${response.status}`);
}
@@ -107,27 +163,60 @@ export default function M18SynPage() {
}
};

/** DO(加單):M18 搜尋需含備註「(加單)」,本地 isEtra = true */
/** DO(加單):手動按 code 同步,並寫入本地 isEtra=true(可輸入多個 code,用逗號或換行分隔) */
const handleSyncM18DoExtraByCode = async () => {
if (m18DoExtraInFlightRef.current) return;
if (!m18DoExtraCode.trim()) {
alert("Please enter DO / shop PO code (加單).");
const raw = m18DoExtraCode.trim();
if (!raw) {
alert("Please enter DO / shop PO code(s) (加單).");
return;
}
const codes = raw
.split(/[\n,]+/g)
.map((s) => s.trim())
.filter(Boolean);
if (codes.length === 0) {
alert("Please enter at least one code.");
return;
}
m18DoExtraInFlightRef.current = true;
setIsSyncingM18DoExtra(true);
setM18DoExtraSyncResult("");
try {
const response = await clientAuthFetch(
`${NEXT_PUBLIC_API_URL}/m18/test/do-by-code-extra?code=${encodeURIComponent(m18DoExtraCode.trim())}`,
{ method: "GET" },
);
if (response.status === 401 || response.status === 403) return;
const text = await response.text();
setM18DoExtraSyncResult(text);
if (!response.ok) {
alert(`Sync failed: ${response.status}`);
const outputs: string[] = [];
const okCodes: string[] = [];
const existsCodes: string[] = [];
const notFoundCodes: string[] = [];
const otherFailCodes: string[] = [];
for (const code of codes) {
const response = await clientAuthFetch(
`${NEXT_PUBLIC_API_URL}/m18/test/do-by-code-extra?code=${encodeURIComponent(code)}`,
{ method: "GET" },
);
if (response.status === 401 || response.status === 403) return;
const text = await response.text();
const parsed = parseSyncResult("送貨訂單", text || "");
if (parsed.kind === "success") okCodes.push(code);
else if (parsed.kind === "exists") existsCodes.push(code);
else if (parsed.kind === "not_found") notFoundCodes.push(code);
else otherFailCodes.push(code);

const perCodeMsg =
parsed.kind === "success" ? "成功同步" :
parsed.kind === "exists" ? `失敗: ${parsed.message}` :
parsed.kind === "not_found" ? parsed.message :
parsed.message;
outputs.push(`${code}: ${perCodeMsg}${response.ok ? "" : ` (HTTP ${response.status})`}`);
}

// Summary
outputs.push("");
outputs.push(`共${okCodes.length}張送貨訂單成功`);
if (notFoundCodes.length > 0) outputs.push(`共${notFoundCodes.length}張在M18找不到訂單 (${notFoundCodes.join(", ")})`);
if (existsCodes.length > 0) outputs.push(`共${existsCodes.length}張訂單已存在系統 (${existsCodes.join(", ")})`);
if (otherFailCodes.length > 0) outputs.push(`共${otherFailCodes.length}張同步失敗 (${otherFailCodes.join(", ")})`);

setM18DoExtraSyncResult(outputs.join("\n"));
} catch (e) {
console.error("M18 DO (加單) Sync By Code Error:", e);
alert("M18 DO (加單) sync failed. Check console/network.");
@@ -153,7 +242,7 @@ export default function M18SynPage() {
);
if (response.status === 401 || response.status === 403) return;
const text = await response.text();
setM18ProductSyncResult(text);
setM18ProductSyncResult(formatSyncResultText("產品", text));
if (!response.ok) {
alert(`Sync failed: ${response.status}`);
}
@@ -185,14 +274,14 @@ export default function M18SynPage() {
</Typography>

<Tabs value={tabValue} onChange={(_, v) => setTabValue(v)} aria-label="M18 sync by code" centered variant="fullWidth">
<Tab label="1. PO" id="m18syn-tab-0" />
<Tab label="2. DO" id="m18syn-tab-1" />
<Tab label="3. DO (加單)" id="m18syn-tab-2" />
<Tab label="1. 採購單" id="m18syn-tab-0" />
<Tab label="2. 送貨訂單" id="m18syn-tab-1" />
<Tab label="3. 送貨訂單 (加單)" id="m18syn-tab-2" />
<Tab label="4. Product" id="m18syn-tab-3" />
</Tabs>

<TabPanel value={tabValue} index={0}>
<Section title="M18 Purchase Order — sync by code">
<Section title="M18 採購單 — sync by code">
<Stack direction="row" spacing={2} sx={{ mb: 2, alignItems: "center" }}>
<TextField
size="small"
@@ -221,7 +310,7 @@ export default function M18SynPage() {
</TabPanel>

<TabPanel value={tabValue} index={1}>
<Section title="M18 Delivery Order — sync by code">
<Section title="M18 送貨訂單 — sync by code">
<Stack direction="row" spacing={2} sx={{ mb: 2, alignItems: "center" }}>
<TextField
size="small"
@@ -250,27 +339,28 @@ export default function M18SynPage() {
</TabPanel>

<TabPanel value={tabValue} index={2}>
<Section title="M18 Delivery Order — 加單 (備註含「(加單)」,isEtra)">
<Typography variant="body2" color="textSecondary" sx={{ mb: 2 }}>
與「2. DO」相同以單號從 M18 同步 shop PO/送貨單,但 M18 列表條件會限制備註含「(加單)」;同步寫入之 delivery_order.isEtra = true。
</Typography>
<Stack direction="row" spacing={2} sx={{ mb: 2, alignItems: "center", flexWrap: "wrap" }}>
<Section title="M18 送貨訂單 — 加單 (isEtra)">
<Stack spacing={2} sx={{ mb: 2 }}>
<TextField
size="small"
label="DO / Shop PO Code(加單)"
value={m18DoExtraCode}
onChange={(e) => setM18DoExtraCode(e.target.value)}
placeholder="e.g. 與一般 DO 相同單號,但須為加單單據"
sx={{ minWidth: 320 }}
placeholder="可輸入多個 code,用逗號或換行分隔"
fullWidth
multiline
minRows={6}
maxRows={14}
/>
<Button
variant="contained"
color="primary"
onClick={handleSyncM18DoExtraByCode}
disabled={isSyncingM18DoExtra}
>
{isSyncingM18DoExtra ? "Syncing..." : "Sync DO(加單)from M18"}
</Button>
<Box sx={{ display: "flex", justifyContent: "flex-end" }}>
<Button
variant="contained"
color="primary"
onClick={handleSyncM18DoExtraByCode}
disabled={isSyncingM18DoExtra}
>
{isSyncingM18DoExtra ? "Syncing..." : "Sync 送貨訂單(加單)from M18"}
</Button>
</Box>
</Stack>
{m18DoExtraSyncResult ? (
<TextField


Yükleniyor…
İptal
Kaydet