869 lines
26 KiB
JavaScript
869 lines
26 KiB
JavaScript
// @vitest-environment jsdom
|
|
import { defineComponent, h, nextTick, ref } from "vue";
|
|
import { flushPromises, mount } from "@vue/test-utils";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { INVOICE_COLLECTION_BULK_ACTIONS } from "@/components/displays/department/pos/orders/invoiceCollectionBulkActions.js";
|
|
import { setUseLargeTableHeaders } from "@/services/tableHeaderPreferences.js";
|
|
|
|
const getSingleMock = vi.hoisted(() => vi.fn());
|
|
const bulkActionPreviewMock = vi.hoisted(() => vi.fn());
|
|
const bulkActionApplyMock = vi.hoisted(() => vi.fn());
|
|
const sessionState = vi.hoisted(() => ({
|
|
canAccessSuperUser: false,
|
|
}));
|
|
const routeState = vi.hoisted(() => ({
|
|
path: "/admin/12/modules/pos/orders",
|
|
fullPath: "/admin/12/modules/pos/orders",
|
|
name: "posorders",
|
|
params: {
|
|
departmentId: "12",
|
|
},
|
|
}));
|
|
const invoiceQueueState = vi.hoisted(() => {
|
|
const { ref } = require("vue");
|
|
|
|
return {
|
|
inProgress: ref([]),
|
|
failed: ref([]),
|
|
success: ref([]),
|
|
queued: ref([]),
|
|
log: ref([]),
|
|
};
|
|
});
|
|
|
|
vi.mock("vue-i18n", () => ({
|
|
useI18n: () => ({
|
|
t: (key) => key,
|
|
locale: {
|
|
value: "en",
|
|
},
|
|
}),
|
|
createI18n: () => ({
|
|
global: {
|
|
t: (key) => key,
|
|
},
|
|
install: () => {},
|
|
}),
|
|
}));
|
|
|
|
vi.mock("vue-router", () => ({
|
|
useRoute: () => routeState,
|
|
}));
|
|
|
|
vi.mock("sweetalert2", () => ({
|
|
default: {
|
|
fire: vi.fn(() => Promise.resolve()),
|
|
close: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
vi.mock("@/components/shop/POSDepartmentProcess.vue", () => ({
|
|
downloadAttachment: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("@/components/pagination/departmentTabs.vue", () => ({
|
|
departments: ref([{ id: 12, name: "Demo" }]),
|
|
getDepartments: vi.fn(),
|
|
isLoading: ref(false),
|
|
getDepartmentName: (departmentId) => (Number(departmentId) === 12 ? "Demo" : `Department ${departmentId}`),
|
|
}));
|
|
|
|
vi.mock("@/components/displays/PopperDefault.vue", () => ({
|
|
showPopper: vi.fn(),
|
|
removePopperIfOpen: vi.fn(),
|
|
popperBox: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("@/components/pagination/paginatedList.vue", () => {
|
|
const orderBy = ref("id");
|
|
const orderDirection = ref("asc");
|
|
return {
|
|
orderBy,
|
|
orderDirection,
|
|
setOrder: vi.fn(),
|
|
loadList: vi.fn(),
|
|
usePaginatedListInstance: () => ({
|
|
orderBy,
|
|
orderDirection,
|
|
setOrder: vi.fn(),
|
|
loadList: vi.fn(),
|
|
}),
|
|
};
|
|
});
|
|
|
|
vi.mock(
|
|
"@/views/dashboards/superUserDashboard/InvoicingBillingPeriod/imports/InvoicingBillingPeriodImportInvoiceQueue.vue",
|
|
() => ({
|
|
invoiceQueue: {
|
|
invoiceCollectionQueueInProgress: invoiceQueueState.inProgress,
|
|
invoiceCollectionQueueFailed: invoiceQueueState.failed,
|
|
invoiceCollectionQueueSuccess: invoiceQueueState.success,
|
|
invoiceCollectionQueue: invoiceQueueState.queued,
|
|
collectionQueueLog: invoiceQueueState.log,
|
|
addInvoiceCollectionsToQueue: vi.fn(),
|
|
processInvoiceCollectionQueue: vi.fn(),
|
|
retryInvoiceCollection: vi.fn(),
|
|
},
|
|
})
|
|
);
|
|
|
|
vi.mock("@/components/session/token/SessionUser.vue", () => ({
|
|
SessionUser: {
|
|
canAccessDepartment: vi.fn(() => false),
|
|
canAccessAdmin: vi.fn(() => false),
|
|
canAccessSuperUser: vi.fn(() => sessionState.canAccessSuperUser),
|
|
hasPermission: vi.fn(() => false),
|
|
hasAttribute: vi.fn(() => false),
|
|
objects: {
|
|
orders: {
|
|
columns: {
|
|
id: { label: "ID" },
|
|
customer_id: { label: "Customer" },
|
|
cashier_id: { label: "Cashier" },
|
|
department_id: { label: "Department" },
|
|
reference: { label: "Reference" },
|
|
notes: { label: "Notes" },
|
|
po: { label: "PO" },
|
|
created_at: { label: "Created" },
|
|
},
|
|
meta: {
|
|
labels: {
|
|
single: "Order",
|
|
multiple: "Orders",
|
|
entries: "Orders",
|
|
},
|
|
},
|
|
showEditObjectFieldForm: vi.fn(() => Promise.resolve()),
|
|
},
|
|
vehicles: {
|
|
meta: {
|
|
labels: {
|
|
multiple: "Vehicles",
|
|
},
|
|
},
|
|
},
|
|
departments: {
|
|
meta: {
|
|
labels: {
|
|
single: "department",
|
|
},
|
|
},
|
|
},
|
|
collectedOrderInvoices: {
|
|
get: {
|
|
single: getSingleMock,
|
|
},
|
|
functions: {
|
|
bulk_action_preview: bulkActionPreviewMock,
|
|
bulk_action_apply: bulkActionApplyMock,
|
|
},
|
|
},
|
|
global: {
|
|
language: {
|
|
showing: "Showing",
|
|
showing_of_separator: "of",
|
|
unselect: "Unselect",
|
|
select: "Select",
|
|
all: "All",
|
|
invoice: "Invoice",
|
|
collection: "Collection",
|
|
yes: "Yes",
|
|
no: "No",
|
|
handheld_pending_order: "Pending handheld",
|
|
confirmation_needed: "Confirmation needed",
|
|
},
|
|
system_user_ids: [],
|
|
},
|
|
},
|
|
functions: {
|
|
ucFirst: (value) => {
|
|
const normalized = String(value || "");
|
|
return normalized.charAt(0).toUpperCase() + normalized.slice(1);
|
|
},
|
|
currency: {
|
|
toLocal: (value) => String(value ?? ""),
|
|
},
|
|
text: {
|
|
truncate: (text, length = 25) => (text.length > length ? `${text.substring(0, length)}...` : text),
|
|
},
|
|
date: {
|
|
timeAgo: () => "just now",
|
|
},
|
|
parseErrorMessage: (error) => error?.message || String(error),
|
|
},
|
|
},
|
|
}));
|
|
|
|
import OrdersTable from "@/components/displays/department/pos/orders/ordersTable.vue";
|
|
|
|
const ViewportResponsiveWrapperStub = {
|
|
template: `
|
|
<div class="viewport-wrapper-stub">
|
|
<slot name="desktop"></slot>
|
|
</div>
|
|
`,
|
|
};
|
|
|
|
const EditableTableColumnStub = defineComponent({
|
|
name: "EditableTableColumn",
|
|
props: {
|
|
object: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
column: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
parseFunction: {
|
|
type: Function,
|
|
default: null,
|
|
},
|
|
virtualColumn: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
componentWrapper: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
},
|
|
setup(props) {
|
|
return () => {
|
|
const tagName = props.componentWrapper || "td";
|
|
const rawValue = props.virtualColumn ? props.object : props.object?.[props.column];
|
|
const content = props.parseFunction ? props.parseFunction(rawValue) : rawValue;
|
|
return h(tagName, { class: "editable-table-column-stub" }, String(content ?? ""));
|
|
};
|
|
},
|
|
});
|
|
|
|
const ColorIndicatorStub = defineComponent({
|
|
name: "ColorIndicator",
|
|
props: {
|
|
label: {
|
|
type: Object,
|
|
default: () => ({}),
|
|
},
|
|
icon_class: {
|
|
type: String,
|
|
default: "fas fa-circle",
|
|
},
|
|
color_class: {
|
|
type: String,
|
|
default: "has-text-grey",
|
|
},
|
|
},
|
|
setup(props) {
|
|
return () =>
|
|
h(
|
|
"span",
|
|
{
|
|
class: "color-indicator-stub",
|
|
"data-label": String(props.label?.text ?? ""),
|
|
"data-icon": props.icon_class,
|
|
"data-color-class": props.color_class,
|
|
},
|
|
String(props.label?.text ?? "")
|
|
);
|
|
},
|
|
});
|
|
|
|
const ActionSettingsWheelButtonStub = {
|
|
template: '<div class="action-settings-wheel-button-stub"><slot name="actions"></slot></div>',
|
|
};
|
|
|
|
const InvoiceCollectionSelectionActionWheelStub = defineComponent({
|
|
name: "InvoiceCollectionSelectionActionWheel",
|
|
props: {
|
|
selectedInvoiceCollectionIds: {
|
|
type: Array,
|
|
default: () => [],
|
|
},
|
|
totalInvoiceCollectionCount: {
|
|
type: Number,
|
|
default: 0,
|
|
},
|
|
allSelected: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
allExpanded: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
invoiceQueueBusy: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
},
|
|
emits: ["invoiceSelected", "bulkAction", "toggleSelectAll", "toggleExpandAll"],
|
|
template: `
|
|
<div
|
|
class="invoice-collection-selection-action-wheel-stub"
|
|
:data-selected-count="selectedInvoiceCollectionIds.length"
|
|
:data-total-count="totalInvoiceCollectionCount"
|
|
:data-all-selected="String(allSelected)"
|
|
:data-all-expanded="String(allExpanded)"
|
|
:data-queue-busy="String(invoiceQueueBusy)"
|
|
>
|
|
<button data-testid="selection-wheel-invoice-selected" @click="$emit('invoiceSelected')"></button>
|
|
<button data-testid="selection-wheel-clean-rules" @click="$emit('bulkAction', '${INVOICE_COLLECTION_BULK_ACTIONS.CLEAN_CUSTOMER_RULES}')"></button>
|
|
<button data-testid="selection-wheel-select-all" @click="$emit('toggleSelectAll')"></button>
|
|
<button data-testid="selection-wheel-expand-all" @click="$emit('toggleExpandAll')"></button>
|
|
</div>
|
|
`,
|
|
});
|
|
|
|
const BCheckboxStub = defineComponent({
|
|
name: "BCheckbox",
|
|
props: {
|
|
modelValue: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
disabled: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
},
|
|
emits: ["update:modelValue"],
|
|
template: `
|
|
<label :class="$attrs.class" :data-testid="$attrs['data-testid']">
|
|
<input
|
|
type="checkbox"
|
|
:aria-label="$attrs['aria-label']"
|
|
:checked="modelValue"
|
|
:disabled="disabled"
|
|
@change="$emit('update:modelValue', $event.target.checked)"
|
|
/>
|
|
</label>
|
|
`,
|
|
});
|
|
|
|
const ActionSettingsWheelItemStub = {
|
|
template: '<div class="action-settings-wheel-item-stub"></div>',
|
|
};
|
|
|
|
const OrderContentTableStub = {
|
|
template: '<div class="order-content-table-stub"></div>',
|
|
};
|
|
|
|
const PosDepartmentStepMobileAttachmentStub = {
|
|
template: '<div class="pos-mobile-attachment-stub"></div>',
|
|
};
|
|
|
|
const OrderAttachmentsActionButtonStub = {
|
|
template: '<div class="order-attachments-action-button-stub"></div>',
|
|
};
|
|
|
|
const AssignDraftOrderCustomerModalStub = {
|
|
template: '<div class="assign-draft-order-customer-modal-stub"></div>',
|
|
};
|
|
|
|
const WhiteBoxCardStub = {
|
|
template:
|
|
'<div class="white-box-card-stub"><slot name="header"></slot><slot></slot><slot name="footer"></slot></div>',
|
|
};
|
|
|
|
const InvoiceMultipleCollectionsModalStub = {
|
|
template: '<div class="invoice-multiple-collections-modal-stub"></div>',
|
|
};
|
|
|
|
const InvoicingBillingPeriodInvoiceProgressBarStub = {
|
|
template: '<div class="invoice-progress-bar-stub"></div>',
|
|
};
|
|
|
|
const createOrder = (overrides = {}) => ({
|
|
id: 1,
|
|
customer_id: 12345,
|
|
department_id: 12,
|
|
reference: "",
|
|
notes: "",
|
|
po: "",
|
|
reg_1: "AB12345",
|
|
reg_2: "",
|
|
reg_3: "",
|
|
created_at: "2026-04-13 11:00:00",
|
|
total_net_amount: 100,
|
|
invoice_collection_id: null,
|
|
invoice_collection: null,
|
|
attachments: [],
|
|
economic_invoice_module: null,
|
|
stripe_invoice_module: null,
|
|
error_message: null,
|
|
pending_handheld: false,
|
|
completed_at: null,
|
|
customer_name: "Demo Customer",
|
|
cashier_id: 7,
|
|
cashier_name: "Demo Cashier",
|
|
...overrides,
|
|
});
|
|
|
|
const mountOrdersTable = (props = {}) => {
|
|
return mount(OrdersTable, {
|
|
props: {
|
|
orders: [],
|
|
isCustomerView: true,
|
|
invoiceView: false,
|
|
allowSelectMultiple: false,
|
|
groupInvoiceCollection: false,
|
|
showOnlyWithIds: [],
|
|
autoExpandAll: false,
|
|
excludedOrderIds: [],
|
|
...props,
|
|
},
|
|
global: {
|
|
stubs: {
|
|
ViewportResponsiveWrapper: ViewportResponsiveWrapperStub,
|
|
EditableTableColumn: EditableTableColumnStub,
|
|
ColorIndicator: ColorIndicatorStub,
|
|
ActionSettingsWheelButton: ActionSettingsWheelButtonStub,
|
|
InvoiceCollectionSelectionActionWheel: InvoiceCollectionSelectionActionWheelStub,
|
|
BCheckbox: BCheckboxStub,
|
|
ActionSettingsWheelItem: ActionSettingsWheelItemStub,
|
|
OrderContentTable: OrderContentTableStub,
|
|
PosDepartmentStepMobileAttachment: PosDepartmentStepMobileAttachmentStub,
|
|
OrderAttachmentsActionButton: OrderAttachmentsActionButtonStub,
|
|
AssignDraftOrderCustomerModal: AssignDraftOrderCustomerModalStub,
|
|
WhiteBoxCard: WhiteBoxCardStub,
|
|
InvoiceMultipleCollectionsModal: InvoiceMultipleCollectionsModalStub,
|
|
InvoicingBillingPeriodInvoiceProgressBar: InvoicingBillingPeriodInvoiceProgressBarStub,
|
|
},
|
|
},
|
|
});
|
|
};
|
|
|
|
describe("OrdersTable", () => {
|
|
beforeEach(() => {
|
|
global.ResizeObserver = class ResizeObserver {
|
|
observe() {}
|
|
unobserve() {}
|
|
disconnect() {}
|
|
};
|
|
sessionState.canAccessSuperUser = false;
|
|
routeState.path = "/admin/12/modules/pos/orders";
|
|
routeState.fullPath = "/admin/12/modules/pos/orders";
|
|
routeState.name = "posorders";
|
|
routeState.params = {
|
|
departmentId: "12",
|
|
};
|
|
invoiceQueueState.inProgress.value = [];
|
|
invoiceQueueState.failed.value = [];
|
|
invoiceQueueState.success.value = [];
|
|
invoiceQueueState.queued.value = [];
|
|
invoiceQueueState.log.value = [];
|
|
setUseLargeTableHeaders(false);
|
|
getSingleMock.mockReset();
|
|
bulkActionPreviewMock.mockReset();
|
|
bulkActionPreviewMock.mockResolvedValue({
|
|
data: {
|
|
data: {
|
|
preview_id: "preview-1",
|
|
confirmation_phrase: "Confirm",
|
|
summary: {
|
|
changed_count: 0,
|
|
},
|
|
blockers: [],
|
|
},
|
|
},
|
|
});
|
|
bulkActionApplyMock.mockReset();
|
|
});
|
|
|
|
afterEach(() => {
|
|
setUseLargeTableHeaders(false);
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it("renders customer orders with sparse text fields without crashing or preloading invoice collections", async () => {
|
|
const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
|
|
const wrapper = mountOrdersTable({
|
|
orders: [
|
|
createOrder({
|
|
id: 10,
|
|
reference: undefined,
|
|
notes: null,
|
|
po: undefined,
|
|
invoice_collection_id: undefined,
|
|
}),
|
|
],
|
|
});
|
|
|
|
await flushPromises();
|
|
|
|
expect(wrapper.text()).toContain("10");
|
|
expect(getSingleMock).not.toHaveBeenCalled();
|
|
expect(consoleErrorSpy.mock.calls.map((call) => call.join(" ")).join("\n")).not.toContain(
|
|
"Cannot read properties of undefined"
|
|
);
|
|
});
|
|
|
|
it("preloads only valid invoice collection ids and keeps rendering when a lookup fails", async () => {
|
|
sessionState.canAccessSuperUser = true;
|
|
const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
|
|
getSingleMock.mockImplementation(async (invoiceCollectionId) => {
|
|
if (invoiceCollectionId === 14) {
|
|
return {
|
|
id: 14,
|
|
orders: [{ id: 1001 }, { id: 1002 }, { id: 3000 }],
|
|
};
|
|
}
|
|
|
|
throw new Error("Forbidden");
|
|
});
|
|
|
|
const wrapper = mountOrdersTable({
|
|
groupInvoiceCollection: true,
|
|
orders: [
|
|
createOrder({
|
|
id: 20,
|
|
invoice_collection_id: 14,
|
|
invoice_collection: { id: 14, closed_at: null, booked_invoice_id: null },
|
|
}),
|
|
createOrder({
|
|
id: 21,
|
|
invoice_collection_id: "14",
|
|
invoice_collection: { id: 14, closed_at: null, booked_invoice_id: null },
|
|
}),
|
|
createOrder({
|
|
id: 22,
|
|
invoice_collection_id: undefined,
|
|
}),
|
|
createOrder({
|
|
id: 23,
|
|
invoice_collection_id: "",
|
|
}),
|
|
createOrder({
|
|
id: 24,
|
|
invoice_collection_id: -1,
|
|
}),
|
|
createOrder({
|
|
id: 25,
|
|
invoice_collection_id: 21,
|
|
invoice_collection: { id: 21, closed_at: null, booked_invoice_id: null },
|
|
}),
|
|
],
|
|
excludedOrderIds: [3000],
|
|
});
|
|
|
|
await flushPromises();
|
|
await flushPromises();
|
|
|
|
const requestedIds = getSingleMock.mock.calls
|
|
.map(([invoiceCollectionId]) => invoiceCollectionId)
|
|
.sort((left, right) => left - right);
|
|
|
|
expect(requestedIds).toEqual([14, 21]);
|
|
expect(wrapper.text()).toContain("Faktura samling ID: 14");
|
|
expect(wrapper.text()).toContain("Faktura samling ID: 21");
|
|
expect(consoleErrorSpy.mock.calls.map((call) => call.join(" ")).join("\n")).not.toContain(
|
|
"Unhandled error during execution of component update"
|
|
);
|
|
});
|
|
|
|
it("renders the draft assignment action when explicitly enabled", async () => {
|
|
const wrapper = mountOrdersTable({
|
|
showDraftAssignmentActions: true,
|
|
orders: [
|
|
createOrder({
|
|
id: 56625,
|
|
}),
|
|
],
|
|
});
|
|
|
|
await flushPromises();
|
|
|
|
expect(wrapper.find('[data-testid="draft-order-assign-customer-button-56625"]').exists()).toBe(true);
|
|
expect(wrapper.text()).toContain("admin.pos.drafts_assignment.button");
|
|
});
|
|
|
|
it("does not mount the legacy invoice progress toast on superuser invoice routes", async () => {
|
|
routeState.path = "/superuser/invoices";
|
|
routeState.fullPath = "/superuser/invoices?activeTab=period";
|
|
routeState.name = "superuser-invoices";
|
|
routeState.params = {};
|
|
invoiceQueueState.failed.value = [3001];
|
|
invoiceQueueState.success.value = [3002];
|
|
|
|
const wrapper = mountOrdersTable({
|
|
invoiceView: true,
|
|
orders: [
|
|
createOrder({
|
|
id: 56626,
|
|
invoice_collection_id: 3001,
|
|
}),
|
|
],
|
|
});
|
|
|
|
await flushPromises();
|
|
|
|
expect(wrapper.find(".invoice-progress-bar-stub").exists()).toBe(false);
|
|
});
|
|
|
|
it("keeps the legacy invoice progress toast available outside superuser invoice routes", async () => {
|
|
invoiceQueueState.queued.value = [3003];
|
|
|
|
const wrapper = mountOrdersTable({
|
|
invoiceView: true,
|
|
orders: [
|
|
createOrder({
|
|
id: 56627,
|
|
invoice_collection_id: 3003,
|
|
}),
|
|
],
|
|
});
|
|
|
|
await flushPromises();
|
|
|
|
expect(wrapper.find(".invoice-progress-bar-stub").exists()).toBe(true);
|
|
});
|
|
|
|
it("renders invoice-period order flags below the matching order row", async () => {
|
|
const wrapper = mountOrdersTable({
|
|
invoiceView: true,
|
|
orders: [
|
|
createOrder({
|
|
id: 56628,
|
|
}),
|
|
createOrder({
|
|
id: 56629,
|
|
}),
|
|
],
|
|
invoicePeriodFlags: [
|
|
{
|
|
id: "manual-order-flag",
|
|
source: "manual",
|
|
target_type: "order",
|
|
target_id: 56628,
|
|
order_id: 56628,
|
|
reason: "Review this order.",
|
|
message: "Review this order.",
|
|
},
|
|
{
|
|
id: "item-flag",
|
|
source: "automatic",
|
|
target_type: "order_item_field",
|
|
target_id: 7701,
|
|
order_id: 56628,
|
|
order_item_id: 7701,
|
|
message: "Item flag belongs on the item line.",
|
|
},
|
|
],
|
|
});
|
|
|
|
await flushPromises();
|
|
|
|
expect(wrapper.get('[data-testid="pos-order-flags-row-56628"]').text()).toContain("Review this order.");
|
|
expect(wrapper.find('[data-testid="pos-order-flags-row-56629"]').exists()).toBe(false);
|
|
expect(wrapper.get('[data-testid="pos-order-flags-row-56628"]').text()).not.toContain(
|
|
"Item flag belongs on the item line."
|
|
);
|
|
});
|
|
|
|
it("uses a flag icon for orders containing invoice-period flags", async () => {
|
|
const wrapper = mountOrdersTable({
|
|
invoiceView: true,
|
|
orders: [
|
|
createOrder({
|
|
id: 56630,
|
|
pending_handheld: true,
|
|
}),
|
|
createOrder({
|
|
id: 56631,
|
|
}),
|
|
createOrder({
|
|
id: 56632,
|
|
}),
|
|
],
|
|
invoicePeriodFlags: [
|
|
{
|
|
id: "manual-order-flag",
|
|
source: "manual",
|
|
target_type: "order",
|
|
target_id: 56630,
|
|
order_id: 56630,
|
|
reason: "Review this order.",
|
|
message: "Review this order.",
|
|
},
|
|
{
|
|
id: "automatic-item-flag",
|
|
source: "automatic",
|
|
target_type: "order_item_field",
|
|
target_id: 7701,
|
|
order_id: 56631,
|
|
order_item_id: 7701,
|
|
message: "Item flag belongs to this order.",
|
|
},
|
|
],
|
|
});
|
|
|
|
await flushPromises();
|
|
|
|
const getIndicatorForOrder = (orderId) => wrapper.get(`.color-indicator-stub[data-label="${orderId}"]`);
|
|
|
|
expect(getIndicatorForOrder(56630).attributes("data-icon")).toBe("fas fa-flag");
|
|
expect(getIndicatorForOrder(56630).attributes("data-color-class")).toBe("has-text-danger");
|
|
expect(getIndicatorForOrder(56631).attributes("data-icon")).toBe("fas fa-flag");
|
|
expect(getIndicatorForOrder(56631).attributes("data-color-class")).toBe("has-text-warning");
|
|
expect(getIndicatorForOrder(56632).attributes("data-icon")).toBe("fas fa-circle");
|
|
});
|
|
|
|
it("routes invoice collection selection controls through the action wheel", async () => {
|
|
const wrapper = mountOrdersTable({
|
|
invoiceView: true,
|
|
allowSelectMultiple: true,
|
|
autoExpandAll: false,
|
|
orders: [
|
|
createOrder({
|
|
id: 56633,
|
|
invoice_collection_id: 3001,
|
|
invoice_collection: { id: 3001, closed_at: null, booked_invoice_id: null },
|
|
}),
|
|
createOrder({
|
|
id: 56634,
|
|
invoice_collection_id: 3001,
|
|
invoice_collection: { id: 3001, closed_at: null, booked_invoice_id: null },
|
|
}),
|
|
createOrder({
|
|
id: 56635,
|
|
invoice_collection_id: 3002,
|
|
invoice_collection: { id: 3002, closed_at: null, booked_invoice_id: null },
|
|
}),
|
|
],
|
|
});
|
|
|
|
await flushPromises();
|
|
|
|
const wheel = () => wrapper.get(".invoice-collection-selection-action-wheel-stub");
|
|
const firstCollectionSelector = () =>
|
|
wrapper.get('[data-testid="pos-order-invoice-collection-selector-56633"] input');
|
|
const secondSameCollectionSelector = () =>
|
|
wrapper.get('[data-testid="pos-order-invoice-collection-selector-56634"] input');
|
|
expect(wheel().attributes("data-total-count")).toBe("2");
|
|
expect(wheel().attributes("data-selected-count")).toBe("0");
|
|
expect(wheel().attributes("data-all-selected")).toBe("false");
|
|
expect(wheel().attributes("data-all-expanded")).toBe("false");
|
|
expect(wrapper.findAll("button").some((button) => /select|unselect|vælg|fravælg/i.test(button.text()))).toBe(false);
|
|
expect(wrapper.get('[data-testid="pos-order-invoice-collection-selector-56633"]').classes()).toContain(
|
|
"pos-order-invoice-collection-selector"
|
|
);
|
|
|
|
await firstCollectionSelector().setValue(true);
|
|
await flushPromises();
|
|
|
|
expect(wheel().attributes("data-selected-count")).toBe("1");
|
|
expect(firstCollectionSelector().element.checked).toBe(true);
|
|
expect(secondSameCollectionSelector().element.checked).toBe(true);
|
|
|
|
await secondSameCollectionSelector().setValue(false);
|
|
await flushPromises();
|
|
|
|
expect(wheel().attributes("data-selected-count")).toBe("0");
|
|
expect(firstCollectionSelector().element.checked).toBe(false);
|
|
|
|
await wrapper.get('[data-testid="selection-wheel-select-all"]').trigger("click");
|
|
await flushPromises();
|
|
|
|
expect(wheel().attributes("data-selected-count")).toBe("2");
|
|
expect(wheel().attributes("data-all-selected")).toBe("true");
|
|
|
|
await wrapper.get('[data-testid="selection-wheel-expand-all"]').trigger("click");
|
|
await flushPromises();
|
|
|
|
expect(wheel().attributes("data-all-expanded")).toBe("true");
|
|
|
|
await wrapper.get('[data-testid="selection-wheel-clean-rules"]').trigger("click");
|
|
await flushPromises();
|
|
|
|
expect(bulkActionPreviewMock).toHaveBeenCalledWith(
|
|
INVOICE_COLLECTION_BULK_ACTIONS.CLEAN_CUSTOMER_RULES,
|
|
[3001, 3002],
|
|
{},
|
|
"en"
|
|
);
|
|
});
|
|
|
|
it("uses compact table headers by default and restores large headers from the user setting", async () => {
|
|
const wrapper = mountOrdersTable({
|
|
invoiceView: true,
|
|
orders: [createOrder()],
|
|
});
|
|
|
|
await flushPromises();
|
|
|
|
expect(wrapper.find("th.pos-orders-table-header--compact").exists()).toBe(true);
|
|
expect(wrapper.find("th.pos-orders-table-header--vehicles.pos-orders-table-header--compact").exists()).toBe(true);
|
|
expect(wrapper.find("th .pleno-table-header-content").exists()).toBe(true);
|
|
|
|
setUseLargeTableHeaders(true);
|
|
await nextTick();
|
|
|
|
expect(wrapper.find("th.pos-orders-table-header--compact").exists()).toBe(false);
|
|
expect(wrapper.find("th.pos-orders-table-header--vehicles").exists()).toBe(true);
|
|
});
|
|
|
|
it("renders stacked desktop registration numbers in the compact vehicle column style", async () => {
|
|
const wrapper = mountOrdersTable({
|
|
invoiceView: true,
|
|
orders: [
|
|
createOrder({
|
|
reg_1: "AB12345",
|
|
reg_2: "CD67890",
|
|
reg_3: "EF24680",
|
|
}),
|
|
],
|
|
});
|
|
|
|
await flushPromises();
|
|
|
|
const vehicleCell = wrapper.get(".pos-order-vehicle-cell");
|
|
const registrationLines = vehicleCell.findAll(".pos-order-vehicle-registration-line");
|
|
|
|
expect(registrationLines).toHaveLength(2);
|
|
expect(registrationLines.map((line) => line.text())).toEqual(["AB12345", "CD67890/EF24680"]);
|
|
});
|
|
|
|
it("centers the desktop ID status indicator group in the ID cell", async () => {
|
|
const wrapper = mountOrdersTable({
|
|
invoiceView: true,
|
|
allowSelectMultiple: true,
|
|
isCustomerView: false,
|
|
orders: [createOrder({ id: 54518 })],
|
|
});
|
|
|
|
await flushPromises();
|
|
|
|
const idCell = wrapper.get(".pos-orders-table-id-cell");
|
|
expect(idCell.classes()).toContain("is-narrow");
|
|
expect(idCell.text()).toContain("54518");
|
|
expect(idCell.find(".pos-orders-table-id-cell-content").exists()).toBe(true);
|
|
expect(idCell.find(".color-indicator-stub").exists()).toBe(true);
|
|
});
|
|
|
|
it("uses row-level alignment classes for the desktop orders table row", async () => {
|
|
const wrapper = mountOrdersTable({
|
|
invoiceView: true,
|
|
allowSelectMultiple: true,
|
|
isCustomerView: false,
|
|
orders: [createOrder({ id: 54518 })],
|
|
});
|
|
|
|
await flushPromises();
|
|
|
|
const row = wrapper.get(".pos-orders-table-row");
|
|
expect(wrapper.find(".pos-orders-table-selector-header").exists()).toBe(true);
|
|
expect(wrapper.find(".pos-orders-table-collapse-header").exists()).toBe(true);
|
|
expect(row.findAll(".pos-orders-table-leading-control-cell")).toHaveLength(2);
|
|
expect(row.find(".pos-orders-table-id-cell").exists()).toBe(true);
|
|
expect(row.find(".pos-orders-table-customer-cell").exists()).toBe(true);
|
|
expect(row.find(".pos-order-vehicle-cell").exists()).toBe(true);
|
|
expect(row.find(".pos-orders-table-date-cell").text()).toBe("2026-04-13 11:00:00");
|
|
expect(row.find(".pos-orders-table-amount-cell").text()).toBe("100");
|
|
expect(row.find(".pos-orders-table-actions-cell").exists()).toBe(true);
|
|
expect(row.findAll(".pos-orders-table-compact-text-cell")).toHaveLength(7);
|
|
});
|
|
});
|