Skip to main content

Working a Lead

Dealer-side leads go from Negotiation to Won through a real Odoo sales flow: a draft quotation, a confirmed sales order, a validated delivery, and only then Mark Won. This page is the day-to-day dealer agent's reference.

The pipeline

New ──▶ Qualified ──▶ Negotiation ──▶ Won
└─▶ Lost (with reason)

Endorsed leads start directly in Negotiation. Manual leads start in New. Both follow the same forward-only, single-step rule enforced on the server.

My Pipeline

Dealer CRM → Pipeline → My Pipeline is a personal kanban, filtered to user_id = <self>. To work a lead, open it from either My Pipeline or All Leads.

Dealer my pipeline

Lead form header

On a claimed dealer lead, the header exposes:

ButtonWhen visibleWhat it does
Log CallAlways (while open)Opens the call log wizard
Convert to QuotationNo linked sale order yetCreates a draft sale.order pre-filled from the lead and links it both ways
Open QuotationOnce a sale order existsNavigates to the linked SO
Mark WonLinked SO exists and is gated ready (see below)Moves the lead to Won, and on endorsed leads notifies BPO via callback
Mark LostAlways (while open)Opens the Lost wizard to capture a reason

Dealer lead form header

Convert to Quotation

This is how a Negotiation-stage lead turns into something you can actually sell. Click Convert to Quotation on the form header.

Convert to quotation button

What happens:

  1. Validates the lead has a Brand and a Vehicle (product). A free-text vehicle_model_text is not enough — the dealer agent has to pick the actual product.product. If the product doesn't exist yet, create it under the brand first.
  2. Finds or creates a res.partner for the lead's customer (same matching as the endorsement intake — mobile, then email, then new partner).
  3. Creates a draft sale.order with:
    • partner_id = resolved customer
    • brand_id = lead's brand
    • origin = dealer lead reference (DLEAD/...)
    • dealer_crm_lead_id = the lead id (back-link)
    • One order line: the lead's product, product_uom_qty = lead.quantity, price_unit = lead.expected_price or product list price
  4. Stores the new SO id on lead.sale_order_id (forward link)
  5. Opens the SO form

From here the flow is standard Odoo: review the lines, adjust pricing, add accessories or trade-in adjustments, Confirm the order to move it from draft to sale, process the delivery picking to done.

:::tip If Convert fails The only thing that can make this fail is missing brand or product on the lead. Set them from the lead form, save, and try again. No clean-up needed — nothing was created yet. :::

Confirming the SO

Standard Odoo. From the draft SO:

  1. Review and adjust order lines
  2. Click Confirm → moves the SO to sale state
  3. Odoo automatically creates a stock.picking for the delivery

Pre-existing dealer workflows (discount approval, manager sign-off, etc.) attach here the same way they do to any other sale.order — Dealer CRM doesn't add or remove anything in this phase.

Delivering the vehicle

Also standard Odoo. Open the SO's delivery from Deliveries on the SO button box, or from Inventory → Transfers, then:

  1. Check Availability (if units are on hand)
  2. Validate once the customer has physically taken delivery — move the picking to done

With the picking done, the dealer can now go back to the lead and click Mark Won.

Mark Won

Click Mark Won on the dealer lead's header.

Mark Won on a delivered lead

Gate checks (all enforced server-side — no sneaky bypass):

  1. Linked SO existssale_order_id must be set. If not, you get "Create a quotation first with the Convert to Quotation button."
  2. SO is confirmedstate must be sale or done. A draft quotation can't win — confirm it first.
  3. All deliveries done — every stock.picking on the SO must be in done state. Outstanding pickings list the specific SOs you still need to validate.

Once the gate passes:

  • The lead moves to the Won stage
  • date_won is stamped
  • If the lead was originally endorsed from BPO (i.e. it has a bpo_source_lead_id), the dealer instance opens a JSON-RPC session to BPO CRM and calls crm.lead.update_from_dealer_callback(payload). The BPO source lead flips to Won, stamps its own date_won, records the dealer SO reference, and writes a chatter entry describing the delivery.
  • A confirmation chatter entry lands on the dealer lead as well

If the BPO callback fails for any reason (network, wrong creds, BPO down), the dealer lead still transitions to Won locally. The failure is logged and written to the dealer lead's chatter. See BPO CRM Connection for retry guidance.

Mark Lost

Click Mark Lost on the header (no gating — can be clicked from any open stage).

Mark Lost wizard

The wizard requires a reason:

ReasonWhen to use
Price Too HighCouldn't close on price
Chose CompetitorBuyer went with another brand
Not Ready to BuyStill shopping, not timing-ready
Financing IssuesLoan application failed
No Stock AvailableCouldn't deliver the unit the buyer wanted
Buyer CancelledChanged their mind after quotation/confirm
OtherDoesn't fit the above — explain in Notes

Optional Notes — always helpful for post-mortems. Free text.

Confirm the wizard and the lead moves to Lost. Any linked draft SO stays as-is — it's not auto-cancelled because the cancel action has its own approvals and side effects. Cancel it manually from the SO form if needed.

If the lead was originally endorsed from BPO, the Lost callback fires the same way the Won one does: update_from_dealer_callback is called with outcome='lost', carrying the reason and notes. The BPO lead flips to Lost with the same reason (or other if BPO's selection doesn't have a matching code).

Call logging

Standard call history on the lead form — Log Call opens a wizard for duration, result, and a summary. Each call becomes a dealer.crm.call.log record visible under Dealer CRM → Call Logs.

Access matrix

GroupClaimConvert to QuotationConfirm SOValidate DeliveryMark WonMark LostReassign
Dealer UserOwn + unassignedOwn leadsStandard Odoo Sales rulesStandard Odoo Stock rulesOwn leadsOwn leads
Dealer ManagerAnyAnyAnyAnyAnyAny
Dealer AdminAnyAnyAnyAnyAnyAny✅ + config

Dealer users can only touch leads they own or that are unassigned. Managers see and act on everything. The Convert to Quotation action creates a sale.order as the triggering user, so whatever restrictions the Sales module imposes (approval rules, discount caps, etc.) still apply.

Audit trail

Every state change in the work flow leaves a trace:

On the dealer lead chatter:

  • Stage transitions (New → Qualified → Negotiation → Won/Lost) — each with the triggering user and timestamp
  • user_id reassignments — who moved the lead to whom
  • Tracked field edits — customer info, vehicle interest, budget, trade-in, financing, notes
  • The linked sale_order_id gaining or changing — auditable as a tracked many2one
  • A message on Convert to Quotation: "Quotation SO003 created from this lead."
  • A message on Mark Won: the transition plus, for endorsed leads, a "BPO CRM source lead LEAD/01234 updated to won" confirmation once the callback succeeds
  • A message on Mark Lost: reason + notes captured from the wizard
  • Failed BPO callbacks are also logged in chatter with the exception text — the local transition is preserved but the auditor can see the mismatch between dealer side and BPO side

On the linked sale.order:

  • The full Sales module audit trail: quotation creation, confirmation, lines edited, cancellations
  • origin = DLEAD/... back-links the SO to the dealer lead for traceability
  • dealer_crm_lead_id is an explicit many2one back-link — searchable and filterable

On the stock.picking records:

  • Validation date, picker, lines moved, quantities received — standard Odoo Inventory audit

On the BPO side (for endorsed leads):

  • The BPO lead's chatter gets a matching entry when the dealer callback succeeds, including the dealer SO reference and the dealer lead name — so BPO managers see their lead's fate without logging into the dealer instance

Typical audit questions

"Walk me through DLEAD/00042 from endorsement to delivered."

  1. Open DLEAD/00042 and scroll chatter to the bottom — find the initial endorsement entry (who endorsed, when, BPO source URL)
  2. Scroll up through stage transitions — Negotiation → (Convert to Quotation)(Mark Won)
  3. Click the Quotation / Order reference to jump to the SO — verify it was confirmed and delivered
  4. Click the SO's delivery — verify date_done matches the Mark Won date
  5. Click BPO Source URL in the BPO Origin tab — verify BPO side is in sync

"Did the BPO callback fire for every won endorsed lead?"

  • Filter: Endorsed from BPO + Won. Open each. The chatter should contain "BPO CRM source lead ... updated to won". If instead you see a failure message, the BPO side is stale — fix the BPO CRM Connection and manually re-trigger.

"Show me every lost reason for Q2."

  • Filter: Lost + date range on date_lost. Group by Lost Reason. Export.

Retention

  • Leads — never auto-deleted. Archive via active = False if you need them out of the active kanban. Hard-delete is blocked if sale_order_id is set.
  • Linked sale.order records — retention follows the Sales module. Confirmed SOs become audit records.
  • Stock.picking records — same as above; validated pickings are inventory history.
  • Call logs — linked via call_log_ids. See Call Logs for the detail.
  • Chatter messages — never auto-deleted; each message is a mail.message row with full attribution.

Troubleshooting

SymptomLikely cause
Mark Won button missingNo linked SO yet. Convert to Quotation first.
Mark Won raises "must be confirmed"SO is still in draft. Confirm the quotation, then try again.
Mark Won raises "X delivery order(s) still pending"Validate the listed pickings in Inventory, then retry.
BPO side never updates after Mark WonBPO CRM Connection is missing, wrong, or the BPO instance is unreachable. Check the dealer lead's chatter for the error, fix the config, manually retry the transition.
Convert to Quotation raises "Select a vehicle first"Lead has vehicle_model_text but no product_id. Create or pick the product.
Mark Lost wizard won't closeA Lost Reason is required — not optional.
Dragging a lead from New to Negotiation is rejectedSkipping Qualified. Go one step at a time.