Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3fac27697b |
@@ -234,6 +234,7 @@ jobs:
|
|||||||
--env PLAYWRIGHT_WORKERS="$PLAYWRIGHT_WORKERS" \
|
--env PLAYWRIGHT_WORKERS="$PLAYWRIGHT_WORKERS" \
|
||||||
--env PLAYWRIGHT_VIDEO_MODE="$PLAYWRIGHT_VIDEO_MODE" \
|
--env PLAYWRIGHT_VIDEO_MODE="$PLAYWRIGHT_VIDEO_MODE" \
|
||||||
--env PLAYWRIGHT_DEV_PORT="$playwright_dev_port" \
|
--env PLAYWRIGHT_DEV_PORT="$playwright_dev_port" \
|
||||||
|
--env PLAYWRIGHT_WORKERS="${PLAYWRIGHT_WORKERS:-1}" \
|
||||||
--env MATRIX_SUITE="$MATRIX_SUITE" \
|
--env MATRIX_SUITE="$MATRIX_SUITE" \
|
||||||
--env MATRIX_PROJECT="$MATRIX_PROJECT" \
|
--env MATRIX_PROJECT="$MATRIX_PROJECT" \
|
||||||
--env DIFF_BASE_REF="$DIFF_BASE_REF" \
|
--env DIFF_BASE_REF="$DIFF_BASE_REF" \
|
||||||
|
|||||||
@@ -65,8 +65,19 @@ export const sourceMappings = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "pos",
|
name: "pos",
|
||||||
patterns: [/\/pos[/-]/iu, /POS/iu, /^src\/assets\/pos\.css$/u],
|
patterns: [
|
||||||
specs: ["tests/e2e/pos-flow.spec.js", "tests/e2e/pos-mobile-order-flow.spec.js", "tests/e2e/admin-pos-orders.spec.ts"],
|
/\/pos[/-]/iu,
|
||||||
|
/POS/iu,
|
||||||
|
/^src\/assets\/pos\.css$/u,
|
||||||
|
/^src\/components\/displays\/boxes\/ProductBox\.vue$/u,
|
||||||
|
/^src\/features\/customer\/customerProductRules\.js$/u,
|
||||||
|
],
|
||||||
|
specs: [
|
||||||
|
"tests/e2e/pos-flow.spec.js",
|
||||||
|
"tests/e2e/pos-mobile-order-flow.spec.js",
|
||||||
|
"tests/e2e/admin-pos-orders.spec.ts",
|
||||||
|
"tests/e2e/pos-customer-rules.spec.js",
|
||||||
|
],
|
||||||
projects: chromiumProjects,
|
projects: chromiumProjects,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import Swal from "sweetalert2";
|
|||||||
import CustomerProductDiscountDisplay
|
import CustomerProductDiscountDisplay
|
||||||
from "@/components/displays/department/pos/displays/CustomerProductDiscountDisplay.vue";
|
from "@/components/displays/department/pos/displays/CustomerProductDiscountDisplay.vue";
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { isProductRestrictedForCustomer } from "@/features/customer/customerProductRules.js";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const expandIcon = ref(null);
|
const expandIcon = ref(null);
|
||||||
@@ -83,23 +84,6 @@ const hasRestrictAdditionalServices = () => {
|
|||||||
return customerAttributesSafe.value.some((attribute) => attribute.attribute === 'restrictAdditionalServices');
|
return customerAttributesSafe.value.some((attribute) => attribute.attribute === 'restrictAdditionalServices');
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasRestrictSpotFree = () => {
|
|
||||||
return customerAttributesSafe.value.some((attribute) => attribute.attribute === 'restrictSpotFree');
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasRestrictInteriorCleaning = () => {
|
|
||||||
return customerAttributesSafe.value.some((attribute) => attribute.attribute === 'restrictInteriorCleaning');
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasRestrictTankCleaning = () => {
|
|
||||||
return customerAttributesSafe.value.some((attribute) => attribute.attribute === 'restrictTankCleaning');
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasOnlyTankCleaning = () => {
|
|
||||||
return customerAttributesSafe.value.some((attribute) => attribute.attribute === 'onlyTankCleaning');
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On before add product addon
|
* On before add product addon
|
||||||
* This is used to check if adding a product has any requirements.
|
* This is used to check if adding a product has any requirements.
|
||||||
@@ -162,37 +146,19 @@ const doesAddonHaveNoteRequirement = (addon) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isAddonRestricted = (addon) => {
|
const isAddonRestricted = (addon) => {
|
||||||
const addonName = getAddonName(addon).toLowerCase();
|
const addonProduct = getAddonProduct(addon);
|
||||||
// Check if the addon has "Spot Free" in the name
|
return isProductRestrictedForCustomer({
|
||||||
if (hasRestrictSpotFree() && addonName.includes('spot free')) {
|
...addonProduct,
|
||||||
return true;
|
name: getAddonName(addon) || addonProduct.name,
|
||||||
}
|
category: addonProduct.category ?? addon?.category,
|
||||||
// Check if the addon has "Indvendig vask" in the name
|
}, customerAttributesSafe.value);
|
||||||
if (hasRestrictInteriorCleaning() && addonName.includes('indvendig vask')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const isProductRestricted = () => {
|
const isProductRestricted = () => {
|
||||||
const productName = String(props.name || '').toLowerCase();
|
return isProductRestrictedForCustomer({
|
||||||
// Check if the product has "Spot Free" in the name
|
...props.product,
|
||||||
if (hasRestrictSpotFree() && productName.includes('spot free')) {
|
name: props.name || props.product?.name,
|
||||||
return true;
|
}, customerAttributesSafe.value);
|
||||||
}
|
|
||||||
// Check if the product has "Indvendig vask" in the name
|
|
||||||
if (hasRestrictInteriorCleaning() && productName.includes('indvendig vask')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Check if the product category is 5 (Tank Cleaning)
|
|
||||||
if (props.product?.category === 5) {
|
|
||||||
if (hasRestrictTankCleaning()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!hasOnlyTankCleaning()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const orderByOrderPriority = (addons) => {
|
const orderByOrderPriority = (addons) => {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
getProductsCategory,
|
getProductsCategory,
|
||||||
loadOrderItems,
|
loadOrderItems,
|
||||||
productCategoryAllowed,
|
productCategoryAllowed,
|
||||||
|
isProductRestricted,
|
||||||
setProductsCategory,
|
setProductsCategory,
|
||||||
getUserProductDiscount,
|
getUserProductDiscount,
|
||||||
showFakeCreateOrderItem,
|
showFakeCreateOrderItem,
|
||||||
@@ -13,7 +14,8 @@ import {
|
|||||||
getUserGlobalDiscount,
|
getUserGlobalDiscount,
|
||||||
getUserDiscounts,
|
getUserDiscounts,
|
||||||
customer_id,
|
customer_id,
|
||||||
customer_attributes
|
customer_attributes,
|
||||||
|
loadCustomerAttributes
|
||||||
} from "@/components/shop/POSDepartmentProcess.vue";
|
} from "@/components/shop/POSDepartmentProcess.vue";
|
||||||
import {getProductCategory, getProducts} from "@/components/shop/Products.vue";
|
import {getProductCategory, getProducts} from "@/components/shop/Products.vue";
|
||||||
import {createOrderItem} from "@/components/shop/OrdersItems.vue";
|
import {createOrderItem} from "@/components/shop/OrdersItems.vue";
|
||||||
@@ -151,6 +153,10 @@ const fetchProductsForCategory = async ({
|
|||||||
products.value = [];
|
products.value = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (customerNumber) {
|
||||||
|
await loadCustomerAttributes(customerNumber);
|
||||||
|
}
|
||||||
|
|
||||||
const response = normalizedCategory === null
|
const response = normalizedCategory === null
|
||||||
? await getProducts(departmentId, props.finalPrice, customerNumber)
|
? await getProducts(departmentId, props.finalPrice, customerNumber)
|
||||||
: await getProductCategory(normalizedCategory, departmentId, props.finalPrice, customerNumber);
|
: await getProductCategory(normalizedCategory, departmentId, props.finalPrice, customerNumber);
|
||||||
@@ -593,7 +599,7 @@ const getRecommendedProductPrice = (productId) => {
|
|||||||
|
|
||||||
const isRecommendedProductCategoryAllowed = (productId) => {
|
const isRecommendedProductCategoryAllowed = (productId) => {
|
||||||
const product = getProductById(productId);
|
const product = getProductById(productId);
|
||||||
return !!product && productCategoryAllowed(product.category);
|
return !!product && !isProductRestricted(product);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addRecommendedProductToOrder = async (productId) => {
|
const addRecommendedProductToOrder = async (productId) => {
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ import { getAttributes } from "@/components/shop/CustomerAttributes.vue";
|
|||||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||||
import { authenticatedRequest } from "@/components/session/authenticatedRequest.vue";
|
import { authenticatedRequest } from "@/components/session/authenticatedRequest.vue";
|
||||||
import { doesOrderContainWashCertificateProduct } from "@/components/displays/department/pos/utils/washCertificate.js";
|
import { doesOrderContainWashCertificateProduct } from "@/components/displays/department/pos/utils/washCertificate.js";
|
||||||
|
import {
|
||||||
|
isProductCategoryRestrictedForCustomer,
|
||||||
|
isProductRestrictedForCustomer,
|
||||||
|
} from "@/features/customer/customerProductRules.js";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -153,6 +157,8 @@ export const nextStep = async (options = { isMobile: false, orderCreation: true
|
|||||||
clearErrors();
|
clearErrors();
|
||||||
// If the current step is 1, create the order
|
// If the current step is 1, create the order
|
||||||
if (step.value === 1) {
|
if (step.value === 1) {
|
||||||
|
await loadCustomerAttributes(customer_id.value);
|
||||||
|
|
||||||
if (normalizedOptions.orderCreation === false) {
|
if (normalizedOptions.orderCreation === false) {
|
||||||
step.value++;
|
step.value++;
|
||||||
return;
|
return;
|
||||||
@@ -1613,24 +1619,7 @@ export const customerUsesPONumbers = () => {
|
|||||||
|
|
||||||
/** Check if the product category is allowed */
|
/** Check if the product category is allowed */
|
||||||
export const productCategoryAllowed = (category) => {
|
export const productCategoryAllowed = (category) => {
|
||||||
// Check if the product category is the addons category
|
return !isProductCategoryRestrictedForCustomer(category, customer_attributes.value);
|
||||||
if (category === "addons") {
|
|
||||||
// Check if the customer has the attribute restrictAdditionalServices
|
|
||||||
return !hasAttribute("restrictAdditionalServices");
|
|
||||||
}
|
|
||||||
if (category === "spot_free") {
|
|
||||||
// Check if the customer has the attribute restrictSpotFree
|
|
||||||
return !hasAttribute("restrictSpotFree");
|
|
||||||
}
|
|
||||||
if (category === "interior_cleaning") {
|
|
||||||
// Check if the customer has the attribute restrictInteriorCleaning
|
|
||||||
return !hasAttribute("restrictInteriorCleaning");
|
|
||||||
}
|
|
||||||
if (category === "tank_cleaning") {
|
|
||||||
// Check if the customer has the attribute restrictTankCleaning
|
|
||||||
return !hasAttribute("restrictTankCleaning");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Check if the customer should be billed per order, or per billing period */
|
/** Check if the customer should be billed per order, or per billing period */
|
||||||
@@ -1676,43 +1665,12 @@ export const hasOnlyTankCleaning = () => {
|
|||||||
|
|
||||||
/** Check if a product is restricted based on customer attributes */
|
/** Check if a product is restricted based on customer attributes */
|
||||||
export const isProductRestricted = (product) => {
|
export const isProductRestricted = (product) => {
|
||||||
if (!product) return false;
|
return isProductRestrictedForCustomer(product, customer_attributes.value);
|
||||||
const productName = (product.name || "").toLowerCase();
|
|
||||||
|
|
||||||
// Check if the product has "Spot Free" in the name
|
|
||||||
if (!canBuySpotFree() && productName.includes("spot free")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Check if the product has "Indvendig vask" in the name
|
|
||||||
if (!canBuyInteriorCleaning() && productName.includes("indvendig vask")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Check if the product category is 5 (Tank Cleaning)
|
|
||||||
if (product.category === 5) {
|
|
||||||
if (!canBuyTankCleaning()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!hasOnlyTankCleaning()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Check if an addon is restricted based on customer attributes */
|
/** Check if an addon is restricted based on customer attributes */
|
||||||
export const isAddonRestricted = (addon) => {
|
export const isAddonRestricted = (addon) => {
|
||||||
if (!addon) return false;
|
return isProductRestrictedForCustomer(addon, customer_attributes.value);
|
||||||
const addonName = (addon.name || "").toLowerCase();
|
|
||||||
|
|
||||||
// Check if the addon has "Spot Free" in the name
|
|
||||||
if (!canBuySpotFree() && addonName.includes("spot free")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Check if the addon has "Indvendig vask" in the name
|
|
||||||
if (!canBuyInteriorCleaning() && addonName.includes("indvendig vask")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function loadOrderDetails(onAfterSuccess = null, options = {}) {
|
export function loadOrderDetails(onAfterSuccess = null, options = {}) {
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
const TANK_CLEANING_TERMS = ["tank cleaning", "tankcleaning", "tankrens"];
|
||||||
|
|
||||||
|
const normalizeAttributeName = (attribute) => {
|
||||||
|
if (typeof attribute === "string") {
|
||||||
|
return attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
return String(attribute?.attribute ?? "");
|
||||||
|
};
|
||||||
|
|
||||||
|
export const hasCustomerAttribute = (attributes, attributeName) => {
|
||||||
|
if (!Array.isArray(attributes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return attributes.some((attribute) => normalizeAttributeName(attribute) === attributeName);
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizedText = (value) => String(value ?? "").trim().toLowerCase();
|
||||||
|
|
||||||
|
const textContainsAny = (value, terms) => {
|
||||||
|
const text = normalizedText(value);
|
||||||
|
return terms.some((term) => term !== "" && text.includes(term));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isTankCleaningCategory = (category) => {
|
||||||
|
if (Number(category) === 5) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return textContainsAny(category, ["tank_cleaning", ...TANK_CLEANING_TERMS]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isTankCleaningProduct = (product) => {
|
||||||
|
if (!product) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTankCleaningCategory(product.category ?? product.product_category)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return textContainsAny(
|
||||||
|
[
|
||||||
|
product.name,
|
||||||
|
product.product_name,
|
||||||
|
product.category_name,
|
||||||
|
product.categoryName,
|
||||||
|
].join(" "),
|
||||||
|
TANK_CLEANING_TERMS
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isProductCategoryRestrictedForCustomer = (category, attributes = []) => {
|
||||||
|
if (category === "addons" && hasCustomerAttribute(attributes, "restrictAdditionalServices")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (category === "spot_free" && hasCustomerAttribute(attributes, "restrictSpotFree")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (category === "interior_cleaning" && hasCustomerAttribute(attributes, "restrictInteriorCleaning")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (isTankCleaningCategory(category) && hasCustomerAttribute(attributes, "restrictTankCleaning")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (hasCustomerAttribute(attributes, "onlyTankCleaning") && !isTankCleaningCategory(category)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isProductRestrictedForCustomer = (product, attributes = []) => {
|
||||||
|
if (!product) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const productName = normalizedText(product.name ?? product.product_name);
|
||||||
|
|
||||||
|
if (hasCustomerAttribute(attributes, "restrictSpotFree") && productName.includes("spot free")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (hasCustomerAttribute(attributes, "restrictInteriorCleaning") && productName.includes("indvendig vask")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (isTankCleaningProduct(product) && hasCustomerAttribute(attributes, "restrictTankCleaning")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (hasCustomerAttribute(attributes, "onlyTankCleaning") && !isTankCleaningProduct(product)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
@@ -257,3 +257,70 @@ test("customer details renders economic customer mobilePhone object as number on
|
|||||||
await expect(phoneValue).not.toContainText("country_code");
|
await expect(phoneValue).not.toContainText("country_code");
|
||||||
await expect(phoneValue).not.toContainText("number");
|
await expect(phoneValue).not.toContainText("number");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("only tankcleaning customers can only add tankcleaning products", async ({ page }, testInfo) => {
|
||||||
|
test.skip(!testInfo.project.name.includes("desktop"), "Desktop only");
|
||||||
|
|
||||||
|
const baseFixture = createPosFixture();
|
||||||
|
const tankCleaningProduct = {
|
||||||
|
id: 66,
|
||||||
|
name: "Tank cleaning 4 spulehoveder",
|
||||||
|
description: "Tankcleaning service",
|
||||||
|
price: 799,
|
||||||
|
subscription_allowed: true,
|
||||||
|
category: 5,
|
||||||
|
piktogram: "truck",
|
||||||
|
apply_category_discount: true,
|
||||||
|
requires_note: false,
|
||||||
|
is_wash: false,
|
||||||
|
display_in_booking_form: true,
|
||||||
|
order_priority: 1,
|
||||||
|
addons: [],
|
||||||
|
};
|
||||||
|
const fixture = createPosFixture({
|
||||||
|
customerAttributesByNumber: {
|
||||||
|
12345679: [{ id: 1, customer_number: 12345679, attribute: "onlyTankCleaning" }],
|
||||||
|
},
|
||||||
|
products: [...baseFixture.products, tankCleaningProduct],
|
||||||
|
departmentCategories: [
|
||||||
|
...baseFixture.departmentCategories,
|
||||||
|
{ id: 13, department_id: 12, category: { id: 5, name: "Tankcleaning", meta: { products: [66] } } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await mockApi(page, {
|
||||||
|
authenticated: true,
|
||||||
|
permissions: POS_PERMISSIONS,
|
||||||
|
edgeGateways: false,
|
||||||
|
pos: fixture,
|
||||||
|
});
|
||||||
|
await primeOperatorSession(page, "pos-only-tankcleaning-token");
|
||||||
|
|
||||||
|
const customer = fixture.customersByNumber[12345679];
|
||||||
|
const customerAttributesResponse = page.waitForResponse((response) => {
|
||||||
|
return (
|
||||||
|
response.request().method() === "GET" &&
|
||||||
|
response.url().includes(`/customer/attributes?customer_number=${customer.customerNumber}`)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
await openPosAndSelectCustomer(page, customer);
|
||||||
|
await customerAttributesResponse;
|
||||||
|
|
||||||
|
await page.getByTestId("pos-step-1").getByTestId("pos-next-step").click();
|
||||||
|
await expect(page.getByTestId("pos-step-2")).toBeVisible({ timeout: POS_STEP_TIMEOUT });
|
||||||
|
|
||||||
|
await page.getByTestId("pos-product-card-53").first().click();
|
||||||
|
await expect(page.getByTestId("pos-add-to-cart-53").first()).toBeDisabled();
|
||||||
|
|
||||||
|
await page.locator(".tabs li").filter({ hasText: "Tankcleaning" }).click();
|
||||||
|
await expect(page.getByTestId("pos-product-card-66").first()).toBeVisible();
|
||||||
|
await page.getByTestId("pos-product-card-66").first().click();
|
||||||
|
await expect(page.getByTestId("pos-add-to-cart-66").first()).toBeEnabled();
|
||||||
|
|
||||||
|
const addRequest = page.waitForRequest((request) => {
|
||||||
|
return request.method() === "POST" && request.url().includes("/order/items");
|
||||||
|
});
|
||||||
|
await page.getByTestId("pos-add-to-cart-66").first().click();
|
||||||
|
const request = await addRequest;
|
||||||
|
expect(request.postDataJSON().product_id).toBe(66);
|
||||||
|
});
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import {
|
||||||
|
isProductCategoryRestrictedForCustomer,
|
||||||
|
isProductRestrictedForCustomer,
|
||||||
|
isTankCleaningProduct,
|
||||||
|
} from "@/features/customer/customerProductRules.js";
|
||||||
|
|
||||||
|
const onlyTankCleaning = [{ attribute: "onlyTankCleaning" }];
|
||||||
|
const restrictTankCleaning = [{ attribute: "restrictTankCleaning" }];
|
||||||
|
|
||||||
|
describe("customer product rules", () => {
|
||||||
|
it("recognizes tankcleaning products by category and legacy names", () => {
|
||||||
|
expect(isTankCleaningProduct({ category: 5, name: "Saebe/kemi" })).toBe(true);
|
||||||
|
expect(isTankCleaningProduct({ category: 4, name: "Tank cleaning 4 spulehoveder" })).toBe(true);
|
||||||
|
expect(isTankCleaningProduct({ category: 4, name: "Saebe/kemi", category_name: "Tankrens" })).toBe(true);
|
||||||
|
expect(isTankCleaningProduct({ category: 4, name: "Forvogn" })).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("limits only-tankcleaning customers to tankcleaning products", () => {
|
||||||
|
expect(isProductRestrictedForCustomer({ category: 4, name: "Forvogn" }, onlyTankCleaning)).toBe(true);
|
||||||
|
expect(isProductRestrictedForCustomer({ category: 5, name: "Tank cleaning" }, onlyTankCleaning)).toBe(false);
|
||||||
|
expect(isProductRestrictedForCustomer({ category: 4, name: "Forvogn" }, [])).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not invert tankcleaning access for regular customers", () => {
|
||||||
|
expect(isProductRestrictedForCustomer({ category: 5, name: "Tank cleaning" }, [])).toBe(false);
|
||||||
|
expect(isProductRestrictedForCustomer({ category: 5, name: "Tank cleaning" }, restrictTankCleaning)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("applies category restrictions consistently", () => {
|
||||||
|
expect(isProductCategoryRestrictedForCustomer(4, onlyTankCleaning)).toBe(true);
|
||||||
|
expect(isProductCategoryRestrictedForCustomer(5, onlyTankCleaning)).toBe(false);
|
||||||
|
expect(isProductCategoryRestrictedForCustomer("tank_cleaning", restrictTankCleaning)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -54,6 +54,11 @@ describe("Playwright PR mapping", () => {
|
|||||||
expect(triggersFallback("scripts/run-playwright-full-slice.mjs")).toBe(false);
|
expect(triggersFallback("scripts/run-playwright-full-slice.mjs")).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("maps customer product rule changes to POS customer rule E2E coverage", () => {
|
||||||
|
expect(specsFor("src/features/customer/customerProductRules.js")).toContain("tests/e2e/pos-customer-rules.spec.js");
|
||||||
|
expect(specsFor("src/components/displays/boxes/ProductBox.vue")).toContain("tests/e2e/pos-customer-rules.spec.js");
|
||||||
|
});
|
||||||
|
|
||||||
it("maps limited backoffice changes to the limited backoffice E2E coverage", () => {
|
it("maps limited backoffice changes to the limited backoffice E2E coverage", () => {
|
||||||
expect(specsFor("src/views/backoffice/LimitedBackofficeEmployees.vue")).toContain(
|
expect(specsFor("src/views/backoffice/LimitedBackofficeEmployees.vue")).toContain(
|
||||||
"tests/e2e/limited-backoffice.spec.ts"
|
"tests/e2e/limited-backoffice.spec.ts"
|
||||||
|
|||||||
Reference in New Issue
Block a user