feat(payments): integrate VAT into gift cards, payment handlers, and till sales
Track voucher_type_at_purchase on gift card creation. Apply VAT at gift card purchase (SPV), at redemption (MPV). Use transaction-aware GetCardByIDQuerier for till saved card lookups. Apply VAT to all booking payments (split records, terminal, tip, checkout). Add TotalVATAmount and TotalNetAmount to PaymentSummary responses. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -354,11 +354,16 @@ func CreateGiftCard(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var gc GiftCard
|
||||
var lastUsedAt sql.NullTime
|
||||
var purchaseVoucherType string
|
||||
_ = tx.QueryRow(ctx, `SELECT COALESCE(voucher_type, 'SPV') FROM business_settings LIMIT 1`).Scan(&purchaseVoucherType)
|
||||
if purchaseVoucherType == "" {
|
||||
purchaseVoucherType = "SPV"
|
||||
}
|
||||
err = tx.QueryRow(ctx, `
|
||||
INSERT INTO gift_cards (total_funds_added, amount_remaining, created_by, is_inventory, last_used_at)
|
||||
VALUES ($1, $1, $2, $3, NOW())
|
||||
INSERT INTO gift_cards (total_funds_added, amount_remaining, created_by, is_inventory, last_used_at, voucher_type_at_purchase)
|
||||
VALUES ($1, $1, $2, $3, NOW(), $4)
|
||||
RETURNING id, total_funds_added, amount_remaining, created_by, created_at, is_inventory, last_used_at
|
||||
`, req.Amount, adminID, req.IsInventory).Scan(
|
||||
`, req.Amount, adminID, req.IsInventory, purchaseVoucherType).Scan(
|
||||
&gc.ID,
|
||||
&gc.TotalFundsAdded,
|
||||
&gc.AmountRemaining,
|
||||
@@ -903,11 +908,16 @@ func BuyGiftCard(w http.ResponseWriter, r *http.Request) {
|
||||
var cardID string
|
||||
|
||||
if req.RecipientType == "self" {
|
||||
var purchaseVoucherType string
|
||||
_ = tx.QueryRow(ctx, `SELECT COALESCE(voucher_type, 'SPV') FROM business_settings LIMIT 1`).Scan(&purchaseVoucherType)
|
||||
if purchaseVoucherType == "" {
|
||||
purchaseVoucherType = "SPV"
|
||||
}
|
||||
err = tx.QueryRow(ctx, `
|
||||
INSERT INTO gift_cards (total_funds_added, amount_remaining, created_by, redeemed_at, redeemed_by, is_inventory)
|
||||
VALUES ($1, 0, $2, NOW(), $2, FALSE)
|
||||
INSERT INTO gift_cards (total_funds_added, amount_remaining, created_by, redeemed_at, redeemed_by, is_inventory, voucher_type_at_purchase)
|
||||
VALUES ($1, 0, $2, NOW(), $2, FALSE, $3)
|
||||
RETURNING id
|
||||
`, amountPounds, userID).Scan(&cardID)
|
||||
`, amountPounds, userID, purchaseVoucherType).Scan(&cardID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to insert gift card: %v", err)
|
||||
http.Error(w, "internal server error", http.StatusInternalServerError)
|
||||
@@ -937,11 +947,16 @@ func BuyGiftCard(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
var purchaseVoucherType string
|
||||
_ = tx.QueryRow(ctx, `SELECT COALESCE(voucher_type, 'SPV') FROM business_settings LIMIT 1`).Scan(&purchaseVoucherType)
|
||||
if purchaseVoucherType == "" {
|
||||
purchaseVoucherType = "SPV"
|
||||
}
|
||||
err = tx.QueryRow(ctx, `
|
||||
INSERT INTO gift_cards (total_funds_added, amount_remaining, created_by, is_inventory)
|
||||
VALUES ($1, $1, $2, FALSE)
|
||||
INSERT INTO gift_cards (total_funds_added, amount_remaining, created_by, is_inventory, voucher_type_at_purchase)
|
||||
VALUES ($1, $1, $2, FALSE, $3)
|
||||
RETURNING id
|
||||
`, amountPounds, userID).Scan(&cardID)
|
||||
`, amountPounds, userID, purchaseVoucherType).Scan(&cardID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to insert gift card: %v", err)
|
||||
http.Error(w, "internal server error", http.StatusInternalServerError)
|
||||
@@ -982,16 +997,25 @@ func BuyGiftCard(w http.ResponseWriter, r *http.Request) {
|
||||
CreatedBy: &userID,
|
||||
}
|
||||
|
||||
_, err = tx.Exec(ctx, `
|
||||
var buyPaymentID string
|
||||
err = tx.QueryRow(ctx, `
|
||||
INSERT INTO payments (payment_type, payment_method, status, amount, square_payment_id, idempotency_key, fees, user_saved_card_id, created_by, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||
`, record.PaymentType, record.PaymentMethod, record.Status, record.Amount, record.SquarePaymentID, record.IdempotencyKey, record.Fees, record.UserSavedCardID, record.CreatedBy, record.CreatedAt, record.UpdatedAt)
|
||||
RETURNING id
|
||||
`, record.PaymentType, record.PaymentMethod, record.Status, record.Amount, record.SquarePaymentID, record.IdempotencyKey, record.Fees, record.UserSavedCardID, record.CreatedBy, record.CreatedAt, record.UpdatedAt).Scan(&buyPaymentID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to insert payment record: %v", err)
|
||||
http.Error(w, "internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
vatCfg, vatErr := GetVATConfig(ctx, tx)
|
||||
if vatErr == nil && vatCfg.IsVATRegistered && vatCfg.VoucherType == "SPV" {
|
||||
if _, vatExecErr := tx.Exec(ctx, "SELECT apply_vat_to_payment($1, $2)", buyPaymentID, vatCfg.DefaultVATRate); vatExecErr != nil {
|
||||
log.Printf("Failed to apply VAT to buy gift card payment %s: %v", buyPaymentID, vatExecErr)
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Commit(ctx); err != nil {
|
||||
log.Printf("Failed to commit buy transaction: %v", err)
|
||||
http.Error(w, "internal server error", http.StatusInternalServerError)
|
||||
|
||||
Reference in New Issue
Block a user