Tự động hoá · 9 phút đọc · 2026-09-11
Tự động hoá bán hàng với n8n: đọc file đơn hàng, cộng doanh thu và gửi email báo cáo
Một luồng n8n đọc CSV đơn hàng, lọc trạng thái Đã giao, cộng doanh thu rồi gửi báo cáo nội bộ qua email. Có cả bước kiểm tra đầu ra pass/fail để bắt lỗi dữ liệu và lỗi SMTP.

Luồng dưới đây chỉ đọc một file CSV, lọc các dòng có trạng thái Đã giao, cộng thành tiền và gửi một email báo cáo nội bộ. Bạn sẽ dựng được một bài kiểm thử có đầu ra pass/fail rõ ràng; luồng này chưa tự chốt đơn, chưa cập nhật trạng thái và chưa gửi email cho khách.
Tự động hoá bán hàng ở đây chỉ là báo cáo CSV, không phải tự động chốt đơn
Trong bài này, “tự động hoá bán hàng” có phạm vi hẹp: lấy dữ liệu đã có sẵn trong CSV, chọn đúng các đơn đã giao, cộng doanh thu rồi gửi kết quả qua email. Đây là một phần của sales automation, gần với tự động hoá báo cáo hơn là một hệ thống bán hàng tự động. Shop đang xuất đơn từ phần mềm khác, doanh nghiệp có người tổng hợp doanh thu cuối ngày, hoặc đội kỹ thuật chỉ cần một luồng kiểm tra nội bộ đều có thể áp dụng ngay. Bạn không cần bắt đầu bằng việc nối website, cổng thanh toán và CRM.
Luồng này không làm các việc như nhận đơn mới, tự động cập nhật trạng thái đơn hàng, trừ tồn kho, đối soát thanh toán hay tự động hóa quy trình chốt đơn. Nó cũng không chứng minh rằng dữ liệu trong CSV là đúng với hệ thống nguồn. Nếu file đã sai mã đơn hoặc sai thành tiền, n8n vẫn có thể cộng ra một kết quả rất “gọn” nhưng không có nghĩa kết quả đó đúng. Vì vậy, mục tiêu của bài là dựng một đường ống nhỏ có thể kiểm từng chặng: file có đọc được không, CSV có chuyển thành dòng dữ liệu không, có đúng năm dòng Đã giao không và email thử có xuất hiện trong Mailpit không.
Chuẩn bị file đơn hàng: CSV phải chứa giá trị, không chứa công thức
Mình dùng lại đúng tám đơn trong bài file Excel quản lý bán hàng trên blog này. Ở bài đó, bảng tính đã tính sẵn hai con số làm mốc: doanh thu các đơn "Đã giao" là 7.820.000, số đơn đã giao là 5. Luồng n8n ra đúng hai số này thì mới coi là chạy đúng.
Có một chi tiết phải xử lý trước. File CSV lưu từ bảng tính có công thức sẽ chứa chữ của công thức, kiểu =E2*F2, chứ không chứa kết quả — CSV chỉ là văn bản, nó không tính gì cả. Nên mình xuất lại một file chỉ có giá trị, cột Thành tiền là số đã nhân sẵn:
Ngày,Mã đơn,Khách hàng,Sản phẩm,Số lượng,Đơn giá,Thành tiền,Trạng thái
2026-09-01,DH001,Chị Lan,Áo thun cotton,12,145000,1740000,Đã giao
2026-09-01,DH002,Anh Dũng,Quần jean nam,3,320000,960000,Đã giao
2026-09-02,DH003,Chị Hoa,Váy hoa nhí,5,285000,1425000,Đang giao
2026-09-03,DH004,Anh Tuấn,Áo khoác gió,2,450000,900000,Đã giao
2026-09-04,DH005,Chị Mai,Áo thun cotton,20,145000,2900000,Đã huỷ
2026-09-05,DH006,Anh Nam,Quần short kaki,8,190000,1520000,Đã giao
2026-09-06,DH007,Chị Thu,Váy hoa nhí,4,285000,1140000,Đang giao
2026-09-08,DH008,Anh Bình,Áo khoác gió,6,450000,2700000,Đã giaoTên cột giữ nguyên tiếng Việt có dấu. Node Code phía sau gọi đúng tên cột này, nên bạn đổi tên cột thì phải đổi cả trong mã.
Cái bẫy đầu tiên: n8n không cho đọc file ở thư mục tùy ý
Lượt chạy đầu, mình trỏ node đọc file tới bản CSV nằm trong thư mục dự án trên ổ H. Node đọc file chuyển đỏ ngay, cả lượt chạy kết thúc ở trạng thái error, và thông báo lỗi nguyên văn chỉ có một câu:
Access to the file is not allowed.Câu đó không nói file được phép nằm ở đâu. Mình tải toàn bộ dữ liệu của lượt chạy lỗi về để tìm, và không có gợi ý đường dẫn nào trong đó.

Câu trả lời nằm trong cấu hình mặc định của chính n8n: biến N8N_RESTRICT_FILE_ACCESS_TO mặc định là ~/.n8n-files. Trên Windows, đó là thư mục .n8n-files nằm trong thư mục người dùng của bạn. Mình chép file vào đó, đổi đường dẫn trong node, và lượt chạy sau qua ngay. Muốn đọc ở thư mục khác thì đặt lại biến trên trước khi khởi động n8n — mình chưa thử cách này, nên bài không hướng dẫn nó.
Dựng năm node cho luồng tự động hoá bán hàng
Luồng gồm năm node nối thẳng hàng. Tên node dưới đây là tên mình đặt, bạn đặt tên khác cũng được; tên loại node là tên hiện trong n8n 2.35.7, bản mình dùng để chạy toàn bộ bài này.
Node 1 — Manual Trigger
Thêm node Manual Trigger để bấm chạy bằng tay. Khi đang dựng, chạy tay cho thấy kết quả của từng node ngay lập tức; chỉ nên thay bằng lịch chạy sau khi cả luồng đã ra đúng số.
Node 2 — Read/Write Files from Disk
Chọn thao tác đọc file (giao diện ghi là Read File(s) From Disk), rồi điền đường dẫn tới file trong thư mục .n8n-files. Mình dùng dấu gạch chéo xuôi:
C:/Users/<tên-người-dùng>/.n8n-files/don-hang.csvChạy riêng node này: đầu ra là 1 item chứa file ở dạng nhị phân, trong trường data. Thấy lỗi "Access to the file is not allowed." thì quay lại mục trên — file đang nằm sai thư mục.
Node 3 — Extract from File
Chọn chế độ CSV, trường nhị phân để mặc định là data. Chạy riêng node này: đầu ra phải là 8 item, mỗi item mang đủ tám cột Ngày, Mã đơn, Khách hàng, Sản phẩm, Số lượng, Đơn giá, Thành tiền, Trạng thái. Ra 1 item thì dòng tiêu đề hoặc dấu phân cách của file đang có vấn đề.
Node 4 — Code
Chọn chế độ Run Once for All Items, ngôn ngữ JavaScript, rồi dán nguyên đoạn này:
// Gom mọi dòng đơn hàng từ file CSV, chỉ cộng những đơn "Đã giao".
const dong = $input.all().map((i) => i.json)
const daGiao = dong.filter((d) => d['Trạng thái'] === 'Đã giao')
const doanhThu = daGiao.reduce((t, d) => t + Number(d['Thành tiền']), 0)
return [{
json: {
tongSoDon: dong.length,
soDonDaGiao: daGiao.length,
doanhThuDaGiao: doanhThu,
doanhThuChu: doanhThu.toLocaleString('vi-VN') + ' đ',
},
}]Phép lọc dùng ===, tức so khớp tuyệt đối: "đã giao" viết thường, "Đã giao " thừa một dấu cách hay "Da giao" thiếu dấu đều bị coi là khác và không được cộng. Chặt như vậy để khỏi cộng nhầm, đổi lại dữ liệu nguồn phải gõ thống nhất.

Node 5 — Send Email
Điền máy chủ SMTP mà bạn dùng để thử; mình dùng Mailpit chạy trên máy, nghe ở 127.0.0.1 cổng 1025, không mật khẩu. Chọn định dạng thư là Text. Hai ô tiêu đề và nội dung phải ở chế độ biểu thức (Expression) thì mới lấy được số từ node Code. Tiêu đề:
Báo cáo bán hàng: {{ $json.soDonDaGiao }} đơn đã giao, doanh thu {{ $json.doanhThuChu }}Nội dung:
Tổng số đơn trong file: {{ $json.tongSoDon }}
Đơn đã giao: {{ $json.soDonDaGiao }}
Doanh thu đã giao: {{ $json.doanhThuChu }}
Báo cáo do n8n tự tổng hợp lúc {{ $now.format("HH:mm dd/MM/yyyy") }}.Hai lỗi mình từng dính ở luồng trước, áp luôn từ đầu để khỏi vấp lại. Thứ nhất, nội dung thư phải đặt đúng vào ô thân thư của node; ở một luồng trước mình đặt nhầm tham số và nhận về một lá thư rỗng, không có lỗi nào báo. Thứ hai, múi giờ mặc định của n8n là America/New_York: vào phần cài đặt của workflow đổi sang Asia/Ho_Chi_Minh, nếu không giờ ghi trong báo cáo lệch 11 tiếng so với giờ Việt Nam.
Chạy thử và đối chiếu: 8 đơn vào, 5 đơn đã giao, 7.820.000 đ
Bấm chạy cả luồng. Cả năm node xanh, và số item trên từng đường nối kể lại đúng câu chuyện: một file đi vào, tám dòng đơn được tách ra, một dòng báo cáo đi tới node gửi thư.

Đối chiếu bằng tay để chắc phép cộng đúng, không chỉ tin con số n8n in ra. Năm đơn đã giao là DH001, DH002, DH004, DH006, DH008:
1.740.000 + 960.000 + 900.000 + 1.520.000 + 2.700.000 = 7.820.000Ba đơn không được cộng là DH003 và DH007 (Đang giao) cùng DH005 (Đã huỷ). Kết quả khớp đúng con số bảng tính đã tính ở bài Excel. Thư tới Mailpit ngay sau đó:

Những gì luồng này chưa làm được
Luồng trên là bước đầu của tự động hoá bán hàng, không phải toàn bộ. Nó chạy bằng tay, chưa chạy theo lịch hằng ngày. Nó đọc CSV, chưa đọc thẳng file .xlsx hay Google Sheets. Thư mới đi tới máy chủ bắt thư trên máy, chưa qua nhà cung cấp thư thật, nên chưa biết thư có vào hộp thư rác không. Mình cũng chưa thử với file vài nghìn dòng.
Muốn đi tiếp, bước an toàn nhất là thay Manual Trigger bằng Schedule Trigger — nhưng chỉ sau khi luồng chạy tay đã ra đúng số, và nhớ kiểm lại múi giờ, vì lịch chạy cũng theo múi giờ của workflow.
- Tác giả
- Quản trị viên
- Đăng ngày
- 2026-09-11
- Cập nhật
- 2026-09-11
- Thời gian đọc
- 9 phút đọc
- Tất cả bài viết