Skip to main content

Quotation Calculator

The Quotation Calculator is a multi-line quote builder on the BPO lead form. Agents add line items, optionally configure bank financing with a per-bank monthly amortization, print a customer-facing PDF, email it as an attachment, and then endorse the lead — at which point the structured quotation flows into the dealer's instance and becomes one sale.order.line per row on the dealer's draft Sales Order.

This page covers the calculator end-to-end: where it lives, how to build a quote, the financing math, the PDF and email actions, and what gets locked once the lead is endorsed.

Where it lives

The calculator is a tab on the lead form, named Quotation. It sits between the Address tab and the Won/Lost details. The tab is editable on any claimed open lead (user_id set, not Won, not Lost, not yet endorsed) and read-only otherwise.

Quotation tab on a claimed lead

The line list is the top half of the tab. Totals and validity sit in the middle. Financing and the customer-facing notes are below.

Quotation lines

Each row is a crm.lead.quotation.line record. Click Add a line to append one.

ColumnMeaning
Sequence (drag handle)Order on the printed PDF
Line TypeSelection — see below
Vehicle Modelm2o → crm.vehicle.model, scoped to the lead's brand. Required for vehicle lines.
DescriptionFree text, required. Auto-fills from the picked vehicle's display name.
QtyFloat, default 1
Unit PriceMonetary. Defaults from the vehicle's synced SRP (crm.vehicle.model.list_price) for vehicle lines.
Discount %Per-line percentage discount, applied AFTER qty × price
SubtotalComputed: qty × unit_price
Discount AmountComputed: subtotal × (discount_pct / 100)
TotalComputed: subtotal − discount_amount

Line types

Line TypeWhen to useNotes
VehicleThe unit being soldMust pick a Vehicle Model. Auto-fills name and unit price.
AccessoryPhysical add-on (alarm, mats, tint)Free description and price
Add-On / ServiceService line (PMS package, extended warranty)Free description and price
FreebieInclusion at zero priceAuto-sets unit price to 0
Fee / ChargeDelivery fee, registration, doc-stamp, etc.Free description and price
DiscountFlat-amount discount across the whole quoteUse a negative unit price

For a percentage discount on a single line, use that line's Discount % column. For a flat-amount discount across the whole quote, add a separate Discount line with a negative unit price.

Vehicle line auto-fill

Picking a Vehicle Model on a vehicle line triggers an onchange that:

  1. Sets the line's Description to the vehicle's display name ([CODE] Model Name)
  2. Sets Unit Price to the vehicle's list_price (the SRP synced from the brand's distributor) only if the unit price is currently empty — agents can override
  3. Auto-flips the line type to vehicle if it was on the default accessory

This makes adding a vehicle to a quote a single click — pick the model, the rest fills in.

Totals panel

Below the line list, the totals panel shows the rolled-up amounts:

FieldComputed from
SubtotalSum of all quotation_line_ids.subtotal
DiscountsSum of all quotation_line_ids.discount_amount
TotalSum of all quotation_line_ids.total

These three are stored fields on crm.lead, so they appear in lead list views, kanban cards, and filters. The quotation_total is also what the financing block uses as the deal's gross amount.

Quotation tab with line items and totals

The Validity (days) field defaults to 30 — change it before printing the PDF if the customer asks for a longer or shorter window.

The Notes / Terms field is a free-text customer-facing block printed on the PDF. Use it for delivery terms, validity caveats, exclusions, paint/colour disclaimers — anything you want in writing.

Financing block

When the customer wants to finance through a bank, fill the Financing block.

Financing block with bank loan selected

FieldNotes
Payment MethodCash / Bank Loan / In-House Financing. Defaults to Cash.
Update Bank RatesManual sync trigger — see below
Financing Bankm2o → crm.bank, scoped to the lead's brand. Visible only when payment method is Bank Loan.
Loan Termm2o → crm.bank.rate, scoped to the chosen bank.
Term (months)Read-only, related from the rate row
Annual Interest %Read-only, related from the rate row
Rate TypeRead-only — Add-On or Effective. Defines the math.
Min DP %Read-only minimum down payment percentage published by the bank
Down PaymentMonetary, agent-entered
Down Payment %Computed: (down_payment / quotation_total) × 100
Loan PrincipalComputed: quotation_total − down_payment
Total InterestComputed (see math below)
Monthly AmortizationComputed (see math below)
Total PayableComputed: down_payment + principal + total_interest

Add-On vs Effective rate math

The math depends on the Rate Type stored on the chosen crm.bank.rate row. Different banks quote differently, so the calculator handles both transparently.

Add-On rate (most Filipino dealer financing):

total_interest = principal × (annual_rate / 100) × years
monthly = (principal + total_interest) / months

Effective rate (standard amortizing PMT):

r = (annual_rate / 100) / 12 # monthly rate
factor = (1 + r) ^ months
monthly = principal × (r × factor) / (factor − 1)

If annual_rate = 0, the effective formula falls back to monthly = principal / months.

Worked example

Customer wants a vehicle priced at ₱1,200,000, with a ₱240,000 down payment (20%), financed over 48 months at a published 6.50% annual rate.

Principal = 1,200,000 − 240,000 = ₱960,000

Add-OnEffective
Years44
Total interest960,000 × 0.065 × 4 = ₱249,600derived from monthly: monthly × 48 − 960,000₱131,000
Monthly amortization(960,000 + 249,600) / 48 = ₱25,200r = 0.005417, monthly₱22,729
Total payable240,000 + 960,000 + 249,600 = ₱1,449,600₱1,331,000

Same vehicle, same down payment, same advertised rate — but the customer pays roughly ₱118,000 more under the add-on convention. This is why the rate type matters and why each bank's crm.bank.rate row is tagged with its specific convention.

Down payment warning

If the entered down payment is below the bank's published min_dp_pct, a soft warning appears on the form:

Down payment 12.00% is below this bank's minimum of 20.00%.

DP warning state

The warning does not block save or endorse — agents can override with manager approval and the warning stays visible to the auditor on the form.

Update Bank Rates button

Banks and their loan terms are synced from the brand's distributor. The cron job CRM: Sync Distributor Banks and Rates runs every 6 hours, but if a bank publishes a new rate sheet between cron runs and the agent needs it now, click Update Bank Rates on the Quotation tab.

The button:

  1. Calls the same classmethod the cron uses (crm.brand._cron_sync_distributor_banks via sudo, so any BPO agent can trigger it without distributor credentials)
  2. Pulls banks and rates from every active distributor-connected brand
  3. Shows a success toast with the bank / rate counts
  4. Re-opens the lead form so the Financing Bank and Loan Term dropdowns refresh immediately

The button is visible only when Payment Method is set to Bank Loan.

Click Print Quotation in the lead form header to render the customer-facing PDF.

Print Quotation button on the lead header

The PDF includes:

  • Header — Brand logo, lead reference (LEAD/00042), date, validity (in days)
  • Customer block — Name, mobile, phone, email, address
  • Vehicle of interest — Display name, preferred colour, quantity
  • Line items table — Description, qty, unit price, discount, total
  • Totals panel — Subtotal, discounts, grand total
  • Financing block (only when Payment Method = Bank Loan) — Bank, term, rate, rate type, down payment + DP%, loan principal, total interest, monthly amortization, total payable
  • Terms / notes — Free text from the Quotation tab
  • Trade-in summary — If the lead has a trade-in vehicle

The PDF is also registered under the form's Print menu binding, so users who prefer the dropdown menu can find it there.

Quotation PDF preview

The action requires at least one quotation line. Clicking Print on an empty quote raises:

No quotation lines on this lead. Add at least one line on the Quotation tab before printing.

Email Quotation

Click Email Quotation in the lead form header to send the PDF directly to the customer.

The action opens the standard Odoo mail composer pre-loaded with the CRM Lead: Customer Quotation template:

  • Toobject.email (the customer's email field on the lead)
  • From — The assigned BPO agent's email (or the company email as fallback)
  • SubjectVehicle Quotation - {brand} {vehicle}
  • Body — Greeting, vehicle, total, monthly amortization (when financed), validity, signed by the assigned agent
  • Attachment — The quotation PDF, automatically generated and attached via report_template_ids

Email Quotation composer

The agent can edit the subject, body, recipients, and add CC/BCC before sending.

The action requires both quotation lines and a customer email field. Missing either raises:

No customer email on this lead. Fill in the Email field before sending the quotation.

Lock on endorse

Once an agent clicks Endorse w/ S.Q. or Endorse w/o S.Q., the entire Quotation tab becomes read-only on the BPO side. The dealer instance is now the source of truth.

Locked fields (enforced server-side):

  • quotation_validity_days
  • quotation_notes
  • quotation_payment_method
  • quotation_bank_id
  • quotation_bank_rate_id
  • quotation_down_payment
  • quotation_line_ids (the entire line editor)

Attempting to edit any locked field raises:

Cannot edit quotation field(s) ... on lead "LEAD/00042" — the lead has been endorsed to the dealership and the quotation is locked. The dealer is the source of truth from this point.

The lock is enforced in crm.lead.write() — it catches all edit paths including kanban inline edits, bulk updates, and direct ORM writes from server actions. Print and Email Quotation actions still work after endorse (so agents can re-print or re-send the original quote for the customer's records).

What the dealer receives on endorse

When a quotation-bearing lead is endorsed (either variant), the BPO push payload now carries:

Line items:

quotation_lines: [
{sequence, line_type, name, quantity, unit_price, discount_pct,
vehicle_default_code, vehicle_external_ref, vehicle_model_name},
...
]
quotation_subtotal, quotation_discount_total, quotation_total,
quotation_validity_days, quotation_notes

Financing snapshot:

quotation_payment_method, quotation_bank_name, quotation_bank_code,
quotation_term_months, quotation_interest_rate, quotation_rate_type,
quotation_min_dp_pct, quotation_down_payment, quotation_down_payment_pct,
quotation_principal, quotation_total_interest,
quotation_monthly_amortization, quotation_total_payable

The dealer side stores all this as a frozen snapshot on dealer.crm.lead.quotation.line and the bpo_quotation_* fields, then — for Endorse w/ S.Q. — generates one sale.order.line per quotation line on the draft sale.order. Vehicle lines map to the matched local product; non-vehicle lines hang on a placeholder service product so the SO line carries the BPO description + price without needing a real catalog entry per accessory or fee.

See the Dealer CRM Lead Intake page for what the dealer agent sees on the receiving side.

Access matrix

GroupBuild quoteEdit after endorsePrint PDFSend EmailTrigger Update Bank Rates
BPO AgentOwn claimed leads❌ (locked)Own leadsOwn leads
BPO SupervisorAny open lead❌ (locked)AnyAny
BPO ManagerAny open lead❌ (locked)AnyAny✅ + Brand-level Sync Banks

The Update Bank Rates button calls the cron classmethod via sudo(), so even agents without direct credentials on the brand's distributor can refresh the rate dropdowns.

Audit trail

crm.lead.quotation.line records carry the standard Odoo bookkeeping fields (create_uid, create_date, write_uid, write_date). The parent crm.lead tracks every change to:

  • quotation_subtotal, quotation_discount_total, quotation_total (stored computed)
  • quotation_payment_method
  • quotation_bank_id, quotation_bank_rate_id
  • quotation_down_payment, quotation_down_payment_pct
  • quotation_principal, quotation_total_interest, quotation_monthly_amortization, quotation_total_payable

Tracked changes appear in the lead's chatter, so an auditor reviewing a closed sale can see exactly when each financing change happened and who made it. The lock-on-endorse rule guarantees that the BPO-side snapshot of the financing terms at endorsement time is immutable for the lifetime of the lead.

Typical audit questions

"Show me every lead where the agent overrode the bank's minimum down payment."

  • The DP warning is computed but not stored, so it doesn't filter directly. Workaround: pull (brand_id, quotation_bank_rate_id, quotation_down_payment_pct) and filter quotation_down_payment_pct < quotation_min_dp_pct. Or look for the warning string in chatter — agents who proceed past the warning leave a system note on save.

"Which agent approved the largest discount last month?"

  • Pipeline → All Leads → Filter by quotation_discount_total > X and write_date in range. Group by Agent.

"Was the quotation that closed this won lead the same as what the customer received?"

  • Open the BPO lead. Even after endorse, the Quotation tab is read-only and shows the snapshot at endorsement time. Compare against the customer's emailed PDF (also stored on the lead's chatter as an attachment).

Retention

  • Quotation lines — never auto-deleted. copy=True so duplicating a lead carries the quote.
  • Financing snapshot fields — frozen at endorse, never auto-deleted.
  • PDF attachments — when the agent clicks Email Quotation, the rendered PDF is stored as a mail.message attachment on the lead. The mail template has auto_delete=True, so the outgoing mail.mail row is cleaned up after delivery, but the chatter attachment persists.

Troubleshooting

SymptomLikely cause
"Add a line" doesn't appearLead is unclaimed (user_id empty) or already endorsed. Claim it first, or accept the lock on endorsed leads.
Vehicle picker dropdown is emptyThe lead has no brand selected, or the brand has no vehicle models synced. Open Brands → click Sync Vehicle Models.
Financing Bank dropdown is emptyThe brand has no synced banks. Click Update Bank Rates on the Quotation tab, or open Brands → click Sync Banks.
Loan Term dropdown is empty after picking a bankThe bank has no rate rows on the distributor side. Distributor admin needs to add at least one cheryapp.bank.rate row.
Monthly Amortization shows 0Either Payment Method ≠ Bank Loan, or no Loan Term selected, or principal ≤ 0 (down payment ≥ total).
DP warning visible but agent wants to proceedClick Save anyway — the warning is informational. Auditor will see it on the form.
Print Quotation raises "no quotation lines"Add at least one line on the Quotation tab.
Email Quotation raises "no customer email"Fill in the Email field on the lead's customer block.
"Cannot edit quotation field(s)" error after endorseThis is correct — the lead is endorsed and the Quotation is locked. To revise, talk to the dealer (who is now the source of truth) or, in extreme cases, ask a manager to use with_context(skip_stage_transition_check=True) via the shell.