Tự Xây Hệ Thống HR Bằng Google Sheets + Apps Script: Hướng Dẫn Hoàn Chỉnh
Hướng dẫn từng bước tự xây hệ thống HR hoàn chỉnh bằng Google Sheets + Apps Script — từ chấm công tự động đến slip lương PDF, dashboard HR metrics, tất cả không tốn phí subscription.
Bài viết dành cho những ai muốn kiểm soát hoàn toàn hệ thống HR của mình, sẵn sàng bỏ thời gian setup một lần để dùng mãi mãi.
Mục lục:
1. Tổng Quan Hệ Thống HR Tự Xây Bằng Google Sheets
Một hệ thống HR tự xây bằng Google Sheets + Apps Script có thể đạt được 80–90% tính năng của phần mềm HRM thương mại trung cấp, với chi phí gần bằng 0 sau khi setup xong.
Hệ thống hoàn chỉnh sau khi xây xong:
- ✅ Chấm công qua Google Forms (mobile-friendly)
- ✅ Tự động tính giờ công, tăng ca
- ✅ Quản lý nghỉ phép với approval workflow
- ✅ Tính lương tự động: BHXH, thuế TNCN
- ✅ Slip lương PDF gửi email tự động
- ✅ Dashboard HR metrics tự cập nhật
- ✅ Cảnh báo: hết hạn hợp đồng, sinh nhật
- ✅ Báo cáo tháng tự động gửi về HR
Thời gian setup: 8–16 giờ nếu tự làm từ đầu, hoặc 1–2 giờ nếu dùng template có sẵn từ SheetStore. Sau đó hệ thống chạy tự động gần như không cần can thiệp thủ công.
2. Cấu Trúc Dự Án Google Sheets HR
Tổ chức hệ thống thành nhiều file Google Sheets thay vì một file lớn — giúp performance tốt hơn và phân quyền dễ hơn:
📁 HR_System_2026/ (Google Drive Folder)
📊 HR_MASTER.xlsx — File chính: hồ sơ NV, cài đặt, danh mục
└─ Sheet: NHAN_VIEN, CAI_DAT, PHONG_BAN, CHUC_VU
📅 CHAM_CONG_2026.xlsx — Dữ liệu chấm công cả năm
└─ Sheet: CC_T1, CC_T2 ... CC_T12 (mỗi tháng 1 sheet)
💰 LUONG_2026.xlsx — Bảng lương cả năm
└─ Sheet: LUONG_T1 ... LUONG_T12
🏖️ NGHI_PHEP_2026.xlsx — Quản lý nghỉ phép + đơn xin nghỉ
📊 HR_DASHBOARD.xlsx — Dashboard metrics tổng hợp
📋 TUYEN_DUNG.xlsx — ATS đơn giản theo dõi ứng viên
3. Module Chấm Công Tự Động
3.1 Tạo Form chấm công
Tạo Google Form "Chấm Công" với các trường:
- Họ tên nhân viên: Dropdown (kéo từ danh sách NV)
- Loại chấm công: Radio (Vào ca / Ra ca)
- Thời gian: Tự động lấy từ timestamp form
- Ghi chú: Text (tuỳ chọn, ghi lý do nếu đi muộn/về sớm)
3.2 Apps Script xử lý chấm công
// Trigger: onFormSubmit — xử lý khi nhân viên submit form chấm công
function processAttendance(e) {
const values = e.values // [timestamp, name, type, note]
const timestamp = new Date(values[0])
const empName = values[1]
const checkType = values[2] // "Vào ca" hoặc "Ra ca"
const month = timestamp.getMonth() + 1
const day = timestamp.getDate()
const hour = timestamp.getHours() + timestamp.getMinutes()/60
// Mở file chấm công tháng tương ứng
const ccFile = SpreadsheetApp.openById(CC_FILE_ID)
const ccSheet = ccFile.getSheetByName('CC_T' + month)
// Tìm row của nhân viên và ghi giờ vào/ra
const empRow = findEmployeeRow(ccSheet, empName)
const dayCol = day + 2 // cột C = ngày 1, cột D = ngày 2...
if (checkType === 'Vào ca') {
ccSheet.getRange(empRow, dayCol).setValue('IN:' + formatTime(hour))
} else {
const currentVal = ccSheet.getRange(empRow, dayCol).getValue()
ccSheet.getRange(empRow, dayCol).setValue(currentVal + ' OUT:' + formatTime(hour))
}
}
4. Module Quản Lý Nghỉ Phép Với Approval Workflow
// Trigger: onLeaveRequest — gửi email approval cho manager
function onLeaveRequest(e) {
const [ts, empName, leaveType, startDate, endDate, reason] = e.values
const managerEmail = getManagerEmail(empName) // VLOOKUP từ HR_MASTER
const days = calcBusinessDays(new Date(startDate), new Date(endDate))
const remainingLeave = getRemainingLeave(empName) // Tính từ NGHI_PHEP sheet
if (remainingLeave < days) {
// Gửi email từ chối tự động
GmailApp.sendEmail(getEmpEmail(empName), 'Đơn nghỉ phép bị từ chối',
'Bạn chỉ còn ' + remainingLeave + ' ngày phép, không đủ cho ' + days + ' ngày.')
return
}
// Tạo link approve/reject cho manager
const approveUrl = ScriptApp.getService().getUrl() + '?action=approve&row=' + getLastRow()
GmailApp.sendEmail(managerEmail, '[Phê duyệt nghỉ phép] ' + empName,
empName + ' xin nghỉ ' + days + ' ngày. Approve: ' + approveUrl)
}
5. Module Tính Lương & Slip Lương PDF
// Tạo slip lương PDF và gửi email — chạy vào ngày 25 hàng tháng
function sendPaySlips() {
const payrollSheet = SpreadsheetApp.openById(LUONG_FILE_ID)
const currentMonth = new Date().getMonth() + 1
const sheet = payrollSheet.getSheetByName('LUONG_T' + currentMonth)
const data = sheet.getDataRange().getValues()
for (let i = 1; i < data.length; i++) {
const [maNV, hoTen, email, luongCB, phucap, tangca, bhxh, thue, luongThucNhan] = data[i]
if (!email) continue
// Tạo HTML slip lương
const slipHtml = createPaySlipHtml({
empName: hoTen, month: currentMonth,
baseSalary: luongCB, allowance: phucap,
overtime: tangca, insurance: bhxh,
tax: thue, netPay: luongThucNhan
})
GmailApp.sendEmail(email, 'Phiếu lương tháng ' + currentMonth + '/2026', '',
{htmlBody: slipHtml, name: 'HR Department'})
}
Logger.log('Đã gửi slip lương cho ' + (data.length-1) + ' nhân viên')
}
6. Dashboard HR Metrics Tự Động
Dashboard HR Metrics tổng hợp từ tất cả các file, tự refresh mỗi ngày:
Tính từ NGHI_PHEP
94.2%
Tỷ lệ có mặt tháng này
Tính từ LUONG
245tr
Tổng quỹ lương tháng
// IMPORTRANGE để kéo dữ liệu cross-file vào Dashboard
// Tổng nhân viên active
=COUNTIF(IMPORTRANGE("HR_MASTER_ID","NHAN_VIEN!E:E"),"Active")
// Tỷ lệ có mặt tháng này
=1 - COUNTIF(IMPORTRANGE("CC_FILE_ID","CC_T"&MONTH(TODAY())&"!AG:AG"),"<20") / COUNTA(IMPORTRANGE("HR_MASTER_ID","NHAN_VIEN!A:A"))
// Cảnh báo HĐ hết hạn trong 30 ngày
=COUNTIFS(IMPORTRANGE("HR_MASTER_ID","NHAN_VIEN!K:K"),">="&TODAY(),IMPORTRANGE("HR_MASTER_ID","NHAN_VIEN!K:K"),"<="&(TODAY()+30))
7. Apps Script Nâng Cao: Tự Động Hóa Toàn Diện
🎂 Sinh nhật nhân viên
Trigger hàng ngày 8:00 — quét danh sách NV, gửi email chúc mừng sinh nhật tự động với tên và thông điệp cá nhân hóa.
⚠️ Hợp đồng sắp hết hạn
Trigger hàng tuần thứ 2 — gửi email cảnh báo HR và manager nếu HĐ NV hết hạn trong 30 ngày tới.
📊 Báo cáo HR hàng tháng
Trigger ngày 1 mỗi tháng — tự tổng hợp báo cáo: tỷ lệ có mặt, quỹ lương, NV mới/nghỉ, gửi PDF cho BOD.
🔔 Nhắc chấm công quên
Trigger 9:30 sáng — gửi Zalo/email nhắc NV chưa check-in hôm nay. Giảm chấm công thiếu xuống còn < 2%.
// Thiết lập tất cả trigger tự động một lần
function setupAllTriggers() {
// Nhắc chấm công 9:30 sáng hàng ngày
ScriptApp.newTrigger('remindMissingCheckIn')
.timeBased().atHour(9).nearMinute(30).everyDays(1).create()
// Slip lương ngày 25 hàng tháng
ScriptApp.newTrigger('sendPaySlips')
.timeBased().onMonthDay(25).atHour(8).create()
// Sinh nhật và HĐ hết hạn mỗi ngày 8:00
ScriptApp.newTrigger('checkBirthdaysAndContracts')
.timeBased().atHour(8).everyDays(1).create()
// Báo cáo tháng ngày 1
ScriptApp.newTrigger('sendMonthlyHRReport')
.timeBased().onMonthDay(1).atHour(7).create()
Logger.log('Đã cài đặt 4 triggers tự động!')
}
Tải Template HR System Hoàn Chỉnh
Không muốn tự xây từ đầu? Template HR System từ SheetStore đã tích hợp đầy đủ 6 module trên, Apps Script đã được viết sẵn — chỉ cần điền dữ liệu và chạy setup trigger là xong.
Xem HR System Template →8. FAQ
Cần biết lập trình để xây hệ thống này không?
Apps Script dùng JavaScript, nhưng các script ở trên đều có thể copy-paste và chỉnh sửa tên file/ID mà không cần hiểu sâu về lập trình. Google cũng có AI giúp viết Apps Script — chỉ cần mô tả bạn muốn làm gì.
Apps Script có giới hạn số lần chạy không?
Google Apps Script giới hạn 6 phút/lần chạy và 90 phút/ngày. Với hệ thống HR cho 30–50 NV, các script này chạy dưới 1–2 phút — hoàn toàn nằm trong giới hạn miễn phí.
Hệ thống này có thể scale lên 100 NV không?
Có, nhưng performance sẽ chậm hơn. Với 50–100 NV, file vẫn hoạt động tốt nếu tối ưu: tách file theo năm, dùng IMPORTRANGE hợp lý, tránh quá nhiều công thức volatile (NOW, TODAY). Trên 100 NV nên xem xét phần mềm HRM thương mại.
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.