Skip to content

Compliance Module

The core compliance module provides a rules engine for controlling product visibility, purchasing, and messaging based on configurable conditions. It supersedes the former catalog_visibility module and the ext_tobacco_compliance extension.

Architecture

The compliance module lives at vectis/backend/vectis/modules/compliance/ and follows the standard module pattern:

File Purpose
models.py ComplianceRule and ComplianceCache SQLAlchemy models
strategies.py ComplianceConditionStrategy ABC + ComplianceContext / ComplianceDecision dataclasses
services.py ComplianceService — evaluation engine, cache management, extension dispatch
resolvers.py GraphQL queries (admin CRUD + storefront productCompliance) and mutations

Data Model

ComplianceRule

Each rule combines:

  • Conditions (JSONB array, AND logic) — what products and contexts the rule targets
  • Enforcement flags — what happens when conditions match (hide prices, block cart, etc.)
  • Messages — optional text shown at five placements (product page, cart header, cart inline, checkout, above shipping)

ComplianceCache

Pre-computed per product x customer_group. Rebuilt nightly via Temporal and incrementally on rule changes. Runtime-only conditions (location, specific customer) are evaluated at request time and merged with the cached result.

Condition Types

Built-in (cacheable)

Type Parameters
product_category category_id
product_collection collection_id
product_trait key, value, operator (eq/ne/in/contains)
product_price_above threshold
product_price_below threshold
customer_group customer_group_id

Built-in (runtime)

Type Parameters
specific_customer customer_id
location_state states (list)
location_country countries (list)
not_logged_in (none)

Extension conditions

Extensions register custom condition evaluators via the strategy resolver:

from vectis.core.strategy import strategy_resolver
from vectis.modules.compliance.strategies import ComplianceConditionStrategy

class HasTobaccoLicenseCondition(ComplianceConditionStrategy):
    def condition_type(self) -> str:
        return "has_tobacco_license"

    async def evaluate(self, condition, product, context):
        # Check if the customer has a valid tobacco license
        ...

strategy_resolver.register(
    ComplianceConditionStrategy,
    HasTobaccoLicenseCondition(),
    name="has_tobacco_license",
    extension_name="ext-my-extension",
)

Enforcement Flags

Flag Effect
warn_only Show messages but do not enforce any restrictions
prevent_add_to_cart Block the product from being added to cart
hide_prices Hide all pricing information
hide_quantity Hide the quantity selector
hide_add_to_cart_button Hide the entire Add to Cart button
prevent_checkout Allow in cart but block checkout
exclude_from_search Remove from Typesense search results (product page still accessible by direct URL)
hide_from_storefront Completely hide from storefront — search, listings, and catalog. Implies exclude_from_search. Product page shows "not available" message.

When multiple rules match, the most restrictive enforcement wins (boolean OR across rules). Messages come from the highest-priority (lowest number) matching rule.

All flags can be combined with any condition type — including customer_group. This means you can hide products from search for one customer group while showing them to another, or restrict entire product lines to specific wholesale tiers.

Integration Points

The compliance module integrates with five touchpoints:

  1. Product listingproducts(respectCompliance: true) query filters out products with hide_from_storefront from listings, respecting the caller's customer group
  2. Product pageProductType.compliance field populated during product(slug) query; hideFromStorefront shows a "not available" page
  3. Add to cart — compliance check before CartService.add_line(); raises ValueError if blocked
  4. Cart displaymyCart query enriches each line with CartLineComplianceType and adds complianceMessages / preventsCheckout at cart level
  5. Checkout — compliance check before order creation; raises ValidationError if prevent_checkout applies
  6. Search — Typesense indexing respects both is_visible and exclude_from_search from the compliance cache, filtering per customer group via visible_group_ids

Cache Rebuild

The cache is rebuilt via Temporal:

  • Nightly: RebuildComplianceCacheWorkflow runs rebuild_compliance_cache activity
  • On rule change: rebuild_cache_for_rule(rule_id) runs incrementally
  • On product save: rebuild_cache_for_product(product_id) runs for a single product
  • Manual: rebuildComplianceCache GraphQL mutation for admin use

Admin UI

The compliance rules are managed at /settings/compliance in the admin panel. The page supports creating, editing, enabling/disabling, and deleting rules with a visual condition builder and enforcement toggle interface.