Fix tip amount-change false dedup, wire BuyerEmail everywhere, clear ESLint errors

Money-moving fixes:
- Tip idempotency key regenerates when the tip amount changes after a failed
  attempt (all 3 tip flows). Cached key still reused on same-amount retry
  (dedup intact) and cleared on success/modal reset. Prevents silent
  under-charge when a user retries at a different amount.
- Till replay path returns actual till_sales.status (may be 'pending') instead
  of hardcoded 'completed' — no more misreported successful charge.
- BuyerEmail wired for CreateBookingPayment, gift card purchases, and till
  sales (saved_card + online_square), matching the tip flow. Email lookup
  errors logged, non-fatal.
- Till buyer-email errors now logged (was silently swallowed).
- on_the_house till top-up uses cached getIdempotencyKey() for retry-safe dedup
  (was fresh crypto.randomUUID()).

Test/validation fixes:
- Add TestPaymentFromSquare_* unit tests (else-branch + nil card details),
  build tag relaxed to 'test' so they run in the standard dev suite.
- Add TestValidateCardInfo table test (7 cases: both/either/neither/empty).
- Add TestCreateTillSale_TwoIdenticalCreateSales_BothSucceed regression test.
- Remove dead mock pre-registration in TestTipPayment_WithSavedCard.
- Correct misleading till regression-test comment.

ESLint cleanup (12 errors -> 0):
- Remove unused loadingCards in tip + pay-tip pages (dead assignments in
  loadSavedCards).
- Scoped eslint-disable for {@html} in CardBrandIcon (hardcoded brand SVGs).
- Remove dead confirmSaveDefaultHours + unused rescheduleVersion prop in
  WeeklySchedule (and its parent pass-through).
- Replace new Date() with SvelteDate in WeeklySchedule + BusinessHours.
- Fix each-block key in BusinessHours skeleton loader.
- Use void expression for reactivity-tracker reads in effects.
This commit is contained in:
2026-08-22 00:34:49 +01:00
parent a4441b6acf
commit 28f0ddb328
15 changed files with 330 additions and 143 deletions
@@ -0,0 +1,45 @@
//go:build test
package square
import "testing"
func TestPaymentFromSquare_ElseBranch_SurfacesBrandWithoutCardID(t *testing.T) {
p := &sqPayment{
ID: "pay_1",
Status: "COMPLETED",
TotalMoney: sqMoney{Amount: 5000, Currency: "GBP"},
CardDetails: &sqCardDetails{
Card: sqCard{
ID: "",
CardBrand: "VISA",
Last4: "4242",
},
},
}
result := paymentFromSquare(p)
if result.CardBrand != "VISA" {
t.Errorf("expected CardBrand VISA, got %s", result.CardBrand)
}
if result.CardLast4 != "4242" {
t.Errorf("expected CardLast4 4242, got %s", result.CardLast4)
}
}
func TestPaymentFromSquare_NilCardDetails(t *testing.T) {
p := &sqPayment{
ID: "pay_2",
Status: "COMPLETED",
TotalMoney: sqMoney{Amount: 2500, Currency: "GBP"},
CardDetails: nil,
}
result := paymentFromSquare(p)
if result.CardBrand != "" {
t.Errorf("expected empty CardBrand when no card details, got %s", result.CardBrand)
}
if result.Amount != 2500 {
t.Errorf("expected amount 2500, got %d", result.Amount)
}
}