Add booking selection popup and refactor POS booking flow logic:

- Introduced `PosDepartmentStepMobilePopupSelectOrderBooking.vue` for mobile booking selection.
- Refactored vehicle and booking handling in `SelectVehicleFormPOS.vue` with better plate matching, booking persistence, and customer association logic.
- Updated `PosVehicle` object type to include booking matches.
- Enhanced POS transaction history and booking views with improved readability, functionality, and `data-testid` attributes.
- Adjusted `PosPopup.vue` components to support the new booking popup.
This commit is contained in:
Jeppe Bundgaard
2026-04-13 17:27:37 +02:00
parent ecfe27571c
commit 8f3ec2cf09
41 changed files with 4153 additions and 1412 deletions
+158
View File
@@ -246,6 +246,35 @@ async function waitForOrderMutation(
});
}
function createRequiredWarningsPosFixture() {
const customerNumber = 12345679;
const baseFixture = createPosFixture();
return createPosFixture({
customerAttributesByNumber: {
[customerNumber]: [
{
id: 11,
customer_number: customerNumber,
attribute: "requiresReferenceNumber",
},
{
id: 12,
customer_number: customerNumber,
attribute: "usePONumbers",
},
],
},
ordersById: {
54518: {
...baseFixture.ordersById[54518],
reference: "",
po: "",
},
},
});
}
test.describe("Admin POS Orders - desktop settings", () => {
test.beforeEach(async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("desktop"), "Desktop-only order settings coverage");
@@ -1097,6 +1126,9 @@ test.describe("Admin POS Orders - desktop settings", () => {
await page.goto("/admin/12/modules/pos/orders");
const paperclipAction = getVisibleTestId(page, "pos-order-list-attachments-54518").locator(
".dropdown-trigger button"
);
const attachmentDropdown = getVisibleTestId(page, `pos-order-list-attachments-${orderId}`);
const attachmentAction = attachmentDropdown.locator(".dropdown-trigger button");
const settingsAction = getVisibleTestId(page, `pos-order-list-settings-${orderId}`).locator(
@@ -1109,14 +1141,35 @@ test.describe("Admin POS Orders - desktop settings", () => {
await expect(attachmentAction).not.toHaveClass(/is-text/);
await expect(attachmentAction).toHaveCSS("background-color", "rgba(0, 0, 0, 0)");
await expect(attachmentAction).toHaveCSS("text-decoration-line", "none");
await expect(attachmentAction).toHaveCSS("justify-content", "center");
await expect(attachmentAction.locator(".fa-plus")).toBeVisible();
await expect(attachmentAction.locator(".ml-2")).toHaveCount(0);
const paperclipBox = await paperclipAction.boundingBox();
const attachmentBox = await attachmentAction.boundingBox();
const settingsBox = await settingsAction.boundingBox();
const plusIconBox = await attachmentAction.locator(".fa-plus").boundingBox();
expect(paperclipBox).not.toBeNull();
expect(attachmentBox).not.toBeNull();
expect(settingsBox).not.toBeNull();
expect(plusIconBox).not.toBeNull();
expect(Math.abs((attachmentBox?.width ?? 0) - (paperclipBox?.width ?? 0))).toBeLessThanOrEqual(1);
expect(Math.abs((attachmentBox?.height ?? 0) - (paperclipBox?.height ?? 0))).toBeLessThanOrEqual(1);
expect(
Math.abs(
(plusIconBox?.x ?? 0) +
(plusIconBox?.width ?? 0) / 2 -
((attachmentBox?.x ?? 0) + (attachmentBox?.width ?? 0) / 2)
)
).toBeLessThanOrEqual(1);
expect(
Math.abs(
(plusIconBox?.y ?? 0) +
(plusIconBox?.height ?? 0) / 2 -
((attachmentBox?.y ?? 0) + (attachmentBox?.height ?? 0) / 2)
)
).toBeLessThanOrEqual(1);
expect(attachmentBox?.x ?? 0).toBeLessThan(settingsBox?.x ?? 0);
await attachmentAction.click();
@@ -1133,6 +1186,111 @@ test.describe("Admin POS Orders - desktop settings", () => {
});
});
test.describe("Admin POS Orders - desktop required warning states", () => {
test.beforeEach(async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("desktop"), "Desktop-only required warning coverage");
await mockApi(page, {
authenticated: true,
permissions: POS_PERMISSIONS,
edgeGateways: false,
pos: createRequiredWarningsPosFixture(),
});
await primeOperatorSession(page, "pos-orders-required-warnings-token");
});
test("shows required reference and po warnings in order detail and clears them without changing field size", async ({
page,
}) => {
await openOrderDetail(page);
const referenceControl = page.getByTestId("pos-order-customer-wishes-reference-control");
const poControl = page.getByTestId("pos-order-customer-wishes-po-control");
await expect(referenceControl).toHaveAttribute("data-warning-state", "danger");
await expect(poControl).toHaveAttribute("data-warning-state", "warning");
await expect(page.getByTestId("pos-order-customer-wishes-reference-warning-icon")).toBeVisible();
await expect(page.getByTestId("pos-order-customer-wishes-po-warning-icon")).toBeVisible();
const warningReferenceBox = await referenceControl.boundingBox();
const warningPoBox = await poControl.boundingBox();
expect(warningReferenceBox).not.toBeNull();
expect(warningPoBox).not.toBeNull();
expect(Math.abs((warningReferenceBox?.height ?? 0) - (warningPoBox?.height ?? 0))).toBeLessThanOrEqual(2);
const referenceValue = "REF1";
const poValue = "PO1";
const referenceRequest = waitForOrderMutation(
page,
"PUT",
"/orders",
(body) => Number(body.id) === 54518 && body.reference === referenceValue
);
await page.getByTestId("pos-order-customer-wishes-reference").click();
const referenceInput = page.getByTestId("pos-order-customer-wishes-reference-input");
await referenceInput.fill(referenceValue);
await expect(referenceControl).not.toHaveAttribute("data-warning-state", "danger");
await expect(page.getByTestId("pos-order-customer-wishes-reference-warning-icon")).toHaveCount(0);
await referenceInput.press("Enter");
await expect(referenceInput).toHaveCount(0);
await referenceRequest;
await expect(referenceControl).not.toHaveClass(/is-loading/);
const poRequest = waitForOrderMutation(
page,
"PUT",
"/orders",
(body) => Number(body.id) === 54518 && body.po === poValue
);
await page.getByTestId("pos-order-customer-wishes-po").click();
const poInput = page.getByTestId("pos-order-customer-wishes-po-input");
await poInput.fill(poValue);
await expect(poControl).not.toHaveAttribute("data-warning-state", "warning");
await expect(page.getByTestId("pos-order-customer-wishes-po-warning-icon")).toHaveCount(0);
await poInput.press("Enter");
await expect(poInput).toHaveCount(0);
await poRequest;
await expect(poControl).not.toHaveClass(/is-loading/);
await expect(page.getByTestId("pos-order-customer-wishes-reference")).toContainText(referenceValue);
await expect(page.getByTestId("pos-order-customer-wishes-po")).toContainText(poValue);
await expect(referenceControl).not.toHaveClass(/is-loading/);
await expect(poControl).not.toHaveClass(/is-loading/);
const clearedReferenceBox = await referenceControl.boundingBox();
const clearedPoBox = await poControl.boundingBox();
expect(clearedReferenceBox).not.toBeNull();
expect(clearedPoBox).not.toBeNull();
expect(Math.abs((clearedReferenceBox?.height ?? 0) - (clearedPoBox?.height ?? 0))).toBeLessThanOrEqual(2);
});
test("shows the same required warnings in the shared desktop step 2 workspace", async ({ page }) => {
await page.goto("/admin/12/modules/pos?step=1");
await expect(page.getByTestId("pos-step-1")).toBeVisible();
await page.locator("#reg_1").fill("WARN123");
await page.locator("#pos_select_customer_input").fill("12345679");
await expect(page.locator(".customer-drop-down-select").first()).toBeVisible();
await page.locator(".customer-drop-down-select").first().click();
await page.getByTestId("pos-step-1").getByTestId("pos-next-step").click();
const stepTwo = page.getByTestId("pos-step-2");
await expect(stepTwo).toBeVisible();
await expect(stepTwo.getByTestId("pos-order-customer-wishes-reference-control")).toHaveAttribute(
"data-warning-state",
"danger"
);
await expect(stepTwo.getByTestId("pos-order-customer-wishes-po-control")).toHaveAttribute(
"data-warning-state",
"warning"
);
await expect(stepTwo.getByTestId("pos-order-customer-wishes-reference-warning-icon")).toBeVisible();
await expect(stepTwo.getByTestId("pos-order-customer-wishes-po-warning-icon")).toBeVisible();
});
});
test.describe("Admin POS Orders - desktop attachment discovery", () => {
test.beforeEach(async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("desktop"), "Desktop-only attachment discovery coverage");