Tự Động Gửi Email Khi Có Row Mới Trong Google Sheets Bằng Apps Script
Giới Thiệu
Mỗi khi nhân viên điền form đặt hàng, khách hàng gửi yêu cầu hỗ trợ, hoặc nhóm bán hàng cập nhật deal mới... bạn muốn nhận email ngay lập tức. Google Apps Script cho phép làm điều này hoàn toàn miễn phí, chạy trực tiếp trên Google infrastructure — không cần server, không cần Zapier trả phí.
Bài này hướng dẫn 3 cách gửi email tự động từ Google Sheets: khi có form submit, khi có row mới, và khi có ô thay đổi giá trị.
Cách 1: Gửi Email Khi Google Form Có Submission Mới
Đây là cách phổ biến nhất — mỗi khi khách hàng điền form, bạn nhận email ngay lập tức.
Bước 1: Mở Apps Script Editor
Trong Google Sheets, vào Extensions → Apps Script. Xóa code mặc định và paste code sau:
function onFormSubmit(e) {
try {
const sheet = e.range.getSheet();
const row = e.range.getRow();
const headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
const values = e.values; // Mảng giá trị từ form
// Tạo nội dung email từ dữ liệu form
let emailBody = '<h2>Có đơn hàng mới!</h2>';
emailBody += '<table border="1" cellpadding="8" style="border-collapse:collapse">';
for (let i = 0; i < headers.length; i++) {
emailBody += '<tr>';
emailBody += '<td><strong>' + headers[i] + '</strong></td>';
emailBody += '<td>' + (values[i] || '') + '</td>';
emailBody += '</tr>';
}
emailBody += '</table>';
emailBody += '<p>Xem chi tiết: <a href="' + SpreadsheetApp.getActiveSpreadsheet().getUrl() + '">Mở Google Sheets</a></p>';
// Gửi email
GmailApp.sendEmail(
'email-cua-ban@gmail.com', // Đổi thành email nhận thông báo
'Đơn hàng mới #' + row,
'', // plain text (để trống vì dùng htmlBody)
{
htmlBody: emailBody,
name: 'SheetStore Thông Báo'
}
);
console.log('Email sent for row: ' + row);
} catch (error) {
console.error('Error sending email: ' + error.toString());
// Không throw để tránh block form submission
}
}
Bước 2: Cài Installable Trigger
Trigger đơn giản onFormSubmit trong code không đủ quyền gửi email. Phải dùng installable trigger:
- Trong Apps Script Editor, click Triggers (icon đồng hồ bên trái)
- Click + Add Trigger
- Function:
onFormSubmit - Event source: From spreadsheet
- Event type: On form submit
- Save → Authorize permissions
Từ giờ, mỗi khi form được submit, hàm onFormSubmit sẽ tự chạy và gửi email cho bạn.
Cách 2: Gửi Email Khi Có Row Mới (Không Dùng Form)
Khi nhân viên tự thêm row trực tiếp vào sheet (không qua form), dùng trigger onChange:
function onSheetChange(e) {
try {
// Chỉ xử lý khi có row mới được thêm
if (e.changeType !== 'INSERT_ROW') return;
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Đơn Hàng');
if (!sheet) return;
const lastRow = sheet.getLastRow();
if (lastRow <= 1) return; // Chỉ có header, bỏ qua
// Lấy dữ liệu row vừa thêm
const headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
const newRowData = sheet.getRange(lastRow, 1, 1, sheet.getLastColumn()).getValues()[0];
let emailBody = '<h2>Row mới được thêm vào sheet "Đơn Hàng"</h2>';
emailBody += '<table border="1" cellpadding="8" style="border-collapse:collapse">';
headers.forEach((header, i) => {
if (newRowData[i]) {
emailBody += '<tr><td><strong>' + header + '</strong></td><td>' + newRowData[i] + '</td></tr>';
}
});
emailBody += '</table>';
MailApp.sendEmail({
to: 'quan-ly@cong-ty.com',
subject: 'Row mới tại hàng ' + lastRow + ' — ' + new Date().toLocaleDateString('vi-VN'),
htmlBody: emailBody
});
} catch (error) {
console.error('onChange error: ' + error.toString());
}
}
Cài trigger: Event type chọn On change, gắn với hàm onSheetChange.
Cách 3: Báo Cáo Tổng Hợp Theo Lịch (Daily/Weekly)
Thay vì nhận email mỗi row, gửi tổng hợp hàng ngày lúc 8 giờ sáng:
function sendDailyReport() {
try {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Đơn Hàng');
const today = new Date();
today.setHours(0, 0, 0, 0);
const allData = sheet.getDataRange().getValues();
const headers = allData[0];
// Tìm cột ngày (giả sử cột đầu tiên là ngày)
const todayRows = allData.slice(1).filter(row => {
const rowDate = new Date(row[0]);
rowDate.setHours(0, 0, 0, 0);
return rowDate.getTime() === today.getTime();
});
if (todayRows.length === 0) {
console.log('No new orders today, skipping email.');
return;
}
let emailBody = '<h2>Báo Cáo Đơn Hàng Ngày ' + today.toLocaleDateString('vi-VN') + '</h2>';
emailBody += '<p>Tổng số đơn hôm nay: <strong>' + todayRows.length + '</strong></p>';
emailBody += '<table border="1" cellpadding="6" style="border-collapse:collapse;width:100%">';
emailBody += '<tr>' + headers.map(h => '<th style="background:#f0f0f0">' + h + '</th>').join('') + '</tr>';
todayRows.forEach(row => {
emailBody += '<tr>' + row.map(cell => '<td>' + cell + '</td>').join('') + '</tr>';
});
emailBody += '</table>';
GmailApp.sendEmail('bao-cao@cong-ty.com', 'Báo cáo đơn hàng ngày ' + today.toLocaleDateString('vi-VN'), '', { htmlBody: emailBody });
console.log('Daily report sent: ' + todayRows.length + ' orders');
} catch (error) {
console.error('Daily report error: ' + error.toString());
}
}
Cài time-based trigger: Event source: Time-driven, Type: Day timer, Time: 8am–9am.
GmailApp vs MailApp: Dùng Cái Nào?
| Tiêu chí | GmailApp | MailApp |
|---|---|---|
| Quota miễn phí | 100 email/ngày | 100 email/ngày |
| Google Workspace | 1.500 email/ngày | 1.500 email/ngày |
| Reply-To tùy chỉnh | Có | Có |
| Đính kèm file | Có (từ Drive) | Có (blob) |
| Nên dùng khi | Cần full Gmail API | Gửi đơn giản |
Xử Lý Lỗi Và Best Practices
- Luôn bọc trong try/catch: Nếu hàm throw error, trigger sẽ bị vô hiệu hoá sau nhiều lần thất bại.
- Dùng PropertiesService để lưu config: Email nhận thông báo nên lưu trong Script Properties, không hardcode.
- Kiểm tra quota: Apps Script giới hạn 6 phút execution/trigger và 100 email/ngày (tài khoản cá nhân).
- Log để debug: Dùng
console.log()và xem trong Executions tab của Apps Script.
// Lấy email từ Script Properties thay vì hardcode
function getRecipientEmail() {
const props = PropertiesService.getScriptProperties();
return props.getProperty('NOTIFY_EMAIL') || Session.getActiveUser().getEmail();
}
Use Case Thực Tế
- Form đặt hàng: Mỗi đơn mới → email cho kho và sales ngay lập tức
- Lead form: Khách tiềm năng đăng ký → email cho sales kèm thông tin đầy đủ
- Báo cáo hàng ngày: 8h sáng nhận tổng kết doanh thu, tồn kho, đơn chờ xử lý
- Cảnh báo tồn kho thấp: Khi ô tồn kho < mức tối thiểu → email cho bộ phận mua hàng
Muốn đi xa hơn? Kết hợp với Apps Script API endpoint để nhận dữ liệu từ bên ngoài vào Sheet rồi gửi email thông báo tự động.
Chia sẻ bài viết:
Tuân Hoang
Đội ngũ SheetStore
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.