Corp/backend/commerce/README.md
2026-08-17 19:31:56 +03:00

3.0 KiB

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.

IntegrationConnection is that encrypted credential boundary for connections created by employees. The first production connector is moysklad: it scans the complete paginated assortment, aggregate stock, shipments and customer returns. Raw API payloads are retained in SourceRecord before normalization. Repeated scans update the same source identities and append stock observations. Celery Beat checks due connections every minute. Each connection stores whether automatic synchronization is enabled, its interval (15 minutes by default) and the next planned run; row locking and connection status prevent overlapping full scans. Both a Celery worker and Celery Beat must be running.

Imported cards are matched by an existing source binding, SKU or barcode. A card that cannot be matched safely is created separately and can later be merged into a canonical product through products/{id}/merge/; bindings and normalized facts move with it.

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/
  • products/{id}/merge/
  • connections/ (test/ and sync/ actions)
  • sync-runs/
  • 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.