chore: run go fix for Go 1.26 modernization
CI / Go vulnerabilities (push) Successful in 1m10s
CI / Build & Vet (push) Successful in 1m39s
CI / Frontend build (gate) (push) Successful in 1m42s
CI / Frontend QC (audit) (push) Successful in 56s
CI / Frontend QC (typecheck) (push) Successful in 1m36s
CI / Frontend QC (lint) (push) Successful in 1m51s
CI / Tests (prod) (push) Has been cancelled
CI / Tests (dev) (push) Has been cancelled
CI / Race (prod) (push) Has been cancelled
CI / Race (dev) (push) Has been cancelled

106 files: interface{}→any, strings.Split→SplitSeq, CutPrefix/Cut, strings.Builder, slices.Contains, remove redundant // +build directives, gofmt import ordering and indentation.

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-07-09 17:25:23 +01:00
co-authored by Sisyphus
parent ef26bd59e9
commit 510828c924
107 changed files with 882 additions and 745 deletions
+177 -177
View File
@@ -2,8 +2,8 @@ package bookings
import (
"context"
"crussell/db"
"crussell/clock"
"crussell/db"
"crussell/handlers/notifications"
"crussell/handlers/payments"
"crussell/handlers/scheduling"
@@ -254,20 +254,20 @@ type AdminBookingSummary struct {
}
type UserSummary struct {
ID string `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
FullName string `json:"full_name"`
Email *string `json:"email,omitempty"`
Phone *string `json:"phone,omitempty"`
ProfilePicURL *string `json:"profile_pic_url,omitempty"`
DateOfBirth *string `json:"date_of_birth,omitempty"`
AccountRole string `json:"account_role"`
LoyaltyStamps *int `json:"loyalty_stamps,omitempty"`
ReferralCode *string `json:"referral_code,omitempty"`
ReferralCodeUses *int `json:"referral_code_uses,omitempty"`
CreatedAt string `json:"created_at"`
Notes *string `json:"notes,omitempty" validate:"omitempty,max=1000000"`
ID string `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
FullName string `json:"full_name"`
Email *string `json:"email,omitempty"`
Phone *string `json:"phone,omitempty"`
ProfilePicURL *string `json:"profile_pic_url,omitempty"`
DateOfBirth *string `json:"date_of_birth,omitempty"`
AccountRole string `json:"account_role"`
LoyaltyStamps *int `json:"loyalty_stamps,omitempty"`
ReferralCode *string `json:"referral_code,omitempty"`
ReferralCodeUses *int `json:"referral_code_uses,omitempty"`
CreatedAt string `json:"created_at"`
Notes *string `json:"notes,omitempty" validate:"omitempty,max=1000000"`
PreviousFirstName *string `json:"previous_first_name,omitempty"`
PreviousLastName *string `json:"previous_last_name,omitempty"`
}
@@ -387,7 +387,7 @@ func GetAllUserBookingsHandler(w http.ResponseWriter, r *http.Request) {
// Build WHERE conditions shared between count and data queries.
whereClause := " WHERE b.user_id = $1"
whereArgs := []interface{}{userID}
whereArgs := []any{userID}
paramCount := 2
if req.Status != nil {
@@ -447,7 +447,7 @@ func GetAllUserBookingsHandler(w http.ResponseWriter, r *http.Request) {
WHERE booking_id = b.id
) pt ON true` + whereClause
dataArgs := make([]interface{}, len(whereArgs))
dataArgs := make([]any, len(whereArgs))
copy(dataArgs, whereArgs)
dataParamCount := paramCount
@@ -683,7 +683,7 @@ func GetAllAdminBookingsHandler(w http.ResponseWriter, r *http.Request) {
) pre_pay ON true
`
var args []interface{}
var args []any
paramCount := 1
addWhereClause := func(condition string) {
@@ -750,7 +750,7 @@ func GetAllAdminBookingsHandler(w http.ResponseWriter, r *http.Request) {
var total int
{
countWhere := ""
var countArgs []interface{}
var countArgs []any
cp := 1
addCountWhere := func(cond string) {
if cp == 1 {
@@ -954,7 +954,7 @@ func GetAllBookingsByUserHandler(w http.ResponseWriter, r *http.Request) {
FROM bookings b
WHERE b.user_id = $1
`
var args []interface{}
var args []any
args = append(args, userID)
paramCount := 2
@@ -1793,7 +1793,7 @@ func SearchAdminBookingsHandler(w http.ResponseWriter, r *http.Request) {
WHERE bcs.booking_id = b.id AND cs.name ILIKE $1 ESCAPE '\'
)
`
var args []interface{}
var args []any
args = append(args, searchPattern)
paramCount := 2
@@ -2639,9 +2639,9 @@ func ProgressBookingHandler(w http.ResponseWriter, r *http.Request) {
if currentStatus == "completed" {
log.Printf("Booking %s is already completed — skipping duplicate completion", bookingID)
} else {
// Collect patch test IDs first so the rows are consumed before INSERT operations.
var patchTestIDs []string
ptRows, err := tx.Query(r.Context(), `
// Collect patch test IDs first so the rows are consumed before INSERT operations.
var patchTestIDs []string
ptRows, err := tx.Query(r.Context(), `
SELECT DISTINCT pt.id
FROM patch_tests pt
JOIN booking_services bs ON bs.booking_id = $1
@@ -2649,42 +2649,42 @@ func ProgressBookingHandler(w http.ResponseWriter, r *http.Request) {
SELECT pt_inner.id FROM patch_tests pt_inner WHERE bs.service_id = ANY(pt_inner.service_ids)
)
`, bookingID)
if err != nil {
log.Printf("Failed to fetch patch tests for booking %s: %v", bookingID, err)
} else {
for ptRows.Next() {
var ptID string
if err := ptRows.Scan(&ptID); err == nil {
patchTestIDs = append(patchTestIDs, ptID)
if err != nil {
log.Printf("Failed to fetch patch tests for booking %s: %v", bookingID, err)
} else {
for ptRows.Next() {
var ptID string
if err := ptRows.Scan(&ptID); err == nil {
patchTestIDs = append(patchTestIDs, ptID)
}
}
ptRows.Close()
}
ptRows.Close()
}
for _, ptID := range patchTestIDs {
if _, err := tx.Exec(r.Context(), `
for _, ptID := range patchTestIDs {
if _, err := tx.Exec(r.Context(), `
INSERT INTO user_patch_tests (user_id, patch_test_id, tested_at)
VALUES ($1, $2, NOW())
ON CONFLICT (user_id, patch_test_id) DO UPDATE SET tested_at = NOW()
`, booking.User.ID, ptID); err != nil {
log.Printf("Failed to update patch test validity for user %s, patch test %s: %v", booking.User.ID, ptID, err)
log.Printf("Failed to update patch test validity for user %s, patch test %s: %v", booking.User.ID, ptID, err)
}
}
}
var bookingTotal float64
if err := tx.QueryRow(r.Context(), `
var bookingTotal float64
if err := tx.QueryRow(r.Context(), `
SELECT total_amount FROM bookings WHERE id = $1
`, bookingID).Scan(&bookingTotal); err != nil {
log.Printf("Failed to calculate booking total for %s: %v", bookingID, err)
}
log.Printf("Failed to calculate booking total for %s: %v", bookingID, err)
}
// 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)
// 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)
var newStampCount int
if bookingTotal > 0 && !loyaltyAppliedOnThisBooking {
if err := tx.QueryRow(r.Context(), `
var newStampCount int
if bookingTotal > 0 && !loyaltyAppliedOnThisBooking {
if err := tx.QueryRow(r.Context(), `
UPDATE users
SET loyalty_stamps = loyalty_stamps + 1
WHERE id = $1
@@ -2697,108 +2697,108 @@ func ProgressBookingHandler(w http.ResponseWriter, r *http.Request) {
)
RETURNING loyalty_stamps
`, booking.User.ID, bookingID).Scan(&newStampCount); err != nil {
if !errors.Is(err, pgx.ErrNoRows) {
log.Printf("Failed to add loyalty stamp for booking %s: %v", bookingID, err)
if !errors.Is(err, pgx.ErrNoRows) {
log.Printf("Failed to add loyalty stamp for booking %s: %v", bookingID, err)
}
}
}
}
// Create pending redemption when stamps reach LoyaltyStampCost
if newStampCount == payments.LoyaltyStampCost {
_, err = tx.Exec(r.Context(), `
// 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, $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)
if err != nil {
log.Printf("Failed to create loyalty redemption for user %s: %v", booking.User.ID, err)
}
}
}
// Skip time-based campaign if already applied at payment time
var timeBasedApplied bool
tx.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'campaign' AND campaign_type = 'time_based')`, bookingID).Scan(&timeBasedApplied)
if bookingTotal > 0 && !timeBasedApplied {
var campaignID string
var campaignPercent float64
if err := tx.QueryRow(r.Context(), `
// Skip time-based campaign if already applied at payment time
var timeBasedApplied bool
tx.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'campaign' AND campaign_type = 'time_based')`, bookingID).Scan(&timeBasedApplied)
if bookingTotal > 0 && !timeBasedApplied {
var campaignID string
var campaignPercent float64
if err := tx.QueryRow(r.Context(), `
SELECT id, discount_percent FROM discount_campaigns
WHERE status = 'active' AND campaign_type = 'time_based'
AND start_date <= NOW() AND end_date >= NOW()
AND (max_redemptions IS NULL OR times_redeemed < max_redemptions)
ORDER BY discount_percent DESC LIMIT 1
`).Scan(&campaignID, &campaignPercent); err == nil && campaignID != "" {
discountAmount := roundTo2(bookingTotal * campaignPercent / 100)
discountAmount := roundTo2(bookingTotal * campaignPercent / 100)
if _, err := tx.Exec(r.Context(), `
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, 'time_based', NULL, $4, $5, $6)
`, bookingID, booking.User.ID, campaignID, campaignPercent, bookingTotal, discountAmount); err != nil {
log.Printf("ALERT: failed to insert booking discount: %v", err)
}
log.Printf("ALERT: failed to insert booking discount: %v", err)
}
if _, err := tx.Exec(r.Context(), `
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)
}
log.Printf("ALERT: failed to insert payment record: %v", err)
}
if _, err := tx.Exec(r.Context(), `
if _, err := tx.Exec(r.Context(), `
UPDATE discount_campaigns SET times_redeemed = times_redeemed + 1 WHERE id = $1
`, campaignID); err != nil {
log.Printf("ALERT: failed to update discount campaign usage: %v", err)
log.Printf("ALERT: failed to update discount campaign usage: %v", err)
}
}
}
}
if bookingTotal > 0 {
var userBookingCount int
_ = tx.QueryRow(r.Context(), `SELECT COUNT(*) FROM bookings WHERE user_id = $1 AND status = 'completed'`, booking.User.ID).Scan(&userBookingCount)
if bookingTotal > 0 {
var userBookingCount int
_ = tx.QueryRow(r.Context(), `SELECT COUNT(*) FROM bookings WHERE user_id = $1 AND status = 'completed'`, booking.User.ID).Scan(&userBookingCount)
var milestoneCampaignID string
var milestonePercent float64
_ = tx.QueryRow(r.Context(), `
var milestoneCampaignID string
var milestonePercent float64
_ = tx.QueryRow(r.Context(), `
SELECT id, discount_percent FROM discount_campaigns
WHERE status = 'active' AND campaign_type = 'milestone' AND milestone_type = 'per_user_booking_count'
AND milestone_value = $1
AND NOT EXISTS (SELECT 1 FROM booking_discounts WHERE user_id = $2 AND source_id = discount_campaigns.id)
`, userBookingCount, booking.User.ID).Scan(&milestoneCampaignID, &milestonePercent)
if milestoneCampaignID != "" {
discountAmount := roundTo2(bookingTotal * milestonePercent / 100)
if _, err := tx.Exec(r.Context(), `
if milestoneCampaignID != "" {
discountAmount := roundTo2(bookingTotal * milestonePercent / 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', 'per_user_booking_count', $4, $5, $6)
`, bookingID, booking.User.ID, milestoneCampaignID, milestonePercent, bookingTotal, discountAmount); err != nil {
log.Printf("ALERT: failed to insert booking discount: %v", err)
}
if _, err := tx.Exec(r.Context(), `
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(), `
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
`, milestoneCampaignID); err != nil {
log.Printf("ALERT: failed to insert payment record: %v", err)
log.Printf("ALERT: failed to insert payment record: %v", err)
}
}
}
var globalMilestoneApplied bool
tx.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'campaign' AND campaign_type = 'milestone' AND milestone_type = 'global_booking_count')`, bookingID).Scan(&globalMilestoneApplied)
if !globalMilestoneApplied {
var globalCount int
_ = tx.QueryRow(r.Context(), `SELECT COUNT(*) FROM bookings WHERE status = 'completed'`).Scan(&globalCount)
var globalMilestoneApplied bool
tx.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'campaign' AND campaign_type = 'milestone' AND milestone_type = 'global_booking_count')`, bookingID).Scan(&globalMilestoneApplied)
if !globalMilestoneApplied {
var globalCount int
_ = tx.QueryRow(r.Context(), `SELECT COUNT(*) FROM bookings WHERE status = 'completed'`).Scan(&globalCount)
var hasInPersonPayment bool
tx.QueryRow(r.Context(), `
var hasInPersonPayment bool
tx.QueryRow(r.Context(), `
SELECT EXISTS(SELECT 1 FROM payments WHERE booking_id = $1 AND payment_method = 'in_person_card')`, bookingID).Scan(&hasInPersonPayment)
if hasInPersonPayment {
var globalCampaignID string
var globalPercent float64
_ = tx.QueryRow(r.Context(), `
if hasInPersonPayment {
var globalCampaignID string
var globalPercent float64
_ = 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
@@ -2806,126 +2806,126 @@ func ProgressBookingHandler(w http.ResponseWriter, r *http.Request) {
ORDER BY milestone_value DESC LIMIT 1
`, globalCount).Scan(&globalCampaignID, &globalPercent)
if globalCampaignID != "" {
discountAmount := roundTo2(bookingTotal * globalPercent / 100)
if _, err := tx.Exec(r.Context(), `
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 {
log.Printf("ALERT: failed to insert booking discount: %v", err)
}
if _, err := tx.Exec(r.Context(), `
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(), `
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 {
log.Printf("ALERT: failed to insert payment record: %v", err)
log.Printf("ALERT: failed to insert payment record: %v", err)
}
}
}
}
}
var firstVisitDate time.Time
_ = tx.QueryRow(r.Context(), `SELECT MIN(start_time) FROM bookings WHERE user_id = $1 AND status = 'completed'`, booking.User.ID).Scan(&firstVisitDate)
if !firstVisitDate.IsZero() {
annRows, err := tx.Query(r.Context(), `
var firstVisitDate time.Time
_ = tx.QueryRow(r.Context(), `SELECT MIN(start_time) FROM bookings WHERE user_id = $1 AND status = 'completed'`, booking.User.ID).Scan(&firstVisitDate)
if !firstVisitDate.IsZero() {
annRows, err := tx.Query(r.Context(), `
SELECT id, discount_percent, milestone_value, milestone_unit FROM discount_campaigns
WHERE status = 'active' AND campaign_type = 'milestone' AND milestone_type = 'anniversary'
AND NOT EXISTS (SELECT 1 FROM booking_discounts WHERE user_id = $1 AND source_id = discount_campaigns.id AND milestone_type = 'anniversary')
`, booking.User.ID)
if err == nil {
// Collect anniversary campaigns first to avoid interleaving rows with writes.
type annCampaign struct {
id string
pct float64
value int
unit string
}
var campaigns []annCampaign
for annRows.Next() {
var c annCampaign
if annRows.Scan(&c.id, &c.pct, &c.value, &c.unit) == nil {
campaigns = append(campaigns, c)
if err == nil {
// Collect anniversary campaigns first to avoid interleaving rows with writes.
type annCampaign struct {
id string
pct float64
value int
unit string
}
}
annRows.Close()
var campaigns []annCampaign
for annRows.Next() {
var c annCampaign
if annRows.Scan(&c.id, &c.pct, &c.value, &c.unit) == nil {
campaigns = append(campaigns, c)
}
}
annRows.Close()
// Sort by milestone_value descending so we apply the longest anniversary only
sort.Slice(campaigns, func(i, j int) bool {
return campaigns[i].value > campaigns[j].value
})
for _, c := range campaigns {
var matches bool
elapsed := time.Since(firstVisitDate)
switch c.unit {
case "months":
months := int(elapsed.Hours() / (30 * 24))
matches = months >= c.value
case "years":
years := int(elapsed.Hours() / (365.25 * 24))
matches = years >= c.value
}
if matches {
discountAmount := roundTo2(bookingTotal * c.pct / 100)
if _, err := tx.Exec(r.Context(), `
// Sort by milestone_value descending so we apply the longest anniversary only
sort.Slice(campaigns, func(i, j int) bool {
return campaigns[i].value > campaigns[j].value
})
for _, c := range campaigns {
var matches bool
elapsed := time.Since(firstVisitDate)
switch c.unit {
case "months":
months := int(elapsed.Hours() / (30 * 24))
matches = months >= c.value
case "years":
years := int(elapsed.Hours() / (365.25 * 24))
matches = years >= c.value
}
if matches {
discountAmount := roundTo2(bookingTotal * c.pct / 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', 'anniversary', $4, $5, $6)
`, bookingID, booking.User.ID, c.id, c.pct, bookingTotal, discountAmount); err != nil {
log.Printf("ALERT: failed to insert booking discount: %v", err)
}
if _, err := tx.Exec(r.Context(), `
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(), `
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
`, c.id); err != nil {
log.Printf("ALERT: failed to insert payment record: %v", err)
log.Printf("ALERT: failed to insert payment record: %v", err)
}
break // apply longest matching only
}
break // apply longest matching only
}
}
}
}
}
var paymentExists bool
if err := tx.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM payments WHERE booking_id = $1)`, bookingID).Scan(&paymentExists); err == nil && paymentExists {
var newDepositsRequired int
if err := tx.QueryRow(r.Context(), `
var paymentExists bool
if err := tx.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM payments WHERE booking_id = $1)`, bookingID).Scan(&paymentExists); err == nil && paymentExists {
var newDepositsRequired int
if err := tx.QueryRow(r.Context(), `
UPDATE users SET deposits_required = GREATEST(0, deposits_required - 1)
WHERE id = $1
RETURNING deposits_required
`, booking.User.ID).Scan(&newDepositsRequired); err != nil {
log.Printf("ALERT: failed to update deposits_required: %v", err)
} else if newDepositsRequired == 0 {
// After 3 paid bookings, forget no-shows so the counter resets.
if _, err := tx.Exec(r.Context(), `
log.Printf("ALERT: failed to update deposits_required: %v", err)
} else if newDepositsRequired == 0 {
// After 3 paid bookings, forget no-shows so the counter resets.
if _, err := tx.Exec(r.Context(), `
INSERT INTO forgiven_no_shows (booking_id)
SELECT id FROM bookings
WHERE user_id = $1 AND status = 'no_show'
AND start_time >= NOW() - INTERVAL '6 months'
AND NOT EXISTS (SELECT 1 FROM forgiven_no_shows WHERE booking_id = bookings.id)
`, booking.User.ID); err != nil {
log.Printf("ALERT: failed to auto-forgive no-shows: %v", err)
log.Printf("ALERT: failed to auto-forgive no-shows: %v", err)
}
}
}
}
// Consume unconsumed name_history entries — this booking is the "first post-name-change
// booking" that completes. After this, we no longer show "formerly" on displays.
if _, err := tx.Exec(r.Context(), `
// Consume unconsumed name_history entries — this booking is the "first post-name-change
// booking" that completes. After this, we no longer show "formerly" on displays.
if _, err := tx.Exec(r.Context(), `
UPDATE name_history SET booking_id = $1
WHERE user_id = $2 AND booking_id IS NULL
`, bookingID, booking.User.ID); err != nil {
log.Printf("Failed to consume name_history for user %s: %v", booking.User.ID, err)
}
} // close the else from alreadyCompleted check
log.Printf("Failed to consume name_history for user %s: %v", booking.User.ID, err)
}
} // close the else from alreadyCompleted check
}
if err := tx.Commit(r.Context()); err != nil {
@@ -3288,7 +3288,7 @@ func DeleteBookingHandler(w http.ResponseWriter, r *http.Request) {
}
}
resp := map[string]interface{}{
resp := map[string]any{
"message": "Booking cancelled successfully",
"id": bookingID,
"status": req.Reason,
@@ -3336,7 +3336,7 @@ func DeleteBookingHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]interface{}{
json.NewEncoder(w).Encode(map[string]any{
"message": "Booking deleted successfully",
"id": bookingID,
})