Operations Setup Guide — Sales Workflow¶
Purpose: Step-by-step configuration checklist for the Odoo operations team to enable the full sales-to-cash workflow described in SALES_WORKFLOW.md.
Who this is for: Odoo administrators and operations leads who configure the Odoo backend.
Who this is NOT for: Developers. For API endpoint details see SALES_WORKFLOW.md. For customer governance see CUSTOMER_LIFECYCLE.md.
Module version: abs_connector 18.0.1.1.4
Before You Start — Installed Modules Checklist¶
Verify these Odoo modules are installed before configuring anything. Go to Settings → Apps.
| Module | Technical name | Required for |
|---|---|---|
| Sales | sale_management |
Quotations, orders, approval workflow |
| Inventory | stock |
Warehouses, deliveries, stock tracking |
| Invoicing / Accounting | account or account_accountant |
Invoices, payments, journals |
| Helpdesk | helpdesk |
Support tickets |
| Subscriptions | sale_subscription |
Recurring billing |
| ABS Connector | abs_connector |
All custom governance, SA, employee JWT |
If any are missing, ask your Odoo administrator to install them before proceeding.
1. Company Settings¶
Path: Settings → General Settings → Companies
1a. Verify your company record¶
| Field | What to set | Why |
|---|---|---|
| Name | Your legal entity name | Appears on invoices and PDFs |
| Currency | Default currency (e.g. XOF, KES, USD) | Controls all invoice and payment amounts |
| Country | Correct country | Controls tax rules and address format |
| Logo | Upload company logo | Appears on pro-forma and invoices |
1b. Multi-company (if you operate in multiple countries)¶
Each country / legal entity should be its own Company in Odoo.
- Each company gets its own warehouse(s).
- Each company has its own chart of accounts.
- Service Accounts (
ov.serviced_account) are company-scoped — a seed SA belongs to one company.
Go to Settings → Users & Companies → Companies to create additional companies.
2. Sales Settings¶
Path: Settings → Sales → Quotations & Orders
2a. Order approval workflow¶
The API endpoints POST /api/orders/<id>/request-approval, approve, and reject only work if this setting is enabled.
| Setting | Value | Where |
|---|---|---|
| Sales Order Approval | ✅ Enabled | Settings → Sales → Quotations & Orders |
| Minimum order amount | Set your threshold (e.g. 500 USD) | Same section — orders above this need approval |
| Lock Confirmed Sales | ✅ Enabled (recommended) | Prevents editing after confirmation |
2b. Quotation settings¶
| Setting | Value | Why |
|---|---|---|
| Default Quotation Validity | e.g. 30 days | Sets expiry on new quotations |
| Pro-Forma Invoice | ✅ Enabled | Required for GET /api/orders/<id>/proforma-pdf |
| Default Terms & Conditions | Add your legal text | Printed on every quotation |
2c. Pricelists (optional but recommended)¶
If you sell in multiple currencies or have customer-specific pricing:
Path: Sales → Configuration → Pricelists
- Create one pricelist per currency.
- Set
currency_idon each pricelist. - Assign the correct pricelist to each customer (
res.partner.property_product_pricelist).
If you do not use pricelists, Odoo defaults to the product's sale price.
3. Inventory / Warehouse Settings¶
Path: Settings → Inventory
3a. Enable required features¶
| Setting | Value | Required for |
|---|---|---|
| Storage Locations | ✅ Enabled | Sub-locations within a warehouse |
| Multi-Step Routes | ✅ Enabled | 2-step or 3-step delivery/receiving |
| Lots & Serial Numbers | ✅ Enabled | Tracking individual solar kit serial numbers |
| Expiration Dates | Optional | Only needed if products expire |
3b. Create your warehouse(s)¶
Path: Inventory → Configuration → Warehouses → New
One warehouse per physical depot or country.
| Field | Example value | Notes |
|---|---|---|
| Warehouse Name | Main Warehouse |
Human-readable name shown in all reports |
| Short Name | WH |
Used as prefix on picking names (WH/OUT/00001) |
| Company | Select your company | Each company can have multiple warehouses |
| Outgoing Shipments | Ship only (1 step) or Pick + Ship (2 steps) |
Start with 1-step unless you have a packing area |
| Incoming Shipments | Receive only (1 step) or Receive + Quality (2 steps) |
1-step is sufficient for most operations |
Odoo automatically creates the required picking types (Receipts, Deliveries, Internal Transfers) and stock locations when you save a new warehouse.
3c. Verify auto-created locations¶
After saving the warehouse, check that these locations exist:
Path: Inventory → Configuration → Locations
| Location | Usage | Notes |
|---|---|---|
WH/Stock |
internal | Main storage — source of all deliveries |
WH/Input |
internal | Goods arrive here (only if 2/3-step receiving) |
WH/Output |
internal | Staging area before dispatch (only if 2-step delivery) |
Partners/Customers |
customer | Virtual — delivered units "live" here |
Partners/Suppliers |
supplier | Virtual — source of all receipts |
Virtual/Inventory |
inventory | Used for stock adjustments |
Virtual/Scrap |
inventory | Damaged / end-of-life units |
These are created automatically. You only need to create sub-locations (e.g.
WH/Stock/Solar Kits,WH/Stock/Accessories) if you want more granular reporting.
3d. Optional sub-locations¶
Create sub-locations under WH/Stock to organise by product family:
Path: Inventory → Configuration → Locations → WH/Stock → Create Child
Suggested structure for this system:
WH/Stock
├── WH/Stock/Solar Kits
├── WH/Stock/E-Mobility
├── WH/Stock/Accessories
└── WH/Stock/Spare Parts
3e. Verify picking types¶
Path: Inventory → Configuration → Operations Types
Each warehouse must have these three operation types:
| Operation Type | Code | Default from | Default to |
|---|---|---|---|
| Receipts | incoming |
Vendors (virtual) | WH/Input or WH/Stock |
| Delivery Orders | outgoing |
WH/Stock or WH/Output | Customers (virtual) |
| Internal Transfers | internal |
WH/Stock | WH/Stock |
If the picking types are missing, delete and re-create the warehouse.
4. Product Configuration¶
This is the most critical section. How a product is configured determines whether it creates a delivery, tracks serial numbers, and when it can be invoiced.
4a. Product type — the most important field¶
Path: Inventory → Products → [Product] → General Information tab
product_type |
Odoo label | Creates delivery on sale | Stock tracked | When to use |
|---|---|---|---|---|
product |
Storable Product | ✅ Yes | ✅ Yes + serial numbers | Solar kits, batteries, physical hardware |
consu |
Consumable | ✅ Yes (no stock check) | ❌ No | Cables, small accessories, installation materials |
service |
Service | ❌ No delivery | ❌ No | Installation fee, warranty fee, SIM card plan |
Rule: Every physical unit that needs a serial number must be storable.
4b. Serial number tracking¶
Path: Product → General Information → Tracking
Only set this on storable products.
| Value | When to use |
|---|---|
By Unique Serial Number |
One serial per unit (e.g. each solar kit gets its own SN) |
By Lots |
Batch of identical units share one lot number (e.g. bulk accessories) |
No Tracking |
Product is storable but individual units are not tracked |
Rule for this system: All solar kits and batteries must be By Unique Serial Number. This is required for asset governance — ov.asset links to a stock.lot (serial number).
4c. Invoice policy¶
Path: Product → General Information → Invoicing Policy
| Value | What it means | When to use |
|---|---|---|
Ordered quantities |
Invoice can be created as soon as the order is confirmed | Services, subscriptions, consumables |
Delivered quantities |
Invoice cannot be created until the delivery is validated | Physical hardware (storable products) |
Rule for this system:
- Solar kits, batteries, accessories → Delivered quantities
- Installation, warranty, service → Ordered quantities
If you set
Delivered quantitieson a storable product,POST /api/orders/<id>/invoicewill fail untilPOST /api/stock/deliveries/<id>/validatehas been called first.
4d. Wangvision PU custom fields¶
Path: Product → [Custom tab or inline fields]
These custom fields drive the Wangvision PU taxonomy. They must be set for all products sold through the system.
| Field | Technical name | Values | Required for |
|---|---|---|---|
| PU Category | x_pu_category |
solar_home, e_mobility, cross_grid, off_grid |
Product filtering in API |
| PU Metric | x_pu_metric |
e.g. watt_peak, kwh, count |
Reporting and pricelist logic |
| Service Type | x_service_type |
hardware, service, subscription, warranty |
Subscription and invoicing routing |
| Contract Type | x_contract_type |
one_off, lease, paygo, subscription |
Subscription module behaviour |
If these fields are not visible on the product form, ask the developer to verify the
abs_connectormodule is installed and the views are loaded.
4e. Set a sales price and tax¶
Path: Product → General Information → Sales Price + Customer Taxes
| Field | What to set |
|---|---|
| Sales Price | Default unit price (can be overridden on the order line) |
| Customer Taxes | Select the correct VAT or sales tax for this product |
Tax must be configured before it appears in the dropdown. See Section 6.
4f. Bill of Products (BOP) — for bundled kits¶
If you sell bundled products (e.g. a "Solar Home System 200W" that contains a panel, battery, and charge controller), you need a Bill of Products.
Path: Product → Bill of Products tab (or via GET/POST /api/products/<ref>/bom)
Each BOP line defines: - Component product - Quantity - Whether it is substitutable
The BOP is not a manufacturing Bill of Materials. It is a custom bundle definition specific to this system. Do not use the standard Odoo BOM (
mrp.bom) unless manufacturing is enabled.
4g. Product categories¶
Path: Inventory → Configuration → Product Categories → New
Odoo uses product.category for accounting rules (which income account to credit on invoice).
Minimum set to create:
| Category | Mapped to |
|---|---|
| Solar Kits | Solar Kit products |
| E-Mobility | E-bike and e-mobility products |
| Accessories & Spare Parts | Non-tracked accessories |
| Services & Warranties | Service-type products |
Each category must have:
- Account Properties → Income Account → correct revenue account
- Account Properties → Expense Account → correct COGS account
If accounts are not set, Odoo cannot post the invoice.
5. Customer (Contact) Configuration¶
Path: Sales → Orders → Customers → New
Or via API: POST /api/contacts
5a. Required fields for a customer to receive a sale order¶
| Field | Technical name | Mandatory? | Notes |
|---|---|---|---|
| Name | name |
✅ Yes | Customer display name |
| Customer Rank | customer_rank |
✅ Yes | Must be ≥ 1 for Odoo to treat as customer |
email |
Recommended | Required for invoice email (POST /api/invoices/<id>/send) |
|
| Phone | phone |
Recommended | Required for field operations |
| Address / Country | street, country_id |
Recommended | Required for proper invoice PDF |
| Payment Terms | property_payment_term_id |
Optional | Overrides default |
| Pricelist | property_product_pricelist |
Optional | Customer-specific pricing |
5b. SA governance (Level 2 customers)¶
If the customer should be visible only within a specific Service Account:
- The customer must be created via
POST /api/contactswith a valid employee JWT (not API key). - The JWT carries the
sub(employee ID) which determines which SA the customer belongs to. - An
ov.sa_customer_assignmentrecord is created automatically linking the customer to the SA.
Customers created with an API key (no JWT) are plain
res.partnerrecords with no SA governance. They are visible to all users.
6. Accounting Setup¶
Path: Accounting → Configuration
6a. Chart of accounts¶
Odoo installs a localised chart of accounts when the Accounting module is first set up. Verify:
- At least one bank journal exists (for cash payments)
- At least one revenue account (e.g.
4000 - Sales Revenue) - At least one receivables account (e.g.
1100 - Accounts Receivable)
Path: Accounting → Configuration → Chart of Accounts
6b. Journals¶
| Journal | Type | Required for |
|---|---|---|
| Customer Invoices | sale |
All customer invoice posting |
| Customer Credit Notes | sale |
Refunds / credit notes |
| Bank | bank |
Payment registration |
| Cash | cash |
Cash payments |
Path: Accounting → Configuration → Journals
Verify each journal has:
- Correct currency_id
- Correct default_account_id (income account for sale journals)
6c. Taxes¶
Path: Accounting → Configuration → Taxes → New
Create your applicable taxes before configuring products.
| Field | Example |
|---|---|
| Tax Name | VAT 16% |
| Tax Type | Sales |
| Tax Computation | Percentage of Price |
| Amount | 16 |
| Tax Account | e.g. 2100 - VAT Payable |
Taxes must be set on each product (Section 4e) for them to appear on invoices.
6d. Payment terms¶
Path: Accounting → Configuration → Payment Terms → New
| Term | Lines |
|---|---|
| Immediate Payment | 100% due in 0 days |
| 30 Days Net | 100% due in 30 days |
| 50% Upfront | 50% now, 50% after 30 days |
Assign a default payment term on the company: Path: Settings → Accounting → Default Payment Terms
7. Service Account (SA) Governance Setup¶
This is the custom Omnivoltaic governance layer. It must be configured before any agent (employee) can create governed customers or orders.
7a. What is a Service Account?¶
A Service Account (SA) (ov.serviced_account) represents a governed operational context — typically a country, region, or depot. All customers, orders, and assets created within an SA are visible only to members of that SA.
7b. Create a Service Account¶
Path: (Custom menu — ask developer to point you to the ov.serviced_account list view)
| Field | Example | Notes |
|---|---|---|
| Name | Togo - Lomé |
Human-readable SA name |
| Company | Select company | SA is company-scoped |
| Code | TG-LOM |
Short identifier used in filters |
| State | active |
Must be active for visibility to work |
| SA Type | seed or branch |
Seed = country root. Branch = sub-region under seed |
| Parent SA | Select parent | Only for branch SAs — links to seed SA |
Every company needs at least one seed SA before agents can be onboarded.
7c. Assign employees to a Service Account (create memberships)¶
Every employee who will use the system must be a member of at least one SA.
Path: (Custom menu — ov.membership list view)
| Field | Value |
|---|---|
| Employee | Select abs.employee record |
| Service Account | Select ov.serviced_account |
| Role | agent or manager |
| State | active |
| Date From | Start date of membership |
Manager role: Can approve orders (
POST /api/orders/<id>/approve) and manage SA membership. Agent role: Can create customers, quotations, and view data within their SA.
7d. Create the employee record¶
Before you can create a membership, the employee must exist as an abs.employee record.
Path: (HR or custom employee menu)
| Field | Value |
|---|---|
| Name | Full name |
| Work email — used for JWT login | |
| Phone | Phone number |
| Company | Must match the SA's company |
| Active | ✅ Yes |
The employee record is what the JWT is issued for. The
subclaim in the JWT is theabs.employee.id.
8. Subscription Configuration¶
Path: Sales → Configuration → Subscription Plans
Or via the sale_subscription settings.
8a. Subscription plans¶
A subscription plan defines the billing cycle.
| Field | Example | Notes |
|---|---|---|
| Name | Monthly PAYGO |
|
| Billing Period | Monthly |
Can be monthly, quarterly, annually |
| Auto-renewal | ✅ Yes | Required for recurring billing |
8b. Subscription products¶
Any product that should generate recurring invoices must:
1. Have product_type = service
2. Have x_contract_type = subscription or paygo
3. Be linked to a subscription plan
Path: Product → Sales tab → Subscription
If a sale order contains a subscription product, Odoo creates a
sale.subscriptionrecord upon order confirmation. The subscription then generates invoices automatically on each billing date.
8c. Subscription invoicing¶
Verify: - Path: Settings → Sales → Subscriptions → Invoice automatically on renewal → ✅ Enabled - Set the invoicing journal to your Customer Invoices journal.
9. Helpdesk Configuration¶
Path: Helpdesk → Configuration → Teams
9a. Create a helpdesk team¶
| Field | Value |
|---|---|
| Team Name | e.g. Customer Support |
| Visibility | All internal users (or specific team) |
| Assignment | Automatic or manual |
9b. Configure ticket stages¶
Path: Helpdesk → Configuration → Stages
The POST /api/tickets/<id>/close endpoint moves a ticket to the first stage with is_close = True. You must configure at least one closing stage:
| Stage name | is_close |
Notes |
|---|---|---|
| New | ❌ | Default for new tickets |
| In Progress | ❌ | Being worked on |
| Waiting on Customer | ❌ | Pending customer reply |
| Resolved | ✅ | This stage must exist — close endpoint targets it |
| Cancelled | ✅ | Optional second closed stage |
If no stage has
is_close = True, the close endpoint will return an error.
9c. Email alias (optional)¶
If customers should be able to open tickets by email:
Path: Helpdesk → Configuration → Teams → [Team] → Email Alias
Set an email alias (e.g. support@yourcompany.com). Odoo creates tickets from incoming emails automatically.
10. Email / Outbound Mail Configuration¶
The following API endpoints send emails. They will silently fail or log errors if outbound mail is not configured:
POST /api/orders/<id>/send— quotation emailPOST /api/invoices/<id>/send— invoice email- Helpdesk reply notifications
Path: Settings → Technical → Outgoing Mail Servers → New
| Field | Value |
|---|---|
| SMTP Server | Your mail server (e.g. smtp.gmail.com) |
| SMTP Port | 587 (TLS) or 465 (SSL) |
| Security | TLS or SSL |
| Username | Your sending email address |
| Password | App-specific password |
Test the configuration using Settings → Technical → Outgoing Mail Servers → [Server] → Test Connection.
11. Roles & Players — Who Does What¶
This section defines every role in the ABS governance system, what they can see, and how to set them up in Odoo.
11a. The Four Roles at a Glance¶
| Role | role_code |
Where they live | Visibility scope | Login method |
|---|---|---|---|---|
| Admin | admin |
Global Root SA only | Every record across every company and every SA | Employee JWT |
| SA Manager | staff + manager_member_id = NULL |
A specific SA | All records within their SA | Employee JWT |
| Staff | staff + has manager_member_id |
A specific SA | All records within their SA | Employee JWT |
| Agent | agent |
A specific SA | Only records explicitly assigned to them | Employee JWT |
These roles are ABS governance roles. They are separate from Odoo user groups (which apply to the Odoo UI only).
11b. Role Details¶
Admin ← The King¶
- Who: The highest authority in the system. Typically OV HQ leadership, national directors, or the system owner. This is the person who sits at the top of the Global Root SA.
- What they control:
- Create, modify, and deactivate any SA anywhere in the system
- Enroll or revoke members of any SA across all companies
- See every record — every
res.partner,sale.order,account.move,stock.picking,ov.asset,helpdesk.ticket, and subscription — across all companies - Manage system-wide governance settings
- How it works: They are
ov.membershiprecords in the Global Root SA withrole_code = 'admin'. The API applies no SA filter when the authenticated employee is an admin. - Constraint:
adminrole is only valid for memberships in the Global Root SA. You cannot assign admin role to any other SA. A validation error is raised if you try. - Setup:
- The Global Root SA is created automatically by migration
18.0.1.1.4(namedOV Global Root SA, codeOVGLOBAL-ROOT). - Create an
abs.employeefor the person. - Create an
ov.membershiprecord:account_id= Global Root SA,employee_id= the employee,role_code=admin,scope_policy=sa_wide.
SA Manager¶
- Who: The person responsible for a single Service Account (branch, territory, or team). There is exactly one SA Manager per SA.
- What they see: All records tagged to their SA — customers, orders, invoices, deliveries, assets, and tickets.
- How it works: The SA Manager's membership has
role_code = 'staff'andmanager_member_id = NULL. Theov.serviced_account.sa_managerfield points to this membership. Becausemanager_member_id = NULL, the system identifies them as the SA manager. - Setup:
- Create an
abs.employeefor the person. - When creating the SA, pass
sa_manager(or the first enrollment automatically promotes the first staff member to SA manager). - To explicitly set: create
ov.membershipwithrole_code = 'staff',manager_member_id = False, then setov.serviced_account.sa_managerto that membership ID.
Staff¶
- Who: Operational team members who need full visibility within the SA but are not the SA Manager. Examples: back-office coordinators, operations leads.
- What they see: All records tagged to the SA (same scope as SA Manager).
- How it works:
role_code = 'staff'andmanager_member_idpoints to the SA Manager or another senior staff member.scope_policyis typicallysa_wideorassigned_plus_unassigned. - Setup:
- Create
abs.employee. - Enroll via
POST /api/service-accounts/<id>/enrollwith body{ "employee_id": X, "role_code": "staff", "scope_policy": "sa_wide" }or createov.membershipdirectly in Odoo.
Agent¶
- Who: Field workers, sales agents, technicians. The front-line people who are assigned to specific customers and orders.
- What they see: Only the records explicitly assigned to them (their assigned customers, their delivery tasks, their tickets).
- How it works:
role_code = 'agent'andscope_policy=assigned_only. The system filters records by checkingx_actor_id = employee.partner_id(V2) andov.sa_*_assignment.actor_id(V3). - Setup:
- Create
abs.employee. - Enroll via
POST /api/service-accounts/<id>/enrollwithrole_code = "agent". - Assign the agent to customers:
POST /api/contacts/<id>/assignwithactor_id. - The agent's orders, deliveries, and assets are then automatically SA-scoped.
11c. The Global Root SA¶
The Global Root SA is the single cross-company root of the entire SA hierarchy.
It is intentionally not tied to any company (company_id = NULL) — it transcends
all tenants and acts as the true system root.
OV Global Root SA ← is_global_root=True, parent_id=NULL, company_id=NULL
├── Oves Holdings (HK) company root SA (source_company_id = Oves Holdings)
├── Oves Kenya company root SA (source_company_id = Oves Kenya)
├── Oves Togo company root SA (source_company_id = Oves Togo)
│ ├── Branch SA A
│ │ ├── SA Manager (staff)
│ │ └── Agents
│ └── Branch SA B
└── … (one root SA per company)
| Field | Value | Why |
|---|---|---|
is_global_root |
True |
System-wide identifier |
company_id |
NULL |
Belongs to no company — truly global |
parent_id |
NULL |
Nothing is above it |
source_company_id |
NULL |
Not seeded from any company |
account_code |
OVGLOBAL-ROOT |
Fixed reference code |
How it is created:
- Migration 18.0.1.1.5 creates it automatically when abs_connector is upgraded.
- Migration 18.0.1.1.6 detaches it from any company (company_id → NULL).
- A validation constraint prevents a second global root SA from being created.
- All existing company root SAs are automatically re-parented under it.
How to verify it exists (API):
GET /api/system/global-root
X-API-KEY: <your-api-key>
"exists": true, "company": null, "is_global_root": true.
How to verify the full SA tree (API):
GET /api/system/sa-hierarchy?flat=true
X-API-KEY: <your-api-key>
How to add an Admin (script):
# Edit ADMINS list in the script, then run:
python scripts/add_global_root_admins.py
Or via the API directly:
POST /api/service-accounts/26/members
X-API-KEY: <your-api-key>
{ "person_partner_id": <partner_id>, "role_code": "admin" }
The Admin employee can then authenticate with their JWT and will have unrestricted visibility and control across all companies and SAs.
11d. Odoo User Groups (for UI access)¶
Employees who also use the Odoo web UI need a res.users account with the correct group. Agents who only use the mobile app do not need a res.users account.
Path: Settings → Users & Companies → Users
| Staff role | Odoo group to assign |
|---|---|
| Admin (Global Root SA member) | Settings / Technical (or Odoo Administrator) |
| Sales Manager / SA Manager | Sales / Administrator |
| Sales Staff / Agent | Sales / User |
| Warehouse Staff | Inventory / User |
| Warehouse Manager | Inventory / Administrator |
| Accountant | Accounting / Accountant or Administrator |
| Helpdesk Agent | Helpdesk / User |
| Helpdesk Manager | Helpdesk / Administrator |
11e. Two login systems side by side¶
| System | Used for | Credential |
|---|---|---|
res.users (Odoo) |
Odoo UI, reports, admin tasks | Username + password |
abs.employee + JWT |
Mobile app, all /api/* endpoints |
Authorization: Bearer <jwt> |
The same person can have both accounts. Create the abs.employee record first, link it to the res.partner (person record), then optionally create the res.users account.
11f. API key vs employee JWT¶
| Auth method | Header | Used for | SA governance |
|---|---|---|---|
| Employee JWT | Authorization: Bearer <token> |
All governed endpoints; scopes data to the employee's SA and role | ✅ Full SA + role filtering |
| API Key | X-API-KEY: <key> |
System integrations, bulk imports, non-governed reads | 🔴 No SA filtering (sees all) |
| Customer JWT | Authorization: Bearer <customer_token> |
Customer self-service portal | Scoped to customer's own records |
Warning: Operations staff should always use Employee JWT, not the API key. The API key bypasses all SA visibility rules.
11g. Technical settings for API access¶
The API key (X-API-KEY) is currently hardcoded in the connector module. Ask your developer for the value and ensure it is kept secret. Rotate it by updating the module configuration.
12. Verification Checklist — Before Going Live¶
Use this checklist to confirm the system is ready for a first sale.
✅ Warehouse & Stock¶
- [ ] At least one warehouse exists with a
WH/Stocklocation - [ ] At least one
Receiptsand oneDelivery Orderspicking type exist - [ ] Storage Locations feature is enabled
- [ ] Lots & Serial Numbers feature is enabled
- [ ] Stock has been received via
POST /api/stock/receiveor manual receipt in Odoo
✅ Products¶
- [ ] All storable hardware products have
product_type = product - [ ] All hardware has
tracking = serial - [ ] All hardware has
invoice_policy = delivery - [ ] All service products have
product_type = serviceandinvoice_policy = order - [ ] All products have
x_pu_categoryset - [ ] All products have a Sales Price and at least one Customer Tax
✅ Customers¶
- [ ] At least one customer exists with
customer_rank ≥ 1 - [ ] Customer has a valid email address
✅ Accounting¶
- [ ] Chart of accounts is configured
- [ ] Customer Invoices journal exists
- [ ] Bank journal exists
- [ ] At least one tax exists and is assigned to products
- [ ] Income accounts are assigned to product categories
✅ SA Governance¶
- [ ] Global Root SA exists (
OV Global Root SA, codeOVGLOBAL-ROOT,is_global_root = True) - [ ] At least one company root SA exists with
source_company_idset - [ ] At least one seed SA (leaf SA) exists and is
active - [ ] At least one
abs.employeeexists - [ ] That employee has an
ov.membershipto the seed SA with stateactive - [ ] At least one Admin membership exists in the Global Root SA (
role_code = admin)
✅ Sales Settings¶
- [ ] Order Approval is enabled in Settings → Sales
- [ ] Approval amount threshold is set correctly
- [ ] Pro-Forma Invoice is enabled
✅ Helpdesk¶
- [ ] At least one helpdesk team exists
- [ ] At least one stage with
is_close = Trueexists
✅ Email¶
- [ ] Outgoing mail server is configured and tested
13. Common Configuration Mistakes and How to Fix Them¶
| Symptom | Cause | Fix |
|---|---|---|
POST /api/orders/<id>/invoice returns error "Nothing to invoice" |
All products have invoice_policy = delivery but delivery not validated |
Validate delivery first via POST /api/stock/deliveries/<id>/validate |
POST /api/stock/deliveries/<id>/validate returns "Insufficient stock" |
Products ordered but not received in warehouse | Receive stock first via POST /api/stock/receive |
POST /api/orders/<id>/request-approval returns error |
Order Approval not enabled in Settings | Enable in Settings → Sales → Quotations & Orders |
POST /api/invoices/<id>/send sends but no email received |
Outbound mail not configured | Configure SMTP in Settings → Technical → Outgoing Mail Servers |
POST /api/tickets/<id>/close returns "No closing stage" |
No helpdesk stage has is_close = True |
Add a closed stage in Helpdesk → Configuration → Stages |
| Serial number reused error on delivery | tracking = serial but same serial already in Partners/Customers |
Return the unit first, or check GET /api/stock/lots/<lot_name> for its current location |
| Invoice posts but no SA filter works on it | account.move has no SA stamp |
This is a known Phase 5 governance gap. See SALES_WORKFLOW.md |
| Agent cannot see customer in their SA | Customer created with API key, not JWT | Re-create customer using JWT — API key creates ungoverned plain contacts |
14. Day-to-Day Operations Reference¶
Once configured, the typical daily operations flow maps to these API calls:
1. Morning — check stock levels
GET /api/stock/levels?warehouse_id=1
2. Check pending deliveries
GET /api/stock/deliveries?state=assigned
3. Validate a delivery (goods dispatched)
POST /api/stock/deliveries/<id>/validate
Body: { "lines": [{ "move_id": 501, "qty_done": 1, "lot_id": 55 }] }
4. Create invoice after delivery
POST /api/orders/<id>/invoice
5. Confirm and send invoice
POST /api/orders/<id>/invoices/<inv_id>/confirm
POST /api/invoices/<inv_id>/send
6. Register payment received
POST /api/orders/<id>/register-payment
7. Check inbound receipts
GET /api/stock/receipts?state=assigned
8. Validate a receipt (stock received from supplier)
POST /api/stock/receive
Body: { "warehouse_id": 1, "validate": true, "lines": [...] }
9. Locate a specific serial number
GET /api/stock/lots/SN-2025-00123
Companion Documents¶
| Document | Purpose |
|---|---|
SALES_WORKFLOW.md |
Full technical workflow, state machines, governance gaps, complete API reference |
CUSTOMER_LIFECYCLE.md |
Customer creation, SA assignment, three-level governance model |
ODOO_MODULES_REFERENCE.md |
All Odoo models used, field inventory, SA governance status |