Sheet 1 · Tổng quan hệ thống
CTC ERP — Tổng quan & Danh mục yêu cầu
CTC ERP (frontend-next, Next.js 16 + React 19 + shadcn) là hệ thống quản trị nội bộ đa tenant của Smart Tech CTC — hợp nhất Công việc, Đối tác/CRM, Dự án, Tài chính, Hoá đơn, Lịch, Quy trình tự động, Kỹ năng và các module vận hành khác trên 1 codebase, phục vụ đồng thời 4 tenant (ctc, ode, lhbd, journalist) bằng cùng 1 bộ source, mỗi tenant 1 SQLite DB riêng biệt. Sản phẩm chạy tại https://ctc.erp.smarttechctc.io/ trên VPS riêng (SSH ctc-erp, unix user erp, mã tại /home/erp/erp-platform) — chuyển sang từ 03/09/2026; PM2 gồm erp-next (web :4123) và erp-crm-sync (API :4114), cạnh đó là môi trường thử https://staging-erp.smarttechctc.io (erp-staging-web / erp-staging-api). Bộ tài liệu bạn đang đọc đã tách sang site riêng https://erp-docs.smarttechctc.io để vòng đời của nó không dính vào một tenant nào.
erp/modules/*(erp)/* (+ 1 trang đăng nhập)sync-server/test/ (20 file), pass hếtCách đếm (03/09/2026): module = số thư mục con cấp 1 dưới frontend-next/src/erp/modules/; route = số file page.tsx dưới frontend-next/src/app/(erp)/ (không tính route demo template (pages)/(blank) chưa gỡ khỏi source); bảng = đếm thật trong sqlite_master của tenant ctc (29). Cách đếm này quan trọng: tenant.js có 21 câu CREATE TABLE IF NOT EXISTS, nhưng 20 trong đó là bảng có tên cố định còn câu thứ 21 nằm trong vòng lặp CREATE TABLE … ${table} sinh ra 9 bảng thực thể — nên "21" là con số của mã nguồn, không phải của cơ sở dữ liệu; test = node --test sync-server/test/*.test.js. Riêng số endpoint không nêu thành thẻ vì cách đếm dễ gây hiểu sai: server.js có 103 chỗ kiểm req.method === '…', nhưng một path thường được kiểm nhiều lần cho nhiều method nên con số đó KHÔNG phải số endpoint riêng biệt.
ai (erp/modules/ai/ai.ts) — đó là AI Narrative Engine cho nghiệp vụ duyệt nội dung bất động sản, gọi /api/ai bằng khoá HTTP của tenant. Nó không liên quan tới AI Gateway hay trợ lý ngoài; hai thứ đó xem trang AI Engine & Gateway.Kiến trúc tóm tắt
| Thành phần | Mô tả |
|---|---|
| Đa tenant | 1 codebase phục vụ 4 khách hàng qua Host header (slugFromHost) → mỗi tenant 1 file SQLite riêng tại sync-server/data/tenants/<slug>/crm.db, cách ly vật lý. |
| Đồng bộ realtime dạng blob | Toàn bộ dữ liệu nghiệp vụ (tasks/contacts/projects/events/invoices/transactions/notifications) đóng gói thành 1 JSON "state" lưu trong bảng revisions (cột rev tăng dần). Client pullState() lấy state mới nhất, PUT /api/state ghi đè state mới (server tự diff ra thay đổi + tăng rev), GET /api/events là kênh SSE đẩy thay đổi tới các tab đang mở. Bảng thực thể (tasks/contacts/...) chỉ là materialized view sinh lại từ blob, không phải nguồn sự thật. |
| Phân quyền theo module | auth_users.modules (mảng JSON) quyết định user thấy menu/route nào; vai trò admin luôn có toàn bộ ALL_MODULE_IDS. Xem chi tiết ma trận ở trang Vai trò. |
| Per-user server-side | Một số dữ liệu KHÔNG nằm trong blob sync (tránh phải bump schema toàn cục): Workflow/Skill/DevRequest/Comment lưu bảng riêng; cấu hình cá nhân (thu nhập, tích hợp relay, đã đọc mail...) lưu bảng user_data qua /api/me/data. |
| Hai tầng lọc dữ liệu | Bắt buộc ở MỌI đường đọc: scopeStateForUser(filterStateForUser(inner, actor), actor). Tầng 1 cắt theo phân hệ, tầng 2 cắt theo phạm vi từng bản ghi. Thiếu tầng 2 là rò dữ liệu cả công ty — đã xảy ra ba lần và được vá 02/09. |
| Trợ lý ngoài gọi VÀO ERP | Chiều duy nhất được phép: trợ lý AI (Claude Code, antigravity, bot Telegram — chạy trên máy khác, unix user khác) gọi vào ERP bằng khoá API của từng người (header X-Agent-Key, bảng bot_api_keys). Khoá gắn chết một tài khoản, chỉ mở đúng phân hệ và phạm vi dữ liệu mà chính người đó có. Có 9 hành động allowlist (viec, tim, help, them, xong, trangthai, han, tao_ky_nang, tao_quy_trinh) — không có hành động xoá nào. Xem chi tiết ở trang Quy trình. |
| ERP không chạy CLI mô hình | ERP không bao giờ chạy claude -p / agy -p. Sáng 03/09 từng có ngoại lệ (ai-cli.js cho tính năng "Dựng bằng AI") và đã gỡ hẳn cùng ngày: endpoint đó cho mọi tài khoản đăng nhập gõ 2000 ký tự tự do rồi nối thẳng vào dòng lệnh chạy dưới user erp — cùng máy với cơ sở dữ liệu của cả 4 khách hàng. Việc sinh nội dung nay do trợ lý bên ngoài làm rồi gọi ngược vào ERP. |
| Hệ thống ngoài | HubSpot (đồng bộ contact/deal), Zalo OA (relay follower/profile), Gmail qua Google Apps Script Web App (ingest mail + gửi), webhook Google Workspace (script.google.com/googleusercontent/chat.googleapis/zapier). Telegram không còn do ERP long-poll — AI Gateway giữ kênh Telegram và gọi vào ERP qua /api/agent/* (đảo chiều 29/08, gỡ bot-worker.js 30/08). |
Danh mục yêu cầu (suy luận từ lịch sử phát triển ghi trong CONTEXT.md — không phải văn bản yêu cầu gốc)
| NO | DESCRIPTION |
|---|---|
| 1 | Trước đây CTC vận hành CRM/công việc rời rạc qua 4 instance riêng biệt (CTC, ODE, journalist, teamwork) không chia sẻ được code lẫn dữ liệu — cần hợp nhất thành 1 nền tảng đa tenant duy nhất. |
| 2 | Quản lý phải theo dõi được toàn bộ vòng đời bán hàng: đối tác (contact) → deal trên dự án (dealStage/dealValue) → hoá đơn → thu tiền, thay vì rải rác trên Excel. |
| 3 | Mỗi nhân sự cần 1 nơi duy nhất quản lý công việc được giao (kanban + bảng), có thể lọc theo dự án/đối tác/giải pháp, sắp xếp theo nhiều cột, sửa nhanh (inline edit) ưu tiên/phụ trách/hạn. |
| 4 | Ban điều hành cần dashboard tổng quan tỷ trọng doanh thu/khối lượng việc theo giải pháp (gp:*) và theo ngành khách hàng (nganh:*) để ra quyết định phân bổ nguồn lực. |
| 5 | Cần phân quyền theo vai trò: admin thấy toàn bộ hệ thống; member (nhân viên) chỉ thấy công việc/dự án/đối tác của chính mình, giới hạn theo module được cấp. |
| 6 | Cần tự động hoá các quy trình lặp lại (nhắc việc, đổi trạng thái, gửi thông báo relay Telegram/GWS) mà không cần biết lập trình — thông qua builder workflow trực quan (canvas kéo-thả các bước). |
| 7 | Cần chuẩn hoá và tái sử dụng tri thức vận hành (quy trình thao tác chuẩn) qua module Kỹ năng, có thể nhập từ file Markdown sẵn có, chia sẻ giữa các nhân sự. |
| 8 | Nhân sự cần kênh đề xuất/yêu cầu phát triển tính năng (workflow/skill mới) có quy trình duyệt rõ ràng (new → reviewing → approved/rejected → released) thay vì báo miệng. |
| 9 | Cần theo dõi tài chính (thu/chi, giao dịch định kỳ), ngân sách truyền thông theo chiến dịch (NCC, nghiệm thu, quy đổi Overtime→ngày nghỉ/tiền OT) và xuất hoá đơn GTGT có OCR quét ảnh hoá đơn đầu vào. |
| 10 | Từng nhân sự cần công cụ cá nhân tính thu nhập/thuế TNCN theo lương + % hoa hồng dự án, không phải làm thủ công ngoài hệ thống. |
| 11 | Cần tích hợp email (Gmail cá nhân qua Apps Script) và Zalo OA ngay trong ERP để không phải chuyển đổi qua lại nhiều công cụ khi chăm sóc khách hàng. |
| 12 | Dữ liệu khách hàng đã có sẵn trên HubSpot (9.949 contact) cần đồng bộ một chiều vào ERP thay vì nhập tay lại. |
| 13 | Cần cơ chế review/bình luận (comment ghim theo trường dữ liệu, @mention) để nhiều người cùng góp ý một bản ghi mà không cần rời khỏi màn hình đang xem. |
| 14 | Cần tìm kiếm toàn hệ thống (⌘K) xuyên suốt các loại bản ghi (task/contact/project/invoice...), có bỏ dấu tiếng Việt, tôn trọng phân quyền của người tìm. |
| 15 | Toàn bộ lịch sử thay đổi dữ liệu phải truy vết được (revision tăng dần + nhật ký hệ thống) để phục vụ audit và khôi phục khi cần. |
Giải thích thuật ngữ
| Thuật ngữ | Giải nghĩa |
|---|---|
| dealStage vs status | Project.dealStage (lead→contact→demo→proposal→negotiation→won/lost) đo tiến độ bán hàng (nằm trên phễu); Project.status (planning→active→on_hold→acceptance→completed) đo tiến độ giao hàng/triển khai. Hai trục độc lập, tách khỏi nhau kể từ đợt "Deal → PROJECT" (27/07). |
Tag gp:* | 8 tag Giải pháp (truyen-thong · su-kien · ai · crm-erp · digital-marketing · business · iot · van-hanh) — chiều dữ liệu xuyên suốt Contact/Project/Task/Transaction/Invoice, dùng tính tỷ trọng doanh thu/khối lượng theo giải pháp. n tag trên 1 bản ghi → mỗi tag trọng số 1/n. |
Tag nganh:* | 10 tag Ngành khách hàng, gắn ở cấp Đối tác (Contact) — dùng tính tỷ trọng doanh thu/số lượng theo ngành trên Dashboard. |
Tag viec:* | 8 tag Loại lao động ở cấp Công việc (noi-dung · thiet-ke-media · ky-thuat · su-kien-hien-truong · sales-deal · quan-he · nghien-cuu · van-hanh-noi-bo) — đo khối lượng việc theo loại. |
Tag tc:* | 8 tag Bản chất dòng tiền, dùng chung cho Giao dịch/Định kỳ/Hoá đơn (thu-du-an · thu-dinh-ky · chi-media-booking · chi-san-xuat · chi-luong · chi-cong-cu · chi-van-hanh · khac). |
| Rollup giá trị đối tác | Contact.dealValue (field nhập tay, còn tồn tại trong schema nhưng KHÔNG còn hiển thị) đã được thay bằng giá trị tính lại (rollup) = tổng dealValue của mọi Project gắn contactId đó (trừ dự án lost). UI hiển thị dưới tên "Giá trị hợp đồng", đọc-only, phụ đề "Tổng từ N dự án". |
| Blob state & rev | Toàn bộ dữ liệu nghiệp vụ của 1 tenant đóng gói thành 1 JSON lớn ("state"), mỗi lần ghi tăng số rev (revision) và lưu 1 bản snapshot mới vào bảng revisions — vừa là cơ chế đồng bộ realtime giữa các tab/thiết bị, vừa là lịch sử thay đổi. |
| Tenant | 1 khách hàng/tổ chức độc lập dùng chung source code nhưng dữ liệu tách vật lý theo file SQLite riêng, resolve theo tên miền (Host header) lúc request tới. |