OVES ABS–Odoo Integration Handbook (n8n-led, Native Odoo Only)¶
Date: 2025-08-26 · Timezone: Asia/Singapore
This single document captures how OVES should run people, products, services, assets, billing, and payments across Odoo (system of record for people & money) and the ABS/Thing/Client microservices (systems of record for services, assets, and distributor ownership), with n8n as the integration glue.
0) Scope & First Principles¶
Business summary (for managers)¶
- Odoo is the truth for people, companies, orders, invoices, payments, pricing, taxes, currency.
- Client MS is the truth only for Client → Fleet (1-to-many ownership).
- Thing MS is the truth only for Fleet → Item (1-to-many assignment) and station topology.
- ABS governs ServicePlan (customer ↔ service bundle of assets) and payment instructions to Odoo.
- Federation prevents duplication: each system owns its domain, Odoo mirrors context for accounting.
Notes to developers¶
Source-of-Truth (SoT) boundaries
| Domain | System of Record (SoT) | In Odoo we store | Write rules |
|---|---|---|---|
| People & Companies | Odoo (res.partner, res.company) |
Full identity; ref holds external IDs |
Odoo owns create/update/merge; emit people.changed |
| Distributor ↦ Fleet ownership (Client→Fleet 1:N) | Client MS | Distributor as res.partner; Fleet as account.analytic.account (anchor only) |
Client pushes creates/updates of Fleets and their owner Distributor; Odoo never changes Fleet owner |
| Fleet ↦ Item (Fleet→Item 1:N) | Thing MS | Optional mirrors: stock.production.lot (Item serial), stock.location (station topology) |
Thing pushes item create/move/owner-of-stock (consignment) events; Odoo never edits Items |
| Commercial docs (SO/Invoice/Payment/Subscription) | Odoo | Full ledger & statuses | ABS/Thing/Client consume facts; do not mutate accounting |
Authority split: Odoo owns People & Money; Client owns who owns which Fleet; Thing owns which Items sit in which Fleet/station. All cross-links are federated, never duplicated.
1) Customers (People)¶
Business summary¶
- Retail buyers and subscribers are “customers.”
- For walk-in POS, we may not collect identity (anonymous customer).
- For web and subscriptions, we collect minimal details and provide a portal.
Notes to developers¶
- Model:
res.partner(SoT = Odoo). - Key fields:
is_company,name,email,phone,company_id,category_id(tags),ref(external IDs:client:user-…). - Portal users:
res.users(group Portal) auto-linked to partner. - Dedupe: run contact merge periodically.
Decision matrix (create partner?)
| Channel | Create real partner? | Typical practice |
|---|---|---|
| POS cash receipt | No (use “Walk-in”) | Fast checkout, no PII |
| POS with loyalty/invoice | Yes (light profile) | Name + phone/email |
| eCommerce | Yes | Auto via website signup/checkout |
| Subscription (BSS) | Yes (required) | KYC + portal |
2) OVES Companies (Multi-Company)¶
Business summary¶
- Each OVES operating entity is its own Odoo company.
- Websites, warehouses, journals, and payment acquirers are scoped to that company.
- Currency and tax logic follow the serving company.
Notes to developers¶
- Model:
res.company. - Currency:
res.company.currency_id(base); sales currency via Pricelists. - Propagation:
company_idexists on SO/Invoice/Subscription/Inventory. - Mapping:
- Website →
website.company_id - POS →
pos.config.company_id - Subscription →
sale.subscription.company_id - Fiscal position for taxes:
account.fiscal.positionrules.
3) Franchises / Distributors (Client Service)¶
Business summary¶
- Internal distributors = OVES companies.
- External franchises = third-party dealers captured as partners.
- Client microservice “owns” distributor hierarchy; Odoo mirrors enough to transact and report.
Notes to developers¶
- Internal: use
res.company. - External:
res.partner(is_company=True, tagDistributor,ref=client:dist-…). - End-users under external distributors generally not created in Odoo (unless warranty/support/portal needed).
- Settlements: wholesale invoices, or revenue share via credit notes based on analytic reports.
- n8n upserts distributors to Odoo; Odoo’s
company_idon partner = the selling OVES company.
Fleet ownership authority
- The field account.analytic.account.partner_id (Fleet owner) is controlled by Client MS.
- n8n performs upsert of Fleet (create if missing; update partner_id, active, name/code/ref only).
- Do not allow Odoo UI to change partner_id on Fleets.
- Recommended: server action or record rule to block manual edits to Fleet partner_id.
4) Assets: FLEETs & ITEMs (Thing Service)¶
Business summary¶
- FLEET is a financial/entitlement unit (e.g., “Nairobi Fleet 1”).
- ITEM is a serialised asset (battery, cabinet slot).
- Thing is the truth for both; Odoo mirrors only what it needs for accounting and traceability.
Notes to developers¶
- FLEET ⇒
account.analytic.account partner_id= owning Distributor (from Client MS).company_id= OVES company.code/ref= external Fleet ID.- ITEM ⇒
stock.production.lot(optional mirror) lot.name= Thing Item ID (authoritative).- Creation, location changes, and ownership (consignment) arrive from Thing events only.
- Odoo UI edits of mirrored Items should be disabled/flagged.
- Containment rule: Fleet→Item membership is SoT = Thing. Odoo does not persist a Fleet FK on Items; only carries Fleet context on accounting lines.
- Stations/Lockers ⇒
stock.locationhierarchy, company-scoped. - Consignment ownership:
stock.quant.owner_id/stock.move.owner_id.
5) Asset Accounts (Analytic, Deposits, Wallets)¶
Business summary¶
- Fleet P&L is reported using analytic accounts.
- Deposits are liabilities; top-ups/prepaid credits recognized on consumption.
Notes to developers¶
- Fleet analytic:
account.analytic.account. - Revenue/cost tagging always via Fleet analytic, not Item.
- Deposits: liability account via product.
- Prepaid credits: top-up product; recognize revenue via journal with analytic = Fleet.
- No Item→Fleet FK in Odoo. Item membership is authoritative in Thing.
6) Payments & Billing (ABS ⇄ Odoo)¶
Business summary¶
- ABS instructs Odoo to bill or collect.
- Odoo returns a pay link and confirms when payment posts.
- Two modes: Invoice-first or Pay-intent (top-up).
- Both end in a signed payment.confirmed back to ABS.
Notes to developers¶
- ServicePlan ref: carried in
subscription.reference,account.move.invoice_origin,payment.transaction.reference. - Invoice-first: ABS requests invoice; Odoo returns pay link; PSP confirms; Odoo posts payment; callback.
- Pay-intent: ABS requests intent; Odoo creates
payment.transaction; PSP confirms; Odoo posts paid invoice; callback. - No Fleet or Item mutations in these flows.
- Fleet reference used only for analytics; Item references optional (free text).
- Security: HMAC, idempotency, timestamp checks.
7) POS (Shopfront B2C)¶
Business summary¶
- POS sales are cash-like; receipts/invoices are paid instantly.
- Usually book to a single Walk-in customer, unless named invoice/loyalty is requested.
Notes to developers¶
- App:
point_of_sale. pos.config.company_id, bank/cash journals per company.- Default partner: “Retail Walk-in (POS)” with
company_id. - Named invoice: create minimal partner.
8) eCommerce (Cash-only)¶
Business summary¶
- Web checkout captures payment immediately; invoices close as paid.
- Each site maps to one OVES company & currency.
Notes to developers¶
- App:
website_sale+ payment acquirers. website.company_id→sale.order.company_id→ invoice.- Currency via pricelist bound to website/country group.
- Guest checkout: “Web Anonymous” partner.
9) Subscriptions / Service Plans (BSS)¶
Business summary¶
- Recurring plans billed monthly/weekly; entitlement depends on paid status.
- Fleets group revenue and usage.
Notes to developers¶
- App:
sale_subscription(Enterprise). - Fields:
partner_id,company_id,pricelist_id,analytic_account_id(Fleet),reference= ServicePlan ref. - Entitlement: Station/Thing queries invoice
payment_state. - Fleet membership context: SoT = Client (Fleet owner) + Thing (Items). Odoo only reflects paid/unpaid.
For configuration and operations details, see: Subscriptions (Odoo + ABS).
10) Customer Experience¶
Business summary¶
- Keep PII minimal; provide portal to view orders, invoices, subscriptions, and payments.
- Returns/warranty handled with named partner or order reference.
Notes to developers¶
- Portal templates per company.
- Tags and UTM fields for segmentation.
- GDPR compliance: opt-in, archiving.
11) Inventory (Odoo) tied to Thing¶
Business summary¶
- We reflect where assets are (stations/lockers), optionally down to serials.
- We can operate real-time or in nightly reconciliation.
- SoT = Thing for Item placement; Odoo mirrors for accounting.
Notes to developers¶
- Products: serial-tracked; valuation as per finance.
- Lots:
stock.production.lot.name= Thing Item ID (authoritative). - Locations:
WH/<Region>/<Station>/<Locker>per company. - Ownership:
owner_idon quants/moves. - Moves: Always come from Thing via n8n; Odoo UI disabled.
- Fleet context: carried as payload info (for accounting analytics), not persisted as FK.
Move payload example: ```json { "event_id":"uuid", "company_ext":"oves:ke", "action":"stock.move", "data":{ "item_id":"thing:item-9F3A", "product_sku":"BATT-SWAP-48V50", "from_location":"WH/NRB/Station-12/Locker-A1", "to_location":"WH/NRB/Station-12/Locker-B7", "owner_partner_ref":"client:dist-023", "fleet_code":"fleet:KE-NBI-001", // context only, SoT = Thing "origin":"abs:svc-abc1234" } } ````
12) Analytics & Reporting¶
Business summary¶
- See profitability by Fleet, Distributor, Company, and Product.
- Track MRR, churn, DSO, payment success rates, inventory presence vs usage.
Notes to developers¶
- Analytic reports: Fleet via
account.analytic.account. - Subscription dashboards.
- Payment state tracking.
- Marketing attribution: UTM fields.
13) Financials, Currency & Taxes¶
Business summary¶
- Customers see the sales currency (pricelist).
- Accounting books in the company base currency.
- Taxes mapped by location via fiscal positions.
Notes to developers¶
- Currency:
product.pricelist.currency_id(sales) vsres.company.currency_id(accounting). - Taxes: company localization +
account.fiscal.position. - Entries: Invoice (Dr AR / Cr Revenue+Tax), Payment (Dr Bank / Cr AR), Inventory moves (valuation as per config).
14) n8n Integration Patterns¶
Business summary¶
- n8n is our message bus: receives ABS/Thing/Client events, calls Odoo, and sends callbacks.
- Reliability via idempotency, signatures, retries, and exception queues.
Notes to developers¶
Core flows
- People (Odoo→Client): Odoo emits
people.changed. Client does not overwrite. - Distributor & Fleet (Client→Odoo): Client upserts Distributor partner + Fleet (
partner_id= owner). Odoo never changes Fleet owner. - Fleet→Item (Thing→Odoo): Thing emits
lot.upsert,stock.move,change_owner; Odoo mirrors moves. Odoo never edits Items. - Billing (ABS→Odoo): Invoice-first / Pay-intent; Odoo sends
payment.confirmed. No Fleet or Item edits. - Entitlement (Thing↔Gateway): Gateway reads Odoo invoice status; Thing enforces service allow/block.
Conflict resolution policy
- Owner conflict → Client wins (Fleet owner).
- Item placement conflict → Thing wins (Fleet→Item).
- Odoo remains read-side.
Controls
- HMAC on all payloads.
- Idempotency with
event_idandidempotency_key. - Always include
company_ext. - Dead-letter queue for failed syncs.
15) Version & Deployment Matrix¶
| Capability | Community | Enterprise | Odoo Cloud | Odoo.sh | On-Prem |
|---|---|---|---|---|---|
| Multi-company, Partners, Pricelists, Fiscal Positions | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| POS, eCommerce, Payment acquirers* | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
Subscriptions (sale_subscription) |
❌ | ✔️ | ✔️ | ✔️ | ✔️ |
| Inventory (serials, locations, consignment) | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| Webhooks/controllers (custom) | ⚠️ | ⚠️ | ❌ | ✔️ | ✔️ |
* Acquirer availability varies by provider on Community.
16) SOPs & Checklists¶
Onboarding (market/company)
- Create
res.company, journals, warehouses, fiscal position, pricelists - Website/POS bound to company
- Walk-in and Web Anonymous partners created
Distributor/Fleet setup
- Client upsert Distributor partner (
ref, tag) - Client upsert Fleet analytic (
code/ref,partner_id,company_id)
BSS subscription
- Create
sale.subscription(partner, company, pricelist, analytic=Fleet, reference=ServicePlan) - Capture payment token; test invoice auto-charge
- Enable entitlement gateway
Inventory sync
- Station
stock.locationmap ready - n8n flows: lot.upsert, stock.move, change_owner, reconcile
- Disable manual transfers
Month-end close
- Reconcile dunning/failed payments
- Recognize prepaid revenue via journal (analytic by Fleet)
- Inventory exceptions resolved
17) Quick Field & Model Reference (dev crib)¶
- Partner (
res.partner):company_id,ref, tags, pricelist, fiscal position - Company (
res.company):currency_id, localization - Fleet (
account.analytic.account):partner_id(SoT = Client),company_id,code/ref - Item (
stock.production.lot):name= Thing Item ID; created/updated by Thing events only - Subscription (
sale.subscription):partner_id,company_id,pricelist_id,analytic_account_id,reference(ServicePlan) - Invoice (
account.move):company_id,currency_id,invoice_origin(ServicePlan),payment_state - Payment (
account.payment/payment.transaction):referenceincludesservice_ref - Inventory:
product.template(serial tracking),stock.production.lot,stock.location,owner_id - POS:
pos.config.company_id - Website:
website.company_id
18) Governance & Conflict Rules¶
- People: Odoo is master. Inbound identity edits rejected; merges emit
people.merged. - Fleet ownership: Client is master for
partner_idon Fleets. Odoo UI blocked from edits. - Item placement: Thing is master for Fleet→Item. Odoo mirrors via inventory events.
- Accounting: Odoo is master. External systems consume, not mutate.
- Conflicts: Client wins on Fleet owner; Thing wins on Item placement.
- Idempotency & Signatures: HMAC + replay-safe.
- Company & Currency: always carried in payloads.
19) Glossary¶
- FLEET: Group of assets and service entitlements. Anchor in Odoo =
account.analytic.account. - ITEM: Serialised asset. SoT = Thing; optional Odoo
lotmirror. - ServicePlan: ABS object binding a person to a service bundle. Referenced in Odoo
subscription.referenceandinvoice.invoice_origin. - Distributor: External dealer (
res.partner) or internal OVES company (res.company). - Entitlement: Right to use service; computed from Odoo payment state; enforced by Thing.
Final Notes¶
- This blueprint honors OVES’s “minimize model extensions” rule.
- Everything rides on native Odoo objects with