← back to Costa Rica

scripts/migrate_009_integrity_guards.sql

48 lines

-- TK-10346: DB integrity guards (all pre-checked 0 violators, reversible). Money in minor units.
-- Reconciliation model is total = platform_fee + host_payout (NOT subtotal+fees; fees is display-only).
-- (Originally committed as root-level "migration 008 integrity_guards"; renumbered to 009 and moved
--  into scripts/ so the manual go-live migration pass applies it in order after 008 double_book.)
BEGIN;

-- RANK 1 — bookings money invariants
ALTER TABLE bookings
  ADD CONSTRAINT bookings_money_nonneg CHECK (subtotal>=0 AND fees>=0 AND total>=0 AND platform_fee>=0 AND host_payout>=0),
  ADD CONSTRAINT bookings_total_reconciles CHECK (total = platform_fee + host_payout),
  ADD CONSTRAINT bookings_guests_positive CHECK (guests >= 1);

-- RANK 2 — payout idempotency (prevents double-paying a host)
CREATE UNIQUE INDEX IF NOT EXISTS payouts_one_per_booking_rail ON payouts (booking_id, rail) WHERE booking_id IS NOT NULL;
CREATE UNIQUE INDEX IF NOT EXISTS payouts_provider_ref_uniq ON payouts (provider_ref) WHERE provider_ref IS NOT NULL;

-- RANK 3 — money non-negativity on remaining money tables + config bounds
ALTER TABLE payments ADD CONSTRAINT payments_money_nonneg CHECK (amount>=0 AND processor_fee>=0);
ALTER TABLE payouts  ADD CONSTRAINT payouts_amount_nonneg CHECK (amount>=0);
ALTER TABLE place_booking
  ADD CONSTRAINT place_booking_money_nonneg CHECK (base_price>=0 AND cleaning_fee>=0),
  ADD CONSTRAINT place_booking_fee_bps_range CHECK (platform_fee_bps BETWEEN 0 AND 10000),
  ADD CONSTRAINT place_booking_guests_nights_pos CHECK (max_guests>=1 AND min_nights>=1);
ALTER TABLE availability ADD CONSTRAINT availability_capacity_nonneg CHECK (capacity>=0);

-- RANK 4 — date/slot consistency
ALTER TABLE bookings
  ADD CONSTRAINT bookings_stay_pair CHECK ((check_in IS NULL) = (check_out IS NULL)),
  ADD CONSTRAINT bookings_slot_pair CHECK ((slot_start IS NULL) = (slot_end IS NULL)),
  ADD CONSTRAINT bookings_has_a_date CHECK (check_in IS NOT NULL OR slot_start IS NOT NULL),
  ADD CONSTRAINT bookings_stay_order CHECK (check_in IS NULL OR check_out > check_in),
  ADD CONSTRAINT bookings_slot_order CHECK (slot_start IS NULL OR slot_end > slot_start);
ALTER TABLE availability
  ADD CONSTRAINT availability_slot_order CHECK (slot_start IS NULL OR slot_end IS NULL OR slot_end > slot_start);

-- RANK 5/6 — hot-path indexes
CREATE INDEX IF NOT EXISTS idx_payouts_booking ON payouts (booking_id);
CREATE INDEX IF NOT EXISTS idx_bookings_place_status_checkin ON bookings (place_id, status, check_in);

-- RANK 7 — NOT NULL hardening on places (0 nulls verified)
ALTER TABLE places
  ALTER COLUMN status SET NOT NULL,
  ALTER COLUMN verified SET NOT NULL,
  ALTER COLUMN created_at SET NOT NULL,
  ALTER COLUMN updated_at SET NOT NULL;

COMMIT;