Bối cảnh
Một hệ thống bán lẻ như Tân Doanh có số liệu nằm ở nhiều chỗ. Kho một nơi, đơn hàng một nơi, tiền thu hộ của hãng vận chuyển một nơi, bảo hành một nơi, sổ kế toán một nơi. Mỗi nơi tự tính một kiểu. Muốn biết lãi gộp hay hãng vận chuyển còn nợ bao nhiêu thì phải ngồi đối chiếu tay.
Tệ hơn, chỉ cần ai đó sửa nhầm một phiếu cũ là con số lệch mà không ai biết lệch từ đâu. Mình bắt đầu dựng ERP Tân Doanh ngày 09/09/2026: một phần mềm duy nhất cho kho, bán hàng, mua hàng, bảo hành, tài chính và kế toán, phục vụ hệ thống bán lẻ Tân Doanh và IGA.
Bài này nói thẳng về trạng thái hiện tại. Phần mềm dùng nội bộ, đang chạy thử và chưa mở công khai. Vì vậy mình chỉ kể về quy mô và cách làm. Mình không có kết quả kinh doanh nào để khoe, và sẽ không bịa ra.

Thách thức
Có ba ràng buộc đi cùng nhau.
Thứ nhất, đây là sổ sách. Sai một con số là sai tiền, và sai thì phải truy được vì sao. Thứ hai, nhiều người dùng ở nhiều phòng ban, mà quyền của phòng này không được lọt sang phòng khác. Thứ ba, dữ liệu thật đang nằm ở các hệ thống đang chạy, nên ERP phải đọc từ đó mà không được làm hỏng chúng.
Và có một ràng buộc thầm lặng: phần kế toán không được tự tin quá sớm. Nó phải chạy nháp song song với phần mềm kế toán đang dùng, cho đến khi hai bên khớp.
Cách làm
1. Sổ đã ghi thì không sửa, chỉ đảo
Mọi thứ khác dựa vào nguyên tắc này. Sổ kho, sổ quỹ, công nợ và tiền thu hộ (COD) đều có một chốt chặn ở ngay cơ sở dữ liệu: nó từ chối lệnh sửa và lệnh xóa trên các sổ này. Nhập sai thì lập một phiếu đảo nối với phiếu sai, rồi nhập lại cho đúng. Cả chuỗi dòng đó vẫn còn nguyên, nên mọi con số đều truy ngược được.

Mình chọn PostgreSQL cho việc này vì cơ sở dữ liệu cần những thứ nó có sẵn: ràng buộc loại trừ, trigger, tìm gần đúng không dấu, kiểu thời gian có múi giờ.
2. Một đơn hàng đi một đường khép kín
Báo giá chuyển thành đơn, đơn giữ hàng trong kho, xuất kho, bàn giao hãng vận chuyển, giao thành công, đối soát COD, trả hàng. Tất cả chạy trên cùng một engine. Vài quy tắc mình chốt sớm vì nó quyết định con số: doanh thu ghi nhận ở lúc giao thành công, giá vốn tính bình quân theo từng mã hàng và từng kho, trả hàng hoàn theo giá sau giảm, và công nợ COD bằng đã thu hộ trừ đã nộp trừ phí duyệt.

3. Quyền theo phòng ban, không theo một danh sách phẳng
Nếu gộp mọi vai trò vào một danh sách quyền chung thì quyền của phòng này dễ lọt sang phòng kia. ERP dùng quyền có cấu trúc: 6 phòng, 4 cấp, và mỗi quyền gắn với một phạm vi (của mình, được giao, cả phòng, toàn bộ). Hàm kiểm quyền luôn nhận ngữ cảnh, và thiếu ngữ cảnh thì từ chối. Ủy quyền phải có lý do, người duyệt và thời hạn, và không được rộng hơn quyền của chính người ủy quyền. Thao tác tài chính tách người lập với người duyệt. Các hàng rào này nằm ở tầng dịch vụ và cơ sở dữ liệu, chứ không dựa vào giao diện.
4. Nói rõ cái gì chưa chắc
Những giả định kinh doanh chưa được chốt đều chạy theo một phương án chọn sẵn và được liệt kê trong mục Sổ quyết định của phần mềm. Đổi phương án thì số báo cáo đổi theo, có kiểm soát, mà không phải sửa mã. Trường nào tự suy ra hoặc là dữ liệu mẫu đều có nhãn Dữ liệu demo trên màn hình, để không nhầm với số thật.
5. Sao lưu phải tự chứng minh
Sao lưu hằng đêm, có mã hóa, giữ 14 bản, và có thêm một bản ngoài máy. Mỗi Chủ nhật hệ thống tự dựng lại cơ sở dữ liệu từ bản sao, đếm bảng, thử chốt chặn chống sửa sổ, và báo ngay nếu hỏng. Bài diễn tập này từng bắt được một lỗi hàm không ghi rõ schema, và một lần báo hỏng oan do đếm quá sớm. Cả hai đều đã vá.
Kết quả
Đây là quy mô của hệ thống, không phải kết quả kinh doanh. Số đo lấy bằng cách đếm trực tiếp trong mã nguồn ngày 08/11/2026.

Ứng dụng có 126 màn hình nghiệp vụ, trải trên kho (21), tài chính (17), kế toán (17), bán hàng (16) và các phân hệ nhỏ hơn. Cơ sở dữ liệu được dựng bằng 120+ tệp migration, đánh số đến 140. Mỗi tính năng mới đi kèm một tệp migration riêng. Theo tài liệu của dự án, có 14 bài kiểm tự động chạy bằng trình duyệt, chạy trên một bản sao dựng từ bản sao lưu mới nhất và bị chặn cứng nếu trỏ vào dữ liệu thật.
Từ 09/09 đến 06/10, dự án đi từng bước: ngày đầu có phần lõi ghi sổ và 18 ca kiểm nghiệp vụ đạt (09/09), hơn 5.000 sản phẩm đưa vào danh mục (11/09), mua hàng (12/09), báo giá (18/09), phân quyền áp dụng thật (29/09), sổ kế toán lên bản chạy nháp (06/10).
Có một điều mình chưa làm được, và nó là điều quan trọng nhất. Mình chưa có kết quả kinh doanh. Phần kế toán đang ghi nháp song song với phần mềm kế toán hiện dùng, và theo kế hoạch hiện tại mới thành sổ chính thức từ ngày 01/01/2027. Cho đến lúc đó, mọi nhận định về việc ERP tiết kiệm được bao nhiêu thời gian hay công sức đều chỉ là phỏng đoán.
Bài học
Chọn nguyên tắc nền trước rồi mới viết màn hình. Câu sổ ghi rồi chỉ đảo nghe cứng, nhưng nó giúp mọi chuyện khác dễ hơn: kiểm tra, truy vết, sửa sai.
Đặt hàng rào ở nơi khó vượt qua nhất. Một nút bị ẩn trên giao diện vẫn có thể bị gọi thẳng. Một trigger trong cơ sở dữ liệu thì không.
Cho phép phần mềm nói "chưa chắc". Sổ quyết định và nhãn dữ liệu demo giúp người dùng biết chỗ nào còn đang thử, thay vì tin hết mọi con số.
Và đừng vội kể kết quả khi chưa có. Một hệ thống sổ sách cần chạy song song đủ lâu để được tin, và nó đáng được viết bài khi đã qua giai đoạn đó.
Cần việc tương tự?
Nếu bạn có số liệu nằm rải ở nhiều chỗ và mỗi chỗ tính một kiểu, hoặc cần một phần mềm nội bộ mà sai một con số là sai tiền, gửi mình vài dòng về quy trình hiện tại. Mình sẽ vẽ lại luồng dữ liệu trước, rồi nói rõ phần nào nên làm trước.




