Sheet 4 · Entity Relationship Diagram (diagram-design)
Sơ đồ quan hệ thực thể ERD
ERD của CTC ERP được thiết kế theo chuẩn editorial diagram-design (thực thể 2 khoang: Header + Thuộc tính mono `#PK` / `→FK`, cardinality 2 đầu, viền accent trên thực thể gốc). Mô hình được phân tách thành 3 tầng kiến trúc để giữ mật độ đọc rõ ràng và chuẩn xác.
1. Bảng mô tả đối tượng (Object Description)
| Đối tượng (Object) | Định dạng khoá | Mô tả & Ý nghĩa nghiệp vụ |
|---|---|---|
| Đối tác (Contact) | ct1..ct10 | Gốc quan hệ khách hàng / đối tác: công ty, ngành (nganh:*), phân loại, tổng giá trị hợp đồng rollup. |
| Dự án (Project) | pj1..pj16 | Thực thể trung tâm vận hành: liên kết contactId, tiến độ bán hàng (dealStage), giá trị (dealValue), và tiến độ giao hàng (status). |
| Công việc (Task) | CTC-n (t1..) | Sản phẩm bàn giao (Deliverable) bóc tách từ WBS: kanban 4 cột, deadline, assignee, tag 3 chiều (gp:*, nganh:*, viec:*). |
| Hoá đơn GTGT (Invoice) | CTC-INV-* | Hoá đơn GTGT bán hàng & báo giá: 5 trạng thái vòng đời, line-items chi tiết, hỗ trợ OCR scan hoá đơn đầu vào. |
| Giao dịch (Transaction) | tx1..tx9 | Sổ thu/chi tài chính thực tế: dòng tiền dự án, gắn contactId/projectId/invoiceId, tag bản chất dòng tiền (tc:*). |
| Nhà cung cấp (Vendor) | vd1.. | Danh bạ NCC (Media, In ấn, Thiết bị, Server...): theo dõi công nợ cam kết vs đã duyệt chi, ngưỡng duyệt BOD > 5tr. |
| Quy trình tự động (Workflow) | wf1..wf3 | Canvas tự động hoá React Flow: trigger (đổi status / định kỳ), condition, actions (tạo task, gửi Telegram/GWS). |
| Tài khoản & Phân quyền | u0..u7 | Gốc danh tính RBAC: vai trò (admin/member), mảng phân quyền 23 module (modules[]), chức danh (title). |
2. Ký pháp sơ đồ (Notation & Legend)
| Ký hiệu | Ý nghĩa trong mô hình dữ liệu |
|---|---|
| 1 | Đúng một (Exactly one) |
| 0..1 | Không hoặc một (Zero or one) |
| 1..* | Một hoặc nhiều (One or more) |
| 0..* / N | Không hoặc nhiều (Zero or more) |
# / → | # Khóa chính (Primary Key PK) · → Khóa ngoại (Foreign Key FK) trong khoang thuộc tính |
Quy ước nhận diện màu (Color Key):
Xanh Lime Accent Thực thể gốc trung tâm (Project, Invoice, AuthUser) · Navy Thực thể dữ liệu chuẩn · Đỏ Rust Điểm kiểm soát phê duyệt / Rủi ro (Vendor Approval >5tr).
Xanh Lime Accent Thực thể gốc trung tâm (Project, Invoice, AuthUser) · Navy Thực thể dữ liệu chuẩn · Đỏ Rust Điểm kiểm soát phê duyệt / Rủi ro (Vendor Approval >5tr).
3. Tầng Vận hành & Bán hàng (Operations & Sales Layer)
gp:*, nganh:*, viec:*) để phục vụ đo lường KPI; Dự án liên kết lịch deadline và sự kiện lặp trên Calendar.Đặc tả quan hệ chi tiết — Tầng Vận hành
| Quan hệ thực thể | Cardinality | Ý nghĩa nghiệp vụ & Ràng buộc toàn vẹn |
|---|---|---|
| Contact → Project | 1 — 0..* | 1 đối tác có thể có nhiều dự án qua các năm; Project.contactId là khoá ngoại bắt buộc; Tổng giá trị đối tác trên UI là tổng dealValue các dự án chưa lost. |
| Project → Task | 1 — 1..* | Mỗi dự án phải bóc tách thành ít nhất 1 Deliverable/Task; Task.projectId liên kết trực tiếp; mã task tự tăng dạng CTC-n duy nhất toàn tenant. |
| Contact → Activity | 1 — 0..* | Lịch sử tương tác chăm sóc khách hàng (gọi điện, họp, email, zalo); phục vụ truy vết quá trình tư vấn. |
| Task → Tag Matrix | N — 1..* | Gắn tag 3 chiều: Giải pháp (gp:), Ngành (nganh:), Loại việc (viec:). Trọng số tính phân bổ doanh số/khối lượng là 1/n cho n tag cùng nhóm. |
| Project → CalendarEvent | 1 — 0..* | Các mốc deadline bàn giao và lịch họp dự án được hiển thị đồng bộ trên Calendar. |
| CalendarEvent → EventRecurrence | N — 0..1 | Sự kiện lặp định kỳ (họp tuần, nhắc việc tháng) được sinh tự động từ khuôn mẫu chu kỳ. |
Ràng buộc toàn vẹn Tầng Vận hành: ① Mã task
CTC-n là duy nhất và tăng đơn điệu · ② Khi tạo task, các mảng subtasks, tags, links luôn được khởi tạo mảng rỗng [] (không được undefined/null) · ③ Khi chuyển task sang done, hệ thống tự động ghi nhận thời điểm completedAt.
4. Tầng Kế toán, Tài chính & Công nợ (Finance & Invoices Layer)
Đặc tả quan hệ chi tiết — Tầng Kế toán & Dòng tiền
| Quan hệ thực thể | Cardinality | Ý nghĩa nghiệp vụ & Ràng buộc toàn vẹn |
|---|---|---|
| Project → Invoice | 1 — 0..* | Hoá đơn GTGT phát hành theo các đợt tạm ứng, nghiệm thu và quyết toán hợp đồng dự án. |
| Invoice → Transaction | 1 — 0..* | Mỗi lần nhận tiền thanh toán hoá đơn sẽ sinh bản ghi giao dịch thu tương ứng gắn invoiceId. |
| Transaction → Recurrence | N — 0..1 | Giao dịch định kỳ tự động chạy vào ngày N hàng tháng (chi phí server, tool, văn phòng, lương). |
| Vendor → VendorPayment | 1 — 0..* | Quản trị công nợ NCC: Giá trị hợp đồng cam kết − Tổng các đợt thanh toán đã duyệt = Còn phải trả. |
| VendorPayment → Transaction | 1 — 1 | Đợt thanh toán NCC sau khi được duyệt sẽ tự động ghi sổ chi (Transaction expense) trong sổ tài chính. |
| Project → IncomeConfig | 1 — 0..* | Tính hoa hồng dự án per-user dựa trên dealValue hoặc thực thu kết hợp biểu thuế TNCN 2026. |
Ràng buộc toàn vẹn Tầng Kế toán: ① Hoá đơn quá hạn (quá
dueDate và chưa thu đủ) tự động gắn tag cảnh báo đỏ overdue · ② Giao dịch dự kiến (status: pending) không được cộng vào số dư thực tế · ③ Mọi khoản chi NCC > 5.000.000đ bắt buộc phải có trường approvedBy của Admin/BOD.
5. Tầng Tự động hoá, AI & Phân quyền (Automation, AI & Security Layer)
Đặc tả quan hệ chi tiết — Tầng Hệ thống & Tự động hoá
| Quan hệ thực thể | Cardinality | Ý nghĩa nghiệp vụ & Ràng buộc toàn vẹn |
|---|---|---|
| AuthUser → Workflow | 1 — 0..* | Nhân sự tạo và sở hữu quy trình tự động hoá; có thể chia sẻ quyền xem qua mảng shared_with[]. |
| Workflow → WorkflowRun | 1 — 0..* | Mỗi lần trigger kích hoạt (status_change hoặc daily_scan) ghi lại 1 log thực thi chống lặp run-key. |
| AuthUser → Skill | 1 — 0..* | Sổ tay SOP chuẩn hoá quy trình thực thi nghiệp vụ, hỗ trợ import Markdown và nâng version. |
| AuthUser → DevRequest | 1 — 0..* | Kênh gửi đề xuất tạo mới hoặc chỉnh sửa tính năng/kỹ năng; Admin duyệt qua 5 trạng thái. |
| AuthUser → CommentThread | 1 — 0..* | Review Layer kiểu Figma: ghim pin toạ độ trên màn hình, @mention thành viên và nhận thông báo realtime SSE. |
| AuthUser → BotConfig | 1 — 0..* | Cấu hình bot Telegram cá nhân, bảo mật token hash phía server, chỉ relay tới domain Whitelist. |
| AuthUser → BotApiKey | 1 — 0..* | Thêm 03/09. bot_api_keys.scope_user_id → auth_users.id. Một người có thể có nhiều khoá (mỗi workspace một khoá), nhưng một khoá chỉ thuộc đúng một người — đó là điều làm khoá trở thành danh tính chứ không phải mật khẩu dùng chung. Chỉ lưu SHA-256; thu hồi bằng revoked_at có hiệu lực tức thì. Khoá không mở được phân hệ mà chính người đó không có. |
| BotApiKey → Skill | 0..* — 0..* | Thêm 03/09. Quan hệ nhiều-nhiều không có bảng nối: danh sách nằm trong cột JSON bot_api_keys.skill_ids. Đây là đường "phát hành kỹ năng cho trợ lý" — agent cầm khoá chỉ biết những kỹ năng có id trong mảng đó. |
| AuthUser → Skill (chia sẻ) | 0..* — 0..* | Đổi 03/09. Hai quan hệ chia sẻ song song, cũng lưu dạng cột JSON: skills.shared_with = mức Xem, skills.shared_edit = mức Sửa. Một người chỉ nằm ở đúng một trong hai. Người mức Sửa đổi được nội dung nhưng không đổi được chính hai danh sách này — chỉ chủ sở hữu làm được. |
| AuthUser → Revision | 1 — 0..* | Thêm 02/09. revisions.actor_id + revisions.source: mỗi phiên bản state biết ai ghi và qua đường nào (web/api-key/agent/system). Trước đó không truy được thay đổi về người nào. |
Ràng buộc toàn vẹn Tầng Hệ thống: ① Token bot Telegram không bao giờ trả full về client · ② Webhook relay chỉ chấp nhận domain nằm trong Whitelist an toàn · ③ Mọi thay đổi dữ liệu đều được ghi nhận vào chuỗi Revision snapshot trong bảng
revisions làm nguồn sự thật duy nhất · ④ Khoá API chỉ lưu băm SHA-256, bản rõ hiện một lần lúc cấp · ⑤ Trợ lý ngoài chỉ tạo được bản ghi mới ở trạng thái nháp (kỹ năng draft, quy trình active = 0) — không sửa bản có sẵn, không xoá, không đổi được hai danh sách chia sẻ.
Sơ đồ đã dựng lại 03/09/2026. Sơ đồ tầng hệ thống nay có
BOT_API_KEY (khoá gắn danh tính cho trợ lý ngoài, chỉ lưu SHA-256, mang skill_ids để phát hành kỹ năng), skills.shared_edit bên cạnh shared_with, và auth_users đủ bốn trục phân quyền kèm data_scope · session_ver. Dựng lại bằng analysis-site/generate_erds.py — sửa mô hình thì sửa script rồi chạy lại, đừng sửa tay tệp SVG.