Serviced Account Mini Model¶
Purpose¶
This document defines the current v0 Serviced Account model for Odoo inside the DIRAC framework.
It is now the primary developer-facing design document for SA governance.
Read this document as the V0 layer in the progressive SA design.
For the intended progression of V0 → V1 → V2, see
SA Design Refinement Layers.
Terminology in this document follows the repo definitions in
Terminology, especially Serviced Account (SA),
Portal App (PA), actor, and sa.
The SA governance kernel currently covers three data domains:
- Accounts
- People through memberships
- Assets through fleet/items
1. Core Principle¶
Governance is separate from ERP transaction mechanics.
| Domain | Responsibility |
|---|---|
| Odoo ERP | Transactions, stock movements, serial tracking, accounting, logistics |
Serviced Account (SA) |
Governance boundary for access, assignment, and authority |
Portal App (PA) |
SA-governed portal form of OVApp; user flow, context establishment, filtering, and proxy execution |
The SA model is a governance model, not a transaction model.
SA does not replace native Odoo transaction behavior.
Native Odoo remains the system of record for:
- customers in
res.partner - sales orders in
sale.order - invoices in
account.move - payments and accounting behavior
- inventory execution and stock movements
The ov.* domain adds governance structure around those native objects.
2. SA Modeling Framework¶
The model should be read in three segments.
| Segment | Core Question | Primary v0 Object |
|---|---|---|
| Accounts | What governed organization context exists? | ov.serviced_account |
| People | Who may act in that context, and with what authority? | ov.membership |
| Assets | What fleet/items are governed within that context? | ov.asset wrapper over native Odoo physical objects |
Current design status:
- Accounts: structurally defined for v0
- People: structurally defined for v0 and implementation-driving
- Assets: governance direction is now explicit through an SA asset wrapper model
This V0 layer should be understood as the minimum pattern that enables PA
surfaces such as:
My QuotesMy SalesMy Tickets
Later layers may refine how hierarchy and durable-object visibility work, but they build on top of this base rather than replacing it.
3. PA Access Model¶
PA is the entry point for SA-governed operations.
In this design, PA is not separate from OVApp.
It is the OVApp surface operating under SA login governance and SA-context selection.
3.1 Login¶
- login is SA-agnostic
- a PA user is any previously registered person with at least one SA membership
3.2 Context Flow¶
The canonical PA flow is:
LoginMy SAsMy Apps
If the user has only one SA, that SA may be auto-selected.
3.3 Operating Context¶
When a user enters an app, PA establishes:
actorsa
This {actor, sa} context is the basis for authorization, attribution, and filtering.
4. Accounts Segment¶
4.1 What Counts as a Serviced Account¶
For v0, a Serviced Account is an organization context that receives governed services, delegated access scope, or account-level operational governance.
This may include:
EXTCfor a true external client/customer organizationOVACfor an affiliated or subsidiary organization that still needs governed service/account treatment
This does not mean all internal Omnivoltaic org units should automatically be modeled as Serviced Accounts.
Working rule:
- if the organization is treated as an account subject of service/governance, it may be a Serviced Account
- if the organization is purely an internal operating structure, it should remain in normal Odoo company/org constructs unless a later need proves otherwise
4.2 ov.serviced_account¶
| Attribute | Type | Purpose | v0 Guidance |
|---|---|---|---|
name |
Char | Human-readable account name | Required |
partner_id |
Many2one(res.partner) |
Canonical organization partner record | Required; exactly one is_company = true partner per SA |
account_class |
Selection | Distinguish OVAC vs EXTC at minimum |
Required |
parent_id |
Many2one(ov.serviced_account) |
Hierarchy / branch structure | Required in practice; root nodes are Omnivoltaic companies |
child_ids |
One2many(ov.serviced_account) |
Convenience inverse for hierarchy | Derived |
state |
Selection | Governance lifecycle state | Start with active, inactive |
notes |
Text | Short admin/governance note | Optional; non-authoritative |
4.3 Account Invariants¶
- A Serviced Account is an organization context, not a person.
- A Serviced Account is the primary governance wall.
- Hierarchy belongs on
ov.serviced_account, not onres.partner.parent_id. - Governance structure must not duplicate CRM, accounting, or company setup.
- An SA is created in hierarchy context, not born without parent context.
4.4 SA Hierarchy Direction¶
Current rule:
SA_ROOTis the system root SA- each
SA<x>is a proper SA underSA_ROOT - every non-root SA is created from a parent SA
- in normal operation, the SA hierarchy should be mirrored by
res.partner.parent_idon the anchor company-partner records, but that mirror is a soft-alignment rule rather than a hard invariant for now
5. People Segment¶
This is the most settled part of the current SA design.
5.1 Core Decision¶
People are anchored in res.partner, but authority never lives on res.partner alone.
Authority is carried by the relationship between the person and the Serviced Account.
That relationship is modeled as ov.membership.
5.2 Why the People Model Works This Way¶
The same person may appear once in Odoo but act in multiple governed contexts.
Example:
- the person is an employee/member inside an
OVACserviced account - the same person is also designated as account manager or sales-facing actor for an
EXTCserviced account
Therefore:
- role meaning cannot safely live on
res.partner - permissions must stay isolated by account context
- authorization must be evaluated in account context, never globally by person
5.3 ov.membership¶
| Attribute | Type | Purpose | v0 Guidance |
|---|---|---|---|
serviced_account_id |
Many2one(ov.serviced_account) |
Governed account context | Required |
person_partner_id |
Many2one(res.partner) |
Person acting in that context | Required; person only |
role_code |
Char or Selection | Optional local role label in this account context | Do not treat as the primary authority model |
membership_state |
Selection | Membership status | Start with active, suspended, revoked |
scope_policy |
Selection | Scope of authority | Keep simple in v0 |
effective_from |
Datetime | Optional activation time | Optional |
effective_to |
Datetime | Optional expiry time | Optional |
5.4 People Invariants¶
- A person may have multiple memberships across multiple Serviced Accounts.
- Role semantics belong to
ov.membership, not directly tores.partner. - Membership is contextual.
- Membership in one SA does not imply rights in another SA.
- Platform/system administration is separate from SA-context authority.
- Membership hierarchy, not semantic role labels, is now the primary authority and reporting structure inside an SA.
- At any point in time, a person should be associated with one company only on the Odoo side. This is an accepted temporary design weakness while the Odoo company model and the SA model continue to converge.
5.5 Hierarchy Direction¶
The v0 shorthand role labels are no longer the primary way to understand SA authority.
Current direction:
- authority inside one SA follows the explicit membership tree
- the current
sa_manageris the root of that membership tree - any member with subordinates may roll up team-level reporting inside the current SA
- only the current
sa_managermay roll up from the current SA into descendant child-SAs - local role labels may still exist for business description, but they should not be used as the primary authority model
5.6 People Design Already Settled¶
The discussions so far establish these current decisions:
res.partneris the identity anchor for the person.ov.membershipis the authority-bearing relationship.- One person can participate in both
OVACandEXTCcontexts without schema breakage. - Context-specific roles are valid even when the same person appears in multiple governance contexts.
- Sensitive cross-company combinations must be reviewable and auditable, but should not be hard-blocked in the base schema.
- Proxy execution must preserve attribution to the real human actor separately from the technical Odoo user.
- Explicit hierarchy is now the primary authority and reporting structure; old
shorthand labels such as
admin,staff, andagentare no longer the semantic model of record.
5.7 Identity and Proxy Implications¶
Keep these layers separate:
- identity source: Teams / Microsoft-based identity, local portal identity, or other future IdP
- person anchor in Odoo:
res.partner - execution identity in Odoo: proxy/internal
res.users - governance authority:
ov.membership
Proxy assumption:
- proxy
res.usersperforms technical execution - real human actor remains attributable separately
- serviced-account context remains explicit for audit and authorization
6. Governance Stamp Pattern¶
PA-governed records carry a permission stamp:
actorsa
Represented together as:
{actor, sa}
Interpretation:
{actor = null, sa = X}means shared or unassigned within SAX{actor = A, sa = X}means assigned to actorAwithin SAX
The current implementation direction is to apply this stamp pattern to Odoo records touched through PA-proxy, including:
res.partner- quotation / sales records
- product / asset-related records
- SA-native governance records
7. Visibility Model¶
7.1 Base Rule¶
A record is visible in PA only when:
record.sa == current_sa
7.2 Actor Rule¶
Inside the SA boundary:
sais the inclusive selectoractoris the exclusive selector
7.3 Policy Shapes¶
Visibility is defined per app.
Current policy shapes:
assigned_only- show only records where
actor == current_actor assigned_plus_unassigned- show records where
actor == current_actororactor is null sa_wide- show all records within the current SA
7.4 Current Role Effect on Visibility¶
For now, visibility and reporting should be understood primarily from hierarchy position:
- ordinary visibility remains scoped first by
sa - actor-level visibility remains app-specific
- any member with subordinates may get direct-report and recursive team reporting within the current SA
- the current
sa_managermay additionally get SA-level roll-up from the current SA through descendant child-SAs
Likely PA surfaces include:
My TeamMy Sales by TeamMy District Sales
These should be implemented as Odoo reporting objects surfaced through PA, not as separate SA-native aggregation logic.
8. Membership Integrity¶
If actor is not null, then that actor must be a valid member of the stamped SA.
Invalid example:
{actor = A, sa = X}- actor
Ais not a member of SAX
This is an integrity failure.
8.1 Membership Removal Rule¶
If membership between actor and sa is removed, integrity must be restored by:
- setting
actor = null
8.2 Reassignment Paths¶
Two reassignment paths are accepted:
- direct reassignment from actor
Xto actorY - indirect normalization to
actor = nullwhen membership removal breaks integrity
9. Assets Segment¶
Asset governance is now modeled through an explicit SA-native wrapper object.
9.1 Core Asset Model¶
Introduce:
ov.asset
This is the governance/application object PA should work with for assets.
It is not the raw physical Odoo object.
9.2 Wrapper Purpose¶
ov.asset gives business and governance meaning to an underlying physical Odoo object.
At minimum it should carry:
namedescriptionasset_classasset_refserviced_account_id- governance/audit fields including
{actor, sa}semantics
Example:
Name: Nairobi Swap Network 1Description: a group of stations on the west part of Nairobi ...
9.3 Type-Dependent Physical Reference¶
asset_ref is type-dependent by asset_class:
FLEET -> stock.locationITEM -> stock.production.lot
This means:
- fleet / operational grouping aligns with
stock.location - serialized item aligns with
stock.production.lot
9.4 Nullability¶
For flexibility:
serviced_account_idmay be nullasset_refmay be null
This supports limbo or revoked-affiliation states where the wrapper still exists but is not currently affiliated to an SA or bound to a current physical reference.
9.5 Governance Rule¶
{actor, sa} should live on the wrapper object.
This means PA primarily governs assets through ov.asset, not by stamping only the raw Odoo physical models.
9.6 Ownership Interpretation¶
Current interpretation:
- a physical asset cannot belong to more than one SA at a time
- SA affiliation reflects ownership or governed possession in the business sense
- initial assignment of assets to an SA happens from the Odoo side, not by self-assignment from the SA side
9.7 No SA-Side Asset Tree¶
Do not introduce an additional SA-side parent/child asset tree.
Current rule:
- a fleet is treated as a single governed concept
- it is not an SA-side collection object with explicit child asset rows
9.8 Fleet and Item Membership¶
If the underlying Odoo location implies item membership, that membership should be inferred from Odoo structure.
Therefore:
- an item should not maintain a separate independent SA-side membership while already bound through a fleet/location relationship
- if a location has no fleet binding, an item may be affiliated directly as an
ITEM
9.9 Wrapper Continuity¶
ov.asset is a governance hat placed on a physical thing.
Current direction:
- the hat may change
- the physical thing wearing the hat may also change
So the wrapper is allowed to persist while its current physical reference changes over time.
10. Separation of Concerns¶
| Domain | Responsibility |
|---|---|
res.partner |
Identity anchor for organizations and people |
| Odoo core models | Transaction and accounting truth |
ov.serviced_account |
Governed service-account tree |
ov.membership |
Authorization relationship in account context |
ov.asset |
SA-native governance wrapper for assets |
stock.location / stock.production.lot |
Native asset/logistics substrate |
| PA / proxy layer | Context establishment, permission resolution, privileged execution |
11. Developer Starting Point¶
If developers start coding now, the first prototype should prove:
- An organization partner can be wrapped by
ov.serviced_account. - A person partner can hold multiple
ov.membershiprows. - One person can appear in both
OVACandEXTCcontexts without schema breakage. - Role lives on membership, not on partner.
{actor, sa}can stamp PA-governed Odoo records.- Asset governance can be expressed through
ov.assetas a wrapper over native Odoo asset structures. - Explicit hierarchy can drive team-level and SA-level reporting without depending on shorthand role labels.
12. Deferred by Design¶
Explicitly defer these items until after the first prototype:
- exact field names and base-model strategy for the
{actor, sa}stamp - exact field design for
ov.asset - final membership-management action naming and UI sequencing
- approval workflow engine
- detailed SSO mapper implementation
- advanced conflict-of-interest rule engine
13. Guiding Principle¶
Keep the Serviced Account model:
- structural, not transactional
- contextual, not person-global
- explicit in authority boundaries
- aligned with native Odoo primitives
- conservative in scope
14. SAA And SAM Operating Split¶
Use the following table as the working division between SA Administration
(SAA) and SA Management (SAM).
The main rule is:
SAAassigns or governs what belongs to an SASAMmanages work and operations inside the already valid SA scope
| Record or decision area | SAA responsibility |
SAM responsibility |
Notes |
|---|---|---|---|
| SA creation and hierarchy | Create SA, set parent SA, preserve hierarchy invariants | Use the resulting SA structure | Structural governance belongs to SAA |
| SA anchor partner | Create and govern the canonical company-partner anchor | Consume it as context | Anchor integrity is not ordinary business management |
| SA membership | Add, remove, suspend, and structurally govern membership rows | Work through valid memberships | Participation in SA is established by SAA |
sa_manager identity |
Change manager through invariant-safe structural operation | Operate with current manager authority | Manager transition is structural, not casual reassignment |
| Assigning a shared structural object to an SA | Decide whether the object is explicitly SA-scoped | Use the object once it is in SA scope | Example: service location, fleet, or other shared governed object |
| Removing a shared structural object from an SA | De-affiliate or retire the SA assignment | Stop using it operationally | Structural scope changes belong to SAA |
| Shared SA-wide records already in scope | Define the allowed boundary and lifecycle rules | Read and manage them inside policy | SAM manages within the structural boundary that SAA set |
| Actor assignment inside an SA | Define permitted assignment rules and invariants | Assign, reassign, and work records within those rules | Example: lead owner, reservation owner, customer steward |
| Customer/account operational ownership | Define whether the customer model is SA-scoped at all | Manage customer relationships and assignments within the SA | Do not collapse customer identity and customer ownership |
| Location or fleet operational use | Govern whether the location or fleet belongs to the SA | Use, monitor, reserve, issue, and coordinate service through it | Shared structural assignment first, operations second |
| Item-level reservation or custody | Usually only define invariant rules | Create and manage the actual operational assignment | These are typically SAM work objects |
| Reporting roll-up | Define hierarchy and manager structure that makes roll-up possible | Consume team-level and SA-level views | Hierarchy from SAA, business use by SAM |
SAA Versus SAM Summary¶
| Question | Primary owner |
|---|---|
Does this record or structure belong to SA X at all? |
SAA |
Is this person allowed to participate in SA X? |
SAA |
Can this shared location, fleet, or governed object be used by SA X? |
SAA |
Who is currently handling this lead, customer, reservation, or service action inside SA X? |
SAM |
| Who may see and manage work records within the current SA? | SAM, subject to the structure set by SAA |
| Who preserves hierarchy, membership, and root-manager invariants? | SAA |