008 — Sales Order Lifecycle¶
The order lifecycle is the heart of the platform. A single sale.order record carries two
status fields: Odoo's native state (the ERP truth) and portal_state (the B2B business
truth shown in both portals).
Portal State Machine¶
stateDiagram-v2
[*] --> draft: customer creates a basket/draft request
draft --> rfq_submitted: customer submits for quotation
rfq_submitted --> rfq_updated: customer revises before pricing
rfq_updated --> rfq_submitted
rfq_submitted --> quotation_submitted: account manager prices & sends
rfq_updated --> quotation_submitted
rfq_submitted --> rejected
rfq_updated --> rejected
quotation_submitted --> po_submitted: customer accepts, sends PO
quotation_submitted --> rfq_updated: customer edits → re-submits
quotation_submitted --> cancel
po_submitted --> in_progress: account manager confirms the order
po_submitted --> cancel
in_progress --> delivered
in_progress --> cancel
rejected --> [*]
cancel --> [*]
delivered --> [*]
State reference¶
portal_state |
Business meaning | Mapped Odoostate |
Owner of the next move |
|---|---|---|---|
draft |
Basket / draft request being built | draft |
Customer |
rfq_submitted |
Request for quotation lodged | draft |
Account Manager |
rfq_updated |
Customer revised the request | draft |
Account Manager |
rejected |
Request refused | cancel |
— |
quotation_submitted |
Priced quotation issued (PDF e-mailed) | sent |
Customer |
po_submitted |
Customer sent their purchase order | sent |
Account Manager |
in_progress |
Order confirmed and being fulfilled | sale |
Account Manager |
delivered |
Fulfilment complete | sale |
— |
cancel |
Cancelled | cancel |
— |
State changes arrive through a single update endpoint. Two transitions are not plain field writes — they delegate to real Odoo actions:
| Transition | Delegates to | Side effects |
|---|---|---|
→quotation_submitted |
the quotation-send action | Renders the quotation PDF, sends theEcommerce: Portal Quotation mail template, sets state = sent |
→in_progress |
the order-confirm action | Runs Odoo's standard confirmation, setsstate = sale |
Guard rails¶
- A transition to any state other than
draft,rejectedorcancelis refused if the order has no real (non-display) lines. - An unknown
portal_statevalue is rejected outright. - Only the creator may delete a basket.
- Editing an already-quoted order sets
portal_pending_submit, marking it as changed but not yet re-submitted.
End-to-End Business Workflow¶
sequenceDiagram
autonumber
actor C as Customer Portal User
participant P as Customer Portal
participant API as Odoo REST API
participant O as sale.order
participant N as Notifications + e-mail
actor AM as Account Manager (AMP)
C->>P: browse catalog, add products
P->>API: order create / order_lines create
API->>O: draft order, per-customer sequence number
Note over O: portal_state = draft
C->>P: submit request
P->>API: orders/update state = rfq_submitted
O->>N: notify account manager (in-app + e-mail)
N-->>AM: "New RFQ submitted"
AM->>API: open RFQ queue, edit line prices
API->>O: order_lines/update
AM->>API: orders/update state = quotation_submitted
O->>O: render quotation PDF
O->>N: send quotation mail template
N-->>C: quotation e-mail
Note over O: portal_state = quotation_submitted · state = sent
alt customer accepts
C->>P: submit PO
P->>API: orders/update state = po_submitted
O->>N: notify account manager
AM->>API: orders/update state = in_progress
O->>O: confirm order → state = sale
AM->>API: orders/update state = delivered
else customer revises
C->>P: edit lines
P->>API: orders/update state = rfq_updated
O->>N: notify account manager
else cancelled
C->>P: cancel
P->>API: orders/update state = cancel
end
Notification trigger points. The account manager is notified on exactly three customer-side
transitions: rfq_submitted, rfq_updated, po_submitted — plus when a draft request is shared.
Notifications are only sent when the actor is a portal user, so account-manager-side edits do
not notify the account manager. E-mail delivery is globally switchable via a system setting;
the in-app notification is always created.
The Shared-Request Flow¶
A parallel, lighter-weight flow that does not enter the RFQ pipeline.
sequenceDiagram
actor C as Customer user
participant O as sale.order (draft)
participant N as Notification
actor AM as Account Manager
C->>O: mark basket as "action required" (portal_visible = true)
O->>N: create notification + notify account manager
N-->>AM: "<user> shared draft request <name>"
AM->>O: review in AMP "Shared Request" queue
AM->>O: mark handled (portal_visible = false)
portal_visible is the "action required" flag; it also drives the Action Required filter
and dashboard counter in the Odoo ERP Back Office.
Requesting a Product That Is Not in the Catalog¶
sequenceDiagram
actor C as Customer user
participant R as ecommerce.product.request.line
participant O as sale.order
actor AM as Account Manager
C->>R: create request (name, qty, reference URL) on an order
Note over R: state = submitted
AM->>R: pick up → in_progress
alt product can be sourced
AM->>R: link a created/matched product → product_added
R->>O: auto-create the sale order line
else cannot be sourced
AM->>R: not_found
end
The request line and the order line stay linked: unlinking or re-pointing the request removes or replaces the generated order line. The order carries a live count of unresolved requests, surfaced as a button on the ERP Back Office order form.
Sales Order Numbering and Labelling¶
| Field | Origin | Shown to |
|---|---|---|
name |
Odoo's standard sale-order sequence | ERP Back Office |
portal_order_no |
Thecustomer company's own sequence (portal_order_sequence_id), generated on create |
Both portals |
portal_label |
Free text chosen by the customer ("Q3 lab restock") | Both portals |
Per-customer sequences mean each customer sees a continuous, private numbering series.
Planning and Attention Signals¶
| Signal | Meaning |
|---|---|
portal_planned_order_date + computed planning state |
Late / On Time / Upcoming — drives urgency indicators |
portal_visible |
Action required (unread portal activity or shared request) |
portal_reviewed + portal_reviewed_date |
The customer has reviewed the quotation |
portal_print_quotation_date |
The customer printed/downloaded the quotation |
portal_pending_submit |
The customer edited a quoted order without re-submitting |
portal_messages_count |
Volume of portal-originated chatter |
account_manager_notes / account_manager_comment |
Internal note vs. customer-visible comment |
Where Each State Appears¶
| Surface | Queues |
|---|---|
AMP (/admin) |
Shared Request · RFQ Quotations · Quotations · Orders · Archived Orders |
Customer Portal (/company-profile) |
Draft Requests · RFQs · Orders · Archived |
| Odoo ERP Back Office | Orders Management → RFQ Quotations · Quotations · Orders; dashboard drill-downs for Action Required, New RFQs, Total RFQs; search filters per portal state |