Quản Lý Kho

Tự Động Hóa Quản Lý Kho Với Google Sheets & Apps Script

Tuân HoangTuân Hoang
17 tháng 3, 2026
Cập nhật: 25 tháng 3, 2026
14 phút đọc
Tự Động Hóa Quản Lý Kho Với Google Sheets & Apps Script

Tự động hóa quản lý kho với Google Sheets & Apps Script — từ cảnh báo hàng sắp hết đến báo cáo PDF tự động gửi email mỗi tháng.

Hướng dẫn step-by-step kèm code mẫu hoàn chỉnh. Không cần biết lập trình — chỉ cần copy-paste và cấu hình là dùng được ngay.

1. Tại Sao Cần Tự Động Hóa Quản Lý Kho?

Quản lý kho thủ công tốn rất nhiều thời gian lặp đi lặp lại: kiểm tra tồn kho mỗi sáng, lập báo cáo cuối tháng, gửi thông báo cho từng bộ phận khi hàng sắp hết. Với Google Apps Script (miễn phí 100%), bạn có thể tự động hóa toàn bộ những việc này.

-3h

Tiết kiệm/tuần kiểm tra kho thủ công

Chi phí Apps Script (miễn phí)

24/7

Tự động chạy không cần giám sát

-95%

Giảm sai sót do nhập thủ công

2. Google Apps Script Là Gì?

Google Apps Script (GAS) là ngôn ngữ lập trình dựa trên JavaScript, được tích hợp miễn phí trong Google Workspace. Bạn có thể viết script để tự động hóa Google Sheets, Gmail, Drive, Calendar mà không cần server hay hosting.

Để truy cập Apps Script trong Google Sheets:

  1. Mở file Google Sheets kho hàng
  2. Click menu ExtensionsApps Script
  3. Cửa sổ editor mở ra — paste code vào đây
  4. Click nút Save (Ctrl+S) và Run

3. Script 1: Cảnh Báo Hàng Sắp Hết Qua Email

Script này tự động kiểm tra tồn kho mỗi sáng 8h và gửi email/Telegram cảnh báo khi có hàng dưới mức tối thiểu.

// Script 1: Cảnh báo hàng sắp hết qua Email
function kiemTraTonKhoVaGuiEmail() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheetTonKho = ss.getSheetByName('TonKho');
  const sheetDanhMuc = ss.getSheetByName('DanhMuc');

  // Lấy dữ liệu tồn kho (từ dòng 2 đến hết)
  const data = sheetTonKho.getDataRange().getValues();
  const hangSapHet = [];

  for (let i = 1; i < data.length; i++) {
    const maSP = data[i][0];
    const tenSP = data[i][1];
    const tonHienTai = data[i][5]; // Cột F: Tồn hiện tại
    const tonToiThieu = data[i][6]; // Cột G: Tồn tối thiểu

    if (maSP && tonHienTai <= tonToiThieu) {
      hangSapHet.push({
        maSP: maSP,
        tenSP: tenSP,
        ton: tonHienTai,
        toiThieu: tonToiThieu,
        thieuSL: tonToiThieu - tonHienTai
      });
    }
  }

  // Chỉ gửi email nếu có hàng sắp hết
  if (hangSapHet.length > 0) {
    let noiDung = '<h2>⚠️ Cảnh Báo: ' + hangSapHet.length + ' mặt hàng sắp hết kho</h2>';
    noiDung += '<table border="1" cellpadding="8" style="border-collapse:collapse">';
    noiDung += '<tr style="background:#f3f4f6"><th>Mã SP</th><th>Tên SP</th><th>Tồn hiện tại</th><th>Tối thiểu</th><th>Cần nhập thêm</th></tr>';

    hangSapHet.forEach(function(sp) {
      noiDung += '<tr style="color:red">';
      noiDung += '<td>' + sp.maSP + '</td>';
      noiDung += '<td>' + sp.tenSP + '</td>';
      noiDung += '<td style="text-align:center">' + sp.ton + '</td>';
      noiDung += '<td style="text-align:center">' + sp.toiThieu + '</td>';
      noiDung += '<td style="text-align:center;font-weight:bold">' + sp.thieuSL + '</td>';
      noiDung += '</tr>';
    });
    noiDung += '</table>';

    // Thay EMAIL_QUAN_LY bằng email thực của bạn
    MailApp.sendEmail({
      to: 'EMAIL_QUAN_LY@gmail.com',
      subject: '⚠️ [Kho Hàng] ' + hangSapHet.length + ' mặt hàng sắp hết — ' + new Date().toLocaleDateString('vi-VN'),
      htmlBody: noiDung
    });

    Logger.log('Đã gửi email cảnh báo: ' + hangSapHet.length + ' mặt hàng');
  } else {
    Logger.log('Tồn kho ổn định, không cần cảnh báo');
  }
}

// Cài trigger tự động chạy mỗi sáng 8h:
// Apps Script → Triggers → Add Trigger → kiemTraTonKhoVaGuiEmail → Time-driven → Day timer → 8am-9am

💡 Cách cài trigger tự động:

  1. Trong Apps Script Editor, click biểu tượng đồng hồ (Triggers) ở sidebar trái
  2. Click + Add Trigger góc dưới phải
  3. Chọn function: kiemTraTonKhoVaGuiEmail
  4. Event source: Time-driven
  5. Type: Day timer8am to 9am
  6. Save — script sẽ tự chạy mỗi sáng!

4. Script 2: Nhập Kho Từ Google Form Qua Điện Thoại

Thay vì nhân viên kho phải mở laptop để nhập phiếu, họ dùng điện thoại submit Google Form → dữ liệu tự động ghi vào sheet nhập kho và cập nhật tồn.

// Script 2: Xử lý dữ liệu từ Google Form nhập kho
// Gắn trigger: Form submit → onFormSubmitNhapKho

function onFormSubmitNhapKho(e) {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheetNhap = ss.getSheetByName('NhapKho');
  const sheetTon = ss.getSheetByName('TonKho');

  // Lấy dữ liệu từ form response
  const responses = e.values; // [timestamp, maSP, tenSP, slNhap, giaNhap, ncc, ghiChu]
  const timestamp = responses[0];
  const maSP = responses[1].trim().toUpperCase();
  const slNhap = parseInt(responses[3]);
  const giaNhap = parseFloat(responses[4].replace(/,/g, ''));

  // Tạo số phiếu tự động
  const lastRow = sheetNhap.getLastRow();
  const soPhieu = 'NK' + new Date().getFullYear() + String(lastRow).padStart(4, '0');

  // Ghi vào sheet NhapKho
  sheetNhap.appendRow([
    new Date(),
    soPhieu,
    maSP,
    responses[2], // Tên SP
    slNhap,
    giaNhap,
    slNhap * giaNhap, // Thành tiền
    responses[5], // NCC
    responses[6]  // Ghi chú
  ]);

  // Cập nhật tồn kho trong sheet TonKho
  const dataTon = sheetTon.getDataRange().getValues();
  for (let i = 1; i < dataTon.length; i++) {
    if (dataTon[i][0] === maSP) {
      const tonHienTai = dataTon[i][5];
      sheetTon.getRange(i + 1, 6).setValue(tonHienTai + slNhap);
      break;
    }
  }

  Logger.log('Nhập kho thành công: ' + maSP + ' x ' + slNhap);
}

5. Script 3: Báo Cáo Tồn Kho Tự Động Cuối Tháng

// Script 3: Tổng hợp báo cáo tồn kho và gửi email cuối tháng
function baoCaoTonKhoCuoiThang() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheetTon = ss.getSheetByName('TonKho');
  const data = sheetTon.getDataRange().getValues();

  let tongGiaTri = 0;
  let soSPSapHet = 0;
  let soSPHetHang = 0;
  let hangTonLau = [];

  const today = new Date();

  for (let i = 1; i < data.length; i++) {
    if (!data[i][0]) continue;

    const ton = data[i][5] || 0;
    const giaNhap = data[i][4] || 0;
    const toiThieu = data[i][6] || 0;
    const ngayNhapCuoi = data[i][8] ? new Date(data[i][8]) : null;

    tongGiaTri += ton * giaNhap;
    if (ton === 0) soSPHetHang++;
    else if (ton <= toiThieu) soSPSapHet++;

    // Hàng tồn hơn 90 ngày
    if (ngayNhapCuoi && ((today - ngayNhapCuoi) / 86400000) > 90 && ton > 0) {
      hangTonLau.push(data[i][1] + ' (' + ton + ' SP)');
    }
  }

  const thang = today.getMonth() + 1;
  const nam = today.getFullYear();

  let noiDung = `<h2>📊 Báo Cáo Tồn Kho Tháng ${thang}/${nam}</h2>`;
  noiDung += `<p><strong>Tổng giá trị tồn kho:</strong> ${tongGiaTri.toLocaleString('vi-VN')}đ</p>`;
  noiDung += `<p><strong>Hàng sắp hết:</strong> ${soSPSapHet} mặt hàng</p>`;
  noiDung += `<p><strong>Hàng hết kho:</strong> ${soSPHetHang} mặt hàng</p>`;

  if (hangTonLau.length > 0) {
    noiDung += `<p><strong>Hàng tồn lâu >90 ngày (${hangTonLau.length} SP):</strong></p>`;
    noiDung += '<ul>' + hangTonLau.map(h => '<li>' + h + '</li>').join('') + '</ul>';
  }

  MailApp.sendEmail({
    to: 'EMAIL_QUAN_LY@gmail.com',
    subject: `📊 Báo Cáo Kho Tháng ${thang}/${nam}`,
    htmlBody: noiDung
  });
}
// Cài trigger: ngày 1 hàng tháng lúc 7am

6. Script 4: Tự Động Tạo Đề Xuất Đơn Nhập Hàng

// Script 4: Tạo sheet đề xuất nhập hàng dựa trên ROP
function taoDeXuatNhapHang() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheetTon = ss.getSheetByName('TonKho');
  const data = sheetTon.getDataRange().getValues();

  // Tạo hoặc xóa sheet đề xuất cũ
  let sheetDeXuat = ss.getSheetByName('DeXuatNhap');
  if (sheetDeXuat) ss.deleteSheet(sheetDeXuat);
  sheetDeXuat = ss.insertSheet('DeXuatNhap');

  // Header
  sheetDeXuat.appendRow(['Mã SP', 'Tên SP', 'Tồn hiện tại', 'ROP', 'Cần nhập', 'NCC gợi ý', 'Ưu tiên']);

  let soHangDeXuat = 0;

  for (let i = 1; i < data.length; i++) {
    if (!data[i][0]) continue;
    const ton = data[i][5] || 0;
    const rop = data[i][7] || 0; // Cột H: Reorder Point

    if (ton <= rop) {
      const canNhap = Math.max(rop * 2 - ton, 0); // Nhập đủ cho 2x ROP
      const uuTien = ton === 0 ? 'KHẨN CẤP' : (ton < rop * 0.5 ? 'Cao' : 'Bình thường');

      sheetDeXuat.appendRow([
        data[i][0], data[i][1], ton, rop, canNhap, data[i][9] || '', uuTien
      ]);
      soHangDeXuat++;
    }
  }

  // Format màu sắc
  if (soHangDeXuat > 0) {
    const range = sheetDeXuat.getRange(2, 7, soHangDeXuat, 1);
    // Highlight KHẨN CẤP màu đỏ (cần thêm logic nếu muốn)
  }

  SpreadsheetApp.getUi().alert(`✅ Đã tạo đề xuất nhập ${soHangDeXuat} mặt hàng trong sheet "DeXuatNhap"`);
}

7. Hướng Dẫn Triển Khai Từng Bước

1

Chuẩn bị template Google Sheets kho

Đặt tên sheet đúng: "TonKho", "NhapKho", "XuatKho", "DanhMuc". Đảm bảo cột dữ liệu khớp với script.

2

Mở Apps Script Editor

Extensions → Apps Script. Paste code script vào. Thay "EMAIL_QUAN_LY@gmail.com" bằng email thực.

3

Test thủ công

Chọn function → Run để test. Lần đầu sẽ hỏi cấp quyền Gmail/Sheets — chọn Allow.

4

Cài trigger tự động

Click biểu tượng đồng hồ → Add Trigger → chọn thời gian chạy tự động hàng ngày/tháng.

Template Kho + Apps Script Sẵn Sàng Dùng

SheetStore cung cấp template Google Sheets quản lý kho đã tích hợp sẵn tất cả 4 script trên — chỉ cần nhập email và dữ liệu sản phẩm là chạy ngay, không cần tự viết code.

Xem Template Kho + Automation →

8. Câu Hỏi Thường Gặp

Apps Script có miễn phí hoàn toàn không?
Có, Google Apps Script hoàn toàn miễn phí với tài khoản Google thường. Giới hạn: 6 phút runtime/execution, 100 email/ngày với tài khoản thường (2,000/ngày với Google Workspace). Đủ dùng cho kho doanh nghiệp nhỏ và vừa.
Không biết lập trình có dùng được Apps Script không?
Được! Các script trong bài đã hoàn chỉnh — chỉ cần copy-paste vào Apps Script Editor và sửa tên sheet, email. Không cần hiểu code. SheetStore còn cung cấp template đã tích hợp sẵn nếu không muốn tự setup.
Trigger có thể chạy mỗi bao nhiêu phút một lần?
Tối thiểu là 1 phút (minute-based trigger). Nhưng cho quản lý kho thông thường, trigger hàng ngày (8am) hoặc hàng giờ là đủ. Trigger dưới 5 phút sẽ tốn quota execution khá nhanh.

Chia sẻ bài viết:

Tuân Hoang

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.

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