alter table public.coupons add column if not exists status text not null default 'active', add column if not exists campaign_name text, add column if not exists min_order_amount_cents integer not null default 0, add column if not exists max_discount_cents integer, add column if not exists per_user_limit integer not null default 1, add column if not exists first_order_only boolean not null default false, add column if not exists allowed_plan_ids uuid[] not null default '{}'::uuid[], add column if not exists allowed_region_ids uuid[] not null default '{}'::uuid[], add column if not exists metadata jsonb not null default '{}'::jsonb; do $$ begin if not exists (select 1 from pg_constraint where conname = 'coupons_status_check') then alter table public.coupons add constraint coupons_status_check check (status in ('active', 'disabled', 'archived')); end if; if not exists (select 1 from pg_constraint where conname = 'coupons_min_order_amount_cents_check') then alter table public.coupons add constraint coupons_min_order_amount_cents_check check (min_order_amount_cents >= 0); end if; if not exists (select 1 from pg_constraint where conname = 'coupons_max_discount_cents_check') then alter table public.coupons add constraint coupons_max_discount_cents_check check (max_discount_cents is null or max_discount_cents >= 0); end if; if not exists (select 1 from pg_constraint where conname = 'coupons_per_user_limit_check') then alter table public.coupons add constraint coupons_per_user_limit_check check (per_user_limit > 0 and per_user_limit <= 100); end if; end $$; drop index if exists public.idx_coupon_redemptions_user_coupon_once; create unique index if not exists idx_coupon_redemptions_user_coupon_open on public.coupon_redemptions(tenant_id, user_id, coupon_id) where coupon_id is not null and status in ('claimed', 'pending'); create index if not exists idx_coupons_tenant_status_campaign on public.coupons(tenant_id, status, campaign_name, created_at desc); create index if not exists idx_coupon_redemptions_coupon_status on public.coupon_redemptions(tenant_id, coupon_id, status, created_at desc); create index if not exists idx_coupon_redemptions_used_at on public.coupon_redemptions(tenant_id, used_at desc) where used_at is not null;