Quote builder
@dealdesk/quote-builder
Price a quote from your price book CSV and the products a customer picked. Handles flat, volume, and graduated tier pricing plus line and deal discounts, checks per-product discount limits, and writes a customer-ready quote and a deal file for the order form.
- Activity
- New: be the first to try it
- Installs
- 0 installs
- Likes
- 0 likes
- Clones
- 0 clones
- 1---
- 2name: quote-builder
- 3description: Build a priced quote from a price book CSV and the products and quantities a customer wants. Applies flat, volume (all units at the tier reached), and graduated tier pricing, then line and deal discounts, checks each product's max discount, and writes a customer-ready quote plus a deal file for the order form. Use when the user says "quote 60 seats and 2.5M API calls", "what does this cost over two years", "price this deal", "build a quote for", or "redo the quote with 15% off".
- 4license: MIT
- 5---
- 7# Quote builder
- 9Prices a quote the same way every time, from the price book, so nobody does tier math in their head. The script does the math; you collect the selections, explain the result, and flag anything that needs approval.
- 11## When to use
- 13- A rep needs a quote for a set of products, quantities, a term, and maybe a discount.
- 14- Comparing options: "what if they go to 200 seats?" or "1 year vs 3 years".
- 15- Not for usage billing after the fact, and not for changing the price book itself (offer to edit the CSV only if the user asks).
- 17## Inputs
- 191. **Price book** at `data/price-book.csv` (see the kit's `examples/price-book.csv`). One row per price tier:
- 21 | Column | Meaning |
- 22 | --- | --- |
- 23 | `sku`, `name`, `unit` | Product identity |
- 24 | `billing` | `recurring` (priced per unit per year) or `one_time` |
- 25 | `pricing` | `flat`, `volume` (every unit gets the tier the total reaches), or `tiered` (each band priced on its own, like tax brackets) |
- 26 | `min_qty`, `max_qty` | Tier band. Leave `max_qty` empty on the last tier. Bands must be contiguous |
- 27 | `unit_price` | Price per unit in that band |
- 28 | `max_discount_pct` | Optional: the most total discount off list allowed without approval |
- 30 If the user's price book is a spreadsheet or another layout, convert it to this shape in `data/` and show them the result once.
- 322. **Selections** at `data/selections.json`: `customer`, `term_months` (default 12), `currency` (default USD), optional `deal_discount_pct` (applies to recurring lines that do not set their own), optional `discount_one_time: true` to also discount one-time fees, `valid_days` (default 30), and `lines` of `{ "sku", "quantity", "discount_pct"? }`.
- 34Ask for the term and quantities if missing. Never guess a discount.
- 36## Steps
- 381. Check the price book has every SKU the user named. If a name is close but not exact, ask which SKU they meant.
- 392. Write `data/selections.json` and show the user the selections in one short table.
- 403. Run:
- 42 ```bash
- 43 node scripts/quote.mjs data/price-book.csv data/selections.json --out out --name quote
- 44 ```
- 46 Use a distinct `--name` per scenario (`quote-1yr`, `quote-3yr`) when comparing.
- 484. Read `out/<name>.json`. Report the totals, per-line savings, and every item in `warnings`.
- 495. For comparisons, put scenarios side by side: total for term, annual recurring, effective discount.
- 506. Next steps: `out/<name>-deal.json` feeds straight into `discount-approval-check` and `po-creator` (add the start date, billing contact, and terms first).
- 52## Output to the user
- 54```markdown
- 55**Quote Q-2026-0311, Northwind Traders LLC, 24 months:** $82,970.00 total ($38,985.00 per year recurring, $5,000.00 one-time). 16.2% off list.
- 57| SKU | Qty | Your price / unit | Savings |
- 58| --- | ---: | ---: | ---: |
- 59| PLAT-SEAT | 60 | $486.00 | 19% (volume tier 10% + 10% deal discount) |
- 61Warnings: none. Files: out/quote.md (customer copy), out/quote.json, out/quote-deal.json.
- 62```
- 64The "How the price was built" and "Warnings" sections in `quote.md` are marked internal. Remind the user to delete them before sending the quote to a customer.
- 66## Guardrails
- 68- Prices come only from the price book. If a SKU has no price, stop and ask; never estimate.
- 69- A discount past `max_discount_pct` is a warning, not a block. Say plainly who usually approves it and suggest `discount-approval-check`.
- 70- Do not change the price book unless the user asks you to.
- 71- One currency per quote. If the price book has several, ask which one.
