Google Apps Script

Tự Động Tạo PDF Từ Template Google Docs Bằng Apps Script

Tuân HoangTuân Hoang
14 tháng 5, 2026
5 phút đọc
Ảnh minh họa bài viết: Tự Động Tạo PDF Từ Template Google Docs Bằng Apps Script

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

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.

Nhận thông báo khi có bài viết mới. Không spam, hứa luôn! 😊

Bình luận (0)

Vui lòng đăng nhập để tham gia thảo luận