Factorizer/Cluster ERP WikiCluster ERP Türkçe
Home / Business modules

Module

Products and services module (products_and_services)

Module page for products_and_services, the material master with products, models, versions, categories, vendor sources, and units of measure.

The products_and_services module in src/cluster_erp/modules/products_and_services/ is the material master. It holds products and services with their planning, stock-size, and costing fields, the mandatory product, model, and version breakdown, a category tree, vendor sources per breakdown, and the units of measure. Every other module that names a product asks this module over the EventBus. The shared breakdown rule is explained on product breakdown and stock conversion.

At a glance

Item Value Source
Package src/cluster_erp/modules/products_and_services/ folder
Module class Module src/cluster_erp/modules/products_and_services/module.py:18
name / version products_and_services / 0.1.0 src/cluster_erp/modules/products_and_services/module.py:19-25
API prefix /api/v1/products_and_services (ModuleBase.api_prefix) src/cluster_erp/core/module_base.py:37-38
Sub-modules 6: products, product_models, product_versions, categories, vendors, settings folder listing
Tables 9 (__tablename__ count in the package) src/cluster_erp/modules/products_and_services/models.py
Entities in metadata 7: product, product_model, product_version, product_vendor, category, unit_category, unit src/cluster_erp/modules/products_and_services/module.py:998-1006
Routes 55 route decorators in the package grep for @router.
Web app presence Catalogue app products src/cluster_erp/modules/app_store/catalog.json (entry products)
Desktop presence Generic widgets only; no folder in src/cluster_erp/desktop/modules/ folder

What the module does

  • Keeps products and services with a material type, four capability flags, a status, units, physical data, stock-size and make-or-buy data, planning parameters, costing fields, and trade data (src/cluster_erp/modules/products_and_services/products/models.py:20-351).
  • Gives every product at least one model and every model at least one version, so that stock and documents can always post against a full breakdown (src/cluster_erp/modules/products_and_services/product_versions/models.py:16-19).
  • Links a product to several categories of a category tree (src/cluster_erp/modules/products_and_services/products/models.py:395-417, src/cluster_erp/modules/products_and_services/categories/models.py:11-30).
  • Records vendor sources per product, model, version, and vendor party (src/cluster_erp/modules/products_and_services/vendors/models.py:14-63).
  • Holds unit categories and units with conversion factors and seeds a default set at start-up (src/cluster_erp/modules/products_and_services/module.py:31-50).

Sub-modules

Sub-module Purpose Main entities Router Source
products Products and services, category links, service facet, photo Product, ProductServiceAttributes, ProductCategoryLink /products src/cluster_erp/modules/products_and_services/products/routes.py:32
product_models Models of a product, photo ProductModel /models src/cluster_erp/modules/products_and_services/product_models/routes.py:25
product_versions Versions of a model ProductVersion /versions src/cluster_erp/modules/products_and_services/product_versions/routes.py:22
categories Category tree ProductCategory /categories src/cluster_erp/modules/products_and_services/categories/routes.py:22
vendors Vendor sources ProductVendor /vendors src/cluster_erp/modules/products_and_services/vendors/routes.py:24
settings Unit categories and units UnitCategory, UnitOfMeasure /settings src/cluster_erp/modules/products_and_services/settings/routes.py:29

Router paths are relative to the module prefix; all six routers sit under one module router that depends on the app entitlement (src/cluster_erp/modules/products_and_services/module.py:75-83).

Data model

The product, model, and version breakdown

The diagram shows the breakdown tables and the rows that point at them.

erDiagram
 Product ||--|{ ProductModel : "product_id"
 ProductModel ||--|{ ProductVersion : "product_model_id"
 Product ||--o{ ProductCategoryLink : "product_id"
 ProductCategory ||--o{ ProductCategoryLink : "category_id"
 Product ||--o| ProductServiceAttributes : "product_id"
 ProductVersion ||--o{ ProductVendor : "product_version_id"
 UnitCategory ||--|{ UnitOfMeasure : "category_id"
 UnitOfMeasure ||--o{ Product : "base_unit_id"

In prose: a product has one or more models and a model has one or more versions. Exactly one live model per product and exactly one live version per model carry is_default, enforced by partial unique indexes (src/cluster_erp/modules/products_and_services/product_models/models.py:32-38, src/cluster_erp/modules/products_and_services/product_versions/models.py:35-41). A version name is unique within its model (src/cluster_erp/modules/products_and_services/product_versions/models.py:42-49). A product has category links and, when it is a service, one service facet row. A vendor source names a product, a model, a version, and a vendor party (src/cluster_erp/modules/products_and_services/vendors/models.py:16-49). Units belong to a unit category, and a product points at its base, purchase, sales, weight, volume, and dimension units (src/cluster_erp/modules/products_and_services/products/models.py:103-138).

Entities

Entity Table Key fields Notes Source
Product products_products code, name, sku, barcode, material_type, category_id, is_purchasable, is_sellable, is_manufacturable, is_stockable, status, stock_form, sourcing_preference, standard_cost, extra sku and barcode unique among live rows src/cluster_erp/modules/products_and_services/products/models.py:20-351
ProductServiceAttributes products_service_attributes product_id, billing_method, rate_basis, standard_rate, currency_code, duration_value, is_billable one live row per product src/cluster_erp/modules/products_and_services/products/models.py:353-392
ProductCategoryLink products_product_category_links product_id, category_id unique pair among live rows src/cluster_erp/modules/products_and_services/products/models.py:395-417
ProductModel products_models code, product_id, name, sku, barcode, attributes, is_default, photo_attachment_id one live default per product src/cluster_erp/modules/products_and_services/product_models/models.py:17-66
ProductVersion products_model_versions code, product_model_id, name, sku, barcode, attributes, is_default one live default per model src/cluster_erp/modules/products_and_services/product_versions/models.py:16-63
ProductCategory products_categories code, name, parent_id, sort_order self-referencing tree src/cluster_erp/modules/products_and_services/categories/models.py:11-30
ProductVendor products_product_vendors product_id, product_model_id, product_version_id, party_id, vendor_part_number, lead_time_days, unit_price, currency, is_preferred, valid_from, valid_to unique per breakdown and party among live rows src/cluster_erp/modules/products_and_services/vendors/models.py:14-63
UnitCategory products_unit_categories code, name, translations code and name unique among live rows src/cluster_erp/modules/products_and_services/settings/models.py:13-39
UnitOfMeasure products_units_of_measure code, category_id, name, symbol, is_base_unit, conversion_factor, sort_order symbol unique among live rows; factor Numeric(24, 6) src/cluster_erp/modules/products_and_services/settings/models.py:42-79

Money and quantity columns are Numeric; costs and vendor prices use Numeric(18, 4) (src/cluster_erp/modules/products_and_services/products/models.py:313-314, src/cluster_erp/modules/products_and_services/vendors/models.py:56). Decimal values are serialised as strings (src/cluster_erp/modules/products_and_services/products/schemas.py:149-151, src/cluster_erp/modules/products_and_services/products/schemas.py:478-481). See money and Decimal.

Screens and metadata

get_metadata declares a sidebar group Products & Services (Turkish Ürünler ve Hizmetler) with three sections: Products (a list), Categories (a tree), and Settings with the tabs unit_category and unit (src/cluster_erp/modules/products_and_services/module.py:954-997). The product detail has the tabs General, Service, Physical Properties, Inventory & Planning, Production & Costing, Vendors, Models, Bill of Materials, and Stock (src/cluster_erp/modules/products_and_services/products/metadata.py:222-295). The Service tab shows only for the material type service, and the physical, inventory, production, and stock tabs hide for it. The Bill of Materials tab shows only for manufacturable products and reads the bom module's list, and Stock reads the inventory module's list (src/cluster_erp/modules/products_and_services/products/metadata.py:230-294). The create form collects the first model's name, SKU, and barcode and the first version's name (src/cluster_erp/modules/products_and_services/products/metadata.py:381-424). Products and models have photos (src/cluster_erp/modules/products_and_services/products/metadata.py:118, src/cluster_erp/modules/products_and_services/products/metadata.py:1328). The category tree uses GET /categories/tree (src/cluster_erp/modules/products_and_services/categories/metadata.py:26-32).

Status and actions

Product statuses are draft, active, blocked, and obsolete (src/cluster_erp/modules/products_and_services/products/schemas.py:24-28). The status is a form field, and a change emits ProductStatusChanged (src/cluster_erp/modules/products_and_services/products/services.py:590-598). The module declares one ActionMeta.

Action Label Condition Endpoint Permission Source
seed_defaults (unit category) Seed Defaults none (global) POST /settings/unit-categories/seed-defaults unit_category.seed src/cluster_erp/modules/products_and_services/settings/metadata.py:29-37, src/cluster_erp/modules/products_and_services/settings/routes.py:84-91

Business rules

  • Each material type has default capability flags, used when the payload leaves a flag out (src/cluster_erp/modules/products_and_services/products/schemas.py:34-89, src/cluster_erp/modules/products_and_services/products/services.py:296-312).
  • Hard constraints: a service is neither stockable nor manufacturable, a consumable is not stockable, and a raw material is not manufacturable; a violation answers PRODUCT_CAPABILITY_VIOLATION (src/cluster_erp/modules/products_and_services/products/schemas.py:91-95, src/cluster_erp/modules/products_and_services/products/services.py:671-683).
  • A product that is not stockable has its batch, serial, shelf-life, safety-stock, and reorder-point fields cleared (src/cluster_erp/modules/products_and_services/products/services.py:48-55, src/cluster_erp/modules/products_and_services/products/services.py:316-318).
  • SKU and barcode must be free among live products (src/cluster_erp/modules/products_and_services/products/services.py:272-292).
  • Creating a product creates its default model, named after the product when no name is given, and the model creates its default version, named 1 by default (src/cluster_erp/modules/products_and_services/products/services.py:376-395, src/cluster_erp/modules/products_and_services/product_models/services.py:103-112, src/cluster_erp/modules/products_and_services/product_models/schemas.py:22).
  • The first model of a product and the first version of a model become the default; a new default takes the flag from the old one (src/cluster_erp/modules/products_and_services/product_models/services.py:78-99, src/cluster_erp/modules/products_and_services/product_versions/services.py:72-90).
  • The default model or version cannot be demoted, deactivated, or deleted; the answer is PRODUCT_MODEL_DEFAULT_PROTECTED or PRODUCT_VERSION_DEFAULT_PROTECTED with HTTP 409 (src/cluster_erp/modules/products_and_services/product_models/services.py:115-160, src/cluster_erp/modules/products_and_services/product_versions/services.py:92-140).
  • A product that is a live component of an active BOM version cannot be deleted; the module asks the bom module with GetPartUsageCounts and answers PRODUCT_IN_ACTIVE_BOM (src/cluster_erp/modules/products_and_services/products/services.py:604-626).
  • Deleting a product also soft-deletes its category links and service facet with the same timestamp; reactivating it restores them (src/cluster_erp/modules/products_and_services/products/services.py:628-660, src/cluster_erp/modules/products_and_services/module.py:165-236).
  • Every category named on a product must be live; the first one becomes the denormalised category_id (src/cluster_erp/modules/products_and_services/products/services.py:151-206).
  • A vendor source checks that the product and the party exist, validates the breakdown, and refuses a second live source for the same breakdown and party with HTTP 409 (src/cluster_erp/modules/products_and_services/vendors/services.py:94-143).
  • Unit conversion works only within one unit category: value times the source factor, divided by the target factor (src/cluster_erp/modules/products_and_services/settings/services.py:237-247). The seed has 13 unit categories (src/cluster_erp/modules/products_and_services/settings/seed_data.py, entries with a code key).
  • Codes come from the yearly sequences product, product_model, product_version, product_category, and unit_of_measure (src/cluster_erp/modules/products_and_services/products/services.py:325, src/cluster_erp/modules/products_and_services/product_models/services.py:79, src/cluster_erp/modules/products_and_services/product_versions/services.py:73).

Events and queries

The module consumes 19 contracts and produces 8, counted as on the parties module page. The full list of producers and consumers is on the contracts reference.

Direction Contract class Handler or emitter Source
emits ProductCreated, ServiceCreated ProductService.create src/cluster_erp/modules/products_and_services/products/services.py:407-424
emits ProductUpdated, ProductStatusChanged ProductService.update src/cluster_erp/modules/products_and_services/products/services.py:582-598
emits ProductDeleted ProductService.delete src/cluster_erp/modules/products_and_services/products/services.py:652-659
queries GetPartUsageCounts, GetProductById, GetPartyById product delete; vendor create src/cluster_erp/modules/products_and_services/products/services.py:609-616, src/cluster_erp/modules/products_and_services/vendors/services.py:96-111
answers query ValidateProductBreakdown, GetProductDefaultBreakdown, GetBulkProductDefaultBreakdowns _handle_validate_breakdown, _handle_default_breakdown, _handle_bulk_default_breakdowns src/cluster_erp/modules/products_and_services/module.py:131-138, src/cluster_erp/modules/products_and_services/module.py:759-916
answers query GetProductById _handle_get_product src/cluster_erp/modules/products_and_services/module.py:108, src/cluster_erp/modules/products_and_services/module.py:238-317
answers query ConvertQuantity, GetUnitOfMeasure, FindUnitOfMeasure unit handlers src/cluster_erp/modules/products_and_services/module.py:109-115
answers query IsProductManufacturable, IsProductPurchasable, IsProductSellable, GetProductsByMaterialType flag handlers src/cluster_erp/modules/products_and_services/module.py:116-119
answers query GetProductVendors, GetBulkProductVendors, GetProductPlanningData, SetProductPlanningClassification, ApplyProductBufferZones planning handlers src/cluster_erp/modules/products_and_services/module.py:120-130
answers query CreateScaffoldIntermediate _handle_create_scaffold_intermediate src/cluster_erp/modules/products_and_services/module.py:133-135, src/cluster_erp/modules/products_and_services/module.py:806-875
subscribes RecordActivated, DescribeAnalyticsFacts _on_record_activated, describe_analytics_facts src/cluster_erp/modules/products_and_services/module.py:107, src/cluster_erp/modules/products_and_services/module.py:142-147

CreateScaffoldIntermediate creates, or reuses by SKU, a stockable semi-finished product that is not manufacturable, for the output of a routing operation (src/cluster_erp/modules/products_and_services/module.py:806-875). It is used by the routing scaffold described on the BOM module page.

Permissions

Permission key Used by Source
product.read, .create, .update, .delete, .bulk_delete product and photo routes src/cluster_erp/modules/products_and_services/products/routes.py:55-266
product_model.read, .create, .update, .delete, .bulk_delete model and photo routes src/cluster_erp/modules/products_and_services/product_models/routes.py:32-199
product_version.read, .create, .update, .delete, .bulk_delete version routes src/cluster_erp/modules/products_and_services/product_versions/routes.py:29-101
category.read, .create, .update, .delete, .bulk_delete category routes src/cluster_erp/modules/products_and_services/categories/routes.py:28-97
product_vendor.read, .create, .update, .delete, .bulk_delete vendor routes src/cluster_erp/modules/products_and_services/vendors/routes.py:34-127
unit_category.*, unit.* unit routes src/cluster_erp/modules/products_and_services/settings/routes.py:59-265

Privacy, retention, and history

The module stores no personal data of its own and has no privacy_handlers.py. A vendor source keeps the vendor party's display name in vendor_name_cache (src/cluster_erp/modules/products_and_services/vendors/models.py:51, src/cluster_erp/modules/products_and_services/vendors/services.py:145-148). Changes are captured by change history.

Analytics facts

analytics_facts.py declares one fact set, product, over products_products, with the dimensions material type, status, stock form, sourcing preference, ABC indicator, active, and created date, and the measures product_count and average_lead_time_days (src/cluster_erp/modules/products_and_services/analytics_facts.py:21-87).

Settings and reference data

Unit categories and units are reference data in the database. At every start the module seeds the default set from UNIT_SEED_DATA (src/cluster_erp/modules/products_and_services/module.py:37-50, src/cluster_erp/modules/products_and_services/settings/services.py:86-150). The Seed Defaults action runs the same seeding on demand.

Tests

Found by a grep for modules.products_and_services in tests/ and by file name in web-client/.

Kind File Covers
pytest tests/test_bom_bugfix_batch_2026_10_08.py product metadata, unit seed data, and unit pickers used by the BOM forms (10 tests)
e2e web-client/_e2e_products.mjs the products app: list, category tree, detail, settings
e2e web-client/_e2e_products_service.mjs the service facet, multiple categories, and the type-aware form
e2e web-client/_e2e_product_model_versions.mjs the product, model, and version chain in the web app
e2e web-client/_e2e_make_or_buy_stock.mjs stock size, make-or-buy, and the stock material breakdown

Discrepancies

Terms introduced

Term Code identifier Definition Turkish term Source
product Product An item or service of the material master. Ürün src/cluster_erp/modules/products_and_services/products/models.py:20
product model ProductModel A variant of a product; every product has a default one. Ürün modeli src/cluster_erp/modules/products_and_services/product_models/models.py:17
product version ProductVersion A version of a model; stock is kept per product, model, and version. Ürün versiyonu src/cluster_erp/modules/products_and_services/product_versions/models.py:16
material type material_type The kind of product, which sets default capability flags. Malzeme türü src/cluster_erp/modules/products_and_services/products/schemas.py:12-21
vendor source ProductVendor A vendor party that supplies one product breakdown, with price and lead time. Tedarikçi kaynağı src/cluster_erp/modules/products_and_services/vendors/models.py:14
unit of measure UnitOfMeasure A unit with a conversion factor inside its unit category. Ölçü birimi src/cluster_erp/modules/products_and_services/settings/models.py:42

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

Verified against commit 92f8051 · 2026-11-13