◆ booting phatdev.net
<PhatLam/>
000%
← Tất cả case study
Phần mềm nội bộ · ERP · PostgreSQL

ERP Tân Doanh: kho, bán hàng, bảo hành và kế toán trong một phần mềm, đang chạy thử

Số liệu kho, đơn hàng, tiền thu hộ, bảo hành và sổ kế toán nằm ở nhiều chỗ, mỗi chỗ tính một kiểu. Mình dựng một phần mềm nội bộ cho Tân Doanh với một nguyên tắc nền: sổ đã ghi thì không sửa, chỉ đảo.

Đăng 8/11/2026 · 6 phút đọc

ERP Tân Doanh: kho, bán hàng, bảo hành và kế toán trong một phần mềm, đang chạy thử
Khách hàng
Tân Doanh và IGA (dùng nội bộ)
Lĩnh vực
Bán lẻ, kho, bán hàng và kế toán
Năm
2026
Vai trò
Kế hoạch, kiến trúc và vận hành (code viết cùng Claude Code)
Thời gian
Từ 09/09/2026, đang chạy thử nội bộ
Công nghệ
  • Next.js
  • TypeScript
  • PostgreSQL
  • Docker
  • Playwright
126
màn hình nghiệp vụ, đếm trong mã nguồn ngày 08/11/2026
120+
tệp migration cơ sở dữ liệu, đánh số đến 140, đếm 08/11/2026
14
bài kiểm tự động bằng trình duyệt, theo tài liệu dự án
6 × 4
phòng ban × cấp quyền trong phân quyền có cấu trúc

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.

Sơ đồ các khối chính của ERP: nhân viên, đăng nhập chung, ứng dụng, cơ sở dữ liệu, nguồn dữ liệu, sao lưu
Các khối chính của ERP. Nét đứt là dữ liệu đồng bộ một chiều vào ERP.

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.

Ví dụ minh họa: nhập kho sai, phiếu đảo, nhập kho đúng
Minh họa cách sửa một dòng sổ sai, không phải dữ liệu thật.

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.

Sơ đồ vòng đời một đơn hàng gồm bảy bước
Vòng đời một đơn hàng. Doanh thu được ghi nhận ở bước giao thành công.

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.

Biểu đồ số màn hình theo phân hệ, tổng 126
126 màn hình, chia theo phân hệ. Đếm theo tệp màn hình 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).

Vòng đời đơn hàng
Bản dọc của sơ đồ vòng đời đơn hàng, vẽ minh họa vì mọi màn hình thật đều có tên khách và số tiền.

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.

ERPPostgreSQLNext.jsAccountingInventory

Xem thêm