manicanldes-backend/prisma/migrations/20260911111403_operations_i.../migration.sql

64 lines
3.3 KiB
PL/PgSQL

ALTER TABLE pricing_policies ADD CONSTRAINT pricing_rule_bounds CHECK (
merchandise_tax_bps BETWEEN 0 AND 10000 AND shipping_tax_bps BETWEEN 0 AND 10000
AND shipping_fee >= 0 AND (free_shipping_minimum IS NULL OR free_shipping_minimum >= 0)
AND currency IN ('INR','USD','EUR','GBP') AND country_code ~ '^[A-Z]{2}$' AND region = upper(region)
);
CREATE UNIQUE INDEX pricing_one_active_scope ON pricing_policies (organization_id, currency, country_code, region)
WHERE active = true;
CREATE FUNCTION protect_pricing_policy() RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
IF (to_jsonb(NEW) - 'active') IS DISTINCT FROM (to_jsonb(OLD) - 'active') THEN
RAISE EXCEPTION 'Pricing policies are immutable; create a new version';
END IF;
RETURN NEW;
END;
$$;
CREATE TRIGGER pricing_policy_immutable BEFORE UPDATE ON pricing_policies
FOR EACH ROW EXECUTE FUNCTION protect_pricing_policy();
CREATE FUNCTION reject_commerce_snapshot_mutation() RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
RAISE EXCEPTION 'Commerce snapshots and events are immutable';
END;
$$;
CREATE TRIGGER order_pricing_immutable BEFORE UPDATE OR DELETE ON order_pricing
FOR EACH ROW EXECUTE FUNCTION reject_commerce_snapshot_mutation();
CREATE TRIGGER commerce_events_immutable BEFORE UPDATE OR DELETE ON commerce_events
FOR EACH ROW EXECUTE FUNCTION reject_commerce_snapshot_mutation();
CREATE FUNCTION reconcile_order_pricing() RETURNS trigger LANGUAGE plpgsql AS $$
DECLARE
p pricing_policies%ROWTYPE;
merchandise NUMERIC;
expected_tax NUMERIC;
shipping NUMERIC;
shipping_tax NUMERIC;
payable NUMERIC;
BEGIN
SELECT * INTO STRICT p FROM pricing_policies WHERE id = NEW.policy_id AND organization_id = NEW.organization_id;
SELECT merchandise_total INTO STRICT merchandise FROM orders WHERE id = NEW.order_id AND organization_id = NEW.organization_id;
expected_tax := ROUND(merchandise * p.merchandise_tax_bps /
(10000 + CASE WHEN p.tax_mode = 'INCLUSIVE' THEN p.merchandise_tax_bps ELSE 0 END), 2);
shipping := CASE WHEN p.free_shipping_minimum IS NOT NULL AND merchandise >= p.free_shipping_minimum
THEN 0 ELSE p.shipping_fee END;
shipping_tax := ROUND(shipping * p.shipping_tax_bps / 10000, 2);
payable := merchandise + shipping + shipping_tax + CASE WHEN p.tax_mode = 'EXCLUSIVE' THEN expected_tax ELSE 0 END;
IF NEW.merchandise_tax <> expected_tax OR NEW.shipping_net <> shipping OR NEW.shipping_tax <> shipping_tax
OR NEW.tax_total <> expected_tax + shipping_tax OR NEW.payable_total <> payable THEN
RAISE EXCEPTION 'Final pricing does not match the selected policy';
END IF;
RETURN NEW;
END;
$$;
CREATE TRIGGER order_pricing_reconciled BEFORE INSERT ON order_pricing
FOR EACH ROW EXECUTE FUNCTION reconcile_order_pricing();
ALTER TABLE commerce_events ADD CONSTRAINT commerce_event_kind CHECK (kind IN ('order.created','order.cancelled'));
ALTER TABLE event_deliveries ADD CONSTRAINT delivery_attempt_bounds CHECK (attempts BETWEEN 0 AND 5);
ALTER TABLE event_deliveries ADD CONSTRAINT delivery_lease_pair CHECK ((lease_token IS NULL) = (lease_expires_at IS NULL));
UPDATE roles SET permissions = ARRAY(
SELECT DISTINCT permission FROM unnest(permissions || ARRAY[
'pricing.manage', 'operations.read', 'notifications.retry'
]::text[]) AS permission ORDER BY permission
) WHERE is_system = true;