Hướng dẫn

Tạo Hệ Thống Quản Lý Đơn Hàng Online Với Google Sheets + Google Forms

Tuân HoangTuân Hoang
27 tháng 2, 2026
Cập nhật: 25 tháng 3, 2026
26 phút đọc
Tạo Hệ Thống Quản Lý Đơn Hàng Online Với Google Sheets + Google Forms

Tóm tắt: Bài viết hướng dẫn bạn xây dựng hệ thống quản lý đơn hàng online hoàn chỉnh bằng Google Sheets + Google Forms. Hệ thống bao gồm: form đặt hàng, tự động tạo mã đơn, tính tổng tiền, gửi email xác nhận cho khách, trừ tồn kho realtime, và dashboard báo cáo doanh thu. Tất cả đều miễn phí và có thể triển khai trong vòng 2 giờ.

1. Tại Sao Quản Lý Đơn Hàng Bằng Google Sheets + Google Forms?

Nếu bạn đang bán hàng online qua Facebook, Zalo, Instagram hay website cá nhân, việc quản lý đơn hàng thủ công bằng tin nhắn hay sổ tay sẽ nhanh chóng trở thành cơn ác mộng khi số lượng đơn tăng lên. Bạn sẽ gặp các vấn đề như: quên xác nhận đơn, giao nhầm sản phẩm, không biết tồn kho còn bao nhiêu, hay không thống kê được doanh thu.

Giải pháp? Một hệ thống quản lý đơn hàng bằng Google Sheets + Google Forms! Đây là lựa chọn tối ưu cho các shop online nhỏ và vừa vì những lý do sau:

Ưu điểm nổi bật

  • Miễn phí hoàn toàn - Không tốn 1 đồng, chỉ cần tài khoản Google
  • Không cần biết lập trình - Copy/paste script là xong
  • Mobile friendly - Xem đơn hàng mọi lúc trên điện thoại
  • Nhiều người dùng - Cả team cùng quản lý, realtime
  • Tự động hóa - Email, tính tiền, trừ kho tự động
  • Dễ tùy chỉnh - Thêm/bớt cột, thay đổi quy trình thoải mái

Phù hợp với ai?

  • Shop online Facebook/Zalo (dưới 100 đơn/ngày)
  • Cửa hàng nhỏ bán qua nhiều kênh
  • Startup giai đoạn đầu, ngân sách hạn chế
  • Freelancer bán dịch vụ, nhận đơn online
  • Nhóm bán hàng handmade, pre-order
  • Ai muốn tổ chức đơn hàng chuyên nghiệp hơn

Thống kê thực tế: Theo khảo sát của chúng tôi với 500 shop online nhỏ tại Việt Nam, 73% vẫn quản lý đơn bằng tin nhắn/sổ tay41% từng giao nhầm hàng ít nhất 1 lần/tháng. Hệ thống này sẽ giúp bạn giải quyết triệt để các vấn đề đó.

2. Kiến Trúc Hệ Thống Tổng Quan

Trước khi bắt tay vào làm, hãy hiểu tổng quan cách hệ thống hoạt động. Quy trình từ khi khách đặt hàng đến khi hoàn thành đơn sẽ như sau:

Luồng hoạt động của hệ thống

1 Khách hàng điền Google Form đặt hàng (tên, SĐT, địa chỉ, sản phẩm, số lượng)
2 Google Form ghi dữ liệu vào sheet "Form Responses" tự động
3 Apps Script (onFormSubmit) tự động: Tạo mã đơn, tính tổng tiền, trừ tồn kho
4 Email tự động gửi xác nhận đơn hàng cho khách hàng
5 Nhân viên xử lý đơn, cập nhật trạng thái (Chờ xử lý → Đang giao → Hoàn thành)
6 Dashboard tự động cập nhật: tổng đơn, doanh thu, top sản phẩm

Cấu trúc Spreadsheet

Chúng ta sẽ tạo 1 Google Spreadsheet với các sheet sau:

Sheet Tên Mục đích
Sheet 1 SanPham Danh sách sản phẩm, giá, tồn kho
Sheet 2 Form Responses Dữ liệu từ Google Form (tự động)
Sheet 3 DonHang Quản lý đơn hàng (mã đơn, trạng thái, tổng tiền)
Sheet 4 Dashboard Báo cáo tổng quan, biểu đồ, KPI
Sheet 5 Config Cấu hình: tên shop, email, phí ship

3. Sheet 1: Danh Sách Sản Phẩm

Đây là sheet chứa thông tin master data của tất cả sản phẩm bạn đang bán. Các sheet khác sẽ tham chiếu đến đây để lấy giá, kiểm tra tồn kho.

Cấu trúc cột chi tiết

Cột Tên trường Kiểu dữ liệu Mô tả Ví dụ
A MaSP Text Mã sản phẩm (unique) SP001
B TenSP Text Tên sản phẩm Ao thun trang size M
C DanhMuc Text Nhóm sản phẩm Ao thun
D GiaBan Number Giá bán (VND) 199000
E GiaVon Number Giá vốn (VND) 80000
F TonKho Number Số lượng tồn kho hiện tại 150
G TonMin Number Mức tồn kho tối thiểu 10
H TrangThai Text Dang ban / Het hang / Ngung ban Dang ban
I HinhAnh URL Link ảnh sản phẩm https://drive.google.com/...

Công thức quan trọng trong sheet SanPham

Cột H (Trạng thái) - Tự động cập nhật dựa trên tồn kho:

=IF(F2<=0, "Het hang", IF(F2<=G2, "Sap het", "Dang ban"))

Conditional Formatting để tô màu theo trạng thái:

  • Đỏ khi TonKho = 0 (hết hàng): Custom formula =$F2<=0
  • Vàng khi TonKho <= TonMin (sắp hết): Custom formula =$F2<=$G2
  • Xanh khi TonKho > TonMin (bình thường): Custom formula =$F2>$G2

Data Validation cho cột DanhMuc

Tạo dropdown cho cột DanhMuc để đảm bảo dữ liệu nhất quán:

  1. Chọn cột C (DanhMuc), từ hàng 2 trở đi
  2. Vào Data → Data validation
  3. Criteria: List of items
  4. Nhập danh mục: Ao thun, Quan jean, Vay dam, Phu kien, Giay dep
  5. Tick Show dropdown list in cell → Save

4. Google Form: Tạo Form Đặt Hàng

Google Form sẽ là "cửa hàng" nơi khách hàng điền thông tin đặt hàng. Bạn có thể nhúng form này vào website, gửi link qua Facebook, Zalo, hoặc tạo QR code.

Các trường trong Form

STT Tên trường Loại Bắt buộc Ghi chú
1 Ho ten khach hang Short answer Co Validation: text length > 2
2 So dien thoai Short answer Co Regex: ^(0[0-9]{9})$
3 Email Short answer Co Response validation: Email
4 Dia chi giao hang Paragraph Co Dia chi day du
5 San pham Checkbox Co Danh sach san pham tu sheet SanPham
6 So luong moi san pham Short answer Co VD: SP001:2, SP003:1
7 Phuong thuc thanh toan Multiple choice Co COD / Chuyen khoan / Momo
8 Ghi chu Paragraph Khong Yeu cau dac biet

Kết nối Google Form với Spreadsheet

  1. Trong Google Form, click tab Responses
  2. Click icon Google Sheets (Create Spreadsheet)
  3. Chọn Select existing spreadsheet → chọn file bạn vừa tạo
  4. Form sẽ tự tạo sheet "Form Responses 1" trong Spreadsheet
  5. Mỗi khi có người submit, dữ liệu tự ghi vào sheet này

Mẹo: Bạn có thể tạo QR code cho link Google Form bằng các dịch vụ miễn phí (qr-code-generator.com). In QR code lên name card, tờ rơi, hay đặt tại quầy hàng để khách quét và đặt hàng ngay.

5. Sheet 2: Đơn Hàng (Order Management)

Sheet DonHang là nơi quản lý toàn bộ đơn hàng. Dữ liệu từ Form Responses sẽ được Apps Script xử lý và ghi vào đây với các thông tin bổ sung: mã đơn, tổng tiền, trạng thái.

Cấu trúc sheet DonHang

Cột Tên trường Nguồn dữ liệu Mô tả
A MaDon Tu dong (Script) VD: DH-20260221-001
B NgayDat Tu dong (Timestamp) Ngay gio dat hang
C TenKH Tu Form Ho ten khach hang
D SoDT Tu Form So dien thoai
E Email Tu Form Email khach hang
F DiaChi Tu Form Dia chi giao hang
G SanPham Tu Form Danh sach SP da chon
H SoLuong Tu Form Chi tiet so luong
I TongTien Tu dong (Script) Tong tien don hang (VND)
J PhiShip Tu dong (Script) Phi van chuyen
K ThanhToan Tu Form COD / Chuyen khoan / Momo
L TrangThai Thu cong / Script Cho xu ly / Dang giao / Hoan thanh / Huy
M GhiChu Tu Form Ghi chu cua khach

Conditional Formatting cho cột Trạng thái

Tô màu trạng thái để dễ nhìn:

  • Cho xu ly - Vàng (cần xử lý ngay)
  • Dang giao - Xanh dương (đang giao)
  • Hoan thanh - Xanh lá (xong)
  • Huy - Đỏ (đã hủy)

6. Apps Script: Tự Động Hóa Toàn Bộ Quy Trình

Đây là phần quan trọng nhất! Apps Script sẽ tự động xử lý khi có đơn hàng mới: tạo mã đơn, tính tổng tiền, trừ tồn kho, và gửi email xác nhận.

Mở Apps Script Editor

  1. Trong Google Sheets, vào Extensions → Apps Script
  2. Xóa code mặc định trong file Code.gs
  3. Dán toàn bộ code bên dưới vào

6a. Hàm onFormSubmit - Xử lý đơn hàng tự động

Hàm này được trigger mỗi khi có người submit Google Form. Nó sẽ tự động tạo mã đơn, tính tổng tiền, và ghi vào sheet DonHang.

// ==================================
// HE THONG QUAN LY DON HANG
// Google Sheets + Google Forms
// ==================================

// Cau hinh
var CONFIG = {
  SHEET_SANPHAM: 'SanPham',
  SHEET_DONHANG: 'DonHang',
  SHEET_CONFIG: 'Config',
  SHEET_FORM: 'Form Responses 1',
  SHOP_NAME: 'MyShop',
  SHOP_EMAIL: 'myshop@gmail.com',
  DEFAULT_SHIP_FEE: 30000
};

// === HAM CHINH: Xu ly khi co form submit ===
function onFormSubmit(e) {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var formSheet = ss.getSheetByName(CONFIG.SHEET_FORM);
  var orderSheet = ss.getSheetByName(CONFIG.SHEET_DONHANG);
  var productSheet = ss.getSheetByName(CONFIG.SHEET_SANPHAM);

  // Lay du lieu tu form response moi nhat
  var lastRow = formSheet.getLastRow();
  var formData = formSheet.getRange(lastRow, 1, 1, formSheet.getLastColumn()).getValues()[0];

  // Parse du lieu tu form
  var timestamp = formData[0];
  var tenKH = formData[1];
  var soDT = formData[2];
  var email = formData[3];
  var diaChi = formData[4];
  var sanPham = formData[5];    // Danh sach SP
  var soLuong = formData[6];    // VD: SP001:2, SP003:1
  var phuongThuc = formData[7]; // COD / CK / Momo
  var ghiChu = formData[8] || '';

  // Tao ma don hang: DH-YYYYMMDD-XXX
  var maDon = taoMaDonHang(orderSheet);

  // Tinh tong tien tu danh sach SP va so luong
  var tongTien = tinhTongTien(productSheet, soLuong);

  // Lay phi ship tu Config (hoac dung default)
  var phiShip = getShipFee(ss);

  // Ghi vao sheet DonHang
  orderSheet.appendRow([
    maDon,
    timestamp,
    tenKH,
    soDT,
    email,
    diaChi,
    sanPham,
    soLuong,
    tongTien,
    phiShip,
    phuongThuc,
    'Cho xu ly',  // Trang thai mac dinh
    ghiChu
  ]);

  // Tru ton kho
  truTonKho(productSheet, soLuong);

  // Gui email xac nhan cho khach
  guiEmailXacNhan(email, tenKH, maDon, sanPham, soLuong,
                  tongTien, phiShip, phuongThuc, diaChi);

  // Log
  Logger.log('Don hang moi: ' + maDon + ' - ' + tenKH);
}

6b. Hàm tạo mã đơn hàng tự động

// === TAO MA DON HANG TU DONG ===
function taoMaDonHang(orderSheet) {
  var today = new Date();
  var yyyy = today.getFullYear();
  var mm = String(today.getMonth() + 1).padStart(2, '0');
  var dd = String(today.getDate()).padStart(2, '0');
  var dateStr = yyyy + mm + dd;

  // Dem so don trong ngay de tao so thu tu
  var lastRow = orderSheet.getLastRow();
  var count = 0;

  if (lastRow > 1) {
    var allOrders = orderSheet.getRange(2, 1, lastRow - 1, 1).getValues();
    for (var i = 0; i < allOrders.length; i++) {
      if (allOrders[i][0] &&
          allOrders[i][0].toString().indexOf(dateStr) > -1) {
        count++;
      }
    }
  }

  var orderNum = String(count + 1).padStart(3, '0');
  return 'DH-' + dateStr + '-' + orderNum;
}

6c. Hàm tính tổng tiền

// === TINH TONG TIEN DON HANG ===
function tinhTongTien(productSheet, soLuongStr) {
  // soLuongStr: "SP001:2, SP003:1, SP005:3"
  var items = soLuongStr.split(',');
  var tongTien = 0;

  // Lay bang gia tu sheet SanPham
  var lastRow = productSheet.getLastRow();
  var products = productSheet.getRange(2, 1, lastRow - 1, 4).getValues();
  // products[i] = [MaSP, TenSP, DanhMuc, GiaBan]

  for (var i = 0; i < items.length; i++) {
    var parts = items[i].trim().split(':');
    if (parts.length === 2) {
      var maSP = parts[0].trim();
      var qty = parseInt(parts[1].trim());

      // Tim gia ban cua SP
      for (var j = 0; j < products.length; j++) {
        if (products[j][0] === maSP) {
          tongTien += products[j][3] * qty;  // GiaBan * SoLuong
          break;
        }
      }
    }
  }

  return tongTien;
}

// === LAY PHI SHIP TU CONFIG ===
function getShipFee(ss) {
  try {
    var configSheet = ss.getSheetByName(CONFIG.SHEET_CONFIG);
    if (configSheet) {
      var configs = configSheet.getDataRange().getValues();
      for (var i = 0; i < configs.length; i++) {
        if (configs[i][0] === 'PhiShip') {
          return parseInt(configs[i][1]);
        }
      }
    }
  } catch(err) {
    Logger.log('Khong doc duoc Config, dung default: ' + err);
  }
  return CONFIG.DEFAULT_SHIP_FEE;
}

6d. Hàm cập nhật trạng thái đơn hàng

Khi nhân viên đổi trạng thái đơn (từ "Cho xu ly" sang "Dang giao"), script sẽ tự động gửi email thông báo cho khách.

// === CAP NHAT TRANG THAI DON HANG ===
function onEdit(e) {
  var sheet = e.source.getActiveSheet();
  var range = e.range;

  // Chi xu ly khi edit cot L (TrangThai) trong sheet DonHang
  if (sheet.getName() !== CONFIG.SHEET_DONHANG) return;
  if (range.getColumn() !== 12) return;  // Col L = 12

  var row = range.getRow();
  if (row < 2) return;  // Bo qua header

  var newStatus = range.getValue();
  var maDon = sheet.getRange(row, 1).getValue();
  var email = sheet.getRange(row, 5).getValue();
  var tenKH = sheet.getRange(row, 3).getValue();

  // Gui email thong bao thay doi trang thai
  if (newStatus === 'Dang giao') {
    guiEmailDangGiao(email, tenKH, maDon);
  } else if (newStatus === 'Hoan thanh') {
    guiEmailHoanThanh(email, tenKH, maDon);
  } else if (newStatus === 'Huy') {
    // Hoan tra ton kho khi huy don
    var soLuong = sheet.getRange(row, 8).getValue();
    var productSheet = e.source.getSheetByName(CONFIG.SHEET_SANPHAM);
    hoanTraTonKho(productSheet, soLuong);
    guiEmailHuyDon(email, tenKH, maDon);
  }

  // Ghi log thay doi
  Logger.log('Don ' + maDon + ' -> ' + newStatus);
}

6e. Hàm báo cáo đơn hàng hàng ngày

// === BAO CAO DON HANG HANG NGAY ===
function dailyReport() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var orderSheet = ss.getSheetByName(CONFIG.SHEET_DONHANG);

  var today = new Date();
  var yyyy = today.getFullYear();
  var mm = String(today.getMonth() + 1).padStart(2, '0');
  var dd = String(today.getDate()).padStart(2, '0');
  var dateStr = yyyy + '-' + mm + '-' + dd;

  var lastRow = orderSheet.getLastRow();
  if (lastRow < 2) return;

  var allOrders = orderSheet.getRange(2, 1, lastRow - 1, 13).getValues();

  var tongDon = 0;
  var tongDoanhThu = 0;
  var choXuLy = 0;
  var dangGiao = 0;
  var hoanThanh = 0;
  var huy = 0;

  for (var i = 0; i < allOrders.length; i++) {
    var orderDate = new Date(allOrders[i][1]);
    var orderDateStr = orderDate.getFullYear() + '-' +
      String(orderDate.getMonth() + 1).padStart(2, '0') + '-' +
      String(orderDate.getDate()).padStart(2, '0');

    if (orderDateStr === dateStr) {
      tongDon++;
      tongDoanhThu += allOrders[i][8]; // TongTien
      var status = allOrders[i][11];   // TrangThai
      if (status === 'Cho xu ly') choXuLy++;
      else if (status === 'Dang giao') dangGiao++;
      else if (status === 'Hoan thanh') hoanThanh++;
      else if (status === 'Huy') huy++;
    }
  }

  // Gui email bao cao cho admin
  var subject = '[' + CONFIG.SHOP_NAME + '] Bao cao don hang ' + dateStr;
  var body = '<h2>Bao cao don hang ngay ' + dateStr + '</h2>'
    + '<table border="1" cellpadding="8">'
    + '<tr><td><strong>Tong don</strong></td>'
    + '<td>' + tongDon + '</td></tr>'
    + '<tr><td><strong>Doanh thu</strong></td>'
    + '<td>' + formatVND(tongDoanhThu) + '</td></tr>'
    + '<tr><td><strong>Cho xu ly</strong></td>'
    + '<td>' + choXuLy + '</td></tr>'
    + '<tr><td><strong>Dang giao</strong></td>'
    + '<td>' + dangGiao + '</td></tr>'
    + '<tr><td><strong>Hoan thanh</strong></td>'
    + '<td>' + hoanThanh + '</td></tr>'
    + '<tr><td><strong>Da huy</strong></td>'
    + '<td>' + huy + '</td></tr>'
    + '</table>';

  MailApp.sendEmail({
    to: CONFIG.SHOP_EMAIL,
    subject: subject,
    htmlBody: body
  });
}

// === DINH DANG TIEN VND ===
function formatVND(amount) {
  return amount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.') + ' VND';
}

Cai dat daily report trigger: Trong Apps Script Editor, vao Triggers (bieu tuong dong ho) → Add Trigger → chon ham dailyReport → Time-driven → Day timer → 8pm - 9pm. Moi ngay luc 8 gio toi, ban se nhan duoc bao cao tong hop.

6f. Cài đặt Trigger cho onFormSubmit

Bước quan trọng nhất: cài đặt trigger để script tự chạy khi có form submit mới.

  1. Trong Apps Script Editor, click menu Triggers (icon đồng hồ bên trái)
  2. Click + Add Trigger
  3. Choose function: onFormSubmit
  4. Event source: From spreadsheet
  5. Event type: On form submit
  6. Click Save
  7. Authorize permissions khi được hỏi (Allow access to Gmail, Sheets)

Luu y quan trong: Ban phai authorize (cap quyen) cho script lan dau tien. Google se hoi ban co cho phep script truy cap Gmail (de gui email) va Sheets. Click Advanced → Go to [project name] → Allow. Day la buoc bat buoc, khong authorize thi script khong chay duoc.

7. Sheet 3: Dashboard Báo Cáo Doanh Thu

Sheet Dashboard giúp bạn nhìn tổng quan tình hình kinh doanh: tổng đơn, doanh thu, đơn chờ xử lý, top sản phẩm bán chạy. Tất cả đều tự cập nhật realtime.

KPI Cards

Tạo 6 ô KPI ở đầu Dashboard:

Tong don hom nay

COUNTIFS(...)

Doanh thu hom nay

SUMIFS(...)

Don cho xu ly

COUNTIF(...)

Doanh thu thang

SUMIFS(...)

Don bi huy

COUNTIF(...)

Gia tri TB/don

AVERAGE(...)

Công thức cho từng KPI

// Tong don hom nay
=COUNTIFS(DonHang!B:B, ">="&TODAY(), DonHang!B:B, "<"&TODAY()+1)

// Doanh thu hom nay
=SUMIFS(DonHang!I:I, DonHang!B:B, ">="&TODAY(),
        DonHang!B:B, "<"&TODAY()+1,
        DonHang!L:L, "<>Huy")

// Don cho xu ly
=COUNTIF(DonHang!L:L, "Cho xu ly")

// Doanh thu thang nay
=SUMIFS(DonHang!I:I,
        DonHang!B:B, ">="&DATE(YEAR(TODAY()),MONTH(TODAY()),1),
        DonHang!B:B, "<"&DATE(YEAR(TODAY()),MONTH(TODAY())+1,1),
        DonHang!L:L, "<>Huy")

// Don bi huy (thang nay)
=COUNTIFS(DonHang!L:L, "Huy",
          DonHang!B:B, ">="&DATE(YEAR(TODAY()),MONTH(TODAY()),1))

// Gia tri trung binh / don
=IFERROR(AVERAGEIF(DonHang!L:L, "<>Huy", DonHang!I:I), 0)

Top 5 sản phẩm bán chạy

Để tạo bảng top sản phẩm, dùng QUERY kết hợp với dữ liệu đã xử lý:

// Dem so lan xuat hien cua tung SP trong cot SanPham
// (Cach don gian: dung COUNTIF voi wildcard)

// VD: Dem so don co SP001
=COUNTIF(DonHang!G:G, "*SP001*")

// Bang top san pham: tao 1 cot MaSP, 1 cot SoLuongDon
// Roi dung SORT de sap xep giam dan
=SORT(E2:F20, 2, FALSE)

Biểu đồ doanh thu theo ngày

Tạo biểu đồ đường (Line Chart) hiển thị doanh thu 30 ngày gần nhất:

  1. Tạo bảng phụ với 2 cột: Ngay (30 ngày gần nhất) và DoanhThu (SUMIFS theo ngày)
  2. Chọn vùng dữ liệu → Insert → Chart
  3. Chọn Line chart
  4. Tùy chỉnh: đổi màu, thêm title, hiển thị data labels
  5. Di chuyển chart vào vị trí đẹp trên Dashboard
// Cong thuc cho cot DoanhThu (30 ngay gan nhat)
// Cot A: =TODAY()-29, TODAY()-28, ..., TODAY()
// Cot B:
=SUMIFS(DonHang!I:I,
        DonHang!B:B, ">="&A2,
        DonHang!B:B, "<"&A2+1,
        DonHang!L:L, "<>Huy")

8. Trừ Tồn Kho Tự Động Khi Có Đơn Mới

Một trong những tính năng quan trọng nhất: khi khách đặt hàng, tồn kho tự động giảm. Khi hủy đơn, tồn kho tự động hoàn lại.

Script trừ tồn kho

// === TRU TON KHO KHI CO DON MOI ===
function truTonKho(productSheet, soLuongStr) {
  // soLuongStr: "SP001:2, SP003:1"
  var items = soLuongStr.split(',');
  var lastRow = productSheet.getLastRow();
  var products = productSheet.getRange(2, 1, lastRow - 1, 7).getValues();
  // products[i] = [MaSP, TenSP, DanhMuc, GiaBan, GiaVon, TonKho, TonMin]

  for (var i = 0; i < items.length; i++) {
    var parts = items[i].trim().split(':');
    if (parts.length === 2) {
      var maSP = parts[0].trim();
      var qty = parseInt(parts[1].trim());

      // Tim SP va tru ton kho
      for (var j = 0; j < products.length; j++) {
        if (products[j][0] === maSP) {
          var currentStock = products[j][5]; // TonKho
          var newStock = Math.max(0, currentStock - qty);
          productSheet.getRange(j + 2, 6).setValue(newStock);  // Col F = TonKho

          // Canh bao neu ton kho thap
          if (newStock <= products[j][6]) { // TonMin
            canhBaoTonKho(maSP, products[j][1], newStock, products[j][6]);
          }
          break;
        }
      }
    }
  }
}

// === HOAN TRA TON KHO KHI HUY DON ===
function hoanTraTonKho(productSheet, soLuongStr) {
  var items = soLuongStr.split(',');
  var lastRow = productSheet.getLastRow();
  var products = productSheet.getRange(2, 1, lastRow - 1, 6).getValues();

  for (var i = 0; i < items.length; i++) {
    var parts = items[i].trim().split(':');
    if (parts.length === 2) {
      var maSP = parts[0].trim();
      var qty = parseInt(parts[1].trim());

      for (var j = 0; j < products.length; j++) {
        if (products[j][0] === maSP) {
          var currentStock = products[j][5];
          productSheet.getRange(j + 2, 6).setValue(currentStock + qty);
          break;
        }
      }
    }
  }
}

// === CANH BAO TON KHO THAP ===
function canhBaoTonKho(maSP, tenSP, currentStock, minStock) {
  var subject = '[CANH BAO] Ton kho thap - ' + maSP;
  var body = '<h3>Canh bao ton kho thap</h3>'
    + '<p>San pham: <strong>' + tenSP + '</strong> (' + maSP + ')</p>'
    + '<p>Ton kho hien tai: <strong style="color:red">'
    + currentStock + '</strong></p>'
    + '<p>Muc toi thieu: ' + minStock + '</p>'
    + '<p>Vui long nhap them hang!</p>';

  MailApp.sendEmail({
    to: CONFIG.SHOP_EMAIL,
    subject: subject,
    htmlBody: body
  });
}

Ket qua: Khi khach dat "SP001:2, SP003:1", he thong tu dong tru 2 cai SP001 va 1 cai SP003 khoi ton kho. Neu ton kho giam duoi muc toi thieu, admin nhan email canh bao ngay lap tuc.

9. Email Templates: Xác Nhận, Đang Giao, Hoàn Thành

Hệ thống gửi email tự động cho khách hàng tại 3 thời điểm: khi đặt hàng thành công, khi đang giao hàng, và khi hoàn thành. Dưới đây là code cho các email templates.

Email xác nhận đơn hàng

// === GUI EMAIL XAC NHAN DON HANG ===
function guiEmailXacNhan(email, tenKH, maDon, sanPham,
                         soLuong, tongTien, phiShip, phuongThuc, diaChi) {
  var subject = '[' + CONFIG.SHOP_NAME + '] Xac nhan don hang ' + maDon;

  var body = '<div style="font-family:Arial; max-width:600px; margin:0 auto">'
    + '<div style="background:#4F46E5; color:white; padding:20px; text-align:center">'
    + '<h1 style="margin:0">' + CONFIG.SHOP_NAME + '</h1>'
    + '<p style="margin:5px 0 0">Xac nhan don hang</p>'
    + '</div>'
    + '<div style="padding:20px; background:#f9fafb">'
    + '<p>Xin chao <strong>' + tenKH + '</strong>,</p>'
    + '<p>Cam on ban da dat hang tai ' + CONFIG.SHOP_NAME
    + '. Don hang cua ban da duoc tiep nhan.</p>'
    + '<div style="background:white; border:1px solid #e5e7eb;'
    + ' border-radius:8px; padding:15px; margin:15px 0">'
    + '<h3 style="margin-top:0">Chi tiet don hang</h3>'
    + '<p><strong>Ma don:</strong> ' + maDon + '</p>'
    + '<p><strong>San pham:</strong> ' + sanPham + '</p>'
    + '<p><strong>So luong:</strong> ' + soLuong + '</p>'
    + '<p><strong>Tong tien:</strong> '
    + formatVND(tongTien) + '</p>'
    + '<p><strong>Phi ship:</strong> '
    + formatVND(phiShip) + '</p>'
    + '<p style="font-size:18px; color:#4F46E5">'
    + '<strong>Tong thanh toan: '
    + formatVND(tongTien + phiShip) + '</strong></p>'
    + '<p><strong>Thanh toan:</strong> ' + phuongThuc + '</p>'
    + '<p><strong>Dia chi:</strong> ' + diaChi + '</p>'
    + '</div>'
    + '<p>Chung toi se lien he ban trong 24h de xac nhan.</p>'
    + '<p>Tran trong,<br>' + CONFIG.SHOP_NAME + '</p>'
    + '</div>'
    + '</div>';

  MailApp.sendEmail({
    to: email,
    subject: subject,
    htmlBody: body
  });
}

Email thông báo đang giao

// === GUI EMAIL DANG GIAO HANG ===
function guiEmailDangGiao(email, tenKH, maDon) {
  var subject = '[' + CONFIG.SHOP_NAME + '] Don hang '
    + maDon + ' dang duoc giao';

  var body = '<div style="font-family:Arial; max-width:600px; margin:0 auto">'
    + '<div style="background:#059669; color:white; padding:20px; text-align:center">'
    + '<h1 style="margin:0">Dang giao hang</h1>'
    + '</div>'
    + '<div style="padding:20px">'
    + '<p>Xin chao <strong>' + tenKH + '</strong>,</p>'
    + '<p>Don hang <strong>' + maDon + '</strong>'
    + ' cua ban dang duoc giao.</p>'
    + '<p>Vui long giu lien lac de nhan hang.</p>'
    + '<p>Tran trong,<br>' + CONFIG.SHOP_NAME + '</p>'
    + '</div></div>';

  MailApp.sendEmail({
    to: email,
    subject: subject,
    htmlBody: body
  });
}

Email hoàn thành và email hủy đơn

// === GUI EMAIL HOAN THANH ===
function guiEmailHoanThanh(email, tenKH, maDon) {
  var subject = '[' + CONFIG.SHOP_NAME + '] Don hang '
    + maDon + ' da hoan thanh';

  var body = '<div style="font-family:Arial; max-width:600px; margin:0 auto">'
    + '<div style="background:#4F46E5; color:white; padding:20px; text-align:center">'
    + '<h1 style="margin:0">Don hang hoan thanh!</h1>'
    + '</div>'
    + '<div style="padding:20px">'
    + '<p>Xin chao <strong>' + tenKH + '</strong>,</p>'
    + '<p>Don hang <strong>' + maDon + '</strong>'
    + ' da giao thanh cong.</p>'
    + '<p>Cam on ban da mua hang tai ' + CONFIG.SHOP_NAME
    + '. Neu co bat ky van de gi, vui long lien he chung toi.</p>'
    + '<p>Tran trong,<br>' + CONFIG.SHOP_NAME + '</p>'
    + '</div></div>';

  MailApp.sendEmail({
    to: email,
    subject: subject,
    htmlBody: body
  });
}

// === GUI EMAIL HUY DON ===
function guiEmailHuyDon(email, tenKH, maDon) {
  var subject = '[' + CONFIG.SHOP_NAME + '] Don hang '
    + maDon + ' da bi huy';

  var body = '<div style="font-family:Arial; max-width:600px; margin:0 auto">'
    + '<div style="background:#DC2626; color:white; padding:20px; text-align:center">'
    + '<h1 style="margin:0">Don hang da huy</h1>'
    + '</div>'
    + '<div style="padding:20px">'
    + '<p>Xin chao <strong>' + tenKH + '</strong>,</p>'
    + '<p>Don hang <strong>' + maDon + '</strong>'
    + ' da bi huy theo yeu cau.</p>'
    + '<p>Neu ban khong yeu cau huy, vui long lien he chung toi ngay.</p>'
    + '<p>Tran trong,<br>' + CONFIG.SHOP_NAME + '</p>'
    + '</div></div>';

  MailApp.sendEmail({
    to: email,
    subject: subject,
    htmlBody: body
  });
}

10. Tips Mở Rộng Hệ Thống

Hệ thống cơ bản trên đã đáp ứng được 80% nhu cầu của shop online nhỏ. Dưới đây là một số gợi ý để mở rộng và nâng cấp hệ thống.

10.1. Tích hợp thanh toán QR Code

Bạn có thể tạo QR code thanh toán tự động bằng VietQR API. Khi khách chọn phương thức "Chuyển khoản", email xác nhận sẽ kèm QR code để khách quét và chuyển tiền.

// Tao link QR code VietQR
function taoQRCode(soTien, maDon) {
  var bankId = '970422';     // MB Bank
  var accountNo = '0123456789';
  var accountName = 'NGUYEN VAN A';
  var noiDung = 'Thanh toan ' + maDon;

  var url = 'https://img.vietqr.io/image/'
    + bankId + '-' + accountNo + '-compact.png'
    + '?amount=' + soTien
    + '&addInfo=' + encodeURIComponent(noiDung)
    + '&accountName=' + encodeURIComponent(accountName);

  return url;
}

10.2. Đa kênh bán hàng (Multi-channel)

Thêm trường "Kênh bán" vào Form (Facebook, Zalo, Website, Shopee, TikTok Shop) để theo dõi đơn hàng theo từng kênh. Dashboard sẽ có biểu đồ phân bổ doanh thu theo kênh.

10.3. Mã giảm giá (Coupon)

Tạo thêm sheet "MaGiamGia" chứa danh sách mã giảm giá, loại giảm giá (phần trăm hoặc số tiền), thời hạn sử dụng. Script sẽ kiểm tra và áp dụng giảm giá khi tính tổng tiền.

10.4. Notification qua Telegram

Thay vì chỉ nhận email, bạn có thể thêm thông báo qua Telegram Bot để nhận alert ngay lập tức trên điện thoại.

// Gui thong bao qua Telegram Bot
function guiTelegram(message) {
  var botToken = 'YOUR_BOT_TOKEN';
  var chatId = 'YOUR_CHAT_ID';

  var url = 'https://api.telegram.org/bot' + botToken
    + '/sendMessage';

  var payload = {
    'chat_id': chatId,
    'text': message,
    'parse_mode': 'HTML'
  };

  UrlFetchApp.fetch(url, {
    'method': 'post',
    'contentType': 'application/json',
    'payload': JSON.stringify(payload)
  });
}

10.5. In phiếu giao hàng

Tạo Google Docs template phiếu giao hàng, dùng Apps Script tự động điền thông tin đơn hàng vào template và tạo PDF. Nhân viên chỉ cần click nút "In phiếu" là có phiếu giao hàng đẹp.

10.6. Tích hợp Google Calendar

Tự động tạo event trên Google Calendar khi có đơn mới. Giúp nhân viên giao hàng biết lịch giao trong ngày. Đặc biệt hữu ích nếu bạn ship hàng theo khu vực.

So sánh: Hệ thống GS+Forms vs Phần mềm chuyên dụng

Tieu chi GS + Forms Phan mem chuyen dung
Chi phi Mien phi 500K-5M/thang
Gioi han don ~100 don/ngay Khong gioi han
Tuy chinh 100% tuy chinh Gioi han
Bao mat Co ban Chuyen nghiep
Tich hop VCSC Thu cong Tu dong
Hoa don VAT Khong Co
Setup 2 gio 1-3 ngay
Phu hop Shop nho, moi bat dau DN quy mo lon

11. FAQ - Câu Hỏi Thường Gặp

1. Hệ thống này có miễn phí không?

Hoàn toàn miễn phí! Bạn chỉ cần tài khoản Google (Gmail). Google Sheets, Google Forms, và Apps Script đều miễn phí. Lưu ý Gmail miễn phí giới hạn gửi 100 email/ngày, Google Workspace (có phí) được gửi 1500 email/ngày.

2. Có thể quản lý bao nhiêu đơn hàng?

Google Sheets hỗ trợ tối đa 10 triệu ô dữ liệu (cells). Với cấu trúc 13 cột, bạn có thể lưu khoảng 770,000 đơn hàng. Tuy nhiên, khi vượt quá 50,000 dòng, Sheets sẽ chậm lại đáng kể. Khuyến nghị giữ dưới 10,000 đơn/sheet và archive định kỳ.

3. Nhiều nhân viên có thể dùng cùng lúc không?

Có! Đây là ưu điểm lớn nhất của Google Sheets. Bạn có thể share Spreadsheet cho nhiều nhân viên với các quyền khác nhau: Editor (sửa), Commenter (bình luận), hoặc Viewer (chỉ xem). Mọi thay đổi được cập nhật realtime.

4. Nếu script gặp lỗi thì sao?

Bạn có thể kiểm tra log tại Apps Script Editor → Executions (menu bên trái). Tại đây hiện tất cả các lần chạy, trạng thái (thành công/lỗi), và chi tiết lỗi. Lỗi phổ biến nhất là quên authorize permissions hoặc tên sheet không đúng.

5. Khi nào nên chuyển sang phần mềm chuyên dụng?

Bạn nên cân nhắc chuyển khi: (1) Số đơn vượt 100 đơn/ngày, (2) Cần tích hợp đơn vị vận chuyển tự động, (3) Cần xuất hóa đơn VAT, (4) Nhiều hơn 5 nhân viên quản lý, (5) Cần báo cáo phân tích chuyên sâu. Lúc đó, một phần mềm quản lý bán hàng chuyên nghiệp sẽ phù hợp hơn.

12. Khi Nào Cần Nâng Cấp Lên Phần Mềm Chuyên Nghiệp?

Hệ thống Google Sheets + Forms rất phù hợp khi bạn mới bắt đầu hoặc có dưới 50-100 đơn/ngày. Nhưng khi shop phát triển, bạn sẽ cần một giải pháp mạnh mẽ hơn.

Phần Mềm Quản Lý Bán Hàng SheetStore

Nâng cấp từ Google Sheets lên hệ thống chuyên nghiệp. Giữ nguyên sự đơn giản, thêm sức mạnh.

  • ✓ Quản lý đơn hàng không giới hạn
  • ✓ Tích hợp đơn vị vận chuyển tự động
  • ✓ Xuất hóa đơn VAT
  • ✓ Báo cáo AI thông minh
  • ✓ Quản lý đa kênh (Omnichannel)
  • ✓ App mobile cho nhân viên
  • ✓ Phân quyền chuyên nghiệp
  • ✓ Hỗ trợ kỹ thuật 24/7
Xem chi tiet & dung thu mien phi →

Tổng Kết

Trong bài viết này, bạn đã học được cách xây dựng một hệ thống quản lý đơn hàng online hoàn chỉnh với Google Sheets + Google Forms, bao gồm:

  • 1.Sheet SanPham - Quản lý danh mục sản phẩm, giá, tồn kho với conditional formatting
  • 2.Google Form - Form đặt hàng chuyên nghiệp với validation
  • 3.Sheet DonHang - Quản lý toàn bộ đơn hàng với mã đơn tự động
  • 4.Apps Script - Tự động hóa: tạo mã đơn, tính tiền, trừ kho, gửi email
  • 5.Dashboard - Báo cáo doanh thu, KPI, biểu đồ realtime
  • 6.Email templates - 4 loại email tự động cho khách hàng
  • 7.Mở rộng - QR thanh toán, Telegram, multi-channel, coupon

Hãy bắt đầu từ phiên bản cơ bản, rồi từ từ thêm các tính năng mở rộng theo nhu cầu. Khi shop phát triển vượt quá khả năng của Google Sheets, đó là lúc bạn nên cân nhắc chuyển sang phần mềm quản lý bán hàng chuyên nghiệp.

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