Преглед изворни кода

fix for exp date

fix負數倉
PC-20260115JRSN\Administrator пре 4 недеља
родитељ
комит
02c4f1e5bf
2 измењених фајлова са 85 додато и 15 уклоњено
  1. +57
    -5
      src/app/api/laserPrint/actions.ts
  2. +28
    -10
      src/components/LaserPrint/LaserPrintSearch.tsx

+ 57
- 5
src/app/api/laserPrint/actions.ts Прегледај датотеку

@@ -15,7 +15,8 @@ export interface JobOrderListItem {
lotNo: string | null;
defaultShelfLifeDays?: number | null;
useMinus18?: boolean | null;
expiryDate?: string | null;
/** ISO `yyyy-MM-dd`, or Jackson date array `[yyyy,M,d]`. */
expiryDate?: string | number[] | null;
bagPrintedQty?: number;
labelPrintedQty?: number;
laserPrintedQty?: number;
@@ -114,6 +115,39 @@ export async function fetchLaserBag2Settings(): Promise<LaserBag2Settings> {
return res.json() as Promise<LaserBag2Settings>;
}

/** List API may return LocalDate as `"2026-08-27"` or `[2026,8,27]` (@EnableWebMvc raw Jackson). */
export function expiryDateForLaserSend(value: unknown): string | null {
if (value == null || value === "") return null;
if (typeof value === "string") {
const s = value.trim();
if (!s) return null;
if (/^\d{4}-\d{2}-\d{2}/.test(s)) return s.slice(0, 10);
return s;
}
if (Array.isArray(value) && value.length >= 3) {
const y = Number(value[0]);
const m = Number(value[1]);
const d = Number(value[2]);
if (!Number.isFinite(y) || !Number.isFinite(m) || !Number.isFinite(d) || y < 1 || m < 1 || d < 1) {
return null;
}
return `${y}-${String(m).padStart(2, "0")}-${String(d).padStart(2, "0")}`;
}
return null;
}

function messageFromLaserSendBody(data: Record<string, unknown>, status: number): string {
const message = typeof data.message === "string" ? data.message.trim() : "";
if (message) return message;
const detail = typeof data.detail === "string" ? data.detail.trim() : "";
if (detail) return detail;
const error = typeof data.error === "string" ? data.error.trim() : "";
if (error) return error;
const traceId = typeof data.traceId === "string" ? data.traceId.trim() : "";
if (traceId) return `送出失敗(traceId ${traceId})`;
return `送出失敗(HTTP ${status})`;
}

export async function sendLaserBag2Job(body: LaserBag2SendRequest): Promise<LaserBag2SendResponse> {
const url = `${NEXT_PUBLIC_API_URL}/plastic/print-laser-bag2`;
const res = await clientAuthFetch(url, {
@@ -121,11 +155,29 @@ export async function sendLaserBag2Job(body: LaserBag2SendRequest): Promise<Lase
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const data = (await res.json()) as LaserBag2SendResponse;
if (!res.ok) {
return data;
let data: Record<string, unknown> = {};
try {
const text = await res.text();
data = text ? (JSON.parse(text) as Record<string, unknown>) : {};
} catch {
return { success: false, message: `送出失敗(HTTP ${res.status},無法解析回應)` };
}
return data;
if (!res.ok || data.success === false) {
return {
success: false,
message: messageFromLaserSendBody(data, res.status),
payloadSent: typeof data.payloadSent === "string" ? data.payloadSent : null,
printerAck: typeof data.printerAck === "string" ? data.printerAck : null,
receiveAcknowledged: Boolean(data.receiveAcknowledged),
};
}
return {
success: true,
message: typeof data.message === "string" && data.message.trim() ? data.message : "已送出",
payloadSent: typeof data.payloadSent === "string" ? data.payloadSent : null,
printerAck: typeof data.printerAck === "string" ? data.printerAck : null,
receiveAcknowledged: Boolean(data.receiveAcknowledged),
};
}

export interface PrinterStatusRequest {


+ 28
- 10
src/components/LaserPrint/LaserPrintSearch.tsx Прегледај датотеку

@@ -1,6 +1,6 @@
"use client";

import React, { useCallback, useEffect, useState } from "react";
import React, { useCallback, useEffect, useRef, useState } from "react";
import {
Alert,
Box,
@@ -26,6 +26,7 @@ import {
type LaserLastReceiveSuccess,
JobOrderListItem,
patchSetting,
expiryDateForLaserSend,
sendLaserBag2Job,
} from "@/app/api/laserPrint/actions";
import dayjs from "dayjs";
@@ -89,6 +90,7 @@ const LaserPrintSearch: React.FC = () => {
const [settingsLoaded, setSettingsLoaded] = useState(false);
const [printerConnected, setPrinterConnected] = useState(false);
const [printerMessage, setPrinterMessage] = useState("檸檬機(激光機)未連接");
const sendInFlightRef = useRef(false);

const loadSystemSettings = useCallback(async () => {
try {
@@ -191,32 +193,38 @@ const LaserPrintSearch: React.FC = () => {
jobOrderId: jo.id,
jobOrderNo: jo.code,
lotNo: jo.lotNo,
expiryDate: jo.expiryDate ?? null,
expiryDate: expiryDateForLaserSend(jo.expiryDate),
source: "MANUAL",
});

const handleRowClick = async (jo: JobOrderListItem) => {
if (sendingJobId !== null) return;
if (sendInFlightRef.current || sendingJobId !== null) return;

if (!laserHost.trim()) {
setErrorSnackbar({ open: true, message: "請在系統設定中填寫檸檬機(激光機) IP。" });
return;
}

sendInFlightRef.current = true;
setSelectedId(jo.id);
setSendingJobId(jo.id);
try {
let lastAck: string | undefined;
let anyReceiveAck = false;
let sentOk = 0;
let laterFail: string | null = null;
for (let i = 0; i < LASER_SEND_COUNT; i++) {
const r = await sendOne(jo);
if (!r.success) {
setErrorSnackbar({
open: true,
message: r.message || "檸檬機(激光機)未收到指令",
});
return;
const failMsg = r.message?.trim() || `第 ${i + 1} 次送出失敗`;
if (sentOk === 0) {
setErrorSnackbar({ open: true, message: failMsg });
return;
}
laterFail = failMsg;
break;
}
sentOk += 1;
if (r.printerAck) lastAck = r.printerAck;
if (r.receiveAcknowledged) anyReceiveAck = true;
if (i < LASER_SEND_COUNT - 1) {
@@ -229,7 +237,11 @@ const LaserPrintSearch: React.FC = () => {
: lastAck
? `(最後回覆:${lastAck})`
: "";
setSuccessSignal(`已送出 ${LASER_SEND_COUNT} 次至檸檬機(激光機)${ackHint}`);
setSuccessSignal(
laterFail
? `已送出 ${sentOk} 次至檸檬機(激光機)${ackHint}(後續重送失敗:${laterFail})`
: `已送出 ${sentOk} 次至檸檬機(激光機)${ackHint}`,
);
await loadSystemSettings();
} catch (e) {
setErrorSnackbar({
@@ -238,6 +250,7 @@ const LaserPrintSearch: React.FC = () => {
});
} finally {
setSendingJobId(null);
sendInFlightRef.current = false;
}
};

@@ -269,7 +282,12 @@ const LaserPrintSearch: React.FC = () => {
{settingsLoaded && lastLaserReceive && (
<Alert severity="info" sx={{ mb: 2 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}>
上次印表機已確認工單:{lastLaserReceive.jobOrderNo ?? "—"} {formatHongKongDateTime(lastLaserReceive.sentAt)}
上次印表機已確認工單:{lastLaserReceive.jobOrderNo ?? "—"} 
{formatHongKongDateTime(lastLaserReceive.sentAt)}
{lastLaserReceive.source ? ` (${lastLaserReceive.source === "AUTO" ? "自動送出" : "手動點選"})` : ""}
</Typography>
<Typography variant="body2" sx={{ mt: 0.5 }}>
此時間只會在檸檬機回覆 receive 時更新。之後送出失敗不會改這裡。
</Typography>
</Alert>
)}


Loading…
Откажи
Сачувај