feat(backend): remove auto loyalty redemption from booking progress

Remove auto-apply of pending loyalty redemption during ProgressBookingHandler. Now only checks if loyalty was applied on this booking (via booking_discounts) to skip stamp award. Redemption is purely manual via ApplyLoyaltyRedemption. Use LoyaltyStampCost constant instead of hardcoded 10.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-06-19 11:34:26 +01:00
co-authored by Sisyphus
parent 19996c1e08
commit 1e100e4db7
+9 -47
View File
@@ -2553,51 +2553,13 @@ func ProgressBookingHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("Failed to calculate booking total for %s: %v", bookingID, err)
}
// Apply existing pending loyalty redemption (earned from previous 10 bookings)
// Skip if already applied at payment time (or by admin)
var loyaltyAlreadyApplied bool
tx.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'loyalty')`, bookingID).Scan(&loyaltyAlreadyApplied)
if bookingTotal > 0 && !loyaltyAlreadyApplied {
var redemptionID string
if err := tx.QueryRow(r.Context(), `
SELECT id FROM loyalty_redemptions
WHERE user_id = $1 AND status = 'pending' AND expires_at > NOW()
ORDER BY redeemed_at ASC LIMIT 1
`, booking.User.ID).Scan(&redemptionID); err == nil && redemptionID != "" {
discountAmount := roundTo2(bookingTotal * 0.10)
// Don't award a stamp if this booking already used a loyalty redemption
// (take or receive, never both).
var loyaltyAppliedOnThisBooking bool
tx.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'loyalty')`, bookingID).Scan(&loyaltyAppliedOnThisBooking)
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, 'loyalty', $3, NULL, NULL, 10.00, $4, $5)
`, bookingID, booking.User.ID, redemptionID, 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 {
log.Printf("ALERT: failed to insert payment record: %v", err)
}
if _, err := tx.Exec(r.Context(), `
UPDATE loyalty_redemptions SET status = 'applied', applied_to_booking_id = $1, applied_at = NOW()
WHERE id = $2
`, bookingID, redemptionID); err != nil {
log.Printf("ALERT: failed to update loyalty redemption: %v", err)
}
if _, err := tx.Exec(r.Context(), `
UPDATE users SET loyalty_stamps = GREATEST(0, loyalty_stamps - 10) WHERE id = $1
`, booking.User.ID); err != nil {
log.Printf("ALERT: failed to update loyalty stamps: %v", err)
}
}
}
// Increment stamps (max 1 per day, only for paid bookings)
var newStampCount int
if bookingTotal > 0 {
if bookingTotal > 0 && !loyaltyAppliedOnThisBooking {
if err := tx.QueryRow(r.Context(), `
UPDATE users
SET loyalty_stamps = loyalty_stamps + 1
@@ -2617,12 +2579,12 @@ func ProgressBookingHandler(w http.ResponseWriter, r *http.Request) {
}
}
// Create pending redemption when stamps reach 10
if newStampCount == 10 {
// Create pending redemption when stamps reach LoyaltyStampCost
if newStampCount == payments.LoyaltyStampCost {
_, err = tx.Exec(r.Context(), `
INSERT INTO loyalty_redemptions (user_id, stamps_redeemed, status, redeemed_at)
VALUES ($1, 10, 'pending', NOW())
`, booking.User.ID)
VALUES ($1, $2, 'pending', NOW())
`, booking.User.ID, payments.LoyaltyStampCost)
if err != nil {
log.Printf("Failed to create loyalty redemption for user %s: %v", booking.User.ID, err)
}