Google Sheets Nâng Cao Bài 5: Apps Script - Tự Động Hóa Với JavaScript

Google Sheets Nâng Cao Bài 5: Apps Script - Tự Động Hóa Với JavaScript
Chào mừng bạn đến với Bài 5 trong series Google Sheets Nâng Cao của SheetStore Academy! Nếu bạn đã hoàn thành Bài 4: Hàm QUERY, giờ là lúc bước sang cấp độ hoàn toàn mới — Google Apps Script, công cụ cho phép bạn viết code JavaScript để tự động hóa mọi thứ trong spreadsheet.
Sau bài học này, bạn sẽ biết cách tạo menu tùy chỉnh, trigger tự động, gửi email báo cáo hàng ngày, và xây dựng những quy trình tự động hóa thực sự tiết kiệm hàng giờ làm việc mỗi tuần.
1. Apps Script là gì và tại sao bạn cần?
Google Apps Script là nền tảng lập trình dựa trên JavaScript, được tích hợp sẵn trong hệ sinh thái Google Workspace. Bạn có thể dùng nó để:
- Tự động hóa các tác vụ lặp đi lặp lại trong Google Sheets, Docs, Gmail
- Tạo báo cáo tự động gửi email mỗi sáng
- Kết nối Google Sheets với các dịch vụ bên ngoài qua API
- Xây dựng ứng dụng web đơn giản (Web App) từ dữ liệu spreadsheet
- Tạo workflow phức tạp mà công thức thông thường không thể làm được
Apps Script vs Công thức thông thường
| Tiêu chí | Công thức (VLOOKUP, QUERY...) | Apps Script |
|---|---|---|
| Độ khó | Dễ đến trung bình | Cần biết lập trình cơ bản |
| Tự động hóa | Chỉ tính toán khi có dữ liệu | Chạy theo schedule, trigger |
| Gửi email | Không thể | Dễ dàng với MailApp |
| Kết nối API | Không thể | Hỗ trợ đầy đủ UrlFetchApp |
| Tạo UI | Không thể | Có thể với HtmlService |
| Xử lý dữ liệu lớn | Chậm khi >10,000 rows | Nhanh hơn nhiều |
Lưu ý quan trọng: Apps Script dùng cú pháp JavaScript ES6+, nếu bạn đã học JavaScript hay bất kỳ ngôn ngữ lập trình nào, bạn sẽ cảm thấy quen thuộc ngay.
2. Mở Script Editor lần đầu
Để bắt đầu, mở Script Editor từ bên trong Google Sheets:
- Mở Google Sheets của bạn
- Trên thanh menu, click Extensions (Tiện ích mở rộng)
- Chọn Apps Script
- Trình soạn thảo code sẽ mở trong tab mới
Giao diện Script Editor
Script Editor có các thành phần chính:
- Code editor (giữa màn hình): Nơi bạn viết code JavaScript
- Project files (trái): Danh sách các file script (.gs)
- Toolbar (trên): Nút Run, Debug, Save
- Log (dưới): Output của console.log và lỗi
- Function dropdown: Chọn function muốn chạy
File mặc định: Code.gs
Khi mở lần đầu, bạn sẽ thấy file Code.gs với nội dung mặc định:
function myFunction() {
// Code cua ban o day
}
Mỗi file script trong Apps Script có đuôi .gs (Google Script), nhưng thực chất là JavaScript thuần.
3. Viết Function Đầu Tiên: Hello World
Hãy bắt đầu với ví dụ đơn giản nhất — hiển thị thông báo trên spreadsheet:
function helloWorld() {
// Hien thi dialog don gian
SpreadsheetApp.getUi().alert('Xin chao tu Apps Script!');
}
Để chạy function này:
- Copy code vào editor và Save (Ctrl+S)
- Chọn
helloWorldtrong dropdown function - Click nút Run (▶)
- Lần đầu sẽ yêu cầu cấp quyền — click Review permissions → chọn tài khoản Google → Allow
- Quay lại spreadsheet, bạn sẽ thấy hộp thoại thông báo
Sử dụng Logger để debug
function debugExample() {
// In ra log de debug - xem o View > Logs
console.log('Bat dau chay script...');
const tenToi = 'Nguyen Van A';
const soLuong = 100;
console.log('Ten:', tenToi);
console.log('So luong:', soLuong);
console.log('Tong:', soLuong * 50000);
Logger.log('Function da chay xong!');
}
Xem log output tại: View → Execution log trong Script Editor.
Cấu trúc cơ bản của Apps Script
// Khai bao bien toan cuc
const SHEET_NAME = 'Du lieu chinh';
const EMAIL_BAO_CAO = 'admin@company.com';
// Function chinh
function doSomething() {
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const sheet = spreadsheet.getSheetByName(SHEET_NAME);
console.log('Ten spreadsheet:', spreadsheet.getName());
console.log('So hang co du lieu:', sheet.getLastRow());
}
4. Đọc và Ghi Dữ Liệu vào Spreadsheet
Đây là tính năng cốt lõi — SpreadsheetApp cho phép bạn đọc và ghi dữ liệu vào bất kỳ ô nào trong spreadsheet.
4.1 Các cách lấy Range (vùng ô)
function layDuLieu() {
const sheet = SpreadsheetApp.getActiveSheet();
// Lay mot o cu the
const oA1 = sheet.getRange('A1');
const oB2 = sheet.getRange(2, 2); // row 2, col 2 = B2
// Lay mot vung o
const vungA1C10 = sheet.getRange('A1:C10');
// Lay toan bo du lieu co trong sheet
const tatCaDuLieu = sheet.getDataRange();
console.log('So hang cuoi co data:', sheet.getLastRow());
console.log('So cot cuoi co data:', sheet.getLastColumn());
}
4.2 Đọc giá trị (getValues / getValue)
function docDuLieu() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
// Doc mot o -- getValue() tra ve gia tri don
const tenSanPham = sheet.getRange('A2').getValue();
console.log('Ten san pham:', tenSanPham);
// Doc nhieu o -- getValues() tra ve mang 2 chieu
const duLieu = sheet.getRange('A2:C11').getValues();
// duLieu la [[row1col1, row1col2, row1col3], [row2col1, ...], ...]
console.log('So hang:', duLieu.length);
console.log('So cot moi hang:', duLieu[0].length);
// Lap qua tung hang
duLieu.forEach(function(hang, index) {
const ten = hang[0]; // Cot A
const soLuong = hang[1]; // Cot B
const donGia = hang[2]; // Cot C
if (ten) {
console.log('Hang ' + (index + 2) + ': ' + ten + ' - SL: ' + soLuong + ' - Gia: ' + donGia);
}
});
}
4.3 Ghi giá trị (setValues / setValue)
function ghiDuLieu() {
const sheet = SpreadsheetApp.getActiveSheet();
// Ghi mot gia tri vao o don
sheet.getRange('A1').setValue('Ngay cap nhat');
sheet.getRange('B1').setValue(new Date());
// Ghi nhieu o cung luc (hieu qua hon ghi tung o)
const duLieuGhi = [
['Ten SP', 'So luong', 'Don gia', 'Thanh tien'],
['San pham A', 10, 50000, 500000],
['San pham B', 5, 120000, 600000],
['San pham C', 20, 35000, 700000],
];
// Ghi toan bo mang vao vung A1:D4
sheet.getRange(1, 1, duLieuGhi.length, duLieuGhi[0].length).setValues(duLieuGhi);
// Them mot hang moi vao cuoi
const hangMoi = ['San pham D', 8, 80000, 640000];
const dongCuoi = sheet.getLastRow() + 1;
sheet.getRange(dongCuoi, 1, 1, hangMoi.length).setValues([hangMoi]);
console.log('Da ghi xong du lieu!');
}
4.4 Thao tác nâng cao với Range
function thaoTacNangCao() {
const sheet = SpreadsheetApp.getActiveSheet();
// Xoa noi dung o (giu dinh dang)
sheet.getRange('A5:C10').clearContent();
// Sap xep du lieu theo cot A (tang dan)
const vungDuLieu = sheet.getRange('A2:D100');
vungDuLieu.sort({ column: 1, ascending: true });
// Dat mau nen va dinh dang header
sheet.getRange('A1:D1').setBackground('#4285f4');
sheet.getRange('A1:D1').setFontColor('#ffffff');
sheet.getRange('A1:D1').setFontWeight('bold');
// Dinh dang so tien VND
sheet.getRange('D2:D100').setNumberFormat('#,##0');
// Tu dong can chinh do rong cot
sheet.autoResizeColumns(1, 4);
SpreadsheetApp.flush(); // Ap dung tat ca thay doi ngay lap tuc
}
4.5 Làm việc với nhiều Sheet
function lam_viec_nhieu_sheet() {
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
// Lay tat ca sheets
const tatCaSheets = spreadsheet.getSheets();
console.log('So sheet:', tatCaSheets.length);
// Lay sheet theo ten
const sheetBanHang = spreadsheet.getSheetByName('Ban hang');
const sheetBaoCao = spreadsheet.getSheetByName('Bao cao');
// Tao sheet moi neu chua ton tai
let sheetLog = spreadsheet.getSheetByName('Log');
if (!sheetLog) {
sheetLog = spreadsheet.insertSheet('Log');
sheetLog.getRange('A1:C1').setValues([['Thoi gian', 'Hanh dong', 'Nguoi thuc hien']]);
}
// Sao chep du lieu tu sheet nay sang sheet khac
if (sheetBanHang && sheetBaoCao) {
const duLieuNguon = sheetBanHang.getDataRange().getValues();
sheetBaoCao.clearContents();
sheetBaoCao.getRange(1, 1, duLieuNguon.length, duLieuNguon[0].length).setValues(duLieuNguon);
}
}
6. Triggers: Tự Động Chạy Code Theo Điều Kiện
Triggers (trình kích hoạt) cho phép function của bạn tự động chạy mà không cần can thiệp thủ công.
6.1 Simple Triggers (Tích hợp sẵn)
// Chay khi mo spreadsheet
function onOpen(e) {
console.log('Spreadsheet vua duoc mo boi:', Session.getActiveUser().getEmail());
}
// Chay khi co chinh sua o (real-time)
function onEdit(e) {
// e.range: o vua duoc sua
// e.value: gia tri moi
// e.oldValue: gia tri cu
const range = e.range;
const sheet = range.getSheet();
// Vi du: Tu dong ghi timestamp khi sua cot A
if (sheet.getName() === 'Don hang' && range.getColumn() === 1) {
const dongVuaSua = range.getRow();
const colTimestamp = 10; // Cot J
sheet.getRange(dongVuaSua, colTimestamp).setValue(new Date());
}
}
// Chay khi form Google duoc submit
function onFormSubmit(e) {
const values = e.namedValues;
const ten = values['Ho va ten'][0];
const email = values['Email'][0];
console.log('Form submit tu:', ten, email);
}
6.2 Installable Triggers (Tạo từ code)
// Tao trigger chay hang ngay luc 8 gio sang
function taoTriggerHangNgay() {
// Xoa trigger cu truoc (tranh duplicate)
const triggers = ScriptApp.getProjectTriggers();
triggers.forEach(function(trigger) {
if (trigger.getHandlerFunction() === 'guiBaoCaoSang') {
ScriptApp.deleteTrigger(trigger);
}
});
// Tao trigger moi -- chay moi ngay luc 8-9 AM (gio VN)
ScriptApp.newTrigger('guiBaoCaoSang')
.timeBased()
.atHour(8)
.everyDays(1)
.inTimezone('Asia/Ho_Chi_Minh')
.create();
console.log('Da tao trigger gui bao cao sang hang ngay');
}
// Tao trigger chay moi gio
function taoTriggerMoiGio() {
ScriptApp.newTrigger('capNhatDuLieuTuDong')
.timeBased()
.everyHours(1)
.create();
}
// Tao trigger chay moi thu 2 luc 9 gio sang
function taoTriggerHangTuan() {
ScriptApp.newTrigger('baoCaoTuanDo')
.timeBased()
.onWeekDay(ScriptApp.WeekDay.MONDAY)
.atHour(9)
.create();
}
// Xem danh sach tat ca triggers hien co
function xemTatCaTriggers() {
const triggers = ScriptApp.getProjectTriggers();
triggers.forEach(function(trigger, index) {
console.log('Trigger ' + (index + 1) + ':');
console.log(' Function:', trigger.getHandlerFunction());
console.log(' Type:', trigger.getEventType());
});
}
6.3 Quản lý Triggers qua UI
Ngoài code, bạn có thể quản lý triggers qua giao diện: Trong Script Editor → click icon đồng hồ ⏰ bên trái (Triggers) → Add Trigger.
7. Gửi Email Tự Động với MailApp
MailApp là service tích hợp trong Apps Script cho phép gửi email trực tiếp từ tài khoản Gmail của bạn.
7.1 Gửi email cơ bản
function guiEmailCoBan() {
MailApp.sendEmail({
to: 'khachhang@email.com',
subject: 'Xac nhan don hang #DH001',
body: 'Cam on ban da dat hang. Chung toi se xu ly trong 24 gio.',
});
console.log('Email da duoc gui!');
}
7.2 Gửi email HTML có định dạng đẹp
function guiEmailHTML() {
const ngayHienTai = Utilities.formatDate(new Date(), 'Asia/Ho_Chi_Minh', 'dd/MM/yyyy');
const noiDungHTML = '<div style="font-family: Arial; max-width: 600px;">' +
'<h2 style="color: #4285f4;">Bao Cao Ban Hang Hom Nay</h2>' +
'<p>Kinh gui Ban Giam Doc,</p>' +
'<table border="1" cellpadding="8" style="border-collapse: collapse; width: 100%;">' +
'<tr style="background: #f1f3f4;">' +
'<th>Chi tieu</th><th>Thuc te</th><th>Ke hoach</th><th>Ty le</th>' +
'</tr>' +
'<tr>' +
'<td>Doanh thu</td>' +
'<td style="color: green;"><b>45,200,000d</b></td>' +
'<td>50,000,000d</td>' +
'<td>90.4%</td>' +
'</tr>' +
'</table>' +
'<p style="color: #666; font-size: 12px;">Email tu dong tu he thong - ' + ngayHienTai + '</p>' +
'</div>';
MailApp.sendEmail({
to: 'bgd@company.com',
cc: 'ketoan@company.com',
subject: '[Bao cao] Ban hang ngay ' + ngayHienTai,
htmlBody: noiDungHTML,
});
}
7.3 Gửi email hàng loạt (Mail merge)
function guiEmailHangLoat() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Danh sach');
const duLieu = sheet.getDataRange().getValues();
let soEmailDaGui = 0;
for (let i = 1; i < duLieu.length; i++) {
const ten = duLieu[i][0];
const email = duLieu[i][1];
const soTien = duLieu[i][2];
const trangThai = duLieu[i][3];
// Chi gui cho khach chua duoc thong bao
if (ten && email && trangThai !== 'Da gui') {
const noiDung = 'Kinh chao ' + ten + ',
' +
'So tien thanh toan: ' + soTien.toLocaleString('vi-VN') + 'd
' +
'Tran trong,
Team SheetStore';
try {
MailApp.sendEmail({
to: email,
subject: 'Thong bao thanh toan tu SheetStore',
body: noiDung,
});
// Cap nhat trang thai
sheet.getRange(i + 1, 4).setValue('Da gui');
sheet.getRange(i + 1, 5).setValue(new Date());
soEmailDaGui++;
// Dung 1 giay de tranh vuot quota Gmail
Utilities.sleep(1000);
} catch (error) {
console.error('Loi gui email cho', email, ':', error.message);
sheet.getRange(i + 1, 4).setValue('Loi: ' + error.message);
}
}
}
SpreadsheetApp.getUi().alert('Da gui ' + soEmailDaGui + ' email thanh cong!');
}
Giới hạn quota Gmail: Tài khoản Gmail thường có thể gửi 100 email/ngày, tài khoản Google Workspace được 1,500 email/ngày.
8. Ví Dụ Thực Tế: Báo Cáo Hàng Ngày và Invoice Tự Động
8.1 Hệ thống báo cáo bán hàng tự động hàng ngày
Đây là ví dụ hoàn chỉnh — mỗi sáng lúc 8 giờ, script tự động tổng hợp dữ liệu bán hàng hôm qua và gửi email báo cáo cho quản lý.
Cấu trúc spreadsheet cần có:
- Sheet "Don hang": Cột A=Ngày, B=Mã đơn, C=Khách hàng, D=Doanh thu, E=Trạng thái
- Sheet "Cau hinh": B1=Email nhận báo cáo, B2=Tên công ty
// ============================================
// HE THONG BAO CAO BAN HANG TU DONG
// Cai dat: chay setupTrigger() mot lan
// ============================================
function setupTrigger() {
// Xoa trigger cu
ScriptApp.getProjectTriggers().forEach(function(t) {
if (t.getHandlerFunction() === 'guiBaoCaoSang') ScriptApp.deleteTrigger(t);
});
// Tao trigger chay luc 8AM hang ngay
ScriptApp.newTrigger('guiBaoCaoSang')
.timeBased().atHour(8).everyDays(1)
.inTimezone('Asia/Ho_Chi_Minh').create();
Logger.log('Trigger da duoc cai dat. Bao cao se gui luc 8AM moi ngay.');
}
function guiBaoCaoSang() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheetDonHang = ss.getSheetByName('Don hang');
const sheetCauHinh = ss.getSheetByName('Cau hinh');
if (!sheetDonHang || !sheetCauHinh) {
Logger.log('Loi: Khong tim thay sheet can thiet');
return;
}
const emailNhan = sheetCauHinh.getRange('B1').getValue();
const tenCongTy = sheetCauHinh.getRange('B2').getValue();
// Lay ngay hom qua
const homQua = new Date();
homQua.setDate(homQua.getDate() - 1);
const ngayStr = Utilities.formatDate(homQua, 'Asia/Ho_Chi_Minh', 'dd/MM/yyyy');
// Tong hop du lieu
const duLieu = sheetDonHang.getDataRange().getValues();
let tongDoanhthu = 0, soDon = 0, soHoanThanh = 0;
const danhSachDon = [];
duLieu.slice(1).forEach(function(hang) {
const ngayDon = hang[0];
if (!ngayDon) return;
const ngayDonStr = Utilities.formatDate(new Date(ngayDon), 'Asia/Ho_Chi_Minh', 'dd/MM/yyyy');
if (ngayDonStr === ngayStr) {
soDon++;
tongDoanhthu += parseFloat(hang[3]) || 0;
if (hang[4] === 'Hoan thanh') soHoanThanh++;
danhSachDon.push(hang);
}
});
// Tao noi dung email HTML
const htmlBangDon = danhSachDon.map(function(don) {
return '<tr>' +
'<td style="padding:6px">' + don[1] + '</td>' +
'<td style="padding:6px">' + don[2] + '</td>' +
'<td style="padding:6px;text-align:right">' + parseInt(don[3]).toLocaleString('vi-VN') + 'd</td>' +
'<td style="padding:6px;color:' + (don[4] === 'Hoan thanh' ? 'green' : 'orange') + '">' + don[4] + '</td>' +
'</tr>';
}).join('');
const htmlBaoCao = '<div style="font-family:Arial;max-width:700px">' +
'<div style="background:#1a73e8;color:white;padding:20px">' +
'<h2>Bao Cao Ban Hang Ngay ' + ngayStr + '</h2>' +
'<p>' + tenCongTy + '</p>' +
'</div>' +
'<div style="padding:20px">' +
'<p>Tong doanh thu: <b>' + parseInt(tongDoanhthu).toLocaleString('vi-VN') + 'd</b></p>' +
'<p>Tong don hang: <b>' + soDon + '</b> | Don hoan thanh: <b>' + soHoanThanh + '</b></p>' +
(danhSachDon.length > 0 ?
'<table width="100%" border="1" style="border-collapse:collapse">' +
'<thead><tr style="background:#1a73e8;color:white">' +
'<th style="padding:8px">Ma don</th>' +
'<th style="padding:8px">Khach hang</th>' +
'<th style="padding:8px">Doanh thu</th>' +
'<th style="padding:8px">Trang thai</th>' +
'</tr></thead>' +
'<tbody>' + htmlBangDon + '</tbody></table>'
: '<p>Khong co don hang nao ngay hom qua.</p>') +
'</div></div>';
if (emailNhan) {
MailApp.sendEmail({
to: emailNhan,
subject: '[Bao cao ' + ngayStr + '] Doanh thu: ' + parseInt(tongDoanhthu).toLocaleString('vi-VN') + 'd | ' + soDon + ' don',
htmlBody: htmlBaoCao,
});
Logger.log('Da gui bao cao ngay ' + ngayStr + ' den ' + emailNhan);
}
}
8.2 Tạo Invoice từ Form Data tự động
// Khi Google Form duoc submit, tu dong tao invoice
function taoInvoiceTuForm(e) {
const values = e.namedValues;
const khachHang = values['Ten khach hang'][0];
const email = values['Email khach hang'][0];
const sanPham = values['San pham'][0];
const soLuong = parseInt(values['So luong'][0]);
const donGia = parseInt(values['Don gia'][0]);
const thanhTien = soLuong * donGia;
// Tao ma invoice tu dong
const now = new Date();
const maInvoice = 'INV-' + now.getFullYear() +
'-' + String(now.getMonth() + 1).padStart(2, '0') +
'-' + Math.floor(Math.random() * 9000 + 1000);
// Ghi vao sheet Invoices
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheetInvoice = ss.getSheetByName('Invoices') || ss.insertSheet('Invoices');
sheetInvoice.appendRow([
maInvoice, new Date(), khachHang, email,
sanPham, soLuong, donGia, thanhTien, 'Cho thanh toan'
]);
// Gui email xac nhan cho khach
const emailNoiDung = '<div style="font-family:Arial;max-width:500px">' +
'<h2 style="color:#4285f4">Invoice #' + maInvoice + '</h2>' +
'<p>Kinh chao <b>' + khachHang + '</b>,</p>' +
'<p>Cam on ban da dat hang tai <a href="https://sheet.com.vn">SheetStore</a>!</p>' +
'<table border="1" cellpadding="8" style="border-collapse:collapse;width:100%">' +
'<tr><th>San pham</th><td>' + sanPham + '</td></tr>' +
'<tr><th>So luong</th><td>' + soLuong + '</td></tr>' +
'<tr><th>Don gia</th><td>' + donGia.toLocaleString('vi-VN') + 'd</td></tr>' +
'<tr><th><b>Thanh tien</b></th><td style="color:green"><b>' + thanhTien.toLocaleString('vi-VN') + 'd</b></td></tr>' +
'</table>' +
'<p>Vui long thanh toan trong vong 3 ngay lam viec.</p>' +
'</div>';
MailApp.sendEmail({
to: email,
subject: '[SheetStore] Invoice ' + maInvoice + ' - ' + sanPham,
htmlBody: emailNoiDung,
});
Logger.log('Invoice ' + maInvoice + ' da duoc tao va gui den ' + email);
}
9. Tips Nâng Cao và Tài Nguyên Học Thêm
9.1 Best Practices để code hiệu quả
- Batch operations: Thay vì ghi từng ô một (chậm), hãy gom lại và ghi một lần với setValues()
- Cache dữ liệu: Đọc dữ liệu vào biến một lần, không gọi getValues() nhiều lần trong vòng lặp
- Error handling: Luôn dùng try-catch để xử lý lỗi, tránh script crash giữa chừng
- Thời gian chạy tối đa: Apps Script giới hạn 6 phút/lần chạy — chia nhỏ task nếu cần xử lý data lớn
// CACH SAI (cham -- moi getRange la mot API call)
for (let i = 1; i <= 100; i++) {
sheet.getRange(i, 1).setValue('Gia tri ' + i); // 100 API calls!
}
// CACH DUNG (nhanh -- chi 1 API call)
const duLieu = [];
for (let i = 1; i <= 100; i++) {
duLieu.push(['Gia tri ' + i]);
}
sheet.getRange(1, 1, 100, 1).setValues(duLieu); // 1 API call!
9.2 Sử dụng PropertiesService để lưu cài đặt
// Luu cai dat vinh vien (khong mat khi refresh)
function luuCaiDat() {
const props = PropertiesService.getScriptProperties();
props.setProperties({
'email_nhan': 'bgd@company.com',
'ten_cong_ty': 'Cong ty ABC',
'so_lan_chay': '0'
});
}
function docCaiDat() {
const props = PropertiesService.getScriptProperties();
const email = props.getProperty('email_nhan');
const soLan = parseInt(props.getProperty('so_lan_chay') || '0') + 1;
props.setProperty('so_lan_chay', soLan.toString());
console.log('Email nhan:', email);
console.log('So lan da chay:', soLan);
}
9.3 Tài nguyên học Apps Script
- Google Apps Script Official Docs
- Apps Script Reference: SpreadsheetApp, MailApp, UrlFetchApp
- Google Apps Script Samples trên GitHub
- YouTube: "Google Apps Script Tutorial" (tiếng Việt có nhiều channel)
9.4 Kết nối với SheetStore
Nếu bạn muốn tiết kiệm thời gian, hãy khám phá các template Google Sheets sẵn có tại SheetStore — đã được tích hợp Apps Script tự động hóa, chỉ cần mua và sử dụng ngay. Xem thêm bảng giá các gói của chúng tôi.
Cũng có thể xem Bài 4: Hàm QUERY chuyên sâu nếu bạn chưa học, hoặc tiếp tục với Bài 3: IMPORTRANGE để ôn lại kiến thức nền tảng.
Tổng Kết Bài 5
Qua bài học này, bạn đã nắm được:
- Apps Script là gì và cách mở Script Editor
- Viết function cơ bản, sử dụng Logger để debug
- Đọc/ghi dữ liệu hiệu quả với getValues() và setValues()
- Tạo Custom Menu cho phép người dùng chạy script dễ dàng
- Cài đặt Triggers để tự động hóa theo lịch hoặc sự kiện
- Gửi email tự động với MailApp, kể cả email HTML đẹp
- Ví dụ thực tế: Báo cáo hàng ngày và Invoice tự động
Apps Script mở ra một thế giới hoàn toàn mới cho Google Sheets — từ một công cụ bảng tính đơn giản, bạn có thể biến nó thành một hệ thống quản lý doanh nghiệp hoàn chỉnh. Đây cũng là lý do nhiều doanh nghiệp vừa và nhỏ chọn Google Sheets + Apps Script thay vì các phần mềm đắt tiền.
Khám phá thêm tại phần mềm quản lý bán hàng trên SheetStore để thấy Apps Script được ứng dụng thực tế như thế nào trong các template chuyên nghiệp.
📚 Bài Viết Liên Quan
- Template Google Sheets Báo Cáo Bán Hàng Theo Vùng và Đại Lý 2027: Phân Tích Đa Chiều
- Google Sheets Nâng Cao Bài 9: Bảo Mật, Phân Quyền và Chia Sẻ Chuyên Nghiệp
- Google Sheets Nâng Cao Bài 4: Hàm QUERY - Lọc và Phân Tích Dữ Liệu Chuyên Nghiệp
- Template Google Sheets Quản Lý Phòng Khám và Bệnh Viện Nhỏ 2027
Chia sẻ bài viết:
Tuân Hoang
Đội ngũ SheetStore
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.