"Add auto-apply logic for pending bookings in PosDepartmentStepMobile2.vue: implement booking selection, metadata updates, primary/addon mapping, and watchers for booking/vehicle changes."
This commit is contained in:
@@ -31,6 +31,7 @@ import PosDepartmentStepMobile2AdditionalItems
|
||||
from "@/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobile2AdditionalItems.vue";
|
||||
import PosDepartmentStepMobile2Customer
|
||||
from "@/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobile2Customer.vue";
|
||||
import { pendingBookings, getVehiclePlateBooking } from "@/components/shop/POSDepartmentProcess.vue";
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
@@ -61,6 +62,8 @@ onMounted(() => {
|
||||
|
||||
// Watch for scroll events to handle long press detection
|
||||
window.addEventListener('scroll', onScroll, true);
|
||||
// Try to auto-apply pending booking when Step 2 opens
|
||||
applyPendingBookingFromSelection();
|
||||
});
|
||||
|
||||
const applyPotentialCustomerNumberChange = (oldId, newId) => {
|
||||
@@ -77,6 +80,123 @@ watch(customer_id, (newId, oldId) => {
|
||||
|
||||
const lastFetchedPrimaryItemProduct = ref<PosProduct | null>(null); // This is used to make sure all the addons are fetched when the last order is selected.
|
||||
|
||||
// Keep track so we only apply a booking once (idempotent)
|
||||
const lastAppliedBookingId = ref<number | null>(null);
|
||||
|
||||
const getSelectedPendingBooking = () => {
|
||||
// Prefer explicit booking id if present
|
||||
const bookingId = metadata.getBookingId?.() ?? metadata.bookingId?.value ?? null;
|
||||
let booking = null as any;
|
||||
if (bookingId) {
|
||||
booking = pendingBookings.value?.find?.((b: any) => b?.id === bookingId) || null;
|
||||
if (booking) return booking;
|
||||
}
|
||||
// Fallback: try match current primary vehicle registration
|
||||
const reg = vehicles?.vehicle_1?.value?.reg || null;
|
||||
if (reg) {
|
||||
try {
|
||||
booking = getVehiclePlateBooking(reg);
|
||||
} catch (e) {
|
||||
booking = null;
|
||||
}
|
||||
}
|
||||
return booking || null;
|
||||
}
|
||||
|
||||
const fetchProductById = async (id: number): Promise<PosProduct> => {
|
||||
// Fetch product with final price for current department/customer. Fallback to a minimal shape.
|
||||
try {
|
||||
// Note: metadata.getCustomerId exists in flow; fallback to global customer_id if missing
|
||||
const custId = (metadata.getCustomerId && metadata.getCustomerId()) || (customer_id?.value ? parseInt(customer_id.value as any) : null);
|
||||
const product = await SessionUser.objects.products.get.single(id, {
|
||||
department_id: department_id.value,
|
||||
customer_id: custId,
|
||||
category_id: null,
|
||||
final_price: true
|
||||
});
|
||||
return product as PosProduct;
|
||||
} catch (e) {
|
||||
console.error('Failed to fetch product', id, e);
|
||||
// Minimal fallback; name/price will be set by caller when available
|
||||
return { id, name: 'Produkt', price: 0, subscription_allowed: false } as PosProduct;
|
||||
}
|
||||
}
|
||||
|
||||
const applyPendingBookingFromSelection = async () => {
|
||||
const booking: any = getSelectedPendingBooking();
|
||||
if (!booking) return;
|
||||
if (lastAppliedBookingId.value === booking.id) return; // avoid duplicate application
|
||||
|
||||
try {
|
||||
// Update metadata booking id if not set
|
||||
if (!metadata.getBookingId || !metadata.getBookingId()) {
|
||||
metadata.setBookingId && metadata.setBookingId(booking.id);
|
||||
}
|
||||
|
||||
// Set PO number on the order if provided by the booking
|
||||
if (booking.po && typeof booking.po === 'string' && booking.po.trim() !== '') {
|
||||
try {
|
||||
await SessionUser.objects.orders.set.po(order_id.value, booking.po);
|
||||
console.warn('Applied PO from booking:', booking.po);
|
||||
} catch (e) {
|
||||
console.error('Failed to set PO on order from booking', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Map booking items to primary + addons
|
||||
const items: any[] = Array.isArray(booking.items) ? booking.items : [];
|
||||
if (!items.length) {
|
||||
lastAppliedBookingId.value = booking.id;
|
||||
return;
|
||||
}
|
||||
|
||||
// Primary product is the first item
|
||||
const primaryRaw = items[0];
|
||||
const primaryId = parseInt(primaryRaw?.id);
|
||||
if (!primaryId || isNaN(primaryId)) {
|
||||
lastAppliedBookingId.value = booking.id;
|
||||
return;
|
||||
}
|
||||
const primaryProduct = await fetchProductById(primaryId);
|
||||
// Prefer booking price if provided, otherwise keep fetched price
|
||||
if (typeof primaryRaw.price === 'number') primaryProduct.price = primaryRaw.price;
|
||||
primaryProduct.name = primaryRaw?.name || primaryProduct.name;
|
||||
primaryProduct.quantity = 1; // Primary item quantity is handled as 1 in order creation
|
||||
// Apply as primary item
|
||||
transactionItems.setPrimaryItem(primaryProduct as any);
|
||||
|
||||
// Remaining items become addons
|
||||
const addonRawList = items.slice(1);
|
||||
const addonProducts = await Promise.all(addonRawList.map(async (raw: any) => {
|
||||
const id = parseInt(raw?.id);
|
||||
if (!id || isNaN(id)) return null;
|
||||
const p = await fetchProductById(id);
|
||||
if (typeof raw.price === 'number') p.price = raw.price;
|
||||
p.name = raw?.name || p.name;
|
||||
p.quantity = raw?.quantity || 1;
|
||||
return p;
|
||||
}));
|
||||
const preparedAddons = addonProducts
|
||||
.filter(p => !!p)
|
||||
.map(p => transactionItems.convertProductToAddon(p as any, { quantity: (p as any)?.quantity || 1 }));
|
||||
|
||||
if (!transactionItems.primaryItem.value) {
|
||||
// Safety: if primary was cleared/changed while awaiting fetches, set again
|
||||
transactionItems.setPrimaryItem(primaryProduct as any);
|
||||
}
|
||||
// Attach addons to primary
|
||||
if (transactionItems.primaryItem.value) {
|
||||
// Keep any existing addons that may have been set previously only if they are different.
|
||||
transactionItems.primaryItem.value.addons = preparedAddons as any;
|
||||
}
|
||||
|
||||
lastAppliedBookingId.value = booking.id;
|
||||
console.warn('Applied pending booking to cart (primary + addons):', booking.id);
|
||||
} catch (e) {
|
||||
console.error('Failed to apply pending booking to Step 2', e);
|
||||
}
|
||||
}
|
||||
|
||||
const fetchPrimaryItemProduct = () => {
|
||||
if (!vehicles.vehicle_1.value.type && !transactionItems.primaryItem.value) {
|
||||
vehicleSelection.value = true; // Force the user to select a product.
|
||||
@@ -324,6 +444,14 @@ const onBeforeComplete = () => {
|
||||
});
|
||||
}
|
||||
|
||||
// Re-try applying booking whenever booking id or vehicle reg changes
|
||||
watch(() => metadata.bookingId?.value, () => {
|
||||
applyPendingBookingFromSelection();
|
||||
});
|
||||
watch(() => vehicles?.vehicle_1?.value?.reg, () => {
|
||||
applyPendingBookingFromSelection();
|
||||
});
|
||||
|
||||
// Watch for changes in the primary item and update the vehicle selection if necessary
|
||||
// Utility: map source addons and carry over quantities from previous addons by product.id
|
||||
const mapAddonsWithQuantity = (sourceAddons = [], previousAddons = []) =>
|
||||
|
||||
Reference in New Issue
Block a user