ADR 0004: Fleet And Item Governance Layer¶
Status¶
Accepted
Date¶
2026-05-13
Context¶
dirac-odoo already established the following durable guidance:
- native Odoo inventory models remain the source of truth for physical asset identity and logistics
- new SA-governed visibility scope should default to explicit association models under ADR 0003 - V3 Association-Model Migration Pattern
- asset/item visibility should stay sparse and should not force every serialized asset into direct SA stamping
The updated fleet/item design note adds an important refinement:
- some operational asset groupings must remain stable even when an item's physical location changes
- a customer or operating fleet may therefore need to remain distinct from the item's current warehouse or repair location
- one fleet may also participate in more than one SA-governed stewardship relationship, but those relationships do not all mean the same thing
The repo therefore needs a durable decision for when a dedicated fleet layer is
justified, how it relates to serialized items, how SA scope is attached, and
how that design stays compatible with V3.
Decision¶
1. ITEM identity remains native Odoo serialized inventory¶
The physical ITEM remains a native Odoo serialized asset record.
For repo terminology, use:
stock.production.lot
If a target Odoo version uses stock.lot naming instead, treat it as the
version-local equivalent rather than as a different architectural concept.
Native Odoo also remains the source of truth for:
- current physical location
- stock position
- movement history
- inventory operations
2. A dedicated ov.fleet layer is allowed when operational grouping must remain independent from current physical location¶
Introduce ov.fleet only when the business needs an operational or governance
grouping that should survive physical movement across locations.
Examples:
- customer rental fleet
- delivery fleet
- maintenance reserve fleet
- battery circulation pool
This does not make ov.fleet a universal wrapper for all assets.
If a use case is already correctly modeled by a governed stock.location, do
not add ov.fleet merely because assets exist there.
The justification for ov.fleet is specifically that:
- physical location answers
where is the item now - fleet membership answers
which governed operational grouping does the item belong to
Those are different questions and may legitimately diverge.
3. Physical location and fleet membership are separate dimensions¶
Physical location remains native Odoo truth through models such as:
stock.locationstock.quantstock.move.line
Fleet membership is a separate explicit relationship.
An item may therefore:
- belong to fleet
F - while currently being stored, repaired, or processed at location
L
Do not infer fleet membership from current location when the business meaning of fleet must survive location changes.
3.1 Why stock.location alone is sometimes insufficient¶
This is the main developer onboarding trap in the fleet/item design.
At first glance, using only native Odoo location models may appear simpler. That instinct is valid, and it should be challenged explicitly rather than ignored.
However, stock.location and ov.fleet answer different questions:
stock.locationanswers:where is the item physically nowov.fleetanswers:which operational or governance grouping does this item belong to
Those questions may align in simple cases, but they do not always align in real operations.
Examples:
- a battery belongs to customer fleet
Abut is currently at a repair warehouse - a motorcycle belongs to rental fleet
Bbut is temporarily held by a technician - an item belongs to swap pool
Cbut is physically in transit
If implementation collapses fleet into physical location, then ordinary logistics movement can accidentally rewrite governance meaning.
That would create the wrong semantic effect:
- moving an item for repair would appear to remove it from its fleet
- temporary custody would appear to redefine operational belonging
- warehouse semantics would become overloaded with stewardship semantics
Therefore:
- use governed
stock.locationwhen the physical location itself is the real business boundary - use
ov.fleetwhen the grouping must remain stable across location changes
4. Item-to-fleet membership should be modeled as an explicit association with one current fleet at a time¶
Use an explicit association model between:
- ITEM (
stock.production.lot) ov.fleet
Preferred naming:
ov.fleet_item
This association is the record of current fleet membership.
The current-state integrity rule is:
- one ITEM may belong to at most one fleet at a time
However, do not use a design that blocks needed membership history by accident.
Therefore:
- if only current state matters, one-row-per-item is acceptable
- if historical reassignment must be preserved, use effective dates or state plus a uniqueness rule on the current row only
Do not claim temporal history support while also using a plain
UNIQUE(item_lot_id) across all rows.
5. SA governance over fleets must follow the V3 association pattern¶
SA governance over a fleet should be expressed by an explicit SA-association
model that references ov.fleet.
Preferred naming:
ov.sa_fleet
The fleet-to-SA association should carry explicit metadata such as:
association_kindassociation_roledate_startdate_end- provenance or source references where needed
This keeps the design aligned with ADR 0003:
- native Odoo objects stay native
- governance stays externalized
- the relationship itself remains the source of truth
6. Multiple SA associations to one fleet are allowed, but visibility must not be inferred from every association kind¶
A fleet may participate in more than one SA relationship.
Examples may include:
- operator
- maintenance provider
- financier
- legal owner
- tracking manager
This is acceptable only if the semantics are kept explicit.
Important rule:
- not every fleet-to-SA association implies PA visibility or operational scope
Therefore implementation must distinguish:
- access-bearing or scope-bearing associations
- non-access stewardship or reference associations
PA visibility should only derive from the association kinds that are explicitly defined to grant governed scope.
7. Actor scope, when needed, should be subordinate to SA fleet scope¶
If actor-level handling inside one SA is needed for a fleet, model it as a second layer attached to the SA-fleet association, not directly to the fleet or native item.
Preferred pattern:
ov.sa_fleetov.actor_sa_fleet
This preserves the ADR 0003 subset rule:
- actor reach must remain a subset of SA reach
Do not place direct actor-governance fields on ov.fleet or
stock.production.lot merely for convenience.
8. Item-to-SA scope should derive through the fleet by default¶
When an item is already governed through fleet membership, do not add a second independent SA relationship to the item by default.
Default visibility path:
SAscope attaches to fleet throughov.sa_fleet- item belongs to fleet through
ov.fleet_item - item visibility is derived through that chain
This avoids duplicate governance edges and keeps the governed surface at the collection level where possible.
9. Direct item-level SA association is a documented exception, not the default¶
Add direct item-level SA association only when fleet-derived scope is insufficient.
Examples of possible exceptions:
- the item is intentionally governed outside any fleet
- the item must remain SA-scoped during a liminal workflow where fleet membership is absent or intentionally broken
- item-level lifecycle rules have governance meaning that fleet membership does not express
When such an exception is needed, document it explicitly and keep it aligned
with ADR 0003.
10. ov.asset remains unnecessary as the default fleet/item governance object¶
This ADR does not revive a generic ov.asset wrapper as the default asset
governance layer.
For the fleet/item problem:
- ITEM identity is already native in
stock.production.lot - physical location is already native in
stock.location - fleet membership is explicit in
ov.fleet_item - SA governance is explicit in
ov.sa_fleet
That is sufficient unless a later design proves that a broader asset wrapper adds distinct, durable semantic value beyond those relationships.
11. Fleet governance remains separate from ABS service lifecycle¶
ov.fleet and its associations are Odoo-side governance and operational
grouping constructs.
They are not:
- subscription state
- entitlement state
- billing lifecycle
- ABS runtime FSM
- commercial contract state
Those concerns remain in their respective repos and systems.
12. Querying should start from fleet governance associations, not from the full item table¶
For fleet-scoped or fleet-derived PA views:
- start from
ov.sa_fleetfor the current SA context - join to
ov.fleet_item - join to
stock.production.lot - join to physical-location tables only as needed
Do not start from the entire serialized-item table and filter governance later in Python or ad hoc controller logic.
Consequences¶
- The repo now has a durable pattern for fleet-governed items that stays
additive to
V3rather than reverting to stamp-first design. ov.fleetis justified only for operational groupings that must stay stable independently from current physical location.stock.locationremains physical logistics truth, not the universal substitute for every fleet concept.- One item may have only one current fleet membership at a time, but the data model must preserve history intentionally when history matters.
- Multiple SA relationships to one fleet are allowed, but visibility must be derived only from explicitly access-bearing association kinds.
- Default item scope should be fleet-derived rather than duplicated at the item level.
ov.assetremains unnecessary as the default fleet/item governance wrapper.