import { describe, expect, it } from "vitest"; import { canEnterForecastConfirm, collectForecastImportIssues, } from "@/services/cc/forecast-confirm-gate"; describe("canEnterForecastConfirm", () => { it("allows forecast data regardless of mail main type", () => { expect( canEnterForecastConfirm({ status: "PENDING_CONFIRM", validShipmentCount: 1, }), ).toBe(true); }); it("blocks empty forecast without container header", () => { expect( canEnterForecastConfirm({ status: "PENDING_CONFIRM", validShipmentCount: 0, }), ).toBe(false); }); it("allows header-only forecast (no shipment rows)", () => { expect( canEnterForecastConfirm({ status: "PENDING_CONFIRM", validShipmentCount: 0, hasContainerHeader: true, }), ).toBe(true); }); it("allows PARTIAL_SUCCESS so remaining instructions stay confirmable", () => { expect( canEnterForecastConfirm({ status: "PARTIAL_SUCCESS", validShipmentCount: 1, }), ).toBe(true); }); }); describe("collectForecastImportIssues", () => { const base = { transMode: 0 as const, operationType: 0 as const, containerNo: "TRHU8524770", hasContainerConflict: false, containerSource: null as "body" | "attachment" | null, validRowCount: 1, selectedValidCount: 1, hasUnmappedSelected: false, ackUnmapped: false, }; it("does not block header-only import when no valid rows", () => { const issues = collectForecastImportIssues({ ...base, validRowCount: 0, selectedValidCount: 0, shipmentCount: 0, }); expect(issues.blockers).toEqual([]); expect(issues.notes.some((n) => n.includes("只提交柜头"))).toBe(true); }); it("blocks when valid rows exist but none selected", () => { const issues = collectForecastImportIssues({ ...base, selectedValidCount: 0, }); expect(issues.blockers[0]).toContain("至少勾选一行有效货件"); expect(issues.errors.rows).toBeTruthy(); }); it("explains empty and invalid container numbers", () => { expect( collectForecastImportIssues({ ...base, containerNo: "" }).blockers[0], ).toContain("柜号为空"); expect( collectForecastImportIssues({ ...base, containerNo: "ABCD1234567" }) .blockers[0], ).toContain("ISO 6346"); }); it("summarizes invalid packing rows without blocking header-only", () => { const issues = collectForecastImportIssues({ ...base, validRowCount: 0, selectedValidCount: 0, invalidRows: [ { row_index: 1, invalid_reasons: ["渠道缺失", "件数无效"] }, { row_index: 2, invalid_reasons: ["渠道缺失"] }, ], }); expect(issues.blockers).toEqual([]); expect(issues.notes[0]).toContain("渠道缺失(2行)"); expect(issues.notes[0]).toContain("件数无效"); }); it("does not block client portal on the first tab", () => { const issues = collectForecastImportIssues({ ...base, portal: "client", step: 0, }); expect(issues.blockers).toEqual([]); }); });