How to Model Quotes and Proposals in a Custom CRM
A practical guide to modelling quotes and proposals in a custom CRM: versioned line items, pricing snapshots and approval trails you can audit.
Modelling quotes and proposals in a custom CRM means giving each offer its own record with versioned line items, pricing snapshots and an approval trail, then linking that record to the opportunity it came from. The quote stays structured data instead of a PDF, so pricing, discounts and win rates remain queryable long after the deal closes.
What does it mean to model a quote in a custom CRM?
Modelling a quote means splitting a commercial offer into records a database can query: header details, line items, a pricing snapshot, an approval trail and a status that moves from draft to accepted. The PDF is an output.
In CRM terms, an entity is one kind of record the system stores, such as a contact, an opportunity or a quote. A line item is a single chargeable row inside a quote: one product, one quantity, one agreed price. A snapshot is a frozen copy of values at a point in time, so later changes to a price list cannot rewrite what a customer was sent.
Teams that start from the document design the PDF first and then try to store whatever it contains. That order produces a model that cannot answer basic questions. Design the records first, decide what has to be queryable, and treat the PDF as one view over that data. This is the kind of modelling work that sits at the centre of custom CRM development.
A model is finished when it can answer a fixed set of questions. Write them down before drawing any tables:
- Who is the quote for, and which opportunity does it belong to?
- What is being sold, in what quantity, at what price, and under which discount?
- Who approved that discount, and when?
- Which currency, terms and validity period applied at the moment of sending?
- Which version did the customer actually accept?
Every field should serve one of those questions. Fields that serve none of them belong in a notes field, or nowhere at all.
Which records should a quote data model contain?
A workable quote model needs five records: a quote header, line items, a price book, an approval record and an acceptance record. The rendered PDF and the email that carried it are generated artefacts you can rebuild on demand.
Keep the core small and let everything else reference it.
- Quote header: customer, opportunity, currency, validity dates, status, subtotal, discount total and grand total.
- Line item: product reference, description as sent, quantity, unit price, discount and line total.
- Price book entry: product, tier, unit price, floor price and the date range it applies to.
- Approval: the version approved, the threshold that triggered it, the approver and the timestamp.
- Acceptance: the version accepted, who accepted it, when, and the signature or click record.
Storing the description as sent matters more than it looks. If a product is renamed eighteen months later, the quote should still read the way the customer read it.
Two practical limits show up in most builds. Quotes with more than roughly 40 line items need server-side totals and pagination, because browser-side calculation starts to lag. Discount stacks beyond three levels usually need a rules table rather than conditions written into code.
How should you handle quote versions and approvals?
Version the quote header, not the individual line items, and keep every accepted version immutable. Approvals attach to a version and a discount threshold, so the system can prove who signed off on what price and when.
Versioning strategy is a real decision, and the cheap option has a cost later.
| Strategy | Audit trail | Storage growth | Build effort | Fits |
|---|---|---|---|---|
| Overwrite in place | Current state only | None | A few days | One-off internal estimates |
| Snapshot on each send | Every version the customer saw | Linear with revisions | Typically two to four weeks | Most B2B sales teams |
| Differential versions | Full history, compact | Low | Highest, needs merge logic | High-volume quoting with many revisions |
For approvals, thresholds belong in a table an administrator can edit, not in code. A typical ladder looks like this:
- Discounts up to roughly 10%: the owning rep approves and sends.
- Between 10% and roughly 20%: sales manager.
- Non-standard payment terms or anything involving legal: finance or legal review.
- Below the floor price on the price book: a deal desk or founder.
Each rule writes an approval record against a specific version. Without that link, you have an audit trail of approvals that cannot be matched to the prices they authorised.
Should quoting live inside the CRM or in a dedicated tool?
Build quoting inside the CRM when pricing rules are simple and change slowly. Choose a dedicated CPQ tool when configuration, bundling and multi-year pricing get complicated enough that keeping the rules in-house costs more than the licence.
CPQ stands for configure, price, quote: software that lets a buyer assemble a valid product combination and produces compliant pricing for it. The comparison below is the one most teams actually face.
| Criterion | Built inside the CRM | Dedicated CPQ tool | Hybrid |
|---|---|---|---|
| Pricing complexity it handles | A few tiers, simple bundles | Multi-dimensional rules, configurable products | Complex pricing, simple presentation |
| Cost shape | One-off build | Ongoing per-seat licence | Both |
| Time to first usable quote | Longer | Days | Moderate |
| Control over the data model | Full | Limited to vendor objects | Full on the CRM side |
| Maintenance owner | You | Vendor | Split |
The dividing line is usually maintenance rather than features. If your pricing changes more than a few times a year, or if a single quote can contain hundreds of valid combinations, per-seat licence fees are often cheaper than owning that logic. If pricing is stable and quotes are mostly a list of products with a discount, a custom build wins on cost and on how well the data joins to reporting.
How do you connect an accepted quote to delivery and billing?
Link the accepted quote version to a contract or order record, then generate invoices from that frozen snapshot rather than from live product prices. This keeps revenue figures reproducible even after the price book changes.
The moment a customer accepts, three things should happen automatically. The quote version locks, so no further edits are possible without creating a new version. An order or contract record is created that points back to the locked version. A scheduled job handles expiry, so quotes that pass their validity date move to an expired status instead of sitting in the pipeline as open.
Money is the place where small modelling mistakes become visible. Store amounts as integers in the smallest currency unit, usually cents, and never as floating point numbers. A floating point number is a format that cannot represent most decimal fractions exactly, which is why currency stored that way drifts by fractions of a cent. Round once, at the line level, and compute totals from the rounded lines.
What breaks a quote model once it is live?
The common failures are mutable price books, quotes edited after acceptance, and line items that reference products by name instead of by ID. Each one destroys the audit trail you built the model to protect.
Watch for these in the first months after launch:
- A price book edited in place, which silently recalculates historical quotes on the next report.
- A quote edited after acceptance because a rep asked for one small change.
- Products matched by name in an import script, which breaks the moment a name changes.
- Rounding applied in the interface, the server and the invoice export, producing three totals.
- Money stored as floating point, which shows up as one-cent discrepancies in reconciliation.
None of these need new features. They need constraints: immutable accepted versions, price book rows with date ranges instead of overwrites, and foreign keys instead of name matching. A foreign key is a reference to another record's unique identifier, which survives renames.
The test of the model is simple. Two years from now, can you show exactly what a customer agreed to, what it cost, and who approved it? If yes, the PDF is just a rendering.
Frequently asked questions
Should quotes and proposals be stored as records or as PDFs in a CRM?
Store them as records and treat the PDF as a rendering of those records. A PDF cannot be filtered, totalled or joined to revenue reporting, and re-parsing one to answer a pricing question is unreliable. Keep the file as an attachment alongside the structured quote, not instead of it.
How long does it take to build quoting into a custom CRM?
A first version that handles line items, price books and a single approval step typically takes four to eight weeks for one developer, assuming the CRM already exists. Versioning, e-signature and invoice handoff usually add another two to four weeks. Budget extra time for data migration if historical quotes need importing.
How do you handle discount approvals in a custom CRM?
Model approvals as their own records linked to a quote version, with thresholds stored in an editable table rather than hard-coded. When a rep sends a quote that breaches a threshold, the quote moves to a pending status and the approver receives a task. The approver identity and timestamp stay attached to the version they authorised.
What is the difference between a quote, a proposal and an order in a CRM?
A quote is a priced offer with a validity period, a proposal is a document that may bundle several options with narrative content, and an order is the confirmed commitment created when a quote is accepted. Modelling them as separate records keeps reporting clean, because pipeline value should count quotes while delivery and invoicing should count orders.