156 lines
5.6 KiB
JavaScript
156 lines
5.6 KiB
JavaScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
getCustomerProductRestriction,
|
|
getProductCategoryRestrictionForCustomer,
|
|
hasValidCustomerProductRestrictionContract,
|
|
isProductRestrictedForCustomer,
|
|
normalizeCustomerAttributeProductRestriction,
|
|
} from "@/features/customer/customerProductRules.js";
|
|
import { getCustomerRuleProductImpact } from "@/features/customer/customerRuleProductImpact.js";
|
|
|
|
const attribute = (name, productIds, collections = []) => ({
|
|
attribute: name,
|
|
product_restriction: {
|
|
version: 3,
|
|
disabled_product_ids: productIds,
|
|
collections,
|
|
},
|
|
});
|
|
|
|
describe("customer product rules", () => {
|
|
it("matches only configured product ids and never infers from category or name", () => {
|
|
const attributes = [attribute("restrictTankCleaning", [52])];
|
|
|
|
expect(isProductRestrictedForCustomer({ id: "52", category: 4, name: "Ordinary wash" }, attributes)).toBe(true);
|
|
expect(isProductRestrictedForCustomer({ id: 53, category: 5, name: "Tank cleaning" }, attributes)).toBe(false);
|
|
expect(isProductRestrictedForCustomer({ id: 54, name: "Tankrens premium" }, attributes)).toBe(false);
|
|
});
|
|
|
|
it("keeps a deterministic primary rule and returns every matching rule", () => {
|
|
const attributes = [attribute("restrictInteriorCleaning", [63]), attribute("restrictAdditionalServices", [63])];
|
|
|
|
expect(getCustomerProductRestriction({ id: 63 }, attributes)).toMatchObject({
|
|
restricted: true,
|
|
rule: "restrictAdditionalServices",
|
|
rules: ["restrictAdditionalServices", "restrictInteriorCleaning"],
|
|
messageKey: "pos.restrictions.addons_not_allowed",
|
|
});
|
|
});
|
|
|
|
it("returns the collections that contain a blocked product", () => {
|
|
const attributes = [
|
|
attribute(
|
|
"restrictSpotFree",
|
|
[],
|
|
[
|
|
{ id: 7, name: "Rinses", product_ids: [23, "24"] },
|
|
{ id: 8, name: "Legacy", products: [{ id: 23 }] },
|
|
]
|
|
),
|
|
];
|
|
|
|
const restriction = getCustomerProductRestriction({ product_id: "23" }, attributes);
|
|
|
|
expect(restriction.collections).toEqual([
|
|
{ id: 7, name: "Rinses", product_ids: [23, 24], attribute: "restrictSpotFree" },
|
|
{ id: 8, name: "Legacy", product_ids: [23], attribute: "restrictSpotFree" },
|
|
]);
|
|
});
|
|
|
|
it("treats workflow rules, legacy string rows, and malformed ids as unrestricted", () => {
|
|
expect(getCustomerProductRestriction({ id: 23 }, ["restrictSpotFree"])).toEqual({
|
|
restricted: false,
|
|
rule: null,
|
|
rules: [],
|
|
collections: [],
|
|
messageKey: null,
|
|
});
|
|
expect(isProductRestrictedForCustomer({ id: 23 }, [attribute("requiresReferenceNumber", [23])])).toBe(false);
|
|
expect(isProductRestrictedForCustomer({ id: "not-an-id" }, [attribute("restrictSpotFree", [23])])).toBe(false);
|
|
expect(isProductRestrictedForCustomer({ id: "23-trailing" }, [attribute("restrictSpotFree", [23])])).toBe(false);
|
|
});
|
|
|
|
it("validates the structured exact-product contract for every active product-impact rule", () => {
|
|
expect(
|
|
hasValidCustomerProductRestrictionContract([
|
|
attribute("restrictSpotFree", [23], [{ id: 7, name: "Rinses", product_ids: [23] }]),
|
|
{ attribute: "requiresReferenceNumber", product_restriction: null },
|
|
])
|
|
).toBe(true);
|
|
expect(hasValidCustomerProductRestrictionContract([{ attribute: "restrictSpotFree" }])).toBe(false);
|
|
expect(
|
|
hasValidCustomerProductRestrictionContract([
|
|
{
|
|
attribute: "restrictSpotFree",
|
|
product_restriction: { version: 1, collections: [], disabled_product_ids: null },
|
|
},
|
|
])
|
|
).toBe(false);
|
|
});
|
|
|
|
it("normalizes top-level and nested restriction response variants", () => {
|
|
expect(
|
|
normalizeCustomerAttributeProductRestriction({
|
|
disabled_product_ids: ["23", 23, 0],
|
|
collections: [{ id: 2, products: [{ product_id: 24 }] }],
|
|
})
|
|
).toEqual({
|
|
version: 0,
|
|
collections: [{ id: 2, name: "", product_ids: [24] }],
|
|
disabled_product_ids: [23, 24],
|
|
});
|
|
});
|
|
|
|
it("keeps category helpers as an exact-id compatibility seam", () => {
|
|
const attributes = [attribute("onlyTankCleaning", [10])];
|
|
|
|
expect(getProductCategoryRestrictionForCustomer(4, attributes).restricted).toBe(false);
|
|
expect(getProductCategoryRestrictionForCustomer(4, attributes, { productId: 10 }).restricted).toBe(true);
|
|
});
|
|
|
|
it("describes exact primary and addon impact from the configured set", () => {
|
|
const products = [
|
|
{
|
|
id: 10,
|
|
name: "Truck wash",
|
|
addons: [
|
|
{ id: 900, option_id: 63, name: "Interior", product: { id: 63, name: "Interior" } },
|
|
{ id: 901, option_id: 64, name: "Dolly", product: { id: 64, name: "Dolly" } },
|
|
],
|
|
},
|
|
{ id: 53, name: "Primary product" },
|
|
];
|
|
const impact = getCustomerRuleProductImpact(
|
|
"restrictInteriorCleaning",
|
|
products,
|
|
attribute("restrictInteriorCleaning", [53, 63])
|
|
);
|
|
|
|
expect(impact.blocked.primaryProducts.map((product) => product.id)).toEqual([53]);
|
|
expect(impact.blocked.relatedAddons.map((product) => product.id)).toEqual([63]);
|
|
expect(impact.available.primaryProducts).toEqual([]);
|
|
});
|
|
|
|
it("does not give workflow-only rules product impact", () => {
|
|
expect(
|
|
getCustomerRuleProductImpact(
|
|
"requiresReferenceNumber",
|
|
[{ id: 23, name: "Rinse" }],
|
|
attribute("requiresReferenceNumber", [23])
|
|
)
|
|
).toEqual({
|
|
hasProductImpact: false,
|
|
blocked: {
|
|
primaryProducts: [],
|
|
relatedAddons: [],
|
|
standaloneAdditionalServices: [],
|
|
},
|
|
available: {
|
|
primaryProducts: [],
|
|
relatedAddons: [],
|
|
standaloneAdditionalServices: [],
|
|
},
|
|
});
|
|
});
|
|
});
|