Compare commits
2
Commits
df325487cf
...
f147be0b99
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f147be0b99 | ||
|
|
ad15127777 |
@@ -7,6 +7,7 @@ import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -167,13 +168,15 @@ func GetCustomServices(w http.ResponseWriter, r *http.Request) {
|
||||
// so pgx does not return "conn busy" on the same transaction.
|
||||
if q != "" {
|
||||
var countTotal int64
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM custom_services WHERE name ILIKE $1 OR description ILIKE $1", "%"+q+"%").Scan(&countTotal)
|
||||
if err := db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM custom_services WHERE name ILIKE $1 OR description ILIKE $1", "%"+q+"%").Scan(&countTotal); err != nil {
|
||||
log.Printf("Failed to scan filtered custom services count: %v", err)
|
||||
}
|
||||
total = countTotal
|
||||
} else {
|
||||
var countTotal int64
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM custom_services").Scan(&countTotal)
|
||||
if err := db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM custom_services").Scan(&countTotal); err != nil {
|
||||
log.Printf("Failed to scan custom services count: %v", err)
|
||||
}
|
||||
total = countTotal
|
||||
}
|
||||
|
||||
|
||||
@@ -2776,8 +2776,9 @@ func ProgressBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if bookingTotal > 0 {
|
||||
var userBookingCount int
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = tx.QueryRow(r.Context(), `SELECT COUNT(*) FROM bookings WHERE user_id = $1 AND status = 'completed'`, booking.User.ID).Scan(&userBookingCount)
|
||||
if err := tx.QueryRow(r.Context(), `SELECT COUNT(*) FROM bookings WHERE user_id = $1 AND status = 'completed'`, booking.User.ID).Scan(&userBookingCount); err != nil {
|
||||
log.Printf("Failed to scan user completed booking count: %v", err)
|
||||
}
|
||||
|
||||
var milestoneCampaignID string
|
||||
var milestonePercent float64
|
||||
@@ -2817,8 +2818,9 @@ func ProgressBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
if !globalMilestoneApplied {
|
||||
var globalCount int
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = tx.QueryRow(r.Context(), `SELECT COUNT(*) FROM bookings WHERE status = 'completed'`).Scan(&globalCount)
|
||||
if err := tx.QueryRow(r.Context(), `SELECT COUNT(*) FROM bookings WHERE status = 'completed'`).Scan(&globalCount); err != nil {
|
||||
log.Printf("Failed to scan global completed booking count: %v", err)
|
||||
}
|
||||
|
||||
var hasInPersonPayment bool
|
||||
if err := tx.QueryRow(r.Context(), `
|
||||
@@ -2863,8 +2865,9 @@ func ProgressBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
var firstVisitDate time.Time
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = tx.QueryRow(r.Context(), `SELECT MIN(start_time) FROM bookings WHERE user_id = $1 AND status = 'completed'`, booking.User.ID).Scan(&firstVisitDate)
|
||||
if err := tx.QueryRow(r.Context(), `SELECT MIN(start_time) FROM bookings WHERE user_id = $1 AND status = 'completed'`, booking.User.ID).Scan(&firstVisitDate); err != nil {
|
||||
log.Printf("Failed to scan first visit date: %v", err)
|
||||
}
|
||||
if !firstVisitDate.IsZero() {
|
||||
annRows, err := tx.Query(r.Context(), `
|
||||
SELECT id, discount_percent, milestone_value, milestone_unit FROM discount_campaigns
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
//lint:ignore U1000 referenced from tests
|
||||
//lint:ignore U1000 referenced from tests; route DELETE /api/bookings/{id} conflicts with bookings.DeleteBookingHandler at main.go:360
|
||||
func UserCancelBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
bookingID := chi.URLParam(r, "id")
|
||||
if bookingID == "" || !validators.IsValidID(bookingID) {
|
||||
@@ -305,7 +305,7 @@ type AdminCreateBookingForUserRequest struct {
|
||||
OutOfHours bool `json:"out_of_hours"`
|
||||
}
|
||||
|
||||
//lint:ignore U1000 referenced from tests
|
||||
//lint:ignore U1000 referenced from tests; route GET /api/admin/today/current-next conflicts with today.GetCurrentAndNextHandler at main.go:452
|
||||
func AdminGetInProgressBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var b Booking
|
||||
var userID, fullName string
|
||||
@@ -1685,8 +1685,9 @@ func AdminListEditRequestsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var total int
|
||||
|
||||
// Count query (no ORDER BY needed).
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM booking_edit_requests").Scan(&total)
|
||||
if err := db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM booking_edit_requests").Scan(&total); err != nil {
|
||||
log.Printf("Failed to scan booking edit request count: %v", err)
|
||||
}
|
||||
|
||||
rows, err := db.Conn.Query(r.Context(), baseQuery, args...)
|
||||
if err != nil {
|
||||
|
||||
@@ -126,8 +126,9 @@ func GetNotifications(w http.ResponseWriter, r *http.Request) {
|
||||
if !includeAcknowledged {
|
||||
countWhere += " WHERE an.acknowledged_at IS NULL"
|
||||
}
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM admin_notifications an"+countWhere).Scan(&total)
|
||||
if err := db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM admin_notifications an"+countWhere).Scan(&total); err != nil {
|
||||
log.Printf("Failed to scan notification count: %v", err)
|
||||
}
|
||||
|
||||
// Query
|
||||
rows, err := db.Conn.Query(r.Context(), baseQuery, args...)
|
||||
|
||||
@@ -207,11 +207,13 @@ func GetGiftCards(w http.ResponseWriter, r *http.Request) {
|
||||
if searchTerm != "" {
|
||||
countArgs = append(countArgs, "%"+searchTerm+"%")
|
||||
}
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(ctx, "SELECT COUNT(*) FROM gift_cards "+whereSQL, countArgs...).Scan(&gcTotal)
|
||||
if err := db.Conn.QueryRow(ctx, "SELECT COUNT(*) FROM gift_cards "+whereSQL, countArgs...).Scan(&gcTotal); err != nil {
|
||||
log.Printf("Failed to scan filtered gift card count: %v", err)
|
||||
}
|
||||
} else {
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(ctx, "SELECT COUNT(*) FROM gift_cards").Scan(&gcTotal)
|
||||
if err := db.Conn.QueryRow(ctx, "SELECT COUNT(*) FROM gift_cards").Scan(&gcTotal); err != nil {
|
||||
log.Printf("Failed to scan gift card count: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
gcRows, err := db.Conn.Query(ctx, gcListQuery, gcListArgs...)
|
||||
@@ -283,13 +285,15 @@ func GetGiftCards(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Count query for user balances.
|
||||
if searchTerm != "" {
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(ctx, `SELECT COUNT(*) FROM user_giftcard_balances b
|
||||
if err := db.Conn.QueryRow(ctx, `SELECT COUNT(*) FROM user_giftcard_balances b
|
||||
JOIN users u ON b.user_id = u.id
|
||||
WHERE u.n_first_name ILIKE $1 OR u.n_last_name ILIKE $1 OR u.email ILIKE $1`, "%"+searchTerm+"%").Scan(&ubTotal)
|
||||
WHERE u.n_first_name ILIKE $1 OR u.n_last_name ILIKE $1 OR u.email ILIKE $1`, "%"+searchTerm+"%").Scan(&ubTotal); err != nil {
|
||||
log.Printf("Failed to scan filtered user balance count: %v", err)
|
||||
}
|
||||
} else {
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(ctx, "SELECT COUNT(*) FROM user_giftcard_balances").Scan(&ubTotal)
|
||||
if err := db.Conn.QueryRow(ctx, "SELECT COUNT(*) FROM user_giftcard_balances").Scan(&ubTotal); err != nil {
|
||||
log.Printf("Failed to scan user balance count: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for ubRows.Next() {
|
||||
|
||||
@@ -165,8 +165,9 @@ func calculateDiscountPreview(ctx context.Context, bookingID string, userID stri
|
||||
ORDER BY discount_percent DESC LIMIT 1
|
||||
`).Scan(&campaignID, &campaignPercent, &campaignName); err == nil && campaignID != "" {
|
||||
var exists int
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, campaignID).Scan(&exists)
|
||||
if err := db.Conn.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, campaignID).Scan(&exists); err != nil {
|
||||
log.Printf("Failed to scan campaign discount existence: %v", err)
|
||||
}
|
||||
if exists == 0 {
|
||||
amount := roundTo2(bookingTotal * campaignPercent / 100)
|
||||
resp.Discounts = append(resp.Discounts, DiscountPreview{
|
||||
@@ -180,8 +181,9 @@ func calculateDiscountPreview(ctx context.Context, bookingID string, userID stri
|
||||
}
|
||||
|
||||
var userBookingCount int
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(ctx, `SELECT COUNT(*) FROM bookings WHERE user_id = $1 AND status = 'completed'`, userID).Scan(&userBookingCount)
|
||||
if err := db.Conn.QueryRow(ctx, `SELECT COUNT(*) FROM bookings WHERE user_id = $1 AND status = 'completed'`, userID).Scan(&userBookingCount); err != nil {
|
||||
log.Printf("Failed to scan user completed booking count: %v", err)
|
||||
}
|
||||
|
||||
var milestoneCampaignID string
|
||||
var milestonePercent float64
|
||||
@@ -197,8 +199,9 @@ func calculateDiscountPreview(ctx context.Context, bookingID string, userID stri
|
||||
|
||||
if milestoneCampaignID != "" {
|
||||
var exists int
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, milestoneCampaignID).Scan(&exists)
|
||||
if err := db.Conn.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, milestoneCampaignID).Scan(&exists); err != nil {
|
||||
log.Printf("Failed to scan milestone discount existence: %v", err)
|
||||
}
|
||||
if exists == 0 {
|
||||
amount := roundTo2(bookingTotal * milestonePercent / 100)
|
||||
resp.Discounts = append(resp.Discounts, DiscountPreview{
|
||||
@@ -212,8 +215,9 @@ func calculateDiscountPreview(ctx context.Context, bookingID string, userID stri
|
||||
}
|
||||
|
||||
var firstVisitDate time.Time
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(ctx, `SELECT MIN(start_time) FROM bookings WHERE user_id = $1 AND status = 'completed'`, userID).Scan(&firstVisitDate)
|
||||
if err := db.Conn.QueryRow(ctx, `SELECT MIN(start_time) FROM bookings WHERE user_id = $1 AND status = 'completed'`, userID).Scan(&firstVisitDate); err != nil {
|
||||
log.Printf("Failed to scan first visit date: %v", err)
|
||||
}
|
||||
if !firstVisitDate.IsZero() {
|
||||
type annCamp struct {
|
||||
id string
|
||||
@@ -238,9 +242,10 @@ func calculateDiscountPreview(ctx context.Context, bookingID string, userID stri
|
||||
annRows.Close()
|
||||
|
||||
for _, c := range campaigns {
|
||||
var exists int
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, c.id).Scan(&exists)
|
||||
var exists int
|
||||
if err := db.Conn.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, c.id).Scan(&exists); err != nil {
|
||||
log.Printf("Failed to scan anniversary discount existence: %v", err)
|
||||
}
|
||||
if exists > 0 {
|
||||
continue
|
||||
}
|
||||
@@ -276,8 +281,9 @@ func calculateDiscountPreview(ctx context.Context, bookingID string, userID stri
|
||||
LIMIT 1
|
||||
`, userID).Scan(&rdID, &rdPercent); err == nil && rdID != "" {
|
||||
exists := 0
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'referral' AND source_id = $2`, bookingID, rdID).Scan(&exists)
|
||||
if err := db.Conn.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'referral' AND source_id = $2`, bookingID, rdID).Scan(&exists); err != nil {
|
||||
log.Printf("Failed to scan referral discount existence: %v", err)
|
||||
}
|
||||
if exists == 0 {
|
||||
amount := roundTo2(bookingTotal * rdPercent / 100)
|
||||
resp.Discounts = append(resp.Discounts, DiscountPreview{
|
||||
@@ -1080,11 +1086,12 @@ func CreateBookingPayment(w http.ResponseWriter, r *http.Request) {
|
||||
// would create a credit balance or require a refund.
|
||||
func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingID string, userID string) {
|
||||
var existingPayment int
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = q.QueryRow(ctx, `
|
||||
if err := q.QueryRow(ctx, `
|
||||
SELECT COUNT(*) FROM payments
|
||||
WHERE booking_id = $1 AND status = 'completed' AND payment_method NOT IN ('discount', 'on_the_house')
|
||||
`, bookingID).Scan(&existingPayment)
|
||||
`, bookingID).Scan(&existingPayment); err != nil {
|
||||
log.Printf("Failed to scan existing payment count: %v", err)
|
||||
}
|
||||
// Only block if this is the 2nd+ real payment — the first payment should still
|
||||
// trigger discount application (existingPayment counts already-completed payments
|
||||
// visible within the transaction, including the just-inserted one).
|
||||
@@ -1113,8 +1120,9 @@ func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingI
|
||||
ORDER BY discount_percent DESC LIMIT 1
|
||||
`).Scan(&campaignID, &campaignPercent); err == nil && campaignID != "" {
|
||||
var exists int
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, campaignID).Scan(&exists)
|
||||
if err := q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, campaignID).Scan(&exists); err != nil {
|
||||
log.Printf("Failed to scan time-based campaign discount existence: %v", err)
|
||||
}
|
||||
if exists == 0 {
|
||||
discountAmount := roundTo2(bookingTotal * campaignPercent / 100)
|
||||
if _, err := q.Exec(ctx, `
|
||||
@@ -1139,8 +1147,9 @@ func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingI
|
||||
}
|
||||
|
||||
var userBookingCount int
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = q.QueryRow(ctx, `SELECT COUNT(*) FROM bookings WHERE user_id = $1 AND status = 'completed'`, userID).Scan(&userBookingCount)
|
||||
if err := q.QueryRow(ctx, `SELECT COUNT(*) FROM bookings WHERE user_id = $1 AND status = 'completed'`, userID).Scan(&userBookingCount); err != nil {
|
||||
log.Printf("Failed to scan user booking count: %v", err)
|
||||
}
|
||||
|
||||
var milestoneCampaignID string
|
||||
var milestonePercent float64
|
||||
@@ -1155,8 +1164,9 @@ func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingI
|
||||
|
||||
if milestoneCampaignID != "" {
|
||||
var exists int
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, milestoneCampaignID).Scan(&exists)
|
||||
if err := q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, milestoneCampaignID).Scan(&exists); err != nil {
|
||||
log.Printf("Failed to scan milestone campaign discount existence: %v", err)
|
||||
}
|
||||
if exists == 0 {
|
||||
discountAmount := roundTo2(bookingTotal * milestonePercent / 100)
|
||||
if _, err := q.Exec(ctx, `
|
||||
@@ -1181,8 +1191,9 @@ func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingI
|
||||
}
|
||||
|
||||
var firstVisitDate time.Time
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = q.QueryRow(ctx, `SELECT MIN(start_time) FROM bookings WHERE user_id = $1 AND status = 'completed'`, userID).Scan(&firstVisitDate)
|
||||
if err := q.QueryRow(ctx, `SELECT MIN(start_time) FROM bookings WHERE user_id = $1 AND status = 'completed'`, userID).Scan(&firstVisitDate); err != nil {
|
||||
log.Printf("Failed to scan first visit date: %v", err)
|
||||
}
|
||||
if !firstVisitDate.IsZero() {
|
||||
annRows, err := q.Query(ctx, `
|
||||
SELECT id, discount_percent, milestone_value, milestone_unit FROM discount_campaigns
|
||||
@@ -1206,9 +1217,10 @@ func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingI
|
||||
annRows.Close()
|
||||
|
||||
for _, c := range campaigns {
|
||||
var exists int
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, c.id).Scan(&exists)
|
||||
var exists int
|
||||
if err := q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, c.id).Scan(&exists); err != nil {
|
||||
log.Printf("Failed to scan anniversary discount existence: %v", err)
|
||||
}
|
||||
if exists > 0 {
|
||||
continue
|
||||
}
|
||||
@@ -1256,8 +1268,9 @@ func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingI
|
||||
SELECT payment_method FROM payments WHERE booking_id = $1 AND payment_method NOT IN ('discount', 'on_the_house') ORDER BY created_at ASC LIMIT 1
|
||||
`, bookingID).Scan(&firstPaymentMethod); err == nil && firstPaymentMethod == "in_person_card" {
|
||||
var globalCount int
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = q.QueryRow(ctx, `SELECT COUNT(*) FROM bookings WHERE status = 'completed'`).Scan(&globalCount)
|
||||
if err := q.QueryRow(ctx, `SELECT COUNT(*) FROM bookings WHERE status = 'completed'`).Scan(&globalCount); err != nil {
|
||||
log.Printf("Failed to scan global completed booking count: %v", err)
|
||||
}
|
||||
|
||||
var globalCampaignID string
|
||||
var globalPercent float64
|
||||
@@ -1274,8 +1287,9 @@ func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingI
|
||||
|
||||
if globalCampaignID != "" {
|
||||
var exists int
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, globalCampaignID).Scan(&exists)
|
||||
if err := q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, globalCampaignID).Scan(&exists); err != nil {
|
||||
log.Printf("Failed to scan global campaign discount existence: %v", err)
|
||||
}
|
||||
if exists == 0 {
|
||||
discountAmount := roundTo2(bookingTotal * globalPercent / 100)
|
||||
if _, err := q.Exec(ctx, `
|
||||
@@ -1310,8 +1324,9 @@ func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingI
|
||||
LIMIT 1
|
||||
`, userID).Scan(&rdID, &rdPercent); err == nil && rdID != "" {
|
||||
exists := 0
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'referral' AND source_id = $2`, bookingID, rdID).Scan(&exists)
|
||||
if err := q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'referral' AND source_id = $2`, bookingID, rdID).Scan(&exists); err != nil {
|
||||
log.Printf("Failed to scan referral discount existence: %v", err)
|
||||
}
|
||||
if exists == 0 {
|
||||
discountAmount := roundTo2(bookingTotal * rdPercent / 100)
|
||||
if _, err := q.Exec(ctx, `
|
||||
@@ -1433,25 +1448,6 @@ func buildSplitRecords(primary PaymentRecord, reqPaymentType string, info *Booki
|
||||
return records
|
||||
}
|
||||
|
||||
// nonDepositPaymentType picks the right label for the non-deposit portion of a
|
||||
// split payment, following the same rules as the frontend's handlePayFull:
|
||||
// 'balance' when some payment already exists, 'full' when covering everything,
|
||||
// 'partial' when leaving a remainder.
|
||||
//
|
||||
//lint:ignore U1000 reserved for future use
|
||||
func nonDepositPaymentType(reqType string, totalPaidAfterThis float64, thisPortion float64, bookingTotal float64) string {
|
||||
if totalPaidAfterThis >= bookingTotal {
|
||||
if totalPaidAfterThis-thisPortion > 0 {
|
||||
return "balance"
|
||||
}
|
||||
return "full"
|
||||
}
|
||||
if reqType == "full" || reqType == "deposit" {
|
||||
return "partial"
|
||||
}
|
||||
return "partial"
|
||||
}
|
||||
|
||||
func GetUserPaymentMethods(w http.ResponseWriter, r *http.Request) {
|
||||
userID, ok := r.Context().Value(mw.UserIDKey).(string)
|
||||
if !ok || userID == "" {
|
||||
|
||||
@@ -1431,56 +1431,6 @@ func TestBookingPayment_TransactionAtomicity_SplitRollsBackOnError(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// nonDepositPaymentType unit tests — pure function, no DB needed.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestNonDepositPaymentType_FirstPaymentFull(t *testing.T) {
|
||||
// First-ever payment, paying full amount → "full"
|
||||
result := nonDepositPaymentType("full", 100, 100, 100)
|
||||
if result != "full" {
|
||||
t.Errorf("expected 'full', got %q", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonDepositPaymentType_BalanceWhenPriorExists(t *testing.T) {
|
||||
// Total paid after this = 100, portion = 50, prior = 50 → "balance"
|
||||
result := nonDepositPaymentType("balance", 100, 50, 100)
|
||||
if result != "balance" {
|
||||
t.Errorf("expected 'balance', got %q", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonDepositPaymentType_PartialWhenUnderTotal(t *testing.T) {
|
||||
// Paying 30 on a 100 total → "partial"
|
||||
result := nonDepositPaymentType("partial", 30, 30, 100)
|
||||
if result != "partial" {
|
||||
t.Errorf("expected 'partial', got %q", result)
|
||||
}
|
||||
|
||||
// Same result when request type is "full" but amount doesn't cover total
|
||||
result = nonDepositPaymentType("full", 80, 80, 100)
|
||||
if result != "partial" {
|
||||
t.Errorf("expected 'partial' when full doesn't cover total, got %q", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonDepositPaymentType_FullWhenFirstPaymentFullyCovers(t *testing.T) {
|
||||
// First payment ever, exactly covers total → "full"
|
||||
result := nonDepositPaymentType("full", 100, 100, 100)
|
||||
if result != "full" {
|
||||
t.Errorf("expected 'full', got %q", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonDepositPaymentType_DepositReqTypeBecomesPartial(t *testing.T) {
|
||||
// Request type is "deposit" but amount doesn't fully cover → "partial"
|
||||
result := nonDepositPaymentType("deposit", 30, 30, 100)
|
||||
if result != "partial" {
|
||||
t.Errorf("expected 'partial' for deposit type under total, got %q", result)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// buildSplitRecords unit tests — pure function, no DB needed.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -565,13 +565,15 @@ func ListAdminUsersHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Run BEFORE the data query to avoid "conn busy" errors when routing
|
||||
// through a per-test transaction (pgx.Tx does not support concurrent queries).
|
||||
if searchTerm != "" {
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(r.Context(), `SELECT COUNT(DISTINCT u.id) FROM users u
|
||||
if err := db.Conn.QueryRow(r.Context(), `SELECT COUNT(DISTINCT u.id) FROM users u
|
||||
LEFT JOIN bookings b ON u.id = b.user_id
|
||||
WHERE u.fn ILIKE $1 OR u.email ILIKE $1 OR u.phone ILIKE $1`, "%"+searchTerm+"%").Scan(&total)
|
||||
WHERE u.fn ILIKE $1 OR u.email ILIKE $1 OR u.phone ILIKE $1`, "%"+searchTerm+"%").Scan(&total); err != nil {
|
||||
log.Printf("Failed to scan filtered user count: %v", err)
|
||||
}
|
||||
} else {
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM users").Scan(&total)
|
||||
if err := db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM users").Scan(&total); err != nil {
|
||||
log.Printf("Failed to scan user count: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Get users list
|
||||
|
||||
Reference in New Issue
Block a user