-- Shop coupons: a shop-owned template plus a customer-owned snapshot taken at -- claim time. The snapshot copies terms and window so editing or disabling a -- template cannot change a coupon a customer already holds. -- -- `orders` gains the realized discount and the coupon it redeemed. Both tables -- reference each other; ON DELETE SET NULL keeps either side deletable. CREATE TYPE coupon_status AS ENUM ('claimed', 'redeemed', 'expired'); CREATE TABLE coupon_templates ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), shop_id UUID NOT NULL REFERENCES shops (id) ON DELETE CASCADE, title JSONB NOT NULL, amount_minor BIGINT NOT NULL CHECK (amount_minor > 0), threshold_minor BIGINT NOT NULL CHECK (threshold_minor >= 0), currency CHAR(3) NOT NULL REFERENCES currencies (code), stock INT NOT NULL CHECK (stock >= 0), enabled BOOLEAN NOT NULL DEFAULT TRUE, starts_at TIMESTAMPTZ NOT NULL, ends_at TIMESTAMPTZ NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), CONSTRAINT coupon_templates_window CHECK (ends_at >= starts_at) ); CREATE INDEX coupon_templates_shop_idx ON coupon_templates (shop_id); CREATE TABLE coupons ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users (id) ON DELETE CASCADE, -- Nullable so deleting a template keeps the snapshots customers already hold. template_id UUID REFERENCES coupon_templates (id) ON DELETE SET NULL, shop_id UUID NOT NULL REFERENCES shops (id) ON DELETE CASCADE, title JSONB NOT NULL, amount_minor BIGINT NOT NULL CHECK (amount_minor > 0), threshold_minor BIGINT NOT NULL CHECK (threshold_minor >= 0), currency CHAR(3) NOT NULL REFERENCES currencies (code), starts_at TIMESTAMPTZ NOT NULL, ends_at TIMESTAMPTZ NOT NULL, status coupon_status NOT NULL DEFAULT 'claimed', order_id UUID REFERENCES orders (id) ON DELETE SET NULL, claimed_at TIMESTAMPTZ NOT NULL DEFAULT now(), redeemed_at TIMESTAMPTZ ); -- One claim per customer per template. CREATE UNIQUE INDEX coupons_user_template_idx ON coupons (user_id, template_id); CREATE INDEX coupons_user_idx ON coupons (user_id, status); ALTER TABLE orders ADD COLUMN coupon_id UUID REFERENCES coupons (id) ON DELETE SET NULL; ALTER TABLE orders ADD COLUMN discount_minor BIGINT NOT NULL DEFAULT 0 CHECK (discount_minor >= 0);