How to Create Sales Quotes or Estimates from Google Sheets (2026)
A sales quote is just a row of numbers until a client needs a document they can approve. You can keep the pipeline in Google Sheets and still send a branded PDF estimate for each deal, without retyping names and totals into a fresh Doc every time.
This is the same “one document per row” idea as invoices, but the document is a quote: it has a validity date, often a numbered quote ID, and it should only be generated when a row is actually ready to send. For invoices and certificates specifically, see the invoices and certificates guide.
Watch the 2-minute walkthrough on YouTube. Short on time? Watch the 50-second version.
Step 1: a sheet that can become a quote
Use columns such as: Quote #, Client, Email, Item, Qty, Rate, Total, Valid until, Status, and empty Quote Doc / Quote PDF columns for links. Status values like Draft, Ready, Sent and Accepted keep unready rows out of a batch.
Start from a sample tracker (sample data only): Make a copy of the Sales Quote & Estimate Tracker.
Format currency and dates in the sheet, not only in the Doc. A display column such as =TEXT(G2,"$#,##0.00") for Total, and =TEXT(H2,"MMM d, yyyy") for Valid until, gives the template clean text. If you use those helper columns, point the placeholders at the helper headers (for example {{Total display}}).
Step 2: a Google Docs quote template
Create a Doc with your logo, a “Quote” or “Estimate” heading, bill-to lines, a simple items table, a total, payment or acceptance terms, and a validity sentence. Placeholders should match headers exactly, for example:
{{Quote #}},{{Client}},{{Email}}{{Item}},{{Qty}},{{Rate}},{{Total}}{{Valid until}}
Or start from a matching Doc: Make a copy of the quote template.
This pattern handles one item (or one summary line) per row. If a quote has several line items, either keep one row per item and generate one page each, or put a pre-joined summary in a single cell (for example a list of items) and merge that cell into the Doc. Multi-row tables that grow with a variable number of lines need a more involved script.
Copy the template ID (between /d/ and /edit in the URL) and the ID of a Drive folder for finished files (after /folders/).
Step 3: generate PDFs only for Ready rows
In the sheet, open Extensions › Apps Script, paste the script, fill in the two IDs, save, reload the sheet, and use Merge › Create quotes for ready rows. Approve permissions the first time. Rows that already have a Quote PDF link are skipped, so you can safely run it again.
/**
* Create a Google Doc + PDF quote for each row whose Status is "Ready"
* and whose "Quote PDF" cell is still empty.
* Template placeholders must match headers, e.g. {{Quote #}}, {{Client}}, {{Total}}.
* Menu: after saving, reload the sheet and use Merge > Create quotes for ready rows.
*/
const TEMPLATE_ID = 'PASTE_TEMPLATE_DOC_ID';
const FOLDER_ID = 'PASTE_OUTPUT_FOLDER_ID';
function onOpen() {
SpreadsheetApp.getUi().createMenu('Merge')
.addItem('Create quotes for ready rows', 'mergeReadyQuotes')
.addToUi();
}
function mergeReadyQuotes() {
const sh = SpreadsheetApp.getActiveSheet();
const data = sh.getDataRange().getDisplayValues();
const raw = sh.getDataRange().getValues();
const head = data[0];
const statusCol = head.indexOf('Status');
const pdfCol = ensureCol_(sh, head, 'Quote PDF');
const docCol = ensureCol_(sh, head, 'Quote Doc');
const folder = DriveApp.getFolderById(FOLDER_ID);
for (let r = 1; r < data.length; r++) {
if (String(raw[r][statusCol] || '').toLowerCase() !== 'ready') continue;
if (sh.getRange(r + 1, pdfCol).getValue()) continue; // already generated
const rec = {};
head.forEach((h, i) => { if (h) rec[h] = data[r][i]; });
const name = 'Quote-' + (rec['Quote #'] || rec['Client'] || ('row-' + (r + 1)));
const copy = DriveApp.getFileById(TEMPLATE_ID).makeCopy(name, folder);
const doc = DocumentApp.openById(copy.getId());
const body = doc.getBody();
Object.keys(rec).forEach((h) => {
const escaped = h.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
body.replaceText('\\{\\{' + escaped + '\\}\\}', rec[h] || '');
});
doc.saveAndClose();
const pdf = folder.createFile(copy.getAs(MimeType.PDF).setName(name + '.pdf'));
sh.getRange(r + 1, docCol).setValue(copy.getUrl());
sh.getRange(r + 1, pdfCol).setValue(pdf.getUrl());
}
}
function ensureCol_(sh, head, name) {
let i = head.indexOf(name);
if (i === -1) {
i = head.length;
sh.getRange(1, i + 1).setValue(name);
head.push(name);
}
return i + 1;
}
Tips
- Numbering. A simple formula such as
="Q-2026-"&TEXT(ROW()-1,"000")can fill Quote #. Change it if you already have a numbering scheme. - Don't send drafts. Leave Status as Draft until amounts and terms are right, then switch to Ready and run the menu.
- Turning a quote into an invoice. Once it's accepted, copy the row to your invoice sheet (or change the template) rather than editing the PDF by hand. See how to make invoices from rows.
- Execution time. Apps Script runs are capped at six minutes. A large backlog of Ready rows may need more than one run; the empty-PDF-link check is what lets you resume.
See SheetWeave
Related Google Sheets guides
- How to generate a PDF or Google Doc for each row in Google Sheets
- Best Autocrat alternatives for mail merge to PDF and Docs
- Make invoices or certificates from Google Sheets rows
- Create sales quotes or estimates from Google Sheets
- Generate receipts from Google Sheets rows
- Create purchase orders from Google Sheets
- Make packing slips from Google Sheets orders
- Generate a PDF certificate from a Google Form response
- Issue numbered training completion certificates with expiry dates
- Make a PDF progress report for each student
- Generate offer letters from a Google Sheets hiring tracker
- Email each row’s PDF as an attachment
- Automatic email reminders from a date column
- Contract renewal and expiration alerts from Google Sheets
- Track certificate of insurance (COI) expirations in Google Sheets
- Track employee license and certification expirations
- Track equipment and vehicle maintenance with reminders
- Track warranty expiration dates with reminders
- Birthday and work anniversary reminders from Google Sheets
- Rent due and lease renewal reminders from Google Sheets
- Send invoice payment reminders from Google Sheets
- Highlight overdue tasks and send email reminders
- Follow-up reminders for leads, clients and candidates
- Membership renewal reminders for clubs and associations
- SheetWeave vs Autocrat
- SheetWeave vs Document Studio
- SheetWeave vs RowDocs
Instructions refer to Google Sheets, Google Docs, Google Forms, Google Drive and Apps Script as they worked on November 10, 2026. Not affiliated with Google or any other vendor named here. Google Sheets, Google Docs, Google Forms, Google Slides and Google Drive are trademarks of Google LLC. Features change, so verify in your own account. Questions or corrections: hello@loomwise.app.