Design-driven manufacturing for Odoo 18 with lot-level parameters.
Manufacturing companies working on make-to-order basis face a fundamental limitation in Odoo: the standard system manages products as categories, but cannot describe the specific physical item that needs to be produced right now, for this particular customer, with these exact parameters.
The result: engineering departments maintain thousands of product variants (one per parameter combination), or rely on paper-based specifications outside the system. In both cases, planning, traceability, and costing are incomplete.
MRP Design Matrix introduces a new conceptual layer: the production lot carries the design specification of the item. The BoM is parametric -- materials and quantities are computed automatically from the lot's parameters. A rule system (matrix) governs the transformation.
| Concept | Description |
|---|---|
| Lot = Design carrier | stock.lot with Properties describes the specific production run. Variant = category. Lot = physical realization. |
| BoM = Planning view | Contains all possible materials, including O-variants (conditional items with coeff=0.0). |
| MO = Execution view | Only active materials appear. O-variants are activated by the matrix (coeff > 0.0). |
| Design Matrix (T0-T3) | Four rule tables transform design parameters into concrete materials and operations. |
| Properties, not custom fields | All design parameters live in Odoo Properties. Definitions are data, not code. |
This is a multi-module suite (OCA-style). Install the core engine plus the industry vertical(s) you need.
| Module | Version | Summary |
|---|---|---|
mrp_design_matrix |
18.0.2.3.0 | Parametric BoM driven by lot-level design parameters and a DMN rule matrix |
base_zen_decision |
18.0.1.0.0 | ZEN/GoRules decision-table kernel — evaluate + trace + version + sync |
design_param_base |
18.0.1.1.1 | Shared design-parameter definitions with SVG profiles |
stock_lot_properties |
18.0.1.0.0 | Dynamic design parameters (Properties) on stock.lot |
product_design_assets |
18.0.1.1.0 | Link design visual assets (GLB / SVG / PNG) from product attachments |
| Module | Version | Summary |
|---|---|---|
mrp_bom_line_formula_template |
18.0.1.3.0 | Reusable templates for BoM line quantity formulas |
mrp_bom_line_formula_wizard |
18.0.1.1.0 | Edit BoM line formulas in a dedicated wizard |
mrp_bom_line_formula_claude |
18.0.1.0.0 | Generate BoM formulas with Claude AI via MCP |
| Module | Version | Summary |
|---|---|---|
sale_design_configurator |
18.0.1.13.0 | SO-line design configurator with 3D preview |
sale_design_pricing |
18.0.1.3.2 | Cost-plus pricing — separate material / labor markups |
Each vertical is a self-contained story: it ships only the parameter definitions + matrix templates for that product family, on top of the core engine. Pick the one that matches your shop floor.
| Module | Version | Vertical |
|---|---|---|
mrp_design_matrix_shutters |
18.0.1.7.2 | Roller shutters — 5-model range (motor sized by area × slat weight) |
mrp_design_matrix_roller_door |
18.0.1.5.0 | Roller shutter doors |
mrp_design_matrix_roller_garage_door |
18.0.1.0.0 | Roller garage doors |
mrp_design_matrix_security_door |
18.0.1.0.0 | Security doors — RC class forces component minimums |
mrp_design_matrix_interior_door |
18.0.1.0.0 | Interior doors — double-leaf geometry |
mrp_design_matrix_corrugated |
18.0.1.1.0 | Corrugated / cardboard boxes — multi-layer grammage |
mrp_design_matrix_smart_display |
18.0.1.0.0 | Smart-home displays |
mrp_design_matrix_bags |
18.0.1.0.0 | Garbage bags — resin by geometry |
mrp_design_matrix_canned_peppers |
18.0.1.0.0 | Canned roasted peppers |
| Module | Version | Summary |
|---|---|---|
access_control |
18.0.1.12.1 | Polymorphic physical access control — subjects, perimeters, points |
| Industry | Key Challenge | Example |
|---|---|---|
| Garbage bags | Resin consumption depends on geometry | Width x length x thickness x density |
| Corrugated boxes | Board grade, liner combo, trim optimization | Multi-layer grammage calculation |
| Roller shutters | Motor selected by door weight, not user choice | motor_class = f(area x slat_weight) |
| Security doors | RC class forces minimums on all components | sheet_thickness = max(user, RC_min) |
| Interior doors | Double-leaf has different geometry | effective_area = w x h x 2 |
┌──────────────────────────────────┐
│ mrp_design_matrix_bags │
│ mrp_design_matrix_corrugated │
│ mrp_design_matrix_roller_door │ Industry
│ mrp_design_matrix_security_door│ Submodules
│ mrp_design_matrix_interior_door│
└──────────────┬───────────────────┘
│ depends
┌──────────────▼───────────────────┐
│ mrp_design_matrix │ Core Engine
│ (models, views, GoRules, UI) │
└──────────────┬───────────────────┘
│ depends
┌────────────────────────┼────────────────────────┐
│ │ │
┌──────────────▼──────┐ ┌──────────────▼──────┐ ┌──────────────▼────────────┐
│ mrp_bom_formula_ │ │ stock_move_forced_ │ │ stock_move_forced_ │
│ lot_dimension │ │ lot_multi │ │ lot_multi_dim │
│ (bridge ~50 lines) │ │ (PR candidate OCA) │ │ (width/height/thickness) │
└──────────────┬──────┘ └─────────────────────┘ └───────────────────────────┘
│ depends
┌──────────────▼──────────────────┐
│ mrp_bom_line_formula_template │ own formula engine (this repo)
└─────────────────────────────────┘
Executes before anything else. Blocks invalid designs or warns about risky ones.
| Type | Effect | Example |
|---|---|---|
ERROR |
UserError, stops MO creation |
Perforation + insulation (roller) |
WARNING |
message_post, continues |
Width > 4000mm + manual drive (roller) |
Computes intermediate variables and enriches the context for T2/T3.
| Effect Type | Description | Example |
|---|---|---|
context_modify |
Computes new value | effective_length = height x 2 + 50 |
context_force |
Overrides user input with minimum | sheet_thickness = max(user, RC_min) |
context_derive |
Computes from physics | motor_class = f(area x slat_weight) |
Three output types (non-exclusive):
| Type | Mechanism | Use Case |
|---|---|---|
| O-variant activation | {"bom_line_coeff_key": "ink-O", "coefficient": 1.15} |
Activate/deactivate BoM line |
| Direct ref | {"product_ref": "module.product_xmlid", ...} |
Fixed materials without variants |
| PTAV resolution | {"product_tmpl_ref": "...", "param_attribute_map": {...}} |
Design param -> attribute -> variant |
Conditionally adds workorders to the MO.
{"workcenter_ref": "module.wc_assembly", "duration_formula": "width * 0.5", "sequence": 10}All industry-specific parameters live in fields.Properties, following the pattern from product_electrical_properties. Only width, height, and thickness remain as real fields.
<records>
<properties code="bags" name="Bags - Standard">
<items name="bag_type">
<item name="type">selection</item>
<item name="selection">[["sleeve","Sleeve"],["sheet","Sheet"]]</item>
<item name="default">sleeve</item>
</items>
<items name="has_tie">
<item name="type">boolean</item>
<item name="default">false</item>
</items>
</properties>
</records>design_context = {
"width": lot.width, "height": lot.height, "thickness": lot.thickness,
"qty": production.product_qty,
**{k: v for k, v in (lot.design_params or {}).items()},
}
# -> {"width": 600, "bag_type": "sleeve", "has_tie": True, ...}Design parameters map to product attributes, enabling automatic variant selection:
design_params.color = "FF0000"
| param_attribute_map: {"color": "module.attr_color"}
v
attribute: Color / value: "FF0000"
| _get_variant_for_combination(ptav)
v
product.product: Ink + Color/FF0000
Values in design_params must match product.attribute.value.name exactly.
Each semi-finished product gets its own lot with parameters extracted from the parent lot:
End product lot_1: {bag_type, has_tie, width, color, thickness}
MO Level 1
|
+-- Film (semi-finished)
| lot_2: {bag_type, width, color, thickness}
| <- extracted via param_extraction_map
| MO Level 2
| |
| +-- Resin mto_stop=True -> PO
|
+-- Tie mto_stop=True -> PO or stock
lot_3: {tie_length: height + 50}
<- transformation via safe_eval
mto_stop |
Behavior |
|---|---|
False |
Creates new MO + child lot, recursion continues |
True |
Searches stock for matching lot -> if none -> PO with parameters |
1. Select design_param_definition_id on BoM -> lot shows correct Properties
2. Select matrix_template_id -> press [Load from Template]
3. Edit the 4 JSON tables with ace_editor -> changes are per-BoM
4. Template is NEVER edited directly
Templates are shipped by industry submodules. The customer copies and customizes.
| Field | Type | Description |
|---|---|---|
code |
Char |
Unique code: 'bags', 'roller_door' |
name |
Char |
Human-readable name |
industry |
Char |
Grouping/filtering |
parent_id |
Many2one -> self |
Inheritance |
design_params_definition |
PropertiesDefinition |
Parameter schema |
| Field | Type | Description |
|---|---|---|
name |
Char |
Template name |
industry |
Char |
For filtering |
constraint_table |
Json |
T0 rules (GoRules format) |
geometry_table |
Json |
T1 rules |
material_table |
Json |
T2 rules |
operation_table |
Json |
T3 rules |
| Field | Type | Description |
|---|---|---|
design_param_definition_id |
Many2one |
Parameter set for this BoM |
matrix_template_id |
Many2one |
Template reference (read-only) |
constraint_table |
Json |
T0 -- editable copy |
geometry_table |
Json |
T1 -- editable copy |
material_table |
Json |
T2 -- editable copy |
operation_table |
Json |
T3 -- editable copy |
| Field | Type | Description |
|---|---|---|
matrix_coeff_rule |
Char |
Reference to T2 row for coefficient |
coeff_default |
Float |
0.0 = O-variant, 1.0 = real |
product_tmpl_id |
Many2one |
For PTAV resolution |
param_attribute_map |
Json |
{design_key: attr_external_id} |
param_extraction_map |
Json |
{child_key: source_or_formula} |
child_definition_id |
Many2one |
Child lot parameter set |
mto_stop |
Boolean |
Stops MTO chain at this level |
| Field | Type | Description |
|---|---|---|
width / height / thickness |
Float |
Real fields (from _dim module) |
design_param_definition_id |
Many2one |
Parameter set |
design_params |
Properties |
All industry parameters |
mrp_design_matrix/
models/
mrp_design_param_definition.py
mrp_matrix_template.py
mrp_bom.py
mrp_bom_line.py
mrp_production.py
stock_lot.py
views/
mrp_bom_views.xml
mrp_matrix_template_views.xml
mrp_design_param_definition_views.xml
data/
base_param_definitions.xml
mrp_design_matrix_{industry}/
data/
design_param_definitions.xml
install_design_params.xml
matrix_templates/*.json
demo/
demo_bom_{industry}.xml
- Odoo: 18.0 (Community and Enterprise)
- License: AGPL-3
- Dependencies: OCA/Odoo CE modules only
- Rule engine:
zen-engine(Rust + Python bindings, <1ms latency, embeddable) - Pre-commit:
ruff,black, OCA checks - Tests: Required for every module
| Phase | Duration | Key Result |
|---|---|---|
| Phase 0 -- Foundation | 2 weeks | forced_lot PRs in OCA |
| Phase 1 -- Bridge | 1 week | Formula module sees Properties |
| Phase 2 -- Core models | 2 weeks | All models + UI |
| Phase 3 -- Core logic | 2 weeks | Full MO algorithm end-to-end |
| Phase 4 -- Industry submodules | 3 weeks | 5 industry packages |
| Phase 5 -- OCA submission | 1 week | PR submitted |
| Risk | Probability | Mitigation |
|---|---|---|
| OCA review requires significant changes | Medium | Early precheck with OCA maintainer |
| GoRules doesn't cover all T0 cases | Low | cDMN as fallback for constraints |
| PTAV matching requires exact name match | High | Validation on design_params save |
| Industry templates are incomplete | Medium | Demo data + documentation for extension |
Author: Rosen Vladimirov <vladimirov.rosen@gmail.com>
Inspired by: product_electrical_properties (Properties pattern)
This project is dual-licensed: AGPL-3.0 by default, or a commercial license — see LICENSE-COMMERCIAL.md and DUAL_LICENSING.md (contact: vladimirov.rosen@gmail.com).