From 0edc111bfe4605a199602df3eae0e119c4a636a3 Mon Sep 17 00:00:00 2001 From: Stephen Adamson Date: Sat, 11 Jul 2026 17:42:17 +0100 Subject: [PATCH] fix: correct global milestone discount indentation (C1) --- backend/handlers/bookings/bookings.go | 120 +++++++++++++------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/backend/handlers/bookings/bookings.go b/backend/handlers/bookings/bookings.go index e30f7a2..345396f 100644 --- a/backend/handlers/bookings/bookings.go +++ b/backend/handlers/bookings/bookings.go @@ -1450,10 +1450,10 @@ func UpdateBookingServicesHandler(w http.ResponseWriter, r *http.Request) { return } defer func() { - if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" { - slog.Error("failed to rollback transaction", "err", err) - } -}() + if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" { + slog.Error("failed to rollback transaction", "err", err) + } + }() // Evict any pending_release bookings that overlap this slot. if _, evictErr := EvictPendingReleaseOverlapping(r.Context(), tx, startTime, newEndTime); evictErr != nil { @@ -1999,7 +1999,7 @@ func CreateBookingHandler(w http.ResponseWriter, r *http.Request) { // Get payment info var preStartPaid float64 -if err := db.Conn.QueryRow(r.Context(), `SELECT COALESCE(SUM(amount), 0) FROM payments WHERE booking_id = $1 AND payment_type IN ('deposit', 'full') AND status = 'completed'`, existingBooking.ID).Scan(&preStartPaid); err != nil { + if err := db.Conn.QueryRow(r.Context(), `SELECT COALESCE(SUM(amount), 0) FROM payments WHERE booking_id = $1 AND payment_type IN ('deposit', 'full') AND status = 'completed'`, existingBooking.ID).Scan(&preStartPaid); err != nil { log.Printf("Failed to scan preStartPaid for booking %s: %v", existingBooking.ID, err) } populateDepositFields(&existingBooking, existingBooking.DepositRequired, preStartPaid) @@ -2233,10 +2233,10 @@ if err := db.Conn.QueryRow(r.Context(), `SELECT COALESCE(SUM(amount), 0) FROM pa return } defer func() { - if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" { - slog.Error("failed to rollback transaction", "err", err) - } -}() + if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" { + slog.Error("failed to rollback transaction", "err", err) + } + }() // Evict any pending_release bookings that overlap this slot. if _, err := EvictPendingReleaseOverlapping(r.Context(), tx, req.StartTime, endTime); err != nil { @@ -2444,10 +2444,10 @@ func EditBookingHandler(w http.ResponseWriter, r *http.Request) { return } defer func() { - if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" { - slog.Error("failed to rollback transaction", "err", err) - } -}() + if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" { + slog.Error("failed to rollback transaction", "err", err) + } + }() var durationMinutes int if err := tx.QueryRow(r.Context(), ` @@ -2603,10 +2603,10 @@ func ProgressBookingHandler(w http.ResponseWriter, r *http.Request) { return } defer func() { - if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" { - slog.Error("failed to rollback transaction", "err", err) - } -}() + if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" { + slog.Error("failed to rollback transaction", "err", err) + } + }() // Read current status before updating to validate the transition var currentStatus string @@ -2826,42 +2826,42 @@ func ProgressBookingHandler(w http.ResponseWriter, r *http.Request) { log.Printf("Failed to scan global completed booking count: %v", err) } - var hasInPersonPayment bool - if err := tx.QueryRow(r.Context(), ` - SELECT EXISTS(SELECT 1 FROM payments WHERE booking_id = $1 AND payment_method = 'in_person_card')`, bookingID).Scan(&hasInPersonPayment); err != nil { - log.Printf("Failed to check in-person payment on booking %s: %v", bookingID, err) - } - - if hasInPersonPayment { - var globalCampaignID string - var globalPercent float64 + var hasInPersonPayment bool if err := tx.QueryRow(r.Context(), ` - SELECT id, discount_percent FROM discount_campaigns - WHERE status = 'active' AND campaign_type = 'milestone' AND milestone_type = 'global_booking_count' - AND milestone_value <= $1 - AND (max_redemptions IS NULL OR times_redeemed < max_redemptions) - ORDER BY milestone_value DESC LIMIT 1 - `, globalCount).Scan(&globalCampaignID, &globalPercent); err != nil { - log.Printf("Failed to query global milestone campaign for booking %s: %v", bookingID, err) + SELECT EXISTS(SELECT 1 FROM payments WHERE booking_id = $1 AND payment_method = 'in_person_card')`, bookingID).Scan(&hasInPersonPayment); err != nil { + log.Printf("Failed to check in-person payment on booking %s: %v", bookingID, err) } - if globalCampaignID != "" { + if hasInPersonPayment { + var globalCampaignID string + var globalPercent float64 + if err := tx.QueryRow(r.Context(), ` + SELECT id, discount_percent FROM discount_campaigns + WHERE status = 'active' AND campaign_type = 'milestone' AND milestone_type = 'global_booking_count' + AND milestone_value <= $1 + AND (max_redemptions IS NULL OR times_redeemed < max_redemptions) + ORDER BY milestone_value DESC LIMIT 1 + `, globalCount).Scan(&globalCampaignID, &globalPercent); err != nil { + log.Printf("Failed to query global milestone campaign for booking %s: %v", bookingID, err) + } + + if globalCampaignID != "" { discountAmount := roundTo2(bookingTotal * globalPercent / 100) if _, err := tx.Exec(r.Context(), ` - INSERT INTO booking_discounts (booking_id, user_id, discount_source, source_id, campaign_type, milestone_type, discount_percent, original_total, discount_amount) - VALUES ($1, $2, 'campaign', $3, 'milestone', 'global_booking_count', $4, $5, $6) - `, bookingID, booking.User.ID, globalCampaignID, globalPercent, bookingTotal, discountAmount); err != nil { + INSERT INTO booking_discounts (booking_id, user_id, discount_source, source_id, campaign_type, milestone_type, discount_percent, original_total, discount_amount) + VALUES ($1, $2, 'campaign', $3, 'milestone', 'global_booking_count', $4, $5, $6) + `, bookingID, booking.User.ID, globalCampaignID, globalPercent, bookingTotal, discountAmount); err != nil { log.Printf("ALERT: failed to insert booking discount: %v", err) } if _, err := tx.Exec(r.Context(), ` - INSERT INTO payments (booking_id, payment_type, payment_method, amount, status, created_by) - VALUES ($1, 'partial', 'discount', $2, 'completed', $3) - `, bookingID, discountAmount, booking.User.ID); err != nil { + INSERT INTO payments (booking_id, payment_type, payment_method, amount, status, created_by) + VALUES ($1, 'partial', 'discount', $2, 'completed', $3) + `, bookingID, discountAmount, booking.User.ID); err != nil { log.Printf("ALERT: failed to insert payment record: %v", err) } if _, err := tx.Exec(r.Context(), ` - UPDATE discount_campaigns SET times_redeemed = times_redeemed + 1 WHERE id = $1 - `, globalCampaignID); err != nil { + UPDATE discount_campaigns SET times_redeemed = times_redeemed + 1 WHERE id = $1 + `, globalCampaignID); err != nil { log.Printf("ALERT: failed to insert payment record: %v", err) } } @@ -3032,10 +3032,10 @@ func ConfirmBookingHandler(w http.ResponseWriter, r *http.Request) { return } defer func() { - if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" { - slog.Error("failed to rollback transaction", "err", err) - } -}() + if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" { + slog.Error("failed to rollback transaction", "err", err) + } + }() var bkStart time.Time var dur int @@ -3167,7 +3167,7 @@ func ConfirmBookingHandler(w http.ResponseWriter, r *http.Request) { if dav.Service != nil { var durationMinutes int -if err := db.Conn.QueryRow(r.Context(), ` + if err := db.Conn.QueryRow(r.Context(), ` SELECT total_duration_minutes FROM bookings WHERE id = $1 `, bookingID).Scan(&durationMinutes); err != nil { log.Printf("ALERT: failed to scan total_duration_minutes for booking %s: %v", bookingID, err) @@ -3218,7 +3218,7 @@ func DeleteBookingHandler(w http.ResponseWriter, r *http.Request) { } if err := validators.Validate.Struct(&req); err != nil { log.Printf("Failed to process request: %v", err) - http.Error(w, "Invalid request", http.StatusBadRequest) + http.Error(w, "Invalid request", http.StatusBadRequest) return } allowed := map[string]bool{ @@ -3271,10 +3271,10 @@ func DeleteBookingHandler(w http.ResponseWriter, r *http.Request) { return } defer func() { - if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" { - slog.Error("failed to rollback transaction", "err", err) - } -}() + if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" { + slog.Error("failed to rollback transaction", "err", err) + } + }() result, err := tx.Exec(r.Context(), ` UPDATE bookings SET status = $1, updated_at = NOW() WHERE id = $2 AND user_id = $3 @@ -3362,10 +3362,10 @@ func DeleteBookingHandler(w http.ResponseWriter, r *http.Request) { return } defer func() { - if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" { - slog.Error("failed to rollback transaction", "err", err) - } -}() + if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" { + slog.Error("failed to rollback transaction", "err", err) + } + }() if _, err := tx.Exec(r.Context(), `DELETE FROM admin_notifications WHERE booking_id = $1`, bookingID); err != nil { log.Printf("Failed to delete admin notifications for booking %s: %v", bookingID, err) @@ -4201,10 +4201,10 @@ func AdminRescheduleBookingHandler(w http.ResponseWriter, r *http.Request) { return } defer func() { - if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" { - slog.Error("failed to rollback transaction", "err", err) - } -}() + if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" { + slog.Error("failed to rollback transaction", "err", err) + } + }() forgiveNoShow := req.ForgiveNoShow != nil && *req.ForgiveNoShow if forgiveNoShow && bookingUserID != "" {