google-sheets
Cách tạo hóa đơn bán hàng tự động bằng Google Sheets, in A5 ngay

Một file hóa đơn bán hàng tự động trên Google Sheets cần 4 sheet: DanhMuc (mã hàng, giá), KhachHang, HoaDon (mẫu để in) và LichSu (nơi lưu các đơn đã bán). Khi bán, bạn chỉ chọn mã hàng và gõ số lượng; tên hàng, đơn giá, thành tiền, tổng tiền và số tiền bằng chữ tự hiện ra, bấm một nút là đơn được lưu và mẫu trống lại cho đơn sau.
Bài này hướng dẫn dựng file đó từ đầu, công thức và mã chạy được nguyên văn. Phù hợp cửa hàng tạp hóa, vật liệu, văn phòng phẩm, quán bán sỉ lẻ nhỏ muốn bỏ việc viết tay hóa đơn.
Lưu ý pháp lý: file này tạo phiếu bán hàng / hóa đơn bán lẻ giao cho khách để quản lý nội bộ. Nó không thay thế hóa đơn điện tử theo quy định thuế. Nếu khách yêu cầu hóa đơn điện tử, bạn vẫn phải xuất qua phần mềm hóa đơn điện tử đã đăng ký.
Bước 1: Tạo sheet DanhMuc và KhachHang
Tạo file Google Sheets mới, đổi tên sheet đầu thành DanhMuc (nhấp đúp vào tên sheet ở dưới cùng). Nhập từ dòng 1:
| A | B | C | D | |
|---|---|---|---|---|
| 1 | Mã hàng | Tên hàng | ĐVT | Đơn giá |
| 2 | VO96 | Vở kẻ ngang 96 trang | Quyển | 9000 |
| 3 | BUTTL | Bút bi Thiên Long | Cây | 5000 |
| 4 | GIAYA4 | Giấy A4 Double A | Ram | 85000 |
| 5 | KEP32 | Kẹp giấy 32mm | Hộp | 12000 |
Quy tắc quan trọng:
- Mã hàng không trùng, không dấu cách thừa. Mã là "chìa khóa" để công thức tra ra tên và giá.
- Đơn giá là số, không gõ
9.000đ. Định dạng hiển thị sau bằng Format → Number → Number.
Tạo thêm sheet KhachHang (bấm dấu + góc dưới trái): cột A Tên khách, B Điện thoại, C Địa chỉ. Dòng 2 có thể nhập Khách lẻ cho người mua không cần ghi tên.
Bước 2: Dựng mẫu hóa đơn trên sheet HoaDon
Tạo sheet HoaDon, bố trí như sau (bạn có thể đổi vị trí, nhưng khi đó phải sửa địa chỉ ô trong công thức và mã ở các bước sau):
| Ô | Nội dung |
|---|---|
| A1, A2 | Tên cửa hàng, địa chỉ, số điện thoại |
| A3 | HÓA ĐƠN BÁN HÀNG (gộp ô A3:G3, căn giữa) |
| F4 / G4 | Số HĐ: / công thức số hóa đơn |
| F5 / G5 | Ngày: / ngày bán |
| B6 / C6 | Khách hàng: / chọn tên khách |
| B7 / C7 | Điện thoại: / tự hiện |
| B8 / C8 | Địa chỉ: / tự hiện |
| Dòng 11 | Tiêu đề: STT, Mã hàng, Tên hàng, ĐVT, Số lượng, Đơn giá, Thành tiền (cột A → G) |
| Dòng 12-21 | 10 dòng mặt hàng |
| Dòng 22-24 | Cộng tiền hàng, Chiết khấu, Tổng thanh toán (số ở cột G) |
| Dòng 25 | Bằng chữ: và số tiền bằng chữ |
Danh sách chọn mã hàng và khách hàng
- Chọn vùng B12:B21 → menu Data → Data validation → Add rule.
- Ở Criteria chọn Dropdown (from a range), nhập
DanhMuc!A2:A. - Mở Advanced options → If the data is invalid: chọn Reject the input để không ai gõ sai mã. Bấm Done.
- Làm tương tự cho ô C6 với vùng
KhachHang!A2:A.
Chi tiết hơn về dropdown có trong bài tạo dropdown list trong Google Sheets.
Bước 3: Công thức tự điền tên, giá và tính tiền
Gõ các công thức sau vào dòng 12, rồi chọn A12:G12 kéo xuống đến dòng 21 (hoặc copy và dán vào A13:G21).
STT (ô A12) — chỉ hiện khi có mã hàng:
=IF(B12="", "", ROW()-11)
Tên hàng (C12):
=IF(B12="", "", XLOOKUP(B12, DanhMuc!$A$2:$A, DanhMuc!$B$2:$B, "Sai mã"))
ĐVT (D12):
=IF(B12="", "", XLOOKUP(B12, DanhMuc!$A$2:$A, DanhMuc!$C$2:$C, ""))
Đơn giá (F12):
=IF(B12="", "", XLOOKUP(B12, DanhMuc!$A$2:$A, DanhMuc!$D$2:$D, 0))
Thành tiền (G12):
=IF(OR(B12="", E12=""), "", E12*F12)
Giải thích: XLOOKUP(giá trị cần tìm, cột tìm, cột trả về, giá trị nếu không thấy) tìm mã ở cột A của DanhMuc và trả về tên, ĐVT hoặc giá cùng dòng. Dấu $ giữ cố định vùng tra cứu khi kéo công thức xuống. Bọc IF(B12="", "", ...) để dòng trống không hiện chữ "Sai mã" hay số 0. Nếu quen VLOOKUP hơn, thay bằng VLOOKUP(B12, DanhMuc!$A$2:$D, 2, FALSE) — xem VLOOKUP trong Google Sheets khác Excel thế nào.
Cột E (Số lượng) để trống cho người bán gõ.
Thông tin khách hàng
C7 (điện thoại) và C8 (địa chỉ):
=IF(C6="", "", XLOOKUP(C6, KhachHang!$A$2:$A, KhachHang!$B$2:$B, ""))
=IF(C6="", "", XLOOKUP(C6, KhachHang!$A$2:$A, KhachHang!$C$2:$C, ""))
Mẹo: điện thoại bắt đầu bằng số 0 nên định dạng cột B của KhachHang là Format → Number → Plain text trước khi nhập, nếu không số 0 đầu sẽ mất.
Tổng tiền và chiết khấu
- G22 — Cộng tiền hàng:
=SUM(G12:G21) - F23 — ô nhập % chiết khấu (ví dụ gõ
5%, để trống nếu không giảm) - G23 — Tiền chiết khấu:
=ROUND(G22*N(F23), 0) - G24 — Tổng thanh toán:
=G22-G23
Hàm N(F23) biến ô trống thành 0, nên không có chiết khấu thì công thức vẫn chạy. ROUND(..., 0) làm tròn đến đồng để tránh số lẻ như 12.350,5.
Chọn F12:G24 → Format → Number → Custom number format, gõ #,##0 để số có phân cách hàng nghìn.
Bước 4: Số tiền bằng chữ bằng hàm tự viết DOCSO
Google Sheets không có sẵn hàm đọc số thành chữ tiếng Việt, và công thức thuần thì rất dài, khó sửa. Cách gọn nhất là tự thêm một hàm bằng Apps Script:
- Menu Extensions → Apps Script.
- Xóa đoạn
function myFunction() {}có sẵn, dán toàn bộ mã dưới đây. - Bấm biểu tượng Save (hoặc Ctrl + S).
/**
* Đọc số tiền thành chữ tiếng Việt. Dùng: =DOCSO(G24)
* @customfunction
*/
function DOCSO(so) {
if (so === '' || so === null || isNaN(so)) return '';
so = Math.round(Number(so));
if (so === 0) return 'Không đồng';
const am = so < 0;
so = Math.abs(so);
const chuSo = ['không', 'một', 'hai', 'ba', 'bốn', 'năm', 'sáu', 'bảy', 'tám', 'chín'];
const donVi = ['', ' nghìn', ' triệu', ' tỷ', ' nghìn tỷ', ' triệu tỷ'];
// Đọc một nhóm 3 chữ số. day = true: đọc đủ "không trăm", "linh"
function docBaSo(n, day) {
const tram = Math.floor(n / 100);
const chuc = Math.floor((n % 100) / 10);
const dv = n % 10;
const kq = [];
if (day || tram > 0) kq.push(chuSo[tram] + ' trăm');
if (chuc === 0) {
if (dv > 0 && (day || tram > 0)) kq.push('linh');
} else if (chuc === 1) {
kq.push('mười');
} else {
kq.push(chuSo[chuc] + ' mươi');
}
if (dv === 1 && chuc >= 2) kq.push('mốt');
else if (dv === 5 && chuc >= 1) kq.push('lăm');
else if (dv > 0) kq.push(chuSo[dv]);
return kq.join(' ');
}
// Tách thành các nhóm 3 chữ số, từ phải sang trái
const nhom = [];
while (so > 0) {
nhom.push(so % 1000);
so = Math.floor(so / 1000);
}
const phan = [];
for (let i = nhom.length - 1; i >= 0; i--) {
if (nhom[i] === 0) continue; // bỏ qua nhóm toàn số 0
const laNhomDau = (i === nhom.length - 1);
phan.push(docBaSo(nhom[i], !laNhomDau) + donVi[i]);
}
let chu = phan.join(' ') + ' đồng';
if (am) chu = 'âm ' + chu;
return chu.charAt(0).toUpperCase() + chu.slice(1);
}
Quay lại sheet HoaDon, ô C25 gõ =DOCSO(G24).
Hàm xử lý đúng các cách đọc dễ sai: 105 → "Một trăm linh năm đồng", 21 → "Hai mươi mốt", 15 → "Mười lăm", 1.005.000 → "Một triệu không trăm linh năm nghìn đồng", 1.250.000.000 → "Một tỷ hai trăm năm mươi triệu đồng". Nếu cửa hàng quen đọc "lẻ" thay cho "linh", hoặc "tư" thay cho "bốn" ở hàng đơn vị, sửa chữ tương ứng trong mã.
Bước 5: Số hóa đơn tự tăng và nút "Lưu hóa đơn"
Đây là phần biến mẫu hóa đơn thành công cụ bán hàng thật: mỗi đơn có số riêng, được lưu lại để tra cứu, và giá trong lịch sử không bị đổi khi bạn sửa bảng giá.
Tạo sheet LichSu
Tạo sheet LichSu, dòng 1 gõ tiêu đề từ A đến J: Số HĐ, Ngày, Khách hàng, Mã hàng, Tên hàng, ĐVT, Số lượng, Đơn giá, Thành tiền, Chiết khấu %. Mỗi mặt hàng của một đơn là một dòng.
Số hóa đơn tự tăng
Ô G4 của HoaDon:
=MAX(LichSu!A2:A)+1
Công thức lấy số lớn nhất đã lưu cộng 1; lịch sử trống thì ra 1. Muốn hiện dạng HD0001: chọn G4 → Format → Number → Custom number format → gõ "HD"0000. Giá trị bên trong vẫn là số nên vẫn cộng được.
Mã Apps Script lưu hóa đơn
Mở lại Extensions → Apps Script, dán thêm đoạn sau bên dưới hàm DOCSO, rồi lưu:
function luuHoaDon() {
const ss = SpreadsheetApp.getActive();
const hd = ss.getSheetByName('HoaDon');
const ls = ss.getSheetByName('LichSu');
const ui = SpreadsheetApp.getUi();
const soHD = hd.getRange('G4').getValue();
const ngay = hd.getRange('G5').getValue() || new Date();
const khach = hd.getRange('C6').getValue() || 'Khách lẻ';
const chietKhau = hd.getRange('F23').getValue() || 0;
// B12:G21 = Mã hàng, Tên hàng, ĐVT, Số lượng, Đơn giá, Thành tiền
const dong = hd.getRange('B12:G21').getValues();
const duLieu = dong
.filter(r => r[0] !== '' && r[3] !== '')
.map(r => [soHD, ngay, khach, r[0], r[1], r[2], r[3], r[4], r[5], chietKhau]);
if (duLieu.length === 0) {
ui.alert('Hóa đơn chưa có mặt hàng nào có số lượng.');
return;
}
if (dong.some(r => r[1] === 'Sai mã')) {
ui.alert('Có mã hàng không có trong DanhMuc. Kiểm tra lại trước khi lưu.');
return;
}
ls.getRange(ls.getLastRow() + 1, 1, duLieu.length, duLieu[0].length).setValues(duLieu);
// Xóa ô nhập để sẵn sàng cho đơn tiếp theo (không xóa ô công thức)
hd.getRangeList(['B12:B21', 'E12:E21', 'C6', 'G5', 'F23']).clearContent();
ui.alert('Đã lưu hóa đơn số ' + soHD + ' (' + duLieu.length + ' mặt hàng).');
}
Giải thích nhanh:
getValues()đọc cả vùng 10 dòng mặt hàng một lần, nhanh hơn đọc từng ô.filter(...)chỉ giữ dòng có mã hàng và số lượng;map(...)ghép thêm số hóa đơn, ngày, tên khách vào đầu mỗi dòng.setValues()ghi tất cả vào cuối LichSu dưới dạng giá trị, nên sau này đổi giá trong DanhMuc, đơn cũ vẫn giữ giá lúc bán.clearContent()chỉ xóa ô nhập liệu (mã hàng, số lượng, khách, ngày, chiết khấu), công thức giữ nguyên. Số HĐ ở G4 tự tăng vì LichSu đã có số mới.
Gắn nút bấm
- Trên sheet HoaDon, menu Insert → Drawing, vẽ một hình chữ nhật, gõ chữ "Lưu hóa đơn", bấm Save and close.
- Kéo hình ra vùng ngoài khu vực in (ví dụ cột I).
- Bấm vào hình → biểu tượng ba chấm ở góc → Assign script → gõ
luuHoaDon→ OK.
Lần đầu bấm, Google sẽ hỏi cấp quyền: chọn tài khoản → Advanced → Go to ... (unsafe) → Allow. Cảnh báo "unsafe" xuất hiện vì đây là mã bạn tự viết, chưa qua xác minh của Google; mã chỉ đọc và ghi trong chính file này.
Bước 6: In hóa đơn khổ A5
- Chọn vùng A1:G25 (chỉ vùng hóa đơn, không gồm nút bấm).
- File → Print (hoặc Ctrl + P).
- Ở cột bên phải đặt:
- Print: Selected cells
- Paper size: A5, Page orientation: Portrait
- Scale: Fit to width
- Margins: Narrow
- Formatting: bỏ tích Show gridlines
- Bấm Next → chọn máy in, hoặc chọn Save as PDF để gửi Zalo cho khách.
Quan trọng: in hoặc lưu PDF trước, rồi mới bấm "Lưu hóa đơn", vì sau khi lưu mẫu sẽ bị xóa trắng. Muốn in lại đơn cũ, xem phần nâng cao bên dưới.
Nếu cần tự động xuất PDF từng đơn vào Google Drive, kỹ thuật tương tự bài tự động tạo file PDF hàng loạt từ Google Sheets.
Phần nâng cao: tra cứu đơn cũ và bảo vệ công thức
Xem lại một hóa đơn đã lưu
Tạo sheet XemLai, ô B1 gõ số hóa đơn cần tìm, ô A3 gõ:
=FILTER(LichSu!D2:I, LichSu!A2:A=B1)
Kết quả hiện đủ các mặt hàng, số lượng, đơn giá, thành tiền của đơn đó. Thêm =SUM(FILTER(LichSu!I2:I, LichSu!A2:A=B1)) để ra tổng. Cách lọc nhiều điều kiện (theo khách, theo tháng) có trong bài hàm FILTER trong Google Sheets.
Khóa ô công thức để nhân viên không xóa nhầm
Menu Data → Protect sheets and ranges → Add a sheet or range → chọn sheet HoaDon → bấm Except certain cells và nhập các ô được phép sửa: B12:B21, E12:E21, C6, G5, F23 → Set permissions → chỉ bạn được sửa. Nhân viên vẫn nhập đơn bình thường nhưng không làm hỏng công thức.
Đừng dùng TODAY() cho ngày hóa đơn
=TODAY() luôn trả về ngày hiện tại, nên một hóa đơn lập hôm nay mở lại ngày mai sẽ tự đổi ngày. Hãy gõ ngày bằng phím tắt Ctrl + ; (chèn ngày hôm nay dạng giá trị cố định), hoặc để trống ô G5 — mã luuHoaDon sẽ tự ghi thời điểm bấm lưu.
Lỗi thường gặp
Tên hàng hiện "Sai mã" dù mã đúng. Nguyên nhân: mã trong DanhMuc có dấu cách thừa ở cuối, hoặc mã dạng số bị lưu là chữ ở một bên. Cách sửa: chọn cột A của DanhMuc → Data → Data cleanup → Trim whitespace; với mã toàn số, định dạng cả hai nơi cùng kiểu Plain text.
Ô bằng chữ hiện #NAME?.
Nguyên nhân: chưa lưu mã Apps Script hoặc gõ sai tên hàm. Cách sửa: mở lại Extensions → Apps Script, kiểm tra có hàm DOCSO và đã bấm Save; gõ lại công thức =DOCSO(G24).
Bấm nút báo "Script function luuHoaDon could not be found".
Nguyên nhân: gõ sai tên khi Assign script (phân biệt chữ hoa, thường) hoặc chưa lưu mã. Cách sửa: gán lại đúng luuHoaDon, không có dấu ngoặc.
Lỗi "Cannot read properties of null (reading 'getRange')".
Nguyên nhân: tên sheet trong file khác tên trong mã (ví dụ Hóa đơn thay vì HoaDon). Cách sửa: đổi tên sheet cho khớp, hoặc sửa chuỗi trong getSheetByName('...').
In ra bị chia hai trang hoặc chữ quá nhỏ. Nguyên nhân: chọn Current sheet nên in cả nút bấm và các cột trống, hoặc cột quá rộng. Cách sửa: chọn đúng vùng A1:G25 và Selected cells, thu hẹp cột Tên hàng, giữ Fit to width.
Số hóa đơn bị trùng khi hai người cùng bán.
Nguyên nhân: hai máy cùng mở mẫu, cùng thấy một số G4 rồi cùng bấm lưu. Cách sửa: mỗi quầy dùng một file riêng có tiền tố khác nhau (ví dụ "A"0000, "B"0000); nếu cần nhiều người bán chung một kho dữ liệu, đó là lúc nên dùng phần mềm bán hàng hoặc công cụ viết riêng.
Bảng tóm tắt
| Phần | Ô / vị trí | Công thức hoặc thao tác |
|---|---|---|
| Chọn mã hàng | B12:B21 | Data → Data validation → Dropdown từ DanhMuc!A2:A |
| Tên hàng | C12 | =IF(B12="","",XLOOKUP(B12,DanhMuc!$A$2:$A,DanhMuc!$B$2:$B,"Sai mã")) |
| Đơn giá | F12 | =IF(B12="","",XLOOKUP(B12,DanhMuc!$A$2:$A,DanhMuc!$D$2:$D,0)) |
| Thành tiền | G12 | =IF(OR(B12="",E12=""),"",E12*F12) |
| Tổng thanh toán | G24 | =G22-G23 |
| Bằng chữ | C25 | =DOCSO(G24) (hàm Apps Script tự thêm) |
| Số hóa đơn | G4 | =MAX(LichSu!A2:A)+1, định dạng "HD"0000 |
| Lưu đơn | Nút "Lưu hóa đơn" | Gán script luuHoaDon |
| In A5 | A1:G25 | Ctrl + P → Selected cells, A5, Fit to width |
Khi nào nên nhờ viết công cụ riêng?
File trên đủ dùng cho một quầy bán, vài chục đơn mỗi ngày. Khi bạn cần trừ tồn kho tự động theo từng đơn, nhiều chi nhánh cùng ghi vào một nơi, in qua máy in nhiệt khổ 80mm, gửi hóa đơn PDF qua email cho khách, hoặc báo cáo công nợ theo khách hàng, khối lượng mã sẽ tăng nhanh và dễ phát sinh lỗi khó tìm. Lúc đó nên cân nhắc thuê viết công cụ Apps Script riêng theo đúng quy trình cửa hàng, hoặc chuyển sang phần mềm bán hàng có sẵn — xem phân tích ưu nhược điểm trong bài phần mềm quản lý riêng hay phần mềm có sẵn. Điền Nguyễn AZ có nhận viết công cụ VBA, Apps Script theo yêu cầu.
Câu hỏi thường gặp
Hóa đơn tạo bằng Google Sheets có thay được hóa đơn điện tử không?
Không. File Google Sheets chỉ tạo phiếu bán hàng hay hóa đơn bán lẻ để giao khách và quản lý nội bộ. Hóa đơn điện tử phải được lập qua phần mềm hóa đơn điện tử và theo quy định của cơ quan thuế; nếu chưa rõ cửa hàng mình thuộc diện nào, hãy hỏi chi cục thuế hoặc kế toán.
Có dùng mẫu hóa đơn Google Sheets trên điện thoại được không?
Được phần nhập liệu: ứng dụng Google Sheets trên điện thoại vẫn chọn được dropdown, gõ số lượng và công thức vẫn tự tính. Tuy nhiên nút bấm gán script không chạy trên ứng dụng di động, nên bước bấm "Lưu hóa đơn" cần làm trên máy tính hoặc mở Google Sheets bằng trình duyệt ở chế độ máy tính.
Làm sao trừ tồn kho khi bán hàng bằng Google Sheets?
Khi đã có sheet LichSu ghi từng mặt hàng đã bán, tồn kho của một mã bằng tổng nhập trừ tổng bán, tính bằng SUMIFS trên cột mã hàng và cột số lượng. Cách dựng sheet nhập kho và công thức tồn có trong bài quản lý kho hàng nhỏ bằng Google Sheets trên blog Điền Nguyễn AZ.
Hàm DOCSO có dùng được trong Excel không?
Không dùng trực tiếp được, vì DOCSO viết bằng Apps Script (JavaScript) chỉ chạy trong Google Sheets. Trong Excel cần một hàm tương tự viết bằng VBA, hoặc dùng add-in đọc số tiếng Việt; ý tưởng tách nhóm 3 chữ số và đọc "linh, mốt, lăm" vẫn giống nhau.
Bài viết liên quan
- Cách dùng Google Sheets quản lý kho hàng nhỏ
- Cách dùng Google Apps Script tự động hóa Sheets cơ bản
- Cách tạo dropdown list trong Google Sheets
Học Google Sheets cho cửa hàng, văn phòng
Khóa học Google Sheets tại Điền Nguyễn AZ thực hành trên đúng file bán hàng, kho, chấm công của học viên: XLOOKUP, FILTER, Data validation, chia sẻ quyền và Apps Script cơ bản. Học trực tiếp tại TP Vinh (Số 40 ngõ 207 đường Lê Lợi), Hưng Nguyên, Đô Lương, hoặc học online qua Zoom nếu bạn ở Hà Nội, TP.HCM hay tỉnh khác.
📞 Hotline / Zalo: 0362 465 361
👉 Xem chi tiết: Khóa học Google Sheets · Cần công cụ làm sẵn: Dịch vụ VBA, Apps Script
Tài liệu miễn phí
30 phím tắt Excel hay dùng nhất
Tổng hợp phím tắt giúp thao tác Excel nhanh hơn — điền thông tin để tải file PDF miễn phí về dùng ngay.
Bài viết liên quan

31/10/2026
Tạo PDF hàng loạt từ Google Sheets bằng Apps Script và tự gửi email
Tự động tạo PDF hàng loạt từ Google Sheets bằng Apps Script: mỗi nhân viên một phiếu lương PDF, lưu Drive, gửi email đính kèm, có chế độ chạy thử và mã mẫu.

31/10/2026
Hàm FILTER trong Google Sheets: lọc nhiều điều kiện, tự cập nhật
Cách dùng hàm FILTER trong Google Sheets lọc nhiều điều kiện AND, OR, theo khoảng ngày, chứa từ khóa, theo danh sách và làm báo cáo chọn nhân viên tự cập nhật.

27/10/2026
Hàm UNIQUE và SORT trong Google Sheets: lọc trùng, sắp xếp tự động
Cách dùng hàm UNIQUE và SORT trong Google Sheets để lọc trùng, sắp xếp tự động: bỏ ô trống, sắp xếp học sinh theo tên, top N bằng SORTN, kèm lỗi thường gặp.