90 lines
4.1 KiB
Markdown
90 lines
4.1 KiB
Markdown
# 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. `moysklad` scans the complete paginated assortment,
|
|
aggregate stock, shipments and customer returns. `wildberries` reads the
|
|
current Finance API realization details and normalizes sales, returns,
|
|
commissions, acquiring, logistics, storage and deductions. Raw API payloads are
|
|
retained in `SourceRecord` before normalization. Repeated scans update the same
|
|
source identities and append stock observations.
|
|
Wildberries imports save the last `rrdId` after every non-empty page and continue
|
|
on the next scheduled run until the API returns HTTP 204, as required by the
|
|
provider contract. This avoids both duplicates and a worker sleep under the
|
|
provider's one-request-per-minute Finance API limit. After the initial history
|
|
is complete, each run rechecks the latest 30 days so report corrections replace
|
|
prior facts.
|
|
|
|
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.
|
|
|
|
## Local project management
|
|
|
|
Local project cards, teams and daily participation live in the sibling
|
|
[`projects`](../projects/README.md) app. `ProjectExternalBinding` explicitly maps
|
|
source object identities to local projects. Commerce synchronization preserves
|
|
external projects and payments and never overwrites local project management data.
|
|
Active external projects without a binding appear in the `/projects` intake queue,
|
|
where a manager can create or select their local project explicitly.
|
|
Company analytics groups imported sales, costs and project payments by these exact
|
|
bindings. Unbound source records remain visible as a separate reconciliation group.
|