You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

207 lines
7.7 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { describe, expect, it } from "vitest";
import {
detectUiKindsFromText,
extractForecastInstructionText,
extractMailInstructions,
splitBodySegments,
} from "@/services/parse/split-instructions";
import { evaluateTransferGate } from "@/services/cc/container-status";
import { buildCcWorkOrderFormValue } from "@/components/CcWorkOrderForm";
import { buildCcDoUploadFormValue } from "@/components/CcDoUploadPanel";
const MAIL4_SUBJECT =
"Fw: 转发:新增预报136:新辰泽+WHLC027G597465+WHSU8127240+洛杉矶+40HQ+EDT2026.04-25 ETA2026.05-15船名航次HMM EMERALD 013E+提拆派组合柜-不带托架";
const MAIL4_BODY = [
"Dear,",
"原箱号YT2604021091=FBA199R49LD6-MDW2=76件操作指令",
"换标后单号:FBA19HW52S0L-2G1MCB6X-HIA1=76件",
"1:覆盖贴FBA标签,一箱贴两张",
"3:贴好拍照回传",
].join("\n");
describe("four-business extractMailInstructions", () => {
it("mail4: forecast + work_order + maybe do", () => {
const units = extractMailInstructions({
subject: MAIL4_SUBJECT,
body: MAIL4_BODY,
filenames: ["YT2604021091=76件换标走HIA1卡派出库操作指令.xlsx"],
});
const kinds = units.map((u) => u.uiKind);
expect(kinds).toContain("forecast");
expect(kinds).toContain("work_order");
});
it("transfer-only body → transfer", () => {
const units = extractMailInstructions({
subject: "MATU2745683",
body: "新增转仓,请留意",
filenames: [],
});
expect(units.some((u) => u.uiKind === "transfer")).toBe(true);
});
it("DO filename → do_upload", () => {
expect(
detectUiKindsFromText("WHSU5574991", "", ["柜DO.pdf"]),
).toContain("do_upload");
});
});
describe("extractForecastInstructionText P4", () => {
it("does not use 换标 body as forecast instruction", () => {
const text = extractForecastInstructionText({
subject: MAIL4_SUBJECT,
body: MAIL4_BODY,
});
expect(text).not.toMatch(/覆盖贴/);
expect(text).toMatch(/船名航次|HMM|EMERALD/);
});
it("keeps Instruction independent of 拆柜清单 body even if forecast card exists", () => {
const subject =
"Fw: 转发:派送要求更新: 拆柜清单更新: DO请查收+拆柜清单更新: WHSU5574991+WHL063G550810+船名航次:OOCL SINGAPORE / 065W+ETA:6/28+FedEx-29件+拆柜清单";
const body = "拆柜清单更新 以此为准\n注意:需要拦截\n麻烦贴标交付";
const units = extractMailInstructions({ subject, body, filenames: [] });
expect(units.some((u) => u.uiKind === "forecast")).toBe(true);
const instr = extractForecastInstructionText({
subject,
body,
modulesInstruction: "船名航次 OOCL SINGAPORE / 065W",
});
expect(instr).toBe("船名航次 OOCL SINGAPORE / 065W");
expect(instr).not.toMatch(/拦截|贴标|拆柜清单更新/);
});
});
describe("splitBodySegments", () => {
it("splits on quote header", () => {
const segs = splitBodySegments(
"当前换标\n------------------ 原始电子邮件 ------------------\n主题:新增预报\n历史",
);
expect(segs.length).toBeGreaterThanOrEqual(2);
});
});
describe("buildCcWorkOrderFormValue", () => {
it("mail actions stay in tags; 贴标映射 CC 留言类型「操作指令」", () => {
const v = buildCcWorkOrderFormValue({
subject: "操作",
body: "请贴标并拍照",
containerNo: "WHSU8127240",
});
expect(v.F_MessageType).toBe("操作指令");
expect(v.F_MessageType).not.toBe("贴标");
expect(v.mailActions).toEqual(expect.arrayContaining(["贴标", "拍照"]));
expect(v.F_Remark).toContain("邮件动作:");
expect(v.F_ReferCode).toBe("WHSU8127240");
});
it("拦截 → CC 留言类型「货件拦截」", () => {
const v = buildCcWorkOrderFormValue({
subject: "请拦截",
body: "柜号 WHSU8127240 拦截货件",
});
expect(v.F_MessageType).toBe("货件拦截");
expect(v.mailActions).toContain("拦截");
});
it("mail3 渠道拦截-N件 → 备注为拆柜清单而非拦截", () => {
const subject =
"Fw: 转发:派送要求更新: 拆柜清单更新: DO请查收+拆柜清单更新: WHSU5574991+WHL063G550810+船名航次:OOCL SINGAPORE / 065W+ETA:6/28+FedEx-29件+UPS-102件+亚马逊卡派-289件+私人地址-166件+拦截-209件+拆柜清单";
const v = buildCcWorkOrderFormValue({
subject,
body: "派送要求更新,请查收 DO 与拆柜清单。",
containerNo: "WHSU5574991",
actions: [],
});
expect(v.F_Remark).toContain("邮件动作:拆柜清单");
expect(v.F_Remark).not.toContain("拦截");
expect(v.F_MessageType).not.toBe("货件拦截");
expect(v.mailActions).toContain("拆柜清单");
expect(v.mailActions).not.toContain("拦截");
});
it("mail85 76件换标挂对应附件;833件历史轮不误挂", () => {
const files = [
"YT2604021091=76件换标走HIA1卡派出库操作指令.xlsx",
"FBA19HW52S0L-76CTN-HIA1-覆盖贴1箱贴2张.pdf",
"X003UHDF7B-304PCS-覆盖贴1箱贴1张.pdf",
];
const current = buildCcWorkOrderFormValue({
subject: "Re:新增预报136",
body: [
"Dear,",
"原箱号 YT2604021091=FBA199R49LD6-MDW2=76 件操作指令",
"换标后单号: FBA19HW52S0L-2G1MCB6X-HIA1=76 件",
"1:覆盖贴FBA 标签,一箱贴两张",
"2:覆盖贴SKU 标签,一箱 1 张( FNSKU : X003UHDF7B ( 304PCS ))",
].join("\n"),
filenames: files,
containerNo: "WHSU8127240",
});
expect(current.F_AttachFile).toContain("YT2604021091");
expect(current.F_AttachFile).toContain("FBA19HW52S0L");
expect(current.F_AttachFile).toContain("X003UHDF7B");
const hist833 = buildCcWorkOrderFormValue({
subject: "Fw: 新增预报136",
body: [
"Dear,",
"WHSU8127240= 原箱号 YT2604021486- 剩余 540 件",
"两个柜子共计 833 件操作指令",
"换标后单号:FBA19DS6CXV4-2G1XD81L-LGB8=87 件",
].join("\n"),
filenames: files,
containerNo: "WHSU8127240",
isCurrent: false,
});
expect(hist833.F_AttachFile).toBe("");
});
it("strips forward headers; keeps Dear instruction as F_MessageContent", () => {
const v = buildCcWorkOrderFormValue({
subject: "Fw: 新增预报136",
body: [
"---- 转发的邮件 ----",
"发件人新辰泽",
"主题 Re:Re:新增预报136",
"Dear,",
"原箱号 YT2604021091=FBA199R49LD6-MDW2=76 件操作指令",
"1:覆盖贴FBA标签,一箱贴两张",
"3:贴好拍照回传",
].join("\n"),
containerNo: "WHSU8127240",
});
expect(v.F_MessageContent).toMatch(/^Dear,/);
expect(v.F_MessageContent).toContain("覆盖贴");
expect(v.F_MessageContent).not.toContain("转发的邮件");
expect(v.mailActions).toEqual(expect.arrayContaining(["贴标", "拍照"]));
});
});
describe("buildCcDoUploadFormValue", () => {
it("keeps DO pdf filenames", () => {
const v = buildCcDoUploadFormValue({
subject: "DO请查收 WHSU5574991",
filenames: ["WHSU5574991-DO.pdf", "其他.xlsx"],
});
expect(v.filenames.some((f) => /DO/i.test(f))).toBe(true);
});
});
describe("evaluateTransferGate mock", () => {
it("ARRIVED container → work_order fallback", async () => {
const g = await evaluateTransferGate("ARRIVED1234567");
expect(g.canTransfer).toBe(false);
if (!g.canTransfer) {
expect(g.fallback).toBe("work_order");
// mock 模式:柜态 60-已到仓;live 空列表也会降级工单
if (g.shipStatus != null) {
expect(g.shipStatus).toBe(60);
expect(g.reason).toMatch(/已到仓/);
}
}
}, 15_000);
});