|
|
import { describe, expect, it } from "vitest";
|
|
|
import {
|
|
|
extractInstructionsFromMail,
|
|
|
resolveConfirmInstruction,
|
|
|
shouldForceForecastUnit,
|
|
|
} from "@/services/parse/resolve-confirm-instruction";
|
|
|
import type { MailInstructionUnit } from "@/services/parse/split-instructions";
|
|
|
|
|
|
function unit(
|
|
|
partial: Partial<MailInstructionUnit> &
|
|
|
Pick<MailInstructionUnit, "id" | "uiKind">,
|
|
|
): MailInstructionUnit {
|
|
|
return {
|
|
|
mailType: "NEW_CONTAINER",
|
|
|
label: partial.uiKind,
|
|
|
summary: "",
|
|
|
keywords: [],
|
|
|
source: "body_segment",
|
|
|
segmentIndex: 0,
|
|
|
isCurrent: false,
|
|
|
segmentText: "",
|
|
|
...partial,
|
|
|
};
|
|
|
}
|
|
|
|
|
|
describe("resolveConfirmInstruction", () => {
|
|
|
const forecast = unit({
|
|
|
id: "forecast-body_segment-5-派送单请查收",
|
|
|
uiKind: "forecast",
|
|
|
isCurrent: false,
|
|
|
});
|
|
|
const transfer = unit({
|
|
|
id: "transfer-body_segment-0-新增转仓",
|
|
|
uiKind: "transfer",
|
|
|
mailType: "TRANSFER",
|
|
|
isCurrent: true,
|
|
|
});
|
|
|
|
|
|
it("selects the requested instruction even when it is historical forecast", () => {
|
|
|
const resolved = resolveConfirmInstruction({
|
|
|
units: [transfer, forecast],
|
|
|
instructionId: forecast.id,
|
|
|
});
|
|
|
expect(resolved.reason).toBe("ok");
|
|
|
expect(resolved.unit?.id).toBe(forecast.id);
|
|
|
expect(resolved.unit?.uiKind).toBe("forecast");
|
|
|
});
|
|
|
|
|
|
it("does not infer from mail main type when instruction_id is missing and multiple units exist", () => {
|
|
|
const resolved = resolveConfirmInstruction({
|
|
|
units: [transfer, forecast],
|
|
|
});
|
|
|
expect(resolved.reason).toBe("missing_id");
|
|
|
expect(resolved.unit).toBeNull();
|
|
|
expect(resolved.candidates.map((u) => u.uiKind)).toEqual([
|
|
|
"transfer",
|
|
|
"forecast",
|
|
|
]);
|
|
|
});
|
|
|
|
|
|
it("auto-selects the only pending confirmable unit", () => {
|
|
|
const resolved = resolveConfirmInstruction({
|
|
|
units: [forecast],
|
|
|
});
|
|
|
expect(resolved.reason).toBe("ok");
|
|
|
expect(resolved.unit?.id).toBe(forecast.id);
|
|
|
});
|
|
|
|
|
|
it("keeps an already-imported unit visible as readonly", () => {
|
|
|
const resolved = resolveConfirmInstruction({
|
|
|
units: [forecast],
|
|
|
instructionId: forecast.id,
|
|
|
lineage: {
|
|
|
instruction_state: {
|
|
|
[forecast.id]: { status: "IMPORTED", importedAt: "2026-08-17" },
|
|
|
},
|
|
|
},
|
|
|
});
|
|
|
expect(resolved.reason).toBe("already_imported");
|
|
|
expect(resolved.unit?.id).toBe(forecast.id);
|
|
|
});
|
|
|
});
|
|
|
|
|
|
describe("extractInstructionsFromMail forceForecast parity", () => {
|
|
|
it("mail100-style DO + 整柜直送 subject still yields forecast unit", () => {
|
|
|
const mail = {
|
|
|
subject:
|
|
|
"Fw: Fw:回复:柜号:TRHU8524770+提单号:EGLV143661466958+封号:EMCEJJ0635+YWZD+不带车架+整柜直送",
|
|
|
body_text: "TRHU8524770 DO请查收",
|
|
|
attachments: [{ filename: "TRHU8524770-DO-1.pdf" }],
|
|
|
parse_result: {
|
|
|
container_header: { F_ContainerNo: "TRHU8524770" },
|
|
|
mail_record: { kind: "BL_FORECAST" },
|
|
|
},
|
|
|
};
|
|
|
expect(shouldForceForecastUnit(mail)).toBe(true);
|
|
|
const units = extractInstructionsFromMail(mail);
|
|
|
expect(units.some((u) => u.uiKind === "do_upload")).toBe(true);
|
|
|
const forecast = units.find((u) => u.uiKind === "forecast");
|
|
|
expect(forecast).toBeTruthy();
|
|
|
expect(forecast!.id).toMatch(/^forecast-record-/);
|
|
|
const resolved = resolveConfirmInstruction({
|
|
|
units,
|
|
|
instructionId: forecast!.id,
|
|
|
});
|
|
|
expect(resolved.reason).toBe("ok");
|
|
|
expect(resolved.unit?.uiKind).toBe("forecast");
|
|
|
});
|
|
|
|
|
|
it("forces forecast from 整柜直送 subject even without mail_record", () => {
|
|
|
const units = extractInstructionsFromMail({
|
|
|
subject:
|
|
|
"回复:柜号:TRHU8524770+提单号:EGLV143661466958+YWZD+不带车架+整柜直送",
|
|
|
body_text: "DO请查收",
|
|
|
attachments: [{ filename: "TRHU8524770-DO-1.pdf" }],
|
|
|
});
|
|
|
expect(units.some((u) => u.uiKind === "forecast")).toBe(true);
|
|
|
expect(units.some((u) => u.uiKind === "do_upload")).toBe(true);
|
|
|
});
|
|
|
|
|
|
it("does not force forecast for work-order mail that only has container in subject", () => {
|
|
|
const mail = {
|
|
|
subject: "Fw: 转发:TIIU8073522-90022",
|
|
|
body_text:
|
|
|
"主题 TIIU8073522-90022\nBAZUS001787430-90022 4PX 预约码: ==CNF26070717",
|
|
|
mail_type: "WORK_ORDER",
|
|
|
attachments: [] as Array<{ filename: string }>,
|
|
|
parse_result: {
|
|
|
container_header: { F_ContainerNo: "TIIU8073522" },
|
|
|
mail_record: { kind: "WORK_ORDER" },
|
|
|
},
|
|
|
};
|
|
|
expect(shouldForceForecastUnit(mail)).toBe(false);
|
|
|
const units = extractInstructionsFromMail(mail);
|
|
|
expect(units.some((u) => u.uiKind === "forecast")).toBe(false);
|
|
|
});
|
|
|
});
|