package payments import ( "context" "log" "crussell/db" ) type VATConfig struct { IsVATRegistered bool DefaultVATRate float64 VoucherType string } // GetVATConfig reads VAT config using the given querier, which can be either // a *db.PoolProxy (for non-transactional reads), a pgx.Tx (to read inside a // transaction), or any other type that implements db.Querier. func GetVATConfig(ctx context.Context, q db.Querier) (*VATConfig, error) { var cfg VATConfig err := q.QueryRow(ctx, ` SELECT COALESCE(is_vat_registered, FALSE), COALESCE(default_vat_rate, 20.0), COALESCE(voucher_type, 'SPV') FROM business_settings LIMIT 1 `).Scan(&cfg.IsVATRegistered, &cfg.DefaultVATRate, &cfg.VoucherType) if err != nil { return nil, err } return &cfg, nil } // ApplyVATToBookingPayment reads VAT config and calls apply_vat_to_payment // on a booking payment record. The q parameter is used for both reading the // VAT config and for the defensive payment-method check, ensuring all reads // are consistent with the caller's transactional context. Errors are logged // — VAT failure should not block the payment flow. func ApplyVATToBookingPayment(ctx context.Context, q db.Querier, paymentID string) { vatCfg, err := GetVATConfig(ctx, q) if err != nil { log.Printf("Failed to read VAT config for payment %s: %v", paymentID, err) return } if !vatCfg.IsVATRegistered { return } // Discount and on_the_house payments must never have VAT applied. // Read the payment method inside the same transactional context so that // the just-inserted row is visible (defence against READ COMMITTED // isolation when q is a pgx.Tx). var method string if qErr := q.QueryRow(ctx, "SELECT payment_method FROM payments WHERE id = $1", paymentID).Scan(&method); qErr == nil && (method == "discount" || method == "on_the_house") { return } if _, execErr := q.Exec(ctx, "SELECT apply_vat_to_payment($1, $2)", paymentID, vatCfg.DefaultVATRate); execErr != nil { log.Printf("Failed to apply VAT to payment %s: %v", paymentID, execErr) } } // ApplyVATToTillSale reads VAT config and calls apply_vat_to_till_sale for // SPV gift card till sales. Errors are logged — VAT failure should not // block the sale flow. func ApplyVATToTillSale(ctx context.Context, q db.Querier, saleID string) { vatCfg, err := GetVATConfig(ctx, q) if err != nil { log.Printf("Failed to read VAT config for till sale %s: %v", saleID, err) return } if !vatCfg.IsVATRegistered || vatCfg.VoucherType != "SPV" { return } if _, execErr := q.Exec(ctx, "SELECT apply_vat_to_till_sale($1, $2)", saleID, vatCfg.DefaultVATRate); execErr != nil { log.Printf("Failed to apply VAT to till sale %s: %v", saleID, execErr) } }