# 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.