Refactor mobile POS steps: clear localStorage in tests, enhance vehicle type checks, improve safety seal handling logic, and adjust default wait timeout

This commit is contained in:
Jeppe Bundgaard
2026-03-03 15:07:05 +01:00
parent 9473a514da
commit bbedfbdf30
5 changed files with 52 additions and 11 deletions
@@ -307,11 +307,11 @@ const initializeStepTwo = async () => {
}
const fetchPrimaryItemProduct = () => {
if (!vehicles.vehicle_1.value.type && !transactionItems.primaryItem.value) {
if (!vehicles.vehicle_1.value?.type && !transactionItems.primaryItem.value) {
vehicleSelection.value = true; // Force the user to select a product.
} else {
// Load the product from the type id.
if (!vehicles.vehicle_1.value.type) {
if (!vehicles.vehicle_1.value?.type) {
return; // If no vehicle type is set, do not proceed.
}
// If the primary item is already set and matches the vehicle type, do not fetch again.
@@ -630,7 +630,7 @@ const filteredAddons = computed(() => {
<PosDepartmentStepMobileFixedBottomControl>
<!-- Buttons -->
<!-- Next button -->
<PosDepartmentStepMobileButtonNextStep :isWhite="false" :customAction="() => { vehicleSelection = false; fetchPrimaryItemProduct(); }" :buttonClasses="['has-background-primary', 'has-text-black']" v-show="vehicles.vehicle_1.value.type">
<PosDepartmentStepMobileButtonNextStep :isWhite="false" :customAction="() => { vehicleSelection = false; fetchPrimaryItemProduct(); }" :buttonClasses="['has-background-primary', 'has-text-black']" v-show="vehicles.vehicle_1?.value?.type">
<span class="is-float-left " v-if="false">{{ SessionUser.objects.global.language.next }}</span>
<span class="is-float-left">{{ primaryItem ? primaryItem.name : SessionUser.objects.global.language.next }}</span>
<span class="is-float-right ">
@@ -245,14 +245,50 @@ const step2 = () => {
// Set the order bookings id to the metadata
if (metadata.getBookingId() && metadata.getBookingId() > 0) {
SessionUser.objects.order_bookings.set.order_id(metadata.getBookingId(), order_id.value).then(() => {
popups.select('complete_booking', {});
// Check if order requires safety seal popup (product id 41 present as item or addon)
try {
const posStr = localStorage.getItem('pos');
let hasSeal = false;
if (posStr) {
try {
const posData = JSON.parse(posStr);
const primaryItem = posData?.transactionItems?.primaryItem;
const additionalItems = posData?.transactionItems?.additionalItems || [];
const allItems = [primaryItem, ...additionalItems].filter(Boolean);
hasSeal = allItems.some((item: any) =>
item?.product?.id === 41 ||
(item?.addons && item.addons.some((addon: any) => addon?.product?.id === 41))
);
} catch (parseErr) {
console.error('Failed to parse pos data:', parseErr);
}
}
if (!hasSeal) {
metadata.setLoadingState(true);
metadata.setLoadingMessage(t('admin.pos.waiting_for_booking'));
SessionUser.objects.order_bookings.functions.complete(metadata.getBookingId(), null, (response: any) => {
console.log('Auto-completed booking without safety seal prompt (mobile POS - localStorage)');
completeOrder();
});
} else {
popups.select('complete_booking', {});
awaitBookingCompletion(() => {
completeOrder();
});
}
} catch (err) {
console.error('Error checking safety seal via localStorage:', err);
popups.select('complete_booking', {});
awaitBookingCompletion(() => {
completeOrder();
});
}
}).catch((err: any) => {
console.error('Error setting order_id on booking:', err);
errors.value.push(err);
completeOrder();
});
}
// Listen for the booking to be completed
awaitBookingCompletion(() => {
completeOrder();
});
return; // Wait for the booking to be completed
} else {
// The booking has been completed, complete the order
completeOrder();
@@ -261,6 +297,7 @@ const step2 = () => {
errors.value.push(error);
// An error occurred, log it and complete the order anyway
console.warn('An error occurred while checking if the booking has been completed:', error);
completeOrder();
});
} else {
completeOrder();
@@ -291,6 +328,7 @@ const awaitBookingCompletion = (callback: Function) => {
}).catch((error: any) => {
errors.value.push(error);
console.warn('An error occurred while checking if the booking has been completed:', error);
completeOrder();
});
}, 250) // Check every 250ms (Should be quick enough to catch the popup close event);
}
@@ -33,7 +33,7 @@ const safetySealNumber = ref("");
</div>
<!-- Generate certificate button -->
<div class="buttons is-centered mb-4">
<button class="button is-light" @click="emit('close')">
<button class="button is-light" @click="SessionUser.objects.order_bookings.functions.complete(metadata.getBookingId(), '', onSuccessfulCertificateSubmission)">
Intet vaskecertifikat
</button>
<button
+3
View File
@@ -751,6 +751,7 @@ describe('MyWashStart Step Navigation', () => {
describe('TASKS step validation', () => {
it('should require license plate, vehicle type, lane selected, and questions answered', async () => {
await browser.execute(() => localStorage.clear());
await browser.url('/');
const result = await browser.executeAsync(async (done) => {
@@ -828,6 +829,7 @@ describe('MyWashStart Step Navigation', () => {
describe('WASH_IN_PROGRESS step validation', () => {
it('should only be clickable when wash is in progress', async () => {
await browser.execute(() => localStorage.clear());
await browser.url('/');
const result = await browser.executeAsync(async (done) => {
@@ -865,6 +867,7 @@ describe('MyWashStart Step Navigation', () => {
describe('Step transition flow', () => {
it('should follow correct step progression', async () => {
await browser.execute(() => localStorage.clear());
await browser.url('/');
const result = await browser.executeAsync(async (done) => {
+1 -1
View File
@@ -93,7 +93,7 @@ export const config = {
baseUrl: 'http://localhost:5173',
//
// Default timeout for all waitFor* commands.
waitforTimeout: 10000,
waitforTimeout: 30000,
//
// Default timeout in milliseconds for request
// if browser driver or grid doesn't send response