WFDASH-WEBAPP/src/lib/delivery-pool.test.ts

123 lines
5.3 KiB
TypeScript
Raw Normal View History

import { describe, expect, it } from "vitest";
import {
deliveryAssignmentState,
filterDeliveryPool,
groupDeliveryPool,
normalizeInvoiceNumber,
type DeliveryPoolLine,
} from "./delivery-pool";
function line(over: Partial<DeliveryPoolLine>): DeliveryPoolLine {
return {
id: "l1",
description: "M20 nut",
quantity: 1,
selling_price: 10,
customer_invoice_number: "INV100",
delivery_note_number: "",
invoiced_date: "2026-10-30",
delivery_driver: "",
delivery_assigned_at: "",
order_id: "o1",
order_number: "ORD-1",
company_id: "c1",
company_name: "Acme",
invoice_pdf: "",
...over,
};
}
describe("normalizeInvoiceNumber", () => {
it("treats bare digits and the INV prefix as the same invoice", () => {
expect(normalizeInvoiceNumber("30352")).toBe("INV30352");
expect(normalizeInvoiceNumber("inv 30352")).toBe("INV30352");
expect(normalizeInvoiceNumber("INV-30352")).toBe("INV30352");
});
it("keeps non-numeric numbers and blanks", () => {
expect(normalizeInvoiceNumber("ABC-9")).toBe("ABC-9");
expect(normalizeInvoiceNumber(" ")).toBe("");
expect(normalizeInvoiceNumber(null)).toBe("");
});
});
describe("groupDeliveryPool", () => {
it("groups per company, then one card per invoice", () => {
const groups = groupDeliveryPool([
line({ id: "a", customer_invoice_number: "30352", quantity: 2, selling_price: 5 }),
line({ id: "b", customer_invoice_number: "INV30352", delivery_note_number: "DN1" }),
line({ id: "c", customer_invoice_number: "INV1", company_id: "c2", company_name: "Bolt Co" }),
]);
expect(groups.map((g) => g.name)).toEqual(["Acme", "Bolt Co"]);
expect(groups[0].invoices).toHaveLength(1);
const inv = groups[0].invoices[0];
expect(inv.invoiceNumber).toBe("INV30352");
expect(inv.lines.map((l) => l.id)).toEqual(["a", "b"]);
expect(inv.total).toBe(20);
expect(inv.dnNumbers).toEqual(["DN1"]);
expect(groups[0].total).toBe(20);
});
it("skips lines without an invoice number and puts unknown companies last", () => {
const groups = groupDeliveryPool([
line({ id: "x", customer_invoice_number: "" }),
line({ id: "u", customer_invoice_number: "INV9", company_id: "", company_name: "" }),
line({ id: "z", customer_invoice_number: "INV8", company_id: "c9", company_name: "Zulu" }),
]);
expect(groups.map((g) => g.name)).toEqual(["Zulu", "Unknown company"]);
});
it("reports a shared assignment, or mixed when the lines disagree", () => {
const same = groupDeliveryPool([
line({ id: "a", delivery_driver: "d1", delivery_assigned_at: "2026-10-31" }),
line({ id: "b", delivery_driver: "d1", delivery_assigned_at: "2026-10-31" }),
])[0].invoices[0];
expect(same).toMatchObject({ driverId: "d1", assignedAt: "2026-10-31", mixedAssignment: false });
const mixed = groupDeliveryPool([
line({ id: "a", delivery_driver: "d1", delivery_assigned_at: "2026-10-31" }),
line({ id: "b" }),
])[0].invoices[0];
expect(mixed).toMatchObject({ driverId: "", mixedAssignment: true });
});
it("orders invoices newest first and finds the invoice PDF", () => {
const [company] = groupDeliveryPool([
line({ id: "a", customer_invoice_number: "INV1", invoiced_date: "2026-10-30" }),
line({ id: "b", customer_invoice_number: "INV2", invoiced_date: "2026-10-31", invoice_pdf: "inv2_abc.pdf" }),
]);
expect(company.invoices.map((i) => i.invoiceNumber)).toEqual(["INV2", "INV1"]);
expect(company.invoices[0].invoicePdf).toEqual({ lineId: "b", filename: "inv2_abc.pdf" });
});
});
describe("deliveryAssignmentState", () => {
const today = "2026-10-30";
it("covers every state", () => {
expect(deliveryAssignmentState({ driverId: "", assignedAt: "", mixedAssignment: false }, today)).toBe("unassigned");
expect(deliveryAssignmentState({ driverId: "d", assignedAt: today, mixedAssignment: false }, today)).toBe("today");
expect(deliveryAssignmentState({ driverId: "d", assignedAt: "2026-10-29", mixedAssignment: false }, today)).toBe("overdue");
expect(deliveryAssignmentState({ driverId: "d", assignedAt: "2026-11-02", mixedAssignment: false }, today)).toBe("future");
expect(deliveryAssignmentState({ driverId: "", assignedAt: "", mixedAssignment: true }, today)).toBe("mixed");
});
});
describe("filterDeliveryPool", () => {
const groups = groupDeliveryPool([
line({ id: "a", customer_invoice_number: "INV1", delivery_driver: "d1", delivery_assigned_at: "2026-10-30" }),
line({ id: "b", customer_invoice_number: "INV2", description: "Hex bolt" }),
line({ id: "c", customer_invoice_number: "INV3", company_id: "c2", company_name: "Bolt Co" }),
]);
it("matches company, invoice and line text", () => {
expect(filterDeliveryPool(groups, "hex", "all")[0].invoices.map((i) => i.invoiceNumber)).toEqual(["INV2"]);
expect(filterDeliveryPool(groups, "bolt co", "all").map((c) => c.name)).toEqual(["Bolt Co"]);
expect(filterDeliveryPool(groups, "inv3", "all")).toHaveLength(1);
});
it("filters by driver and drops empty companies", () => {
expect(filterDeliveryPool(groups, "", "d1").map((c) => c.invoices.length)).toEqual([1]);
const unassigned = filterDeliveryPool(groups, "", "unassigned");
expect(unassigned.flatMap((c) => c.invoices.map((i) => i.invoiceNumber)).sort()).toEqual(["INV2", "INV3"]);
});
});