Commerce analytics core

commerce is the product-centric analytics boundary of EUSRR. It does not replace an operational CRM or warehouse. External and future internal systems all supply records to the same canonical product model.

Stable model

  • Product is one sellable or stockable SKU. parent groups variants.
  • SourceRecord retains the latest raw payload with source provenance.
  • ProductBinding maps a source product/offer identifier to Product.
  • Sale and SaleLine contain normalized product-level sales facts.
  • Cost contains commissions, delivery, advertising and other expenses.
  • StockSnapshot contains point-in-time stock observations by source and warehouse.

Money is never aggregated across currencies. Returns are represented as a Sale with event_type="return"; line values remain positive and analytics applies the sign.

Integration contract

An adapter subclasses commerce.integrations.SourceAdapter, handles API pagination internally and yields ExternalRecord objects. Register it in adapter_registry, then invoke the Celery task commerce.sync_source.

Adapters must not store credentials in SourceSystem.metadata; secrets belong in environment-backed configuration or a dedicated encrypted credential store.

Source-specific code must not create alternative product models. It stores the raw payload first and then resolves a product through ProductBinding or commerce.services.match_product before writing normalized facts.

API

The authenticated API is mounted at /api/v1/commerce/:

  • products/
  • products/{id}/analytics/
  • sources/
  • product-bindings/
  • sales/
  • sale-lines/
  • costs/
  • stock-snapshots/

All employees may read. Writes require staff status or the corresponding Django model permission. The frontend management screen can bootstrap and correct normalized records manually; integrations should still write them through application services so raw provenance and idempotency are preserved.