Skip to content

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.location
  • stock.quant
  • stock.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.location answers:
  • where is the item physically now
  • ov.fleet answers:
  • 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 A but is currently at a repair warehouse
  • a motorcycle belongs to rental fleet B but is temporarily held by a technician
  • an item belongs to swap pool C but 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.location when the physical location itself is the real business boundary
  • use ov.fleet when 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_kind
  • association_role
  • date_start
  • date_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_fleet
  • ov.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:

  • SA scope attaches to fleet through ov.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_fleet for 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 V3 rather than reverting to stamp-first design.
  • ov.fleet is justified only for operational groupings that must stay stable independently from current physical location.
  • stock.location remains 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.asset remains unnecessary as the default fleet/item governance wrapper.