Concept
Step 1: the template
Create a Google Doc called Invoice Template and design it as you like (logo, address, table). Wherever data should go, type a placeholder in double curly braces:
INVOICE {{InvoiceNo}} Date: {{Date}}
Bill to: {{Customer}}
Phone: {{Phone}}
Item: {{Item}}
Amount: ₹{{Amount}}
GST: ₹{{Gst}}
Total: ₹{{Total}}
Copy the Doc's ID from its URL: docs.google.com/document/d/<TEMPLATE_ID>/edit.
Step 2: the sheet
Tab Invoices:
| A InvoiceNo | B Date | C Customer | D Phone | E Email | F Item | G Amount | H PDF Link |
|---|
Step 3: the script
const TEMPLATE_ID = 'PASTE_TEMPLATE_DOC_ID';
const OUTPUT_FOLDER_ID = 'PASTE_FOLDER_ID';
const GST_RATE = 0.03;
function createInvoices() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Invoices');
const lastRow = sheet.getLastRow();
if (lastRow < 2) return;
const rows = sheet.getRange(2, 1, lastRow - 1, 8).getValues();
const folder = DriveApp.getFolderById(OUTPUT_FOLDER_ID);
const tz = Session.getScriptTimeZone();
rows.forEach((r, i) => {
const [invoiceNo, date, customer, phone, email, item, amount, link] = r;
if (!invoiceNo || link) return; // skip blank rows and rows already done
const gst = Math.round(amount * GST_RATE * 100) / 100;
const total = amount + gst;
const fmt = n => Number(n).toLocaleString('en-IN', { minimumFractionDigits: 2 });
// 1. Copy the template
const copy = DriveApp.getFileById(TEMPLATE_ID).makeCopy(`Invoice ${invoiceNo}`, folder);
// 2. Replace placeholders
const doc = DocumentApp.openById(copy.getId());
const body = doc.getBody();
const values = {
InvoiceNo: invoiceNo,
Date: Utilities.formatDate(new Date(date), tz, 'dd-MMM-yyyy'),
Customer: customer,
Phone: phone,
Item: item,
Amount: fmt(amount),
Gst: fmt(gst),
Total: fmt(total)
};
Object.entries(values).forEach(([key, val]) => {
body.replaceText(`\\{\\{${key}\\}\\}`, String(val));
});
doc.saveAndClose();
// 3. Export as PDF and remove the temporary Doc
const pdf = folder.createFile(copy.getAs(MimeType.PDF)).setName(`Invoice ${invoiceNo}.pdf`);
copy.setTrashed(true);
// 4. Save the link back to the sheet
sheet.getRange(i + 2, 8).setValue(pdf.getUrl());
// 5. Email it (optional)
if (email) {
GmailApp.sendEmail(email, `Invoice ${invoiceNo}`,
`Dear ${customer},\n\nPlease find your invoice attached.\n\nThank you.`,
{ attachments: [pdf.getBlob()], name: 'Accounts Team' });
}
});
}
Why each step matters
makeCopy— never edit the template itself; always work on a copy.replaceTextuses a regular expression. Curly braces have special meaning there, so we escape them:\\{\\{Date\\}\\}. Without escaping, some placeholders won't be found.saveAndClose()— must be called before export, or the PDF may show the old placeholders.getAs(MimeType.PDF)— converts the Doc to a PDF blob;createFilesaves it.setTrashed(true)— moves the temporary Doc to the bin so your folder only has PDFs.- The link column doubles as a "done" marker, so re-running only creates new invoices.
Variations
- Use a Google Slides template for designs with images (
SlidesApp,replaceAllText). - Combine with the year/month folder helper from the Drive lesson.
- Run it from a menu item, or from a form-submit trigger to invoice each new order automatically.
Key points
- Template + placeholders + copy + replace + export = automated documents.
- Escape
{and}inreplaceTextpatterns. - Store the PDF link to avoid creating duplicates.