fix: tip double-count, fully-paid auto-completion, discount-refund hardening
Tip double-count (root cause of £33.75 vs £28.75 display):
- Remove mock's fixed +500p auto-tip when AllowTipping is true (square_dev.go) —
real Square only enables a terminal prompt, it never adds a tip to the amount
- Set AllowTipping=false in CreateTerminalPayment: the frontend already embeds
the tip in the amount, so the terminal must not prompt for a second tip
- M4 tip split now derives the tip as charged amount minus remaining booking
value ('after 100% is tips'), not from Square's TipAmount field
- Success screens divide paymentResult.amount by 100 (pence -> pounds) in both
PaymentModal and UserPaymentModal
Fully-paid bookings auto-complete:
- Extract ApplyBookingCompletionSideEffects into payments package (shared by
admin progress endpoint and payment paths; avoids circular import)
- Add bookingIsFullyPaid + completeFullyPaidBooking: when completed non-tip
payments reach 100% of the booking total, an active booking transitions to
'completed' so it leaves the admin Current Appointment view
- Wired into CreateBookingPayment (inside tx) and GetCheckoutStatus (terminal,
after commit); completion side-effects (loyalty, campaigns, deposits_required)
fire identically to the manual progress endpoint
- Add /admin/bookings/{id}/refund route (AdminRefundBooking)
Discount-refund hardening:
- RefundPayment explicitly rejects discount/on_the_house payments (was relying
on the incidental NULL-square_payment_id guard)
- Hide the Refund button for discount/on_the_house payments in EditBookingModal
- Cancel-refund estimate in BookingModal also excludes on_the_house
- Cancellation refund loop + GetBookingPaymentInfo + GetBookingRefundableAmountCents
exclude payment_type='tip' from refundable totals
Tip flow (start-time guard) fixes tests:
- Tip tests updated to use past-dated bookings (tips now require booking started)
Tests:
- m4_tip_refund_redesign_test.go (tip split, refund exclusion, admin refund cap)
- m5_fully_paid_completion_test.go (online + terminal full-payment completion,
partial stays active, tip excluded, cancelled stays cancelled)
- Full suite passes with -race (25 packages)
This commit is contained in:
@@ -930,7 +930,6 @@ BEGIN
|
||||
phone = '+000000000000', -- NOT NULL column: use placeholder
|
||||
date_of_birth = '1900-01-01', -- NOT NULL column: use placeholder
|
||||
profile_pic_url = NULL,
|
||||
notes = NULL, -- free-text staff notes may contain PII
|
||||
last_login_at = NULL, -- removes activity metadata (right to be forgotten)
|
||||
account_role = 'guest',
|
||||
loyalty_stamps = 0,
|
||||
@@ -1400,11 +1399,28 @@ BEGIN
|
||||
) ORDER BY nh.changed_at DESC), '[]'::json)
|
||||
FROM name_history nh WHERE nh.user_id = target_user_id
|
||||
),
|
||||
'disputes', (
|
||||
SELECT COALESCE(json_agg(json_build_object(
|
||||
'dispute_id', d.id,
|
||||
'payment_id', d.payment_id,
|
||||
'status', d.status,
|
||||
'amount', d.amount,
|
||||
'reason', d.reason,
|
||||
'created_at', d.created_at,
|
||||
'updated_at', d.updated_at
|
||||
) ORDER BY d.created_at DESC), '[]'::json)
|
||||
FROM disputes d
|
||||
JOIN payments p ON d.payment_id = p.id
|
||||
LEFT JOIN bookings b ON p.booking_id = b.id
|
||||
LEFT JOIN gift_cards gc ON p.gift_card_id = gc.id
|
||||
WHERE b.user_id = target_user_id
|
||||
OR gc.created_by = target_user_id
|
||||
),
|
||||
'export_metadata', json_build_object(
|
||||
'exported_at', NOW(),
|
||||
'exported_by', 'system',
|
||||
'user_id', target_user_id,
|
||||
'format_version', '1.0'
|
||||
'format_version', '1.1'
|
||||
)
|
||||
) INTO result;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user