ADR 0008: Assembly-MRP Process with SA Governance¶
Status¶
Draft
Date¶
2026-06-18
Context¶
dirac-odoo already defines:
- SA governance model (ADR 0005): SAs govern POS transactions, with agents (cashiers), SA managers, and three-tier reporting.
- Fleet/ITEM model (ADR 0004):
ov.fleet,ov.fleet_item, and native Odoostock.production.lot(serialized ITEM). - Asset registry (ADR 0007):
ov.asset.registryandov.asset.componentcreated at MO completion.
The assembly-MRP process (CKD → FG transformation) is currently documented in asset-lifecycle repo (ADR 0001, ADR 0002). But the Odoo implementation needs SA-governed access control, following the same pattern as POS:
- Assembly line = SA (like a retail shop)
- Assembly cell = sub-SA or team within the line
- Line worker = agent (like cashier)
- Line manager = SA manager (like shop manager)
This ADR defines the SA governance layer for assembly-MRP, and how it produces ITEM-level asset tracking.
Decision¶
Adopt the SA governance model for assembly-MRP processes.
The assembly line and its cells are organized as SAs. Line workers and line managers have the same organizational structure as sales agents and sales managers in the POS model.
1. Assembly as MRO Function — MRP Process Reference¶
Design Principle¶
Assembly is a manufacturing resource planning (MRP) function, not a sales function. It references Odoo MRP processes and models directly.
Odoo MRP Models Used¶
| Odoo Model | Role in Assembly |
|---|---|
mrp.production |
Manufacturing order (MO) — the assembly work order |
mrp.bom |
Bill of Materials — CKD package → FG transformation |
mrp.bom.line |
BoM line — CKD component consumption |
stock.move |
Stock movement — CKD consumed, FG produced |
stock.lot |
Lot (CKD) and serial (FG) tracking |
stock.quant |
On-hand quantity at each stock location |
stock.location |
OVT/CKD Materials, OVT/Assembly, OVT/Test, OVT/Quality, OVT/Finished Goods |
Assembly Flow (MRP-Native)¶
- Plan: MRP run or manual MO creation
- Issue: CKD lot issued to assembly line (stock move:
OVT/CKD Materials→OVT/Assembly) - Assemble: Capture component serials (
chassis_id,motor_id,mcu_id) - Test: Assembly line test (stock move:
OVT/Assembly→OVT/Test) - Quality: Quality check (stock move:
OVT/Test→OVT/Quality) - Package: Packaging (stock move:
OVT/Quality→OVT/Finished Goods) - Complete: MO completion → FG serial created →
ov.asset.registryrecord created
2. Assembly Generates NEW Item — BoM Consumption + Stock Transfer¶
Design Principle¶
Assembly generates a new ITEM (serialized FG asset) by consuming BoM items (CKD package). Stock transfer happens within the assembly process.
What Happens at Assembly¶
| Step | Physical Action | Odoo Transaction | ITEM-Level Effect |
|---|---|---|---|
| CKD issued | CKD lot moves to assembly line | stock.move: OVT/CKD Materials → OVT/Assembly |
CKD lot still lot-tracked |
| Component captured | chassis_id, motor_id, mcu_id revealed |
Recorded in MO or custom form | Component identity formed |
| FG produced | Assembly complete → FG serial created | mrp.production done → stock.lot (serial) created |
NEW ITEM exists in system |
| Registry created | Asset birth certificate issued | ov.asset.registry record created |
ITEM now governed |
| FG put away | FG moves to finished goods | stock.move: OVT/Assembly → OVT/Finished Goods |
ITEM now available for sale |
Stock Transfer Chain¶
OVT/CKD Materials → OVT/Assembly → OVT/Test → OVT/Quality → OVT/Finished Goods
(CKD lot) (WIP) (WIP) (WIP) (FG serial)
Each transfer is an Odoo stock.move. The assembly line SA governs which locations are accessible to which workers.
BoM Consumption¶
The BoM (mrp.bom) defines:
- CKD package (lot-tracked input)
- FG motorcycle (serial-tracked output)
- Component serialization (captured at assembly, not pre-recorded)
BoM consumption happens at MO confirmation/completion. The CKD lot is consumed, and the FG serial is produced.
3. Model at ITEM Level — FLEET, ITEM, Odoo Serial¶
Design Principle¶
Asset tracking is modeled at ITEM level (serialized asset).
ov.fleet,ov.fleet_item, and Odoostock.production.lot(serial) are the basis.
ITEM Identity Stack¶
| Layer | Model | Description |
|---|---|---|
| Conceptual | asset-lifecycle repo |
Lifecycle rules, warranty scoping |
| Registry | ov.asset.registry |
Stable asset identity anchor |
| Component | ov.asset.component |
Component lineage and replacement history |
| ITEM | stock.production.lot (serial) |
Odoo-native serialized ITEM |
| Fleet | ov.fleet + ov.fleet_item |
Operational grouping (survives location change) |
| SA Governance | ov.sa_fleet + ov.actor_sa_fleet |
Which SA governs which fleet/ITEM |
How ITEM Enters SA Governance¶
After assembly (MO completion + ov.asset.registry creation), the ITEM (FG serial) may be:
- Assigned to a fleet (
ov.fleet_item) - Assigned to an SA (
ov.sa_fleet) - Transferred to sales (stock move to retail location)
- Sold to customer (POS transaction, SA-governed via
ov.sa_pos_order)
The SA governance chain:
SA (assembly line) → ov.sa_fleet → ov.fleet → ov.fleet_item → stock.production.lot (ITEM)
This is the same pattern as POS:
- POS: SA → ov.sa_pos_order → ov.sa_pos_order → pos.order
- Assembly: SA → ov.sa_fleet → ov.fleet → ov.fleet_item → stock.production.lot
4. SA Governance for Assembly-MRP¶
Design Principle¶
Follow the same SA governance model as POS. Assembly line + cells organized as SAs. Line workers and line managers = sales agents and sales manager analog. SA context for each MO is recorded in association tables (
ov.sa_mo,ov.actor_sa_mo), not inmrp.productionitself (per ADR 0003).
SA Structure for Assembly¶
| SA Level | Analog | Role |
|---|---|---|
| Assembly Line SA | Retail shop SA | Governs one assembly line |
| Assembly Cell (sub-SA) | Shop team | Governs one cell within the line (optional) |
| Line Worker | Cashier (agent) | Executes assembly on assigned MOs |
| Line Manager | SA Manager | Manages line, aggregates reports |
SA Governance Rules for Assembly¶
| Rule | Description |
|---|---|
| SA governs MO via association table | Every MO has a corresponding ov.sa_mo row linking it to its governing SA + assigned worker |
| Worker accesses assigned MOs | Worker (agent) sees only MOs linked to them in ov.actor_sa_mo (or unassigned within SA) |
| Manager aggregates | Line manager (SA manager) sees all MOs within the line |
| Parent SA rolls up | Parent SA (regional) sees roll-up over child SAs (multiple lines) |
| Fleet/ITEM scoped to SA | Fleets/ITEMs produced by this line are governed by this SA (until transfer) |
Association Tables for Assembly SA¶
| Table | Purpose | Points To |
|---|---|---|
ov.sa_fleet |
Which SA governs this fleet? | ov.serviced_account + ov.fleet |
ov.actor_sa_fleet |
Which actor (worker) is assigned to this fleet? | ov.sa_fleet + res.partner |
ov.sa_mo (future) |
Which SA governs this MO? | ov.serviced_account + mrp.production |
ov.actor_sa_mo (future) |
Which worker is assigned to this MO? | ov.sa_mo + res.partner |
SA Context Recorded via Association Tables¶
When an MO is created, a corresponding row is inserted into ov.sa_mo linking the MO to its governing SA. When a worker is assigned, a row is inserted into ov.actor_sa_mo. No fields on mrp.production are modified.
SA context row in ov.sa_mo¶
Each row in ov.sa_mo links one MO to its governing SA:
# ov.sa_mo row (association table — does NOT modify mrp.production)
{
'sa_id': ov.serviced_account.id, # Which SA (assembly line) governs this MO
'mo_id': mrp.production.id, # Which MO
'created_at': datetime, # When the MO was created
}
Worker assignment is a separate row in ov.actor_sa_mo:
# ov.actor_sa_mo row (association table)
{
'sa_mo_id': ov.sa_mo.id, # Link to the SA-MO association
'actor_id': res.partner.id, # Which worker is assigned to this MO
'assigned_at': datetime, # When the worker was assigned
}
5. Assembly Worker Identity — System Level + SA Context Level¶
Design Principle¶
All assembly workers are identified at both system level and SA context level. Context of each action is recorded via
ov.actor_sa_morows (per ADR 0003: no modification to native Odoo models).
Two-Level Identity (Same as POS)¶
| Level | What It Means | Odoo Mechanism |
|---|---|---|
| System level | Worker has an Odoo identity (res.partner + ov.membership) | res.users login, res.partner record |
| SA context level | Worker is a member of specific assembly SAs with defined role | ov.membership record, ov.actor_sa_* records |
Worker Action Context: recorded in ov.actor_sa_mo¶
Every assembly action (MO start, component capture, MO complete) triggers a row in ov.actor_sa_mo (or ov.sa_mo_event for action-level granularity). The SA + actor are never written to mrp.production.
Example ov.actor_sa_mo row for worker assignment:
# ov.actor_sa_mo row (association table — no modification to mrp.production)
{
'sa_mo_id': ov.sa_mo.id, # Link to the SA-MO association
'actor_id': res.partner.id, # Which worker
'assigned_at': datetime, # When assigned
}
For action-level tracking (optional future extension), ov.sa_mo_event rows capture:
{
'sa_mo_id': ov.sa_mo.id,
'actor_id': res.partner.id,
'action': 'start' | 'capture_component' | 'complete',
'timestamp': datetime,
}
6. Reporting Privileges — Three Tiers (Same as POS)¶
Design Principle¶
Each worker can view history data within an SA context. Aggregation among members of an SA is the privilege of the SA Manager only. Aggregation along the SA tree is the sole privilege of the parent SA over its child-SAs.
Tier 1: Worker — View Own + Assigned¶
A worker (assembly line agent) can view:
- Their own MO history within the SA
- MOs explicitly assigned to them (if
scope_policy='assigned_only') - Unassigned MOs within the SA (if
scope_policy='assigned_plus_unassigned')
They cannot see other workers' MOs within the same SA (unless scope_policy='sa_wide').
Tier 2: SA Manager — Aggregate Within SA¶
The SA Manager (ov.membership.is_sa_manager=True) can:
- View all MOs within their SA (regardless of which worker executed them)
- Generate aggregate reports (total units produced, yield rate, defect rate)
- Export data for analysis
- Manage membership (add/remove workers, change roles)
They cannot see MOs in other SAs (unless those SAs are child-SAs).
Tier 3: Parent SA — Aggregate Over Child-SAs¶
A parent SA (assembly regional SA with multiple lines) can:
- View MOs across all child-SAs in the SA tree
- Generate roll-up reports ("total production across all lines")
- Compare performance across child-SAs
7. ITEM Production Tracking — Yield, Quality, Productivity¶
Design Principle¶
The birth certificate (ADR 0007) captures what was produced. The SA governance layer captures who, where, and how efficiently.
Production Data Captured at ITEM Level¶
| Data | Source | Purpose |
|---|---|---|
mo_id |
mrp.production |
Which MO produced this ITEM |
sa_id |
ov.sa_mo |
Which assembly line produced it |
actor_id |
ov.actor_sa_mo |
Which worker produced it |
yield_rate |
MO time tracking | How efficiently |
quality_result |
Quality check | Pass/Fail/Rework |
component_ids |
ov.asset.component |
Which components were used |
This data enables:
- Productivity tracking: Which worker/line is most efficient?
- Quality tracking: Which worker/line has highest defect rate?
- Traceability: If a defect is found, which worker/line produced the affected ITEMs?
8. SA Scope = ACL (Same as POS)¶
Design Principle¶
SA scope is a field decision, but carries heavy responsibility — because this defines ACL.
What "Field Decision" Means for Assembly¶
The operations/field team decides:
- How many assembly line SAs to create (one per line? per shift? per product model?)
- Which workers belong to which line SA
- What
scope_policyeach membership has
These are business decisions, not technical decisions. But they have security consequences — because SA scope = access scope.
SA Scope IS ACL¶
There is no separate "ACL configuration" step. The act of:
- Creating an SA (
ov.serviced_account) for an assembly line - Adding a worker to it (
ov.membership) - Setting
scope_policyon the membership
... is the ACL configuration. The ir.rule (Track B in ADR 0006) then enforces this ACL on every query.
9. Open Questions¶
9.1 Multi-Line Worker¶
Can a worker belong to multiple assembly line SAs? (Yes — like a cashier with multiple shop memberships.)
9.2 Cross-Line Material Transfer¶
Can CKD materials be transferred between assembly line SAs? (Yes — but must be governed by a parent SA or material management SA.)
9.3 Quality Check SA¶
Is quality check a separate SA, or part of the assembly line SA? (Open — may be separate for independence.)
9.4 ITEM Transfer Between SAs¶
When an ITEM (FG serial) is transferred from assembly SA to sales SA, how is SA context updated? (Likely: new ov.sa_fleet record for the sales SA, old one marked inactive.)
Consequences¶
- Assembly line = SA — same governance model as POS, consistent across field operations
- Worker identity is two-level — system (Odoo user) + SA context (membership), action context recorded via
ov.actor_sa_morows` - ITEM-level tracking —
ov.fleet,ov.fleet_item,stock.production.lotare the basis; SA governance attaches viaov.sa_fleet - MO context via association tables — every MO has a
ov.sa_morow linking SA + actor; association tables enforce subset rule - Reporting is three-tier — worker sees own, SA manager sees all in SA, parent SA sees roll-up over children
- SA scope = ACL — field team decides scope, but this directly defines access control; heavy responsibility
- Birth certificate + productivity —
ov.asset.registrycaptures asset identity; SA governance captures who/where/how efficiently
Cross-Reference¶
- Conceptual model:
asset-lifecycle/docs/adr/0001-ckd-to-fg-transformation-and-asset-identity-regime.md - Asset registry implementation:
dirac-odoo/docs/adr/0007-asset-registry-and-component-model-implementation.md - SA governance (POS analog):
dirac-odoo/docs/adr/0005-v3-pos-applet-design.md - Fleet/ITEM model:
dirac-odoo/docs/adr/0004-fleet-and-item-governance-layer.md - Association model pattern:
dirac-odoo/docs/adr/0003-v3-association-model-migration-pattern.md