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.

181 lines
5.8 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 ExcelJS from "exceljs";
import { parsePackingListBuffer } from "@/services/parse/packing-list";
import { generateM2Shipments } from "../fixtures/m2-shipments";
async function buildXlsx(rows: ReturnType<typeof generateM2Shipments>): Promise<Buffer> {
const wb = new ExcelJS.Workbook();
const ws = wb.addWorksheet("卡派资料");
ws.addRow([
"仓库ID", "渠道", "件数", "总体积", "毛重", "FBA ID",
"Amazon reference ID", "分货标识", "派送地址", "备注", "柜号",
]);
for (const r of rows) {
ws.addRow([
r.F_FBACode || "",
r.channel_raw || r.F_Transporter || "",
r.F_CTNS ?? 0,
r.F_CBM,
r.F_Weight,
r.F_FBAID,
r.F_ReferenceId,
r.F_ShipmentID,
r.F_Address,
r.F_Remark,
"MATU2745683",
]);
}
const buf = await wb.xlsx.writeBuffer();
return Buffer.from(buf);
}
describe("packing-list", () => {
it("parses 327 rows with gold sample first two", async () => {
const seed = generateM2Shipments();
const buf = await buildXlsx(seed);
const parsed = await parsePackingListBuffer(buf, { enableIsoCheck: false });
expect(parsed.errorCode).toBeUndefined();
expect(parsed.shipments.length).toBe(327);
expect(parsed.shipments[0].F_FBACode).toBe("ABQ2");
expect(parsed.shipments[0].F_Transporter).toBe("TRUCK");
expect(parsed.shipments[0].F_CTNS).toBe(1);
expect(parsed.shipments[1].F_FBACode).toBe("FTW1");
expect(parsed.shipments[1].F_CTNS).toBe(4);
});
it("reports missing columns", async () => {
const wb = new ExcelJS.Workbook();
const ws = wb.addWorksheet("Sheet1");
ws.addRow(["foo", "bar"]);
ws.addRow([1, 2]);
const buf = Buffer.from(await wb.xlsx.writeBuffer());
const parsed = await parsePackingListBuffer(buf);
expect(parsed.errorCode).toBe("PARSE_FAILED");
expect(parsed.lineage.missing_columns).toBeTruthy();
});
it("parses 新辰泽官方模板(多行表头 + UPS空仓库ID)", async () => {
const fs = await import("fs/promises");
const path = await import("path");
const file = path.join(
process.cwd(),
"docx",
"邮件",
"模板",
"数据模版-新辰泽-WHSU8127240.xlsx",
);
const buf = await fs.readFile(file);
const parsed = await parsePackingListBuffer(buf, { enableIsoCheck: false });
expect(parsed.errorCode).toBeUndefined();
expect(parsed.container_header.F_ContainerNo).toBe("WHSU8127240");
expect(parsed.shipments.length).toBeGreaterThan(10);
const ups = parsed.shipments.filter((s) => s.F_Transporter === "UPS");
expect(ups.length).toBeGreaterThan(0);
expect(ups.every((s) => !s.F_FBACode)).toBe(true);
const amazon = parsed.shipments.find((s) => s.F_FBAID === "FBA16SQQ7PTF");
expect(amazon?.F_FBACode).toBe("LAS1");
const priv = parsed.shipments.find((s) =>
(s.F_Address || "").includes("12924 Pierce"),
);
expect(priv).toBeTruthy();
expect(priv!.F_FBACode).toBeFalsy();
expect(parsed.shipments.every((s) => s.F_ShipmentID)).toBe(true);
});
it("skips 注意 instruction footer in 智鸿 gold xlsx", async () => {
const fs = await import("fs/promises");
const path = await import("path");
const file = path.join(
process.cwd(),
"docx",
"邮件",
"模板",
"附件下载_邮件识别",
"智鸿2+WHLC027G597465+WHSU8127240+洛杉矶+40HQ+EDT2026.04-25 ETA2026.05-15船名航次HMM EMERALD 013E+提拆派.xlsx",
);
const buf = await fs.readFile(file);
const parsed = await parsePackingListBuffer(buf, { enableIsoCheck: false });
expect(parsed.errorCode).toBeUndefined();
expect(parsed.shipments.length).toBe(1);
expect(parsed.shipments[0].row_status).toBe("VALID");
expect(parsed.shipments[0].F_Transporter).toBe("留仓");
expect(parsed.shipments[0].F_FBACode).toBe("HOLD");
expect(parsed.shipments[0].F_CTNS).toBe(1041);
expect(
parsed.shipments.every((s) => !String(s.F_FBACode || "").includes("注意")),
).toBe(true);
});
it("parses FormOrder extra columns (PO/PCS/City/送仓/SKU)", async () => {
const wb = new ExcelJS.Workbook();
const ws = wb.addWorksheet("Sheet1");
ws.addRow([
"Move Type",
"Custmer Ref No.",
"FBAId(Amazon)",
"PO(Amazon)",
"FBACode",
"Address",
"Qty.(CTNS)",
"Vol(CBM)",
"G.W.(KGS)",
"PCS",
"City",
"State",
"ZipCode",
"Contact",
"Mobile",
"Email",
"行备注",
"ReferenceId",
"最早送仓日期",
"最晚送仓日期",
"SKU",
"品名",
"英文名称",
]);
ws.addRow([
"卡派",
"REF001",
"FBA16SQQ7PTF",
"PO123",
"LAS1",
"12924 Pierce St",
2,
0.5,
10,
20,
"Los Angeles",
"CA",
"90001",
"Tom",
"123456",
"a@b.com",
"note1",
"REFID1",
"2026-05-01",
"2026-05-10",
"SKU-1",
"玩具",
"Toy",
]);
const buf = Buffer.from(await wb.xlsx.writeBuffer());
const parsed = await parsePackingListBuffer(buf, { enableIsoCheck: false });
expect(parsed.errorCode).toBeUndefined();
expect(parsed.shipments).toHaveLength(1);
const s = parsed.shipments[0];
expect(s.F_Transporter).toBe("TRUCK");
expect(s.F_PO).toBe("PO123");
expect(s.F_Pcs).toBe(20);
expect(s.F_City).toBe("Los Angeles");
expect(s.F_State).toBe("CA");
expect(s.F_ZipCode).toBe("90001");
expect(s.F_Contact).toBe("Tom");
expect(s.F_Remark).toBe("note1");
expect(s.F_SKU).toBe("SKU-1");
expect(s.F_Description).toBe("玩具");
expect(s.F_EnglishName).toBe("Toy");
expect(s.F_Expected_DeliveryDateB).toBeTruthy();
expect(s.F_Expected_DeliveryDateE).toBeTruthy();
});
});