|
|
import { describe, expect, it } from "vitest";
|
|
|
import {
|
|
|
applyServiceFromModules,
|
|
|
applyServiceFromText,
|
|
|
normalizeCabinetType,
|
|
|
stripOrChoiceNoise,
|
|
|
} from "@/services/parse/header-service-hints";
|
|
|
import { extractContainerHeaderFromText } from "@/services/parse/extract-container-header";
|
|
|
import { mapBlModulesToHeader } from "@/services/parse/bl-plus-template";
|
|
|
import { extractOpsSubjectFromMail } from "@/services/parse/ops-subject-template";
|
|
|
import { buildCcForecastFormValue } from "@/components/CcForecastFormOrder";
|
|
|
|
|
|
describe("header-service-hints (AI assistant parity)", () => {
|
|
|
it("strips 海运或空运 / 提拆派或直送 option noise", () => {
|
|
|
const t = stripOrChoiceNoise("运输方式(海运或空运)操作类型(提拆派或直送)");
|
|
|
expect(t).not.toMatch(/海运或空运|提拆派或直送/);
|
|
|
});
|
|
|
|
|
|
it("normalizeCabinetType 40HC → 40HQ", () => {
|
|
|
expect(normalizeCabinetType("40HC")).toBe("40HQ");
|
|
|
expect(normalizeCabinetType("40HQ")).toBe("40HQ");
|
|
|
});
|
|
|
|
|
|
it("拆柜清单 / 提拆派 / 直送 / 只清关", () => {
|
|
|
expect(applyServiceFromText("拆柜清单").F_OperationType).toBe(0);
|
|
|
expect(applyServiceFromText("提拆派组合柜-不带托架")).toMatchObject({
|
|
|
F_OperationType: 0,
|
|
|
F_Classis: 0,
|
|
|
F_Instruction: "提拆派",
|
|
|
});
|
|
|
expect(applyServiceFromText("本票直送").F_OperationType).toBe(2);
|
|
|
expect(applyServiceFromText("只清关").F_OperationType).toBe(4);
|
|
|
expect(
|
|
|
applyServiceFromText("提拆派,说明另有直送类型").F_OperationType,
|
|
|
).toBe(0);
|
|
|
});
|
|
|
|
|
|
it("ops_hints 拆柜清单 → modules path", () => {
|
|
|
expect(
|
|
|
applyServiceFromModules({ ops_hints: ["拆柜清单", "派送要求"] })
|
|
|
.F_OperationType,
|
|
|
).toBe(0);
|
|
|
expect(
|
|
|
applyServiceFromModules({ ops_hints: ["直送"] }).F_OperationType,
|
|
|
).toBe(2);
|
|
|
});
|
|
|
});
|
|
|
|
|
|
describe("extractContainerHeaderFromText assistant parity", () => {
|
|
|
it("mail3 channel summary → 拆柜 + 海运 + OOCL 船司别名", () => {
|
|
|
const h = extractContainerHeaderFromText(
|
|
|
"WHSU5574991+WHL063G550810+船名航次:OOCL SINGAPORE / 065W+ETA:6/28+FedEx-29件+UPS-102件+亚马逊卡派-289件+私人地址-166件+拦截-209件+拆柜清单",
|
|
|
);
|
|
|
expect(h.F_OperationType).toBe(0);
|
|
|
expect(h.F_TransMode).toBe(0);
|
|
|
expect(h.F_ETA).toBe("2026-06-28");
|
|
|
expect(h.F_BLCopyCode).toBe("WHL063G550810");
|
|
|
expect(h.shipping_line_hint).toMatch(/WHL/i);
|
|
|
expect(h.F_Instruction).toMatch(/船名航次.*OOCL SINGAPORE/);
|
|
|
// 禁止 WAN/OOCL 船名首词误当船司时:有提单前缀则用 WHL
|
|
|
});
|
|
|
|
|
|
it("does not take vessel first token as shipping line (WAN HAI)", () => {
|
|
|
const h = extractContainerHeaderFromText(
|
|
|
"船名航次:WAN HAI 511-V.E110 柜号 TCNU4412465",
|
|
|
);
|
|
|
expect(h.shipping_line_hint?.toUpperCase()).not.toBe("WAN");
|
|
|
expect(h.shipping_line_hint?.toUpperCase()).toMatch(/WAN\s*HAI|WHL|WAN HAI/i);
|
|
|
});
|
|
|
|
|
|
it("ignores 提拆派或直送 as selected operation", () => {
|
|
|
const h = extractContainerHeaderFromText(
|
|
|
"请选择操作类型(提拆派或直送)柜号 WHSU8127240",
|
|
|
);
|
|
|
expect(h.F_OperationType).toBeUndefined();
|
|
|
});
|
|
|
});
|
|
|
|
|
|
describe("ops subject → form full chain", () => {
|
|
|
it("mail3 subject fills OperationType via map + form", () => {
|
|
|
const subject =
|
|
|
"Fw: 转发:派送要求更新: 拆柜清单更新: DO请查收+拆柜清单更新: WHSU5574991+WHL063G550810+船名航次:OOCL SINGAPORE / 065W+ETA:6/28+FedEx-29件+UPS-102件+亚马逊卡派-289件+私人地址-166件+拦截-209件+拆柜清单";
|
|
|
const ops = extractOpsSubjectFromMail(subject, { defaultYear: 2026 })!;
|
|
|
const header = mapBlModulesToHeader(
|
|
|
{
|
|
|
F_ContainerNo: ops.modules.container_no || "",
|
|
|
container_no_valid: true,
|
|
|
},
|
|
|
ops.modules,
|
|
|
);
|
|
|
expect(header.F_OperationType).toBe(0);
|
|
|
expect(header.F_TransMode).toBe(0);
|
|
|
expect(header.F_ETA).toBe("2026-06-28");
|
|
|
const form = buildCcForecastFormValue({
|
|
|
header: {},
|
|
|
modules: ops.modules,
|
|
|
});
|
|
|
expect(form.F_OperationType).toBe(0);
|
|
|
expect(form.F_TransMode).toBe(0);
|
|
|
expect(form.F_ETA).toBe("2026-06-28");
|
|
|
expect(form.F_Instruction).toMatch(/船名航次/);
|
|
|
});
|
|
|
});
|