From 35572e1d7006cd8bb27f4d7aef3dd0120460c222 Mon Sep 17 00:00:00 2001 From: Stephen Adamson Date: Thu, 20 Aug 2026 14:31:49 +0100 Subject: [PATCH] fix: persist service price overrides before payment Service price overrides in PaymentModal were only used for frontend calculations but not persisted to the backend. This caused receipts and subsequent payments to use original prices instead of overridden ones. Added saveServiceOverrides() function that calls PUT /api/admin/bookings/{id}/services before payment to persist any price changes. Called in both handleCardPayment and handleSavedCardPayment before applyLoyaltyRedemption(). --- .../components/payments/PaymentModal.svelte | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/frontend/src/lib/components/payments/PaymentModal.svelte b/frontend/src/lib/components/payments/PaymentModal.svelte index c01a3ef..066ccd6 100644 --- a/frontend/src/lib/components/payments/PaymentModal.svelte +++ b/frontend/src/lib/components/payments/PaymentModal.svelte @@ -460,6 +460,47 @@ } } + async function saveServiceOverrides(): Promise { + // Check if any service has an override that differs from the original price + const services = booking.services ?? []; + const hasOverrides = services.some((s) => { + const override = serviceOverrides[s.service_id]; + if (!override) return false; + const overridePrice = parseFloat(override.price); + return !isNaN(overridePrice) && Math.abs(overridePrice - override.originalPrice) > 0.01; + }); + + if (!hasOverrides) return; + + // Build the request payload + const serviceIds = services.map((s) => s.service_id).filter((id): id is string => !!id); + const serviceOverridesPayload = services + .filter((s) => { + const override = serviceOverrides[s.service_id]; + if (!override) return false; + const overridePrice = parseFloat(override.price); + return !isNaN(overridePrice) && Math.abs(overridePrice - override.originalPrice) > 0.01; + }) + .map((s) => ({ + service_id: s.service_id, + override_price: parseFloat(serviceOverrides[s.service_id].price) + })); + + const res = await apiFetch(`/api/admin/bookings/${booking.id}/services`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + service_ids: serviceIds, + service_overrides: serviceOverridesPayload + }) + }); + + if (!res.ok) { + const errData = await res.text(); + throw new Error(extractErrorMessage(errData) || 'Failed to save service overrides'); + } + } + async function handleCardPayment() { if (isProcessingPaymentSync) return; const finalAmount = cardAmountNum; @@ -484,6 +525,7 @@ error = null; try { + await saveServiceOverrides(); await applyLoyaltyRedemption(); const response = await apiFetch(`/api/admin/bookings/${booking.id}/payment`, { @@ -961,6 +1003,7 @@ let responseStatus = 0; try { + await saveServiceOverrides(); await applyLoyaltyRedemption(); // Proactive saved-card (ccof) SCA: run the client-side challenge