Add email notification actions for booking confirmations and wash certificates

This commit is contained in:
Jeppe B
2026-06-01 13:24:29 +02:00
parent 683fce32da
commit acf19c281a
12 changed files with 415 additions and 2 deletions
@@ -63,6 +63,10 @@ const props = defineProps({
type: String,
default: null,
},
reg_3: {
type: String,
default: null,
},
order_booking_id: {
type: Number,
default: null,
@@ -917,6 +921,15 @@ const getAttachmentLabel = (attachment) => {
);
};
const isWashCertificateAttachment = (attachment) => {
const marker = String(getAttachmentOtherPayload(attachment) || "").trim().toUpperCase();
if (marker === "WASH_CERTIFICATE") {
return true;
}
return /(?:^|[/\\])wash[_-]?certificate.*\.pdf$/i.test(getAttachmentLabel(attachment));
};
const getAttachmentExtension = (attachment) => {
const match = String(getAttachmentLabel(attachment))
.toLowerCase()
@@ -1045,6 +1058,10 @@ const activeAttachmentPreviewSource = computed(() => {
return previewSourcesById.value[activeAttachment.value.id] ?? null;
});
const hasWashCertificateAttachment = computed(() =>
attachmentsFromOrder.value.some((attachment) => isWashCertificateAttachment(attachment))
);
const hasCachedPreviewSource = (attachmentId) => {
return Object.prototype.hasOwnProperty.call(previewSourcesById.value, attachmentId);
};
@@ -1609,6 +1626,48 @@ const showCompleteOrderBookingConfirmation = async () => {
}
};
const showEmailNotificationActionResult = async (requestAction, successKey, errorKey) => {
try {
await requestAction();
await Swal.fire({
title: t(successKey),
icon: "success",
showConfirmButton: false,
timer: 2000,
heightAuto: false,
});
} catch (error) {
console.error(error);
await Swal.fire({
title: t("common.error"),
text: [t(errorKey), SessionUser.functions.parseErrorMessage?.(error)].filter(Boolean).join(": "),
icon: "error",
heightAuto: false,
});
}
};
const resendBookingConfirmation = () =>
showEmailNotificationActionResult(
() => SessionUser.objects.order_bookings.functions.resendBookingConfirmation(props.order_booking_id),
"admin.pos.settings_wheel.resend_booking_confirmation_success",
"admin.pos.settings_wheel.resend_booking_confirmation_error"
);
const resendBookingCompletionConfirmation = () =>
showEmailNotificationActionResult(
() => SessionUser.objects.order_bookings.functions.resendBookingCompletionConfirmation(props.order_booking_id),
"admin.pos.settings_wheel.resend_booking_completion_confirmation_success",
"admin.pos.settings_wheel.resend_booking_completion_confirmation_error"
);
const resendWashCertificate = () =>
showEmailNotificationActionResult(
() => SessionUser.objects.orders.functions.resendWashCertificate(props.order_id),
"admin.pos.settings_wheel.resend_wash_certificate_success",
"admin.pos.settings_wheel.resend_wash_certificate_error"
);
const getAvailableInvoiceCollectionsForCustomer = async (customerNumber) => {
const normalizedCustomerNumber = normalizePositiveInteger(customerNumber);
if (!normalizedCustomerNumber) {
@@ -1835,6 +1894,40 @@ const flatBuiltInMenuSections = computed(() => {
}
}
if (props.order_booking_id || hasWashCertificateAttachment.value) {
const emailNotificationsSection = buildMenuSection(
"email-notifications",
t("admin.pos.settings_wheel.email_notifications_section"),
[
props.order_booking_id
? buildMenuAction("email-notifications-resend-booking-confirmation", {
icon: "fas fa-envelope",
label: t("admin.pos.settings_wheel.resend_booking_confirmation"),
clickAction: resendBookingConfirmation,
})
: null,
props.order_booking_id && hasWashCertificateAttachment.value
? buildMenuAction("email-notifications-resend-booking-completion-confirmation", {
icon: "fas fa-envelope-open-text",
label: t("admin.pos.settings_wheel.resend_booking_completion_confirmation"),
clickAction: resendBookingCompletionConfirmation,
})
: null,
!props.order_booking_id && hasWashCertificateAttachment.value
? buildMenuAction("email-notifications-resend-wash-certificate", {
icon: "fas fa-file-pdf",
label: t("admin.pos.settings_wheel.resend_wash_certificate"),
clickAction: resendWashCertificate,
})
: null,
]
);
if (emailNotificationsSection) {
sections.push(emailNotificationsSection);
}
}
if (props.invoice_collection_id && SessionUser.canAccessSuperUser()) {
const invoiceCollectionLinkSection = buildMenuSection(
"invoice-collection-link",
@@ -2136,10 +2229,10 @@ const flatBuiltInMenuSections = computed(() => {
}
}
if (props.reg_1 || props.reg_2) {
if (props.reg_1 || props.reg_2 || props.reg_3) {
const vehicleSection = buildMenuSection(
"vehicle",
props.reg_1 && props.reg_2
[props.reg_1, props.reg_2, props.reg_3].filter(Boolean).length > 1
? SessionUser.objects.vehicles.meta.labels.multiple
: SessionUser.objects.vehicles.meta.labels.single,
[
@@ -2157,6 +2250,13 @@ const flatBuiltInMenuSections = computed(() => {
clickAction: () => SessionUser.functions.redirectTo.superUser("/vehicles/" + props.reg_2, true),
})
: null,
props.reg_3 && SessionUser.canAccessSuperUser()
? buildMenuAction("vehicle-reg-3", {
icon: "fas fa-car",
label: t("admin.pos.settings_wheel.view_vehicle_new_tab", { reg: props.reg_3 }),
clickAction: () => SessionUser.functions.redirectTo.superUser("/vehicles/" + props.reg_3, true),
})
: null,
]
);
@@ -1263,6 +1263,9 @@ const formatCashierName = (order) => {
v-bind:customer_number="order.customer_id"
v-bind:department_id="order.department_id"
v-bind:reg_1="order.reg_1"
v-bind:reg_2="order.reg_2"
v-bind:reg_3="order.reg_3"
v-bind:order_booking_id="order.booking_id"
:refreshFunction="loadList"
@deleted="loadList()"
@flag-created="emitFlagCreated"
@@ -1506,6 +1509,9 @@ const formatCashierName = (order) => {
v-bind:customer_number="order.customer_id"
v-bind:department_id="order.department_id"
v-bind:reg_1="order.reg_1"
v-bind:reg_2="order.reg_2"
v-bind:reg_3="order.reg_3"
v-bind:order_booking_id="order.booking_id"
:refreshFunction="loadList"
@deleted="loadList()"
@flag-created="emitFlagCreated"
@@ -356,6 +356,20 @@ export const OrderBookings = {
{ safety_seal: normalizeCompletionSafetySeal(safetySeal) },
onAfterComplete
);
},
resendBookingConfirmation: async (id) => {
return SessionUser.request(
OrderBookings.meta.endpoint + "/booking-confirmation/resend",
"POST",
{ id: parseInt(id, 10) }
).then((response) => response.data.data);
},
resendBookingCompletionConfirmation: async (id) => {
return SessionUser.request(
OrderBookings.meta.endpoint + "/completion-confirmation/resend",
"POST",
{ id: parseInt(id, 10) }
).then((response) => response.data.data);
}
},
/**
@@ -924,6 +924,13 @@ const assignDraftOrderCustomer = async ({
console.error(error);
});
},
resendWashCertificate: async (id) => {
return SessionUser.request(
Orders.meta.endpoint + "/wash-certificate/resend",
"POST",
{ id: parseInt(id, 10) }
).then((response) => response.data.data);
},
showAttachWashCertificateForm(order_id, onAfterSubmit = null) {
const normalizedOrderId = Number.parseInt(order_id, 10);
if (!Number.isInteger(normalizedOrderId) || normalizedOrderId <= 0) {
+10
View File
@@ -600,10 +600,20 @@
"delete_user": "Slet bruger",
"download_invoice": "Download faktura",
"edit_permissions": "Rediger tilladelser",
"email_notifications_section": "E-mail notifikationer",
"enter_password": "Indtast adgangskode",
"error": "Fejl",
"error_changing_password": "Fejl ved ændring af adgangskode",
"error_downloading_attachment": "Fejl ved download af vedhæftet fil",
"resend_booking_confirmation": "(Gen)send bookingbekræftelse",
"resend_booking_confirmation_error": "Bookingbekræftelsen kunne ikke sendes",
"resend_booking_confirmation_success": "Bookingbekræftelsen er sendt",
"resend_booking_completion_confirmation": "(Gen)send bekræftelse for færdig booking",
"resend_booking_completion_confirmation_error": "Bekræftelsen for færdig booking kunne ikke sendes",
"resend_booking_completion_confirmation_success": "Bekræftelsen for færdig booking er sendt",
"resend_wash_certificate": "(Gen)send vaskecertifikat",
"resend_wash_certificate_error": "Vaskecertifikatet kunne ikke sendes",
"resend_wash_certificate_success": "Vaskecertifikatet er sendt",
"link_copied": "Link kopieret",
"login_as_customer": "Log ind som kunde",
"login_as_user": "Log ind som bruger",
+10
View File
@@ -600,10 +600,20 @@
"delete_user": "Benutzer l?schen",
"download_invoice": "Rechnung herunterladen",
"edit_permissions": "Berechtigungen bearbeiten",
"email_notifications_section": "E-Mail-Benachrichtigungen",
"enter_password": "Passwort eingeben",
"error": "Fehler",
"error_changing_password": "Beim ?ndern des Passworts ist ein Fehler aufgetreten.",
"error_downloading_attachment": "Beim Herunterladen der angeh?ngten Datei ist ein Fehler aufgetreten.",
"resend_booking_confirmation": "(Re)send booking confirmation",
"resend_booking_confirmation_error": "The booking confirmation could not be sent",
"resend_booking_confirmation_success": "The booking confirmation has been sent",
"resend_booking_completion_confirmation": "(Re)send booking completion confirmation",
"resend_booking_completion_confirmation_error": "The booking completion confirmation could not be sent",
"resend_booking_completion_confirmation_success": "The booking completion confirmation has been sent",
"resend_wash_certificate": "(Re)send wash certificate",
"resend_wash_certificate_error": "The wash certificate could not be sent",
"resend_wash_certificate_success": "The wash certificate has been sent",
"link_copied": "Link in die Zwischenablage kopiert",
"login_as_customer": "Als Kunde anmelden",
"login_as_user": "Als Benutzer anmelden",
+10
View File
@@ -600,10 +600,20 @@
"delete_user": "Delete user",
"download_invoice": "Download invoice",
"edit_permissions": "Edit permissions",
"email_notifications_section": "Email notifications",
"enter_password": "Enter password",
"error": "Error",
"error_changing_password": "An error occurred while changing the password.",
"error_downloading_attachment": "An error occurred while downloading the attached file.",
"resend_booking_confirmation": "(Re)send booking confirmation",
"resend_booking_confirmation_error": "The booking confirmation could not be sent",
"resend_booking_confirmation_success": "The booking confirmation has been sent",
"resend_booking_completion_confirmation": "(Re)send booking completion confirmation",
"resend_booking_completion_confirmation_error": "The booking completion confirmation could not be sent",
"resend_booking_completion_confirmation_success": "The booking completion confirmation has been sent",
"resend_wash_certificate": "(Re)send wash certificate",
"resend_wash_certificate_error": "The wash certificate could not be sent",
"resend_wash_certificate_success": "The wash certificate has been sent",
"link_copied": "Link copied to clipboard",
"login_as_customer": "Log in as customer",
"login_as_user": "Log in as user",
+10
View File
@@ -600,10 +600,20 @@
"delete_user": "Delete user",
"download_invoice": "Last ned faktura",
"edit_permissions": "Rediger tillatelser",
"email_notifications_section": "E-postvarsler",
"enter_password": "Skriv inn passord",
"error": "Feil",
"error_changing_password": "Det oppstod en feil under endring av passord.",
"error_downloading_attachment": "Det oppsto en feil under nedlasting av den vedlagte filen.",
"resend_booking_confirmation": "(Re)send booking confirmation",
"resend_booking_confirmation_error": "The booking confirmation could not be sent",
"resend_booking_confirmation_success": "The booking confirmation has been sent",
"resend_booking_completion_confirmation": "(Re)send booking completion confirmation",
"resend_booking_completion_confirmation_error": "The booking completion confirmation could not be sent",
"resend_booking_completion_confirmation_success": "The booking completion confirmation has been sent",
"resend_wash_certificate": "(Re)send wash certificate",
"resend_wash_certificate_error": "The wash certificate could not be sent",
"resend_wash_certificate_success": "The wash certificate has been sent",
"link_copied": "Linken er kopiert til utklippstavlen",
"login_as_customer": "Logg inn som kunde",
"login_as_user": "Logg inn som bruker",
+10
View File
@@ -600,10 +600,20 @@
"delete_user": "Delete user",
"download_invoice": "Ladda ner faktura",
"edit_permissions": "Edit permissions",
"email_notifications_section": "E-postaviseringar",
"enter_password": "Enter password",
"error": "Fel",
"error_changing_password": "Ett fel uppstod när lösenordet ändrades.",
"error_downloading_attachment": "Ett fel uppstod när den bifogade filen laddades ner.",
"resend_booking_confirmation": "(Re)send booking confirmation",
"resend_booking_confirmation_error": "The booking confirmation could not be sent",
"resend_booking_confirmation_success": "The booking confirmation has been sent",
"resend_booking_completion_confirmation": "(Re)send booking completion confirmation",
"resend_booking_completion_confirmation_error": "The booking completion confirmation could not be sent",
"resend_booking_completion_confirmation_success": "The booking completion confirmation has been sent",
"resend_wash_certificate": "(Re)send wash certificate",
"resend_wash_certificate_error": "The wash certificate could not be sent",
"resend_wash_certificate_success": "The wash certificate has been sent",
"link_copied": "Link copied to clipboard",
"login_as_customer": "Log in as customer",
"login_as_user": "Log in as user",
+66
View File
@@ -2747,6 +2747,72 @@ test.describe("Admin POS Orders - desktop action menu layout", () => {
await expect(attachmentsSection).toBeVisible();
});
test("shows email notification actions and resends booking completion confirmation", async ({ page }) => {
const baseFixture = createPosFixture();
await mockApi(page, {
authenticated: true,
permissions: SUPERUSER_POS_PERMISSIONS,
edgeGateways: false,
pos: createPosFixture({
ordersById: {
54518: {
...baseFixture.ordersById[54518],
user_id: 77,
booking_id: 9101,
customer_name: "(TEST) Pleno Vognmandsforretning",
invoice_collection_id: 101,
},
},
attachmentsByOrderId: {
54518: [
{
id: 7401,
object_type: "orders",
object_id: 54518,
content: {
image: null,
document: "wash_certificate_54518.pdf",
relation: null,
other: "WASH_CERTIFICATE",
src: null,
},
created_at: "2026-04-21 08:44:07",
updated_at: "2026-04-21 08:44:07",
deleted_at: null,
},
],
},
}),
});
await primeOperatorSession(page, "pos-orders-email-notifications-token", SUPERUSER_POS_PERMISSIONS);
await page.setViewportSize({ width: 1900, height: 900 });
await page.goto("/admin/12/modules/pos/orders");
await expect(page.locator("table")).toBeVisible();
const dropdownRoot = await openLowestVisibleOrderActionDropdown(page);
const dropdownContent = dropdownRoot.locator(".dropdown-content").first();
const emailSection = dropdownContent.getByTestId("action-settings-wheel-section-email-notifications");
await expect(emailSection).toBeVisible();
await emailSection.hover();
const emailSubmenu = dropdownContent.getByTestId("action-settings-wheel-submenu-email-notifications");
await expect(emailSubmenu).toBeVisible();
await expect(emailSubmenu).toContainText("(Gen)send bookingbekræftelse");
await expect(emailSubmenu).toContainText("(Gen)send bekræftelse for færdig booking");
await expect(emailSubmenu).not.toContainText("(Gen)send vaskecertifikat");
const resendRequestPromise = page.waitForRequest((request) => {
return request.method() === "POST" && request.url().includes("/order-bookings/completion-confirmation/resend");
});
await emailSubmenu.getByRole("button", { name: /\(Gen\)send bekræftelse for færdig booking/i }).click();
const resendRequest = await resendRequestPromise;
expect(resendRequest.postDataJSON()).toEqual({ id: 9101 });
});
test("keeps the first action reachable while async menu sections load", async ({ page }) => {
const posFixture = createAsyncActionMenuPosFixture();
+16
View File
@@ -3203,6 +3203,16 @@ async function handlePosRoute({ route, request, parsedUrl, pathname, method, pos
return true;
}
if (
(pathname.endsWith("/order-bookings/booking-confirmation/resend") ||
pathname.endsWith("/order-bookings/completion-confirmation/resend")) &&
method === "POST"
) {
const body = request.postDataJSON?.() || {};
await route.fulfill(json({ success: true, data: { id: Number(body.id || 0), sent: true } }));
return true;
}
if (pathname.endsWith("/department/numberplatescanners") && method === "GET") {
await route.fulfill(json({ success: true, data: posFixture.numberPlateScanners || [] }));
return true;
@@ -3802,6 +3812,12 @@ async function handlePosRoute({ route, request, parsedUrl, pathname, method, pos
return true;
}
if (pathname.endsWith("/orders/wash-certificate/resend") && method === "POST") {
const body = request.postDataJSON?.() || {};
await route.fulfill(json({ success: true, data: { id: Number(body.id || 0), sent: true } }));
return true;
}
if (pathname.endsWith("/departments/order/recommended") && method === "GET") {
await route.fulfill(
json({
@@ -89,6 +89,8 @@ vi.mock("@/components/session/token/SessionUser.vue", () => ({
},
showEditObjectFieldForm: vi.fn(() => Promise.resolve()),
functions: {
resendBookingCompletionConfirmation: vi.fn(() => Promise.resolve()),
resendBookingConfirmation: vi.fn(() => Promise.resolve()),
showDeleteConfirmationModal: vi.fn(() => Promise.resolve()),
},
},
@@ -120,6 +122,7 @@ vi.mock("@/components/session/token/SessionUser.vue", () => ({
assignDraftCustomer: vi.fn(() => Promise.resolve()),
get_department_id: vi.fn(() => Promise.resolve(1)),
removeAttachment: vi.fn(() => Promise.resolve({ success: true })),
resendWashCertificate: vi.fn(() => Promise.resolve()),
showAttachWashCertificateForm: vi.fn(() => Promise.resolve()),
showChangeCustomerForm: vi.fn(() => Promise.resolve()),
showChangeInvoiceCollectionForm: vi.fn(() => Promise.resolve()),
@@ -184,6 +187,7 @@ const SETTINGS_WHEEL_TRANSLATION_KEYS = [
"admin.pos.settings_wheel.delete_user",
"admin.pos.settings_wheel.download_invoice",
"admin.pos.settings_wheel.edit_permissions",
"admin.pos.settings_wheel.email_notifications_section",
"admin.pos.settings_wheel.enter_password",
"admin.pos.settings_wheel.error",
"admin.pos.settings_wheel.error_changing_password",
@@ -212,6 +216,15 @@ const SETTINGS_WHEEL_TRANSLATION_KEYS = [
"admin.pos.settings_wheel.password_changed_text",
"admin.pos.settings_wheel.please_enter_password",
"admin.pos.settings_wheel.relays_section",
"admin.pos.settings_wheel.resend_booking_completion_confirmation",
"admin.pos.settings_wheel.resend_booking_completion_confirmation_error",
"admin.pos.settings_wheel.resend_booking_completion_confirmation_success",
"admin.pos.settings_wheel.resend_booking_confirmation",
"admin.pos.settings_wheel.resend_booking_confirmation_error",
"admin.pos.settings_wheel.resend_booking_confirmation_success",
"admin.pos.settings_wheel.resend_wash_certificate",
"admin.pos.settings_wheel.resend_wash_certificate_error",
"admin.pos.settings_wheel.resend_wash_certificate_success",
"admin.pos.settings_wheel.scan_qr_to_login",
"admin.pos.settings_wheel.self_serve_customer",
"admin.pos.settings_wheel.self_serve_driver",
@@ -461,6 +474,12 @@ describe("ActionSettingsWheelButton", () => {
SessionUser.objects.orders.functions.downloadAttachment.mockResolvedValue("https://cdn.example.test/attachment");
SessionUser.objects.orders.functions.removeAttachment.mockReset();
SessionUser.objects.orders.functions.removeAttachment.mockResolvedValue({ success: true });
SessionUser.objects.orders.functions.resendWashCertificate.mockReset();
SessionUser.objects.orders.functions.resendWashCertificate.mockResolvedValue({});
SessionUser.objects.order_bookings.functions.resendBookingConfirmation.mockReset();
SessionUser.objects.order_bookings.functions.resendBookingConfirmation.mockResolvedValue({});
SessionUser.objects.order_bookings.functions.resendBookingCompletionConfirmation.mockReset();
SessionUser.objects.order_bookings.functions.resendBookingCompletionConfirmation.mockResolvedValue({});
SessionUser.objects.department_gates.showCreateObjectForm.mockClear();
SessionUser.objects.department_relays.showCreateObjectForm.mockClear();
SessionUser.objects.collectedOrderInvoices.add.mockReset();
@@ -749,6 +768,141 @@ describe("ActionSettingsWheelButton", () => {
expect(wrapper.text()).not.toContain("admin.pos.settings_wheel.mark_as_completed");
});
it("hides email notifications when the order has no booking or wash certificate", async () => {
const wrapper = mountFlatDropdownButton({
order_id: 42,
order_booking_id: null,
});
await flushMicrotasks();
await wrapper.find(".dropdown-trigger button").trigger("click");
await flushMicrotasks();
expect(wrapper.text()).not.toContain("admin.pos.settings_wheel.email_notifications_section");
expect(wrapper.text()).not.toContain("admin.pos.settings_wheel.resend_booking_confirmation");
expect(wrapper.text()).not.toContain("admin.pos.settings_wheel.resend_wash_certificate");
});
it("shows booking email notification actions for booking-only orders", async () => {
const wrapper = mountFlatDropdownButton({
order_id: 42,
order_booking_id: 123,
department_id: 1,
});
await flushMicrotasks();
await wrapper.find(".dropdown-trigger button").trigger("click");
await flushMicrotasks();
expect(wrapper.text()).toContain("admin.pos.settings_wheel.email_notifications_section");
expect(wrapper.text()).toContain("admin.pos.settings_wheel.resend_booking_confirmation");
expect(wrapper.text()).not.toContain("admin.pos.settings_wheel.resend_booking_completion_confirmation");
expect(wrapper.text()).not.toContain("admin.pos.settings_wheel.resend_wash_certificate");
const button = wrapper
.findAll("button.dropdown-item-action")
.find((buttonWrapper) => buttonWrapper.text().includes("admin.pos.settings_wheel.resend_booking_confirmation"));
expect(button).toBeTruthy();
await button.trigger("click");
await flushMicrotasks();
expect(SessionUser.objects.order_bookings.functions.resendBookingConfirmation).toHaveBeenCalledWith(123);
expect(swalFireMock).toHaveBeenCalledWith(
expect.objectContaining({
title: "admin.pos.settings_wheel.resend_booking_confirmation_success",
icon: "success",
})
);
});
it("shows booking completion email action when a booking has a wash certificate", async () => {
SessionUser.objects.orders.functions.fetchAttachments.mockResolvedValue([
{
id: 301,
content: {
document: "wash_certificate_42.pdf",
other: "WASH_CERTIFICATE",
},
},
]);
const wrapper = mountFlatDropdownButton({
order_id: 42,
order_booking_id: 123,
department_id: 1,
});
await flushMicrotasks();
await wrapper.find(".dropdown-trigger button").trigger("click");
await flushMicrotasks();
expect(wrapper.text()).toContain("admin.pos.settings_wheel.email_notifications_section");
expect(wrapper.text()).toContain("admin.pos.settings_wheel.resend_booking_confirmation");
expect(wrapper.text()).toContain("admin.pos.settings_wheel.resend_booking_completion_confirmation");
expect(wrapper.text()).not.toContain("admin.pos.settings_wheel.resend_wash_certificate");
const button = wrapper
.findAll("button.dropdown-item-action")
.find((buttonWrapper) =>
buttonWrapper.text().includes("admin.pos.settings_wheel.resend_booking_completion_confirmation")
);
expect(button).toBeTruthy();
await button.trigger("click");
await flushMicrotasks();
expect(SessionUser.objects.order_bookings.functions.resendBookingCompletionConfirmation).toHaveBeenCalledWith(123);
expect(swalFireMock).toHaveBeenCalledWith(
expect.objectContaining({
title: "admin.pos.settings_wheel.resend_booking_completion_confirmation_success",
icon: "success",
})
);
});
it("shows wash certificate email action when an order has a certificate without booking", async () => {
SessionUser.objects.orders.functions.fetchAttachments.mockResolvedValue([
{
id: 302,
content: {
document: "wash_certificate_42_302.pdf",
other: "wash_certificate_42_302.pdf",
},
},
]);
const wrapper = mountFlatDropdownButton({
order_id: 42,
order_booking_id: null,
});
await flushMicrotasks();
await wrapper.find(".dropdown-trigger button").trigger("click");
await flushMicrotasks();
expect(wrapper.text()).toContain("admin.pos.settings_wheel.email_notifications_section");
expect(wrapper.text()).not.toContain("admin.pos.settings_wheel.resend_booking_confirmation");
expect(wrapper.text()).not.toContain("admin.pos.settings_wheel.resend_booking_completion_confirmation");
expect(wrapper.text()).toContain("admin.pos.settings_wheel.resend_wash_certificate");
const button = wrapper
.findAll("button.dropdown-item-action")
.find((buttonWrapper) => buttonWrapper.text().includes("admin.pos.settings_wheel.resend_wash_certificate"));
expect(button).toBeTruthy();
await button.trigger("click");
await flushMicrotasks();
expect(SessionUser.objects.orders.functions.resendWashCertificate).toHaveBeenCalledWith(42);
expect(swalFireMock).toHaveBeenCalledWith(
expect.objectContaining({
title: "admin.pos.settings_wheel.resend_wash_certificate_success",
icon: "success",
})
);
});
it("routes 'change invoice collection' action without opening a new tab", async () => {
const windowOpenSpy = vi.spyOn(window, "open").mockImplementation(() => null);