Tự Động Tạo PDF Từ Template Google Docs Bằng Apps Script
14 tháng 5, 2026
5 phút đọc
Giới Thiệu
Thay vì tạo PDF thủ công từng cái một, Google Apps Script cho phép bạn tự động: đọc dữ liệu từ Google Sheets, điền vào template Google Docs, xuất PDF, lưu vào Drive và gửi email đính kèm. Hoàn hảo cho hóa đơn hàng tháng, hợp đồng, phiếu lương nhân viên.
Chuẩn Bị Template Google Docs
Tạo một Google Doc với các placeholder dạng {{TEN_KHACH_HANG}}, {{SO_TIEN}}, {{NGAY_HOA_DON}}. Lấy Document ID từ URL.
Script Tạo PDF Từ Template
/**
* Tạo PDF từ template Google Docs, điền dữ liệu từ Sheet
* @param {Object} data - Object chứa {TEN_KHACH_HANG, SO_TIEN, ...}
* @param {string} templateId - ID của Google Doc template
* @param {string} outputFolderName - Tên folder lưu PDF
* @returns {File} Google Drive file
*/
function createPdfFromTemplate(data, templateId, outputFolderName) {
try {
// 1. Copy template sang file tạm
const template = DriveApp.getFileById(templateId);
const tempFolder = DriveApp.getRootFolder();
const tempCopy = template.makeCopy('TEMP_' + Utilities.getUuid(), tempFolder);
const tempDoc = DocumentApp.openById(tempCopy.getId());
const body = tempDoc.getBody();
// 2. Thay thế tất cả placeholders
for (const [key, value] of Object.entries(data)) {
const placeholder = '{{' + key + '}}';
body.replaceText(placeholder, String(value || ''));
}
// 3. Thêm ngày tháng tự động
body.replaceText('{{NGAY_TAO}}', new Date().toLocaleDateString('vi-VN'));
tempDoc.saveAndClose();
// 4. Export sang PDF
const pdfBlob = DriveApp.getFileById(tempCopy.getId())
.getAs('application/pdf')
.setName(data.TEN_FILE || ('document_' + Utilities.formatDate(new Date(), 'Asia/Ho_Chi_Minh', 'yyyy-MM-dd')));
// 5. Lưu PDF vào folder
const outputFolder = getOrCreateFolder(outputFolderName);
const pdfFile = outputFolder.createFile(pdfBlob);
// 6. Xóa file tạm
tempCopy.setTrashed(true);
console.log('PDF created: ' + pdfFile.getName() + ' | URL: ' + pdfFile.getUrl());
return pdfFile;
} catch (error) {
console.error('createPdfFromTemplate error: ' + error.toString());
throw error;
}
}
function getOrCreateFolder(folderName) {
const folders = DriveApp.getFoldersByName(folderName);
return folders.hasNext() ? folders.next() : DriveApp.createFolder(folderName);
}
Tạo Hóa Đơn Hàng Loạt Từ Sheet
/**
* Đọc tất cả rows trong sheet "Hóa Đơn" và tạo PDF cho từng dòng
* Chỉ xử lý các row chưa có PDF (cột "PDF URL" trống)
*/
function generateAllInvoices() {
const TEMPLATE_ID = '1aBcD...'; // Thay bằng ID Google Doc template của bạn
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('Hóa Đơn');
const data = sheet.getDataRange().getValues();
const headers = data[0];
// Tìm index các cột
const colPdfUrl = headers.indexOf('PDF URL');
const colEmail = headers.indexOf('Email Khách');
const colSent = headers.indexOf('Đã Gửi');
let generated = 0;
let errors = 0;
for (let i = 1; i < data.length; i++) {
const row = data[i];
// Bỏ qua nếu đã có PDF
if (row[colPdfUrl]) continue;
try {
// Tạo object data cho template
const invoiceData = {};
headers.forEach((header, j) => {
invoiceData[header.toUpperCase().replace(/ /g, '_')] = row[j];
});
invoiceData.TEN_FILE = 'HoaDon_' + (row[0] || i) + '_' + Utilities.formatDate(new Date(), 'Asia/Ho_Chi_Minh', 'yyyy-MM-dd');
// Tạo PDF
const pdfFile = createPdfFromTemplate(invoiceData, TEMPLATE_ID, 'HoaDon_2026');
// Ghi URL PDF vào sheet
sheet.getRange(i + 1, colPdfUrl + 1).setValue(pdfFile.getUrl());
// Gửi email nếu có địa chỉ email
if (colEmail >= 0 && row[colEmail]) {
sendInvoiceEmail(row[colEmail], pdfFile, invoiceData);
if (colSent >= 0) {
sheet.getRange(i + 1, colSent + 1).setValue(new Date().toLocaleString('vi-VN'));
}
}
generated++;
Utilities.sleep(500); // Tránh rate limit
} catch (err) {
console.error('Row ' + (i + 1) + ' failed: ' + err.toString());
sheet.getRange(i + 1, colPdfUrl + 1).setValue('ERROR: ' + err.message);
errors++;
}
}
console.log('Generated: ' + generated + ' | Errors: ' + errors);
SpreadsheetApp.getUi().alert('Hoàn thành: ' + generated + ' hóa đơn. Lỗi: ' + errors);
}
Gửi Email Đính Kèm PDF
function sendInvoiceEmail(recipientEmail, pdfFile, data) {
const companyName = PropertiesService.getScriptProperties().getProperty('COMPANY_NAME') || 'Công ty của bạn';
const htmlBody = [
'<p>Kính gửi ' + (data.TEN_KHACH_HANG || 'Quý khách') + ',</p>',
'<p>' + companyName + ' xin gửi hóa đơn tháng ' + new Date().toLocaleDateString('vi-VN', { month: 'long', year: 'numeric' }) + ' đến quý khách.</p>',
'<p>Vui lòng xem hóa đơn đính kèm hoặc <a href="' + pdfFile.getUrl() + '">tải tại đây</a>.</p>',
'<p>Nếu có thắc mắc, vui lòng phản hồi email này.</p>',
'<p>Trân trọng,<br>' + companyName + '</p>',
].join('');
// Lấy PDF blob để đính kèm
const pdfBlob = pdfFile.getBlob().setName(pdfFile.getName() + '.pdf');
GmailApp.sendEmail(recipientEmail, 'Hóa đơn từ ' + companyName, '', {
htmlBody: htmlBody,
attachments: [pdfBlob],
name: companyName
});
console.log('Invoice email sent to: ' + recipientEmail);
}
Thêm Menu Trong Google Sheets
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('PDF Tools')
.addItem('Tạo PDF Hóa Đơn', 'generateAllInvoices')
.addItem('Tạo PDF Hợp Đồng', 'generateContracts')
.addSeparator()
.addItem('Cài đặt', 'showSettings')
.addToUi();
}
Use Cases Thực Tế
- Phiếu lương nhân viên: Đọc bảng lương từ sheet, tạo PDF cho từng nhân viên, gửi email đính kèm cuối tháng.
- Hóa đơn khách hàng: Sau khi xác nhận thanh toán, tự động tạo và gửi hóa đơn PDF.
- Hợp đồng dịch vụ: Điền thông tin khách hàng vào template hợp đồng, xuất PDF để ký.
- Giấy chứng nhận: Tạo hàng loạt certificate từ danh sách học viên sau khóa học.
Chia sẻ bài viết:
Tuân Hoang
Đội ngũ SheetStore
Google SheetsGoogle Apps ScriptCRMAutomationPhần mềm quản lý doanh nghiệp
Google Workspace Certified, 5+ years experience
Bạn thấy bài viết hữu ích?
Đăng ký nhận thông báo khi có bài viết mới.