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:
- Product listing —
products(respectCompliance: true)query filters out products withhide_from_storefrontfrom listings, respecting the caller's customer group - Product page —
ProductType.compliancefield populated duringproduct(slug)query;hideFromStorefrontshows a "not available" page - Add to cart — compliance check before
CartService.add_line(); raisesValueErrorif blocked - Cart display —
myCartquery enriches each line withCartLineComplianceTypeand addscomplianceMessages/preventsCheckoutat cart level - Checkout — compliance check before order creation; raises
ValidationErrorifprevent_checkoutapplies - Search — Typesense indexing respects both
is_visibleandexclude_from_searchfrom the compliance cache, filtering per customer group viavisible_group_ids
Cache Rebuild¶
The cache is rebuilt via Temporal:
- Nightly:
RebuildComplianceCacheWorkflowrunsrebuild_compliance_cacheactivity - 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:
rebuildComplianceCacheGraphQL 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.