Factorizer/Cluster ERP WikiCluster ERP Türkçe
Home / Decision records

Explanation

Product, model, and version breakdown

Decision record for the rule that every product reference in Cluster ERP carries product, model, and version, with no silent fallback to the product alone.

Status: Accepted, stated in the project's written engineering rules. This record covers the rule that a document line, a stock row, a bill of materials, a reservation, or a movement never names a bare product: it names the product, one of its models, and one of that model's versions. The shared helpers live in src/cluster_erp/core/product_breakdown.py; the stock side is explained on product breakdown and stock conversion.

Context

Stock is kept per product version, so a reference to a product alone is ambiguous. The project's written engineering rules make the three-level breakdown mandatory across the whole ERP and forbid a silent fallback of the form "use the product when there is no model or version". The helper module states the intent: the ERP is "deterministic about what a product reference means" (src/cluster_erp/core/product_breakdown.py:1-9). Further rationale is not determined from code.

Decision

  • If a line has a product, it must have a model and a version; if it has no product, it has neither (src/cluster_erp/core/product_breakdown.py:94-118).
  • The model must belong to the product and the version to the model. Because that check needs the products module, it is a ValidateProductBreakdown query over the EventBus (src/cluster_erp/core/product_breakdown.py:120-147, src/cluster_erp/core/contracts.py:218-226).
  • Forms offer a dependent picker: the model list is filtered by the chosen product and the version list by the chosen model (src/cluster_erp/core/product_breakdown.py:31-83).

The sequence diagram shows the check on a document line.

sequenceDiagram
 participant Svc as Module service
 participant Core as ensure_product_breakdown
 participant Bus as EventBus
 participant PS as products_and_services
 Svc->>Core: product_id, product_model_id, product_version_id
 Core->>Core: all empty, or all three present?
 Core->>Bus: query ValidateProductBreakdown
 Bus->>PS: _handle_validate_breakdown
 PS-->>Core: ok, or an error code
 Core-->>Svc: return, or raise AppError 422

In prose: the service calls ensure_product_breakdown. A model or version without a product, or a product without both, raises AppError with HTTP 422 at once. Otherwise the helper sends the query; the products module answers with ok or with PRODUCT_MODEL_PRODUCT_MISMATCH or PRODUCT_VERSION_MODEL_MISMATCH, and a mismatch raises 422 (src/cluster_erp/core/product_breakdown.py:103-147, src/cluster_erp/modules/products_and_services/module.py:131).

Consequences

  • Stock levels, goods movements, usage decisions, reservations, and project stock loans declare product_model_id and product_version_id as non-null columns (src/cluster_erp/modules/inventory/stock/models.py:54-63, src/cluster_erp/modules/inventory/stock/models.py:144-153, src/cluster_erp/modules/inventory/stock/models.py:289-297, src/cluster_erp/modules/inventory/stock/models.py:355-364, src/cluster_erp/modules/inventory/stock/models.py:445-453).
  • The live-row unique index of stock levels includes the model and the version (src/cluster_erp/modules/inventory/stock/models.py:28-46).
  • 49 service calls in 23 files under src/cluster_erp/modules run the check, across BOM, inventory, procurement, production, projects, quotations, sales orders, nesting, and fleet (counted with grep -rn "ensure_product_breakdown(").
  • Flows that create documents without a person choosing, such as MRP, ask for the product's default model and version through GetProductDefaultBreakdown and record them explicitly (src/cluster_erp/core/contracts.py:229-235). The default is written on the document; it is not a silent fallback at read time.

Where the code enforces it

Mechanism What it checks Source
ensure_product_breakdown Presence and consistency of the three ids on every write src/cluster_erp/core/product_breakdown.py:86-147
Non-null breakdown columns Stock rows cannot be stored without model and version src/cluster_erp/modules/inventory/stock/models.py:54-63
_e2e_product_model_versions.mjs The product form, the model card, and the version list follow the chain in the browser web-client/_e2e_product_model_versions.mjs:1-4

Not determined from code: a pytest test that calls ensure_product_breakdown directly. None was found in tests/ (searched for product_breakdown).


Verified against commit 92f8051 on 2026-11-13.

Verified against commit 92f8051 · 2026-11-13