// @vitest-environment jsdom import { beforeEach, describe, expect, it, vi } from "vitest"; const mocks = vi.hoisted(() => ({ getOrderItems: vi.fn(), createOrderItem: vi.fn(), removeOrderItem: vi.fn(), })); vi.mock("axios", () => ({ default: { get: vi.fn(), post: vi.fn(), put: vi.fn(), delete: vi.fn(), }, })); vi.mock("@/components/numberplatescanners/Scans.vue", () => ({ getScansDepartmentPagination: vi.fn(), })); vi.mock("vue-router", () => ({ useRouter: () => ({ currentRoute: { value: { params: { departmentId: "1", }, }, }, }), })); vi.mock("@/components/request/HandleGlobalError.vue", () => ({ parseError: vi.fn(), clearErrors: vi.fn(), })); vi.mock("@/components/shop/CustomerNotes.vue", () => ({ getNotes: vi.fn(), createNote: vi.fn(), deleteNote: vi.fn(), })); vi.mock("@/components/shop/OrdersItems.vue", () => ({ createOrderItem: mocks.createOrderItem, getOrderItems: mocks.getOrderItems, removeOrderItem: mocks.removeOrderItem, })); vi.mock("@/components/shop/CustomerAttributes.vue", () => ({ getAttributes: vi.fn(), })); vi.mock("@/components/session/token/SessionUser.vue", () => ({ SessionUser: { request: vi.fn(), hasPermission: vi.fn(() => true), functions: { redirectTo: { department: vi.fn(), }, getDepartmentIdFromUrl: vi.fn(), }, objects: { global: { language: { possible_duplicates: "Possible duplicates", }, }, orders: { meta: { endpoint: "/orders", }, functions: { mark_as_completed: vi.fn(), fetchAttachments: vi.fn(), removeAttachment: vi.fn(), uploadAttachment: vi.fn(), downloadAttachment: vi.fn(), get_department_id: vi.fn(), }, get: { single: vi.fn(), }, set: { booking_id: vi.fn(), department_id: vi.fn(), notes: vi.fn(), po: vi.fn(), reference: vi.fn(), reg_1: vi.fn(), reg_2: vi.fn(), reg_3: vi.fn(), safety_seal: vi.fn(), }, }, order_bookings: { meta: { endpoint: "/order_bookings", }, get: { single: vi.fn(), }, set: { order_id: vi.fn(), }, }, products: { get: { all: vi.fn(), single: vi.fn(), }, }, vehicles: { meta: { endpoint: "/vehicles", }, }, }, }, })); vi.mock("@/components/session/authenticatedRequest.vue", () => ({ authenticatedRequest: vi.fn(), })); vi.mock("sweetalert2", () => ({ default: { fire: vi.fn(() => Promise.resolve({ isConfirmed: false })), showValidationMessage: vi.fn(), isLoading: vi.fn(() => false), }, })); import axios from "axios"; import { getAttributes } from "@/components/shop/CustomerAttributes.vue"; import { getNotes } from "@/components/shop/CustomerNotes.vue"; import { SessionUser } from "@/components/session/token/SessionUser.vue"; import { authenticatedRequest } from "@/components/session/authenticatedRequest.vue"; import { clearActivePosOrderContext, clearCustomerSelection, clearPendingOrderItems, clearStoredPosOrderId, createOrder, completed_at, customer_data, customer_attributes, customer_attributes_customer_number, customer_attributes_status, customer_id, customer_name, department_id, ensureVehiclePlateBookingsLoaded, getAddonRestriction, getVehiclePlateBookings, getProductRestriction, invoiceCollectionId, loadCustomerAttributes, loadOrderItems, nextStep, nextStepDelay, order_id, order_items, hydrateSelectedOrderBookingForDesktop, order_notes, order_po, order_safety_seal, reference, reg_1, reg_2, reg_3, pendingBookings, previousStep, registerPosStepSaveBarrier, retryCustomerAttributes, restoreStoredPosOrderId, saveOrderMetadataField, searchAndSelectCustomer, setOrderId, showFakeCreateOrderItem, scan_data, scans, selectedOrderBookingId, step, } from "@/components/shop/POSDepartmentProcess.vue"; describe("POSDepartmentProcess order metadata persistence", () => { beforeEach(() => { window.history.replaceState({}, "", "/admin/12/modules/pos?step=2"); clearActivePosOrderContext({ clearStep: true }); localStorage.setItem("token", "test-token"); nextStepDelay.value = 0; order_id.value = 54518; customer_id.value = 999; customer_name.value = "Card Terminal Customer"; customer_data.value = {}; department_id.value = 12; step.value = 2; Object.values(SessionUser.objects.orders.set).forEach((setter) => setter.mockReset?.()); }); it("applies saved metadata responses to shared POS state", async () => { SessionUser.objects.orders.set.reference.mockResolvedValueOnce({ data: { data: { id: 54518, customer_id: 999, department_id: 12, reference: "STEP-2-REF", notes: "Visible note", po: "PO-STEP-2", safety_seal: "SEAL-1", reg_1: "aa 12345", reg_2: "", reg_3: "", invoice_collection_id: 200, completed_at: null, }, }, }); await expect(saveOrderMetadataField("reference", "STEP-2-REF", 54518)).resolves.toBe("STEP-2-REF"); expect(SessionUser.objects.orders.set.reference).toHaveBeenCalledWith(54518, "STEP-2-REF"); expect(reference.value).toBe("STEP-2-REF"); expect(order_notes.value).toBe("Visible note"); expect(order_po.value).toBe("PO-STEP-2"); expect(order_safety_seal.value).toBe("SEAL-1"); expect(reg_1.value).toBe("AA12345"); }); it("preserves registration input formatting for the save request and normalizes shared state", async () => { SessionUser.objects.orders.set.reg_1.mockResolvedValueOnce({ data: { data: { id: 54518, reg_1: "CD1234", }, }, }); await expect(saveOrderMetadataField("reg_1", "cd-12 34", 54518)).resolves.toBe("CD1234"); expect(SessionUser.objects.orders.set.reg_1).toHaveBeenCalledWith(54518, "CD-12 34"); expect(reg_1.value).toBe("CD1234"); }); it("waits for registered field saves before moving from step 2 to step 3", async () => { let resolveBarrier; const barrier = vi.fn( () => new Promise((resolve) => { resolveBarrier = () => { reference.value = "BARRIER-SAVED"; resolve(); }; }) ); const unregister = registerPosStepSaveBarrier(barrier); try { const moveToNextStep = nextStep({ isMobile: false, orderCreation: false }); await Promise.resolve(); expect(barrier).toHaveBeenCalledTimes(1); expect(window.location.search).toBe("?step=2"); resolveBarrier(); await moveToNextStep; expect(reference.value).toBe("BARRIER-SAVED"); expect(window.location.search).toContain("step=3"); expect(window.location.search).toContain("id=54518"); expect(window.location.search).toContain("customer_id=999"); } finally { unregister(); nextStepDelay.value = 0; } }); it("waits for registered field saves before moving backward", async () => { step.value = 3; window.history.replaceState({}, "", "/admin/12/modules/pos?id=54518&customer_id=999&step=3"); const barrier = vi.fn(async () => { order_po.value = "PO-BACK-SAVED"; }); const unregister = registerPosStepSaveBarrier(barrier); try { await previousStep(); expect(barrier).toHaveBeenCalledTimes(1); expect(order_po.value).toBe("PO-BACK-SAVED"); expect(step.value).toBe(2); expect(window.location.search).toContain("step=2"); } finally { unregister(); nextStepDelay.value = 0; } }); }); describe("POSDepartmentProcess.ensureVehiclePlateBookingsLoaded", () => { beforeEach(() => { department_id.value = 531; SessionUser.request.mockReset(); }); it("uses exact-plate cache even when forced again", async () => { SessionUser.request.mockImplementation(async (_endpoint, _method, params = {}) => { const filters = String(params.filters || ""); if (filters.includes("reg_1:EC21234")) { return { data: { data: [ { id: 9910, department: 531, reg_1: "EC21234", reg_2: "", order_id: null, datetime: "2026-03-20T08:00:00.000Z", }, ], }, }; } return { data: { data: [] } }; }); await expect(ensureVehiclePlateBookingsLoaded("EC21234", { force: true })).resolves.toEqual([ expect.objectContaining({ id: 9910 }), ]); await expect(ensureVehiclePlateBookingsLoaded("EC21234", { force: true })).resolves.toEqual([ expect.objectContaining({ id: 9910 }), ]); expect(getVehiclePlateBookings("EC21234")).toEqual([expect.objectContaining({ id: 9910 })]); expect(SessionUser.request).toHaveBeenCalledTimes(2); expect(SessionUser.request).toHaveBeenNthCalledWith( 1, "/order_bookings", "GET", expect.objectContaining({ filters: "department:531,reg_1:EC21234,order_id:is null", limit: 250 }) ); expect(SessionUser.request).toHaveBeenNthCalledWith( 2, "/order_bookings", "GET", expect.objectContaining({ filters: "department:531,reg_2:EC21234,order_id:is null", limit: 250 }) ); }); }); describe("POSDepartmentProcess.loadOrderItems", () => { beforeEach(() => { order_id.value = null; order_items.value = []; mocks.getOrderItems.mockReset(); }); it("skips loading when order id is missing or invalid", async () => { order_id.value = null; await expect(loadOrderItems()).resolves.toEqual([]); order_id.value = 0; await expect(loadOrderItems()).resolves.toEqual([]); order_id.value = "abc"; await expect(loadOrderItems()).resolves.toEqual([]); expect(mocks.getOrderItems).not.toHaveBeenCalled(); expect(order_items.value).toEqual([]); }); it("loads order items for a valid order id", async () => { order_id.value = "9201"; mocks.getOrderItems.mockResolvedValue({ data: { data: [{ id: 1, price: 10, quantity: 1 }], }, }); await expect(loadOrderItems()).resolves.toEqual([{ id: 1, price: 10, quantity: 1 }]); expect(mocks.getOrderItems).toHaveBeenCalledWith(9201); expect(order_items.value).toEqual([{ id: 1, price: 10, quantity: 1 }]); }); it("does not create pending order rows without product details", () => { expect(showFakeCreateOrderItem(63, 1, 0)).toBeNull(); expect(showFakeCreateOrderItem({ id: 63 }, 1, 0)).toBeNull(); expect(order_items.value).toEqual([]); }); it("clears pending rows before replacing them with reloaded order items", async () => { order_id.value = "9201"; const pendingProduct = { id: 63, name: "Indvendig vask Forvogn", price: 399 }; const realOrderItem = { id: 9801, product_id: 63, product: pendingProduct, quantity: 1, price: 399, }; mocks.getOrderItems.mockResolvedValue({ data: { data: [realOrderItem], }, }); const pendingItem = showFakeCreateOrderItem(pendingProduct, 1, 399); expect(pendingItem).toEqual(expect.objectContaining({ _pending: true, product: pendingProduct })); expect(order_items.value).toHaveLength(1); clearPendingOrderItems(); expect(order_items.value).toEqual([]); await expect(loadOrderItems()).resolves.toEqual([realOrderItem]); expect(order_items.value).toEqual([realOrderItem]); }); it("falls back to empty list when response payload is malformed", async () => { order_id.value = 9201; mocks.getOrderItems.mockResolvedValue({ data: { data: null, }, }); await expect(loadOrderItems()).resolves.toEqual([]); expect(mocks.getOrderItems).toHaveBeenCalledWith(9201); expect(order_items.value).toEqual([]); }); }); describe("POSDepartmentProcess.setOrderId", () => { beforeEach(() => { localStorage.clear(); order_id.value = null; order_items.value = []; mocks.getOrderItems.mockReset(); }); it("can skip loading order items when the caller already has them", () => { setOrderId(9201, { loadItems: false }); expect(order_id.value).toBe(9201); expect(mocks.getOrderItems).not.toHaveBeenCalled(); }); }); describe("POSDepartmentProcess.searchAndSelectCustomer", () => { beforeEach(() => { clearCustomerSelection(); authenticatedRequest.mockReset(); getNotes.mockReset(); getNotes.mockResolvedValue({ data: { data: [] } }); getAttributes.mockReset(); getAttributes.mockResolvedValue({ data: { success: true, data: [] } }); }); const mockCustomerResponse = (customerNumber = 12345679, name = "Pleno Logistics") => ({ data: { data: { customer_number: customerNumber, customer_name: name, economic_customer: { customerNumber, name, barred: false, }, }, }, }); it("returns the already selected customer without a duplicate lookup", async () => { authenticatedRequest.mockResolvedValueOnce(mockCustomerResponse()); const selectedCustomer = await searchAndSelectCustomer(12345679); const cachedCustomer = await searchAndSelectCustomer(12345679); expect(authenticatedRequest).toHaveBeenCalledTimes(1); expect(authenticatedRequest).toHaveBeenCalledWith("/users/customer?customer_number=12345679", "GET"); expect(cachedCustomer).toBe(customer_data.value); expect(cachedCustomer).toEqual(selectedCustomer); expect(customer_id.value).toBe(12345679); expect(customer_name.value).toBe("Pleno Logistics"); }); it("shares concurrent lookups for the same customer number", async () => { let resolveRequest; authenticatedRequest.mockReturnValueOnce( new Promise((resolve) => { resolveRequest = resolve; }) ); const firstLookup = searchAndSelectCustomer(12345679); const secondLookup = searchAndSelectCustomer(12345679); resolveRequest(mockCustomerResponse()); const [firstCustomer, secondCustomer] = await Promise.all([firstLookup, secondLookup]); expect(authenticatedRequest).toHaveBeenCalledTimes(1); expect(firstCustomer).toEqual(secondCustomer); expect(customer_id.value).toBe(12345679); }); it("performs a new lookup when forceRefresh is requested", async () => { authenticatedRequest .mockResolvedValueOnce(mockCustomerResponse(12345679, "Pleno Logistics")) .mockResolvedValueOnce(mockCustomerResponse(12345679, "Pleno Logistics Updated")); await searchAndSelectCustomer(12345679); const refreshedCustomer = await searchAndSelectCustomer(12345679, { forceRefresh: true }); expect(authenticatedRequest).toHaveBeenCalledTimes(2); expect(refreshedCustomer.name).toBe("Pleno Logistics Updated"); expect(customer_name.value).toBe("Pleno Logistics Updated"); }); it("fails closed while customer restrictions load and resolves exact product IDs when ready", async () => { let resolveAttributes; getAttributes.mockReturnValueOnce( new Promise((resolve) => { resolveAttributes = resolve; }) ); authenticatedRequest.mockResolvedValueOnce(mockCustomerResponse()); await searchAndSelectCustomer(12345679); expect(customer_attributes_status.value).toBe("loading"); expect(getProductRestriction({ id: 53 })).toMatchObject({ restricted: true, messageKey: "pos.restrictions.loading", }); resolveAttributes({ data: { success: true, data: [ { id: 1, attribute: "onlyTankCleaning", product_restriction: { version: 1, disabled_product_ids: [53], collections: [], }, }, ], }, }); await vi.waitFor(() => expect(customer_attributes_status.value).toBe("ready")); expect(getProductRestriction({ id: "53" }).restricted).toBe(true); expect(getProductRestriction({ id: 63 }).restricted).toBe(false); }); it("fails closed when an active product-impact rule lacks its structured restriction payload", async () => { getAttributes.mockResolvedValueOnce({ data: { success: true, data: [{ id: 1, attribute: "restrictSpotFree" }], }, }); authenticatedRequest.mockResolvedValueOnce(mockCustomerResponse()); await searchAndSelectCustomer(12345679); await vi.waitFor(() => expect(customer_attributes_status.value).toBe("error")); expect(customer_attributes.value).toEqual([]); expect(getProductRestriction({ id: 23 })).toMatchObject({ restricted: true, messageKey: "pos.restrictions.load_failed", }); }); it("ignores a late customer-attribute response after the selected customer changes", async () => { let resolveFirstAttributes; getAttributes .mockReturnValueOnce( new Promise((resolve) => { resolveFirstAttributes = resolve; }) ) .mockResolvedValueOnce({ data: { success: true, data: [] } }); authenticatedRequest .mockResolvedValueOnce(mockCustomerResponse(12345679, "First customer")) .mockResolvedValueOnce(mockCustomerResponse(12345680, "Second customer")); await searchAndSelectCustomer(12345679); await searchAndSelectCustomer(12345680); await vi.waitFor(() => expect(customer_attributes_status.value).toBe("ready")); resolveFirstAttributes({ data: { success: true, data: [ { id: 2, attribute: "onlyTankCleaning", product_restriction: { version: 1, disabled_product_ids: [53], collections: [] }, }, ], }, }); await Promise.resolve(); expect(customer_id.value).toBe(12345680); expect(customer_attributes.value).toEqual([]); expect(getProductRestriction({ id: 53 }).restricted).toBe(false); }); it("loads restrictions for an explicit customer when the POS-global customer is blank", async () => { let resolveAttributes; getAttributes.mockReturnValueOnce( new Promise((resolve) => { resolveAttributes = resolve; }) ); customer_id.value = ""; const loadRequest = loadCustomerAttributes(12345679); expect(getAttributes).toHaveBeenCalledWith(12345679); expect(customer_attributes_status.value).toBe("loading"); expect(customer_attributes_customer_number.value).toBeNull(); expect(getProductRestriction({ id: 53 }, { customerNumber: 12345679 })).toMatchObject({ restricted: true, messageKey: "pos.restrictions.loading", }); resolveAttributes({ data: { success: true, data: [ { id: 1, attribute: "restrictSpotFree", product_restriction: { version: 1, disabled_product_ids: [53], collections: [] }, }, ], }, }); await loadRequest; expect(customer_id.value).toBe(""); expect(customer_attributes_status.value).toBe("ready"); expect(customer_attributes_customer_number.value).toBe(12345679); expect(getProductRestriction({ id: 53 }, { customerNumber: 12345679 }).restricted).toBe(true); expect(getAddonRestriction({ option_id: 53 }, { customerNumber: 12345679 }).restricted).toBe(true); }); it("retries an explicit target and then reuses the last requested target", async () => { getAttributes .mockRejectedValueOnce(new Error("attributes unavailable")) .mockResolvedValueOnce({ data: { success: true, data: [] } }) .mockResolvedValueOnce({ data: { success: true, data: [] } }); customer_id.value = ""; await loadCustomerAttributes(12345679); expect(customer_attributes_status.value).toBe("error"); await retryCustomerAttributes(12345680); expect(customer_attributes_status.value).toBe("ready"); expect(customer_attributes_customer_number.value).toBe(12345680); await retryCustomerAttributes(); expect(getAttributes.mock.calls.map(([customerNumber]) => customerNumber)).toEqual([12345679, 12345680, 12345680]); expect(customer_attributes_customer_number.value).toBe(12345680); }); it("ignores stale explicit-target responses without consulting the POS-global customer", async () => { let resolveFirstAttributes; getAttributes .mockReturnValueOnce( new Promise((resolve) => { resolveFirstAttributes = resolve; }) ) .mockResolvedValueOnce({ data: { success: true, data: [ { id: 2, attribute: "restrictSpotFree", product_restriction: { version: 1, disabled_product_ids: [63], collections: [] }, }, ], }, }); customer_id.value = ""; const firstRequest = loadCustomerAttributes(12345679); await loadCustomerAttributes(12345680); resolveFirstAttributes({ data: { success: true, data: [ { id: 1, attribute: "restrictSpotFree", product_restriction: { version: 1, disabled_product_ids: [53], collections: [] }, }, ], }, }); await firstRequest; expect(customer_id.value).toBe(""); expect(customer_attributes_customer_number.value).toBe(12345680); expect(getProductRestriction({ id: 63 }, { customerNumber: 12345680 }).restricted).toBe(true); expect(getProductRestriction({ id: 53 }, { customerNumber: 12345680 }).restricted).toBe(false); expect(getProductRestriction({ id: 63 }, { customerNumber: 12345679 })).toMatchObject({ restricted: true, messageKey: "pos.restrictions.loading", }); }); }); describe("POSDepartmentProcess.hydrateSelectedOrderBookingForDesktop", () => { beforeEach(() => { order_id.value = 51207; customer_id.value = 12345679; customer_name.value = ""; department_id.value = 2; reference.value = ""; order_notes.value = ""; order_po.value = ""; reg_1.value = ""; reg_2.value = ""; order_items.value = []; selectedOrderBookingId.value = 7001; pendingBookings.value = []; mocks.getOrderItems.mockReset(); mocks.createOrderItem.mockReset(); SessionUser.objects.products.get.single.mockReset(); SessionUser.objects.products.get.all.mockReset(); SessionUser.objects.orders.set.reference.mockReset(); SessionUser.objects.orders.set.reg_1.mockReset(); SessionUser.objects.orders.set.reg_2.mockReset(); SessionUser.objects.orders.set.notes.mockReset(); SessionUser.objects.orders.set.po.mockReset(); SessionUser.objects.order_bookings.get.single.mockReset(); }); it("hydrates booking items without forwarding booking-controlled prices", async () => { pendingBookings.value = [ { id: 7001, customer_number: 12345679, reference: "BOOK-7001", reg_1: "AB12345", reg_2: "CD12345", notes: "Booking notes", po: "PO-7001", items: [ { id: 10, price: 1, quantity: 1 }, { id: 20, price: 2, quantity: 3, notes: "Addon note" }, ], }, ]; mocks.getOrderItems.mockResolvedValueOnce({ data: { data: [] } }).mockResolvedValueOnce({ data: { data: [] } }); SessionUser.objects.products.get.single.mockResolvedValue({ id: 10, is_wash: true, price: 500 }); mocks.createOrderItem.mockResolvedValueOnce({ data: { data: { id: 9001 } } }).mockResolvedValueOnce({}); await expect(hydrateSelectedOrderBookingForDesktop()).resolves.toBe(true); expect(mocks.createOrderItem).toHaveBeenNthCalledWith(1, 51207, 10, 1, null, null, 500, null, { extra_sale_comment: null, extra_sale_reason_code: null, }); expect(mocks.createOrderItem).toHaveBeenNthCalledWith(2, 51207, 20, 3, 9001, "Addon note", 500, null, { extra_sale_comment: null, extra_sale_reason_code: null, }); expect(SessionUser.objects.products.get.single).toHaveBeenCalledWith(10, { department_id: 2, customer_id: 12345679, category_id: null, final_price: true, }); }); }); describe("POSDepartmentProcess.clearActivePosOrderContext", () => { it("clears active order, customer, metadata and stored mobile order id", () => { localStorage.setItem("pos_order_id", "51211"); step.value = 2; order_id.value = 51211; customer_id.value = 12345679; customer_name.value = "Existing Customer"; order_items.value = [{ id: 1 }]; reference.value = "STALE-REF"; order_notes.value = "STALE-NOTES"; order_po.value = "STALE-PO"; order_safety_seal.value = "STALE-SEAL"; reg_1.value = "AB12345"; reg_2.value = "CD12345"; reg_3.value = "EF12345"; invoiceCollectionId.value = 7001; completed_at.value = "2026-01-01T10:00:00.000Z"; scans.value = [{ id: 1 }]; scan_data.value = [{ id: 2 }]; expect(clearActivePosOrderContext({ clearStep: true })).toBe(true); expect(step.value).toBe(1); expect(order_id.value).toBeNull(); expect(customer_id.value).toBe(""); expect(customer_name.value).toBe(""); expect(order_items.value).toEqual([]); expect(reference.value).toBe(""); expect(order_notes.value).toBe(""); expect(order_po.value).toBe(""); expect(order_safety_seal.value).toBe(""); expect(reg_1.value).toBe(""); expect(reg_2.value).toBe(""); expect(reg_3.value).toBe(""); expect(invoiceCollectionId.value).toBeNull(); expect(completed_at.value).toBeNull(); expect(scans.value).toEqual([]); expect(scan_data.value).toEqual([]); expect(localStorage.getItem("pos_order_id")).toBeNull(); }); }); describe("POSDepartmentProcess.restoreStoredPosOrderId", () => { beforeEach(() => { clearStoredPosOrderId(); order_id.value = null; department_id.value = ""; SessionUser.objects.orders.get.single.mockReset(); SessionUser.objects.orders.functions.get_department_id.mockReset(); SessionUser.objects.orders.set.department_id.mockReset(); }); it("clears invalid stored order ids before validation", async () => { localStorage.setItem("pos_order_id", "invalid"); await expect(restoreStoredPosOrderId()).resolves.toBeNull(); expect(localStorage.getItem("pos_order_id")).toBeNull(); expect(SessionUser.objects.orders.get.single).not.toHaveBeenCalled(); }); it("restores a valid stored order id when order context matches", async () => { localStorage.setItem("pos_order_id", "51211"); SessionUser.objects.orders.get.single.mockResolvedValue({ id: 51211, customer_id: 12345679, department_id: 2, completed_at: null, }); await expect( restoreStoredPosOrderId({ validateOrder: true, customerId: 12345679, departmentId: 2, }) ).resolves.toBe(51211); expect(order_id.value).toBe(51211); expect(localStorage.getItem("pos_order_id")).toBe("51211"); expect(SessionUser.objects.orders.get.single).toHaveBeenCalledWith(51211); }); it("clears stored order id when customer or department does not match", async () => { localStorage.setItem("pos_order_id", "51211"); order_id.value = 51211; SessionUser.objects.orders.get.single.mockResolvedValue({ id: 51211, customer_id: 999, department_id: 88, completed_at: null, }); await expect( restoreStoredPosOrderId({ validateOrder: true, customerId: 12345679, departmentId: 2, }) ).resolves.toBeNull(); expect(order_id.value).toBeNull(); expect(localStorage.getItem("pos_order_id")).toBeNull(); }); it("clears stored order id when order lookup fails", async () => { localStorage.setItem("pos_order_id", "51211"); order_id.value = 51211; SessionUser.objects.orders.get.single.mockRejectedValue(new Error("Order not found")); await expect( restoreStoredPosOrderId({ validateOrder: true, }) ).resolves.toBeNull(); expect(order_id.value).toBeNull(); expect(localStorage.getItem("pos_order_id")).toBeNull(); }); it("rejects a stored current order from a different department instead of moving it", async () => { localStorage.setItem("pos_order_id", "51211"); department_id.value = 2; SessionUser.objects.orders.get.single.mockResolvedValue({ id: 51211, customer_id: 12345679, department_id: 88, completed_at: null, }); SessionUser.objects.orders.functions.get_department_id.mockResolvedValue(88); await expect( restoreStoredPosOrderId({ validateOrder: true, customerId: 12345679, departmentId: 2, syncDepartment: true, }) ).resolves.toBeNull(); expect(SessionUser.objects.orders.set.department_id).not.toHaveBeenCalled(); expect(order_id.value).toBeNull(); expect(localStorage.getItem("pos_order_id")).toBeNull(); }); }); describe("POSDepartmentProcess.createOrder department sync", () => { beforeEach(() => { window.history.replaceState({}, "", "/admin/1/modules/pos"); order_id.value = null; department_id.value = ""; axios.post.mockReset(); SessionUser.functions.getDepartmentIdFromUrl.mockReset(); SessionUser.functions.getDepartmentIdFromUrl.mockReturnValue(undefined); SessionUser.objects.orders.functions.get_department_id.mockReset(); SessionUser.objects.orders.set.department_id.mockReset(); localStorage.setItem("token", "test-token"); }); it("rejects an existing current order from a different department instead of updating it", async () => { order_id.value = 9201; department_id.value = 7; SessionUser.objects.orders.functions.get_department_id.mockResolvedValue(3); await expect(createOrder({ isMobile: true })).resolves.toBe(false); expect(SessionUser.objects.orders.set.department_id).not.toHaveBeenCalled(); expect(axios.post).not.toHaveBeenCalled(); }); it("reuses an existing current order when it already belongs to the selected department", async () => { order_id.value = 9201; department_id.value = 7; SessionUser.objects.orders.functions.get_department_id.mockResolvedValue(7); await expect(createOrder({ isMobile: true })).resolves.toBe(true); expect(SessionUser.objects.orders.set.department_id).not.toHaveBeenCalled(); expect(axios.post).not.toHaveBeenCalled(); }); it("creates a new order in the selected department", async () => { department_id.value = 7; axios.post.mockResolvedValue({ data: { success: true, data: { id: 9202, }, }, }); await expect(createOrder({ isMobile: true })).resolves.toBe(true); expect(axios.post).toHaveBeenCalledWith( expect.any(String), expect.objectContaining({ department_id: 7, is_handheld: true, }), expect.any(Object) ); expect(order_id.value).toBe(9202); expect(department_id.value).toBe(7); }); it("falls back to the route department when the selected department ref is not numeric", async () => { department_id.value = "Demo"; axios.post.mockResolvedValue({ data: { success: true, data: { id: 9203, }, }, }); await expect(createOrder({ isMobile: false })).resolves.toBe(true); expect(axios.post).toHaveBeenCalledWith( expect.any(String), expect.objectContaining({ department_id: 1, is_handheld: false, }), expect.any(Object) ); expect(order_id.value).toBe(9203); expect(department_id.value).toBe(1); }); });