When to use it
Today a customer who cancels late gets late_cancellation_refund_percent of
the amount paid, up to the start of the appointment. This playbook adds a
per-service window (for example the last 4 hours) in which the refund drops to
zero.
Rules to keep
- Migrations are append-only: never edit
202610060001_booking_domain.sql, add a new file. - The refund decision is pure and lives in
refundForCancellation;workflows.tsapplies it and must not compute amounts itself. - Staff cancellations always refund in full.
- A refund already decided (
refundTargetCents) never decreases.
Steps
- Create
supabase/migrations/<YYYYMMDDHHMM>_service_no_refund_window.sqlwith a timestamp later than every existing migration:
The defaultalter table public.services add column no_refund_hours integer not null default 0 check (no_refund_hours between 0 and 720), add constraint services_no_refund_within_free_window check (no_refund_hours <= free_cancellation_hours);0keeps the current behaviour for existing services. - In
src/lib/booking/policy.ts:- add
noRefundHourstoBookingPolicy; - add
"no_refund_window"toRefundReason; - in
refundForCancellation, for a customer with less thannoRefundHoursleft before the start, set reasonno_refund_windowand target0. Leave the existingMath.maxwithrefundTargetCentsin place.
- add
- In
src/lib/booking/repository.ts, addno_refund_hourstoServiceRow, to theservicepick ofBookingDetails, toSERVICE_COLUMNS, to theservices(...)select inBOOKING_DETAILSand totoPolicy. - In the admin area, add a
noRefundHoursfield to the service form insrc/app/admin/catalog/page.tsx, andno_refund_hours: integer(formData, "noRefundHours", 0)to the insert insrc/app/admin/actions.ts. The CHECK constraints validate the value. - Explain the window to the customer in the cancellation box of
src/app/book/[serviceId]/page.tsx, only whenno_refund_hours > 0. - In
test/policy.test.ts, addnoRefundHoursto the shared policy and test:- no refund inside the window, with reason
no_refund_window; - the late percentage still applies between the two thresholds;
- staff still refunds in full inside the window;
- an earlier refund target is not reduced.
- no refund inside the window, with reason
Done when
- the verification commands pass;
- the migration applies on a Supabase project with existing services;
- services created before the change behave exactly as before.
