Nguồn sự thật: frontend-next/src/erp/types/index.ts (27 interface — dữ liệu nghiệp vụ nằm trong blob sync) và sync-server/tenant.js (8 bảng server riêng, ngoài blob). Tên field/kiểu chép nguyên văn từ code, KHÔNG diễn giải thêm. Cột "Bắt buộc" = KHÔNG có dấu ? sau tên field trong TypeScript.
Nhóm A — Trong blob sync (đồng bộ realtime, đọc/ghi qua pullState() / PUT /api/state)
Task 14 fieldmodule tasks
Công việc — có bảng vật lý riêng tasks (materialized view từ blob) để lọc/sort phía server.
Field
Kiểu
Bắt buộc
Mô tả
id
string
x
Khoá chính
key
string
x
Mã hiển thị dạng CTC-16
title
string
x
Tên công việc
description
string
x
Mô tả (hỗ trợ @mention qua MentionTextarea)
status
TaskStatus = todo|in_progress|review|done
x
Trạng thái kanban
priority
Priority = urgent|high|medium|low
x
Ưu tiên
assigneeId
string
x
User phụ trách (khoá vào auth_users.id)
projectId
string
Dự án liên quan
contactId
string
Đối tác liên quan
dueDate
string
x
Hạn hoàn thành
tags
string[]
x
Tag tự do + có cấu trúc (viec:*, gp:*)
subtasks
SubTask[]
x
Việc con — từng crash trắng trang khi field này undefined (đã vá 2 lớp 28/07)
sortOrder
number
x
Thứ tự kéo-thả kanban
createdAt
string
x
Ngày tạo
completedAt
string
Ngày hoàn thành — set/clear tự động khi đổi status (từ GĐ Analytics)
archived
boolean
Đã lưu trữ (backlog)
links
DocLink[]
Tài liệu đính kèm
SubTask 3 field
Field
Kiểu
Bắt buộc
Mô tả
id
string
x
Khoá
title
string
x
Tên việc con
done
boolean
x
Đã hoàn thành
DocLink 3 field
Dùng chung cho Task/Project/Invoice/Transaction/Vendor... (tài liệu đính kèm)
Field
Kiểu
Bắt buộc
Mô tả
id
string
x
Khoá
title
string
x
Tên hiển thị
url
string
x
Đường dẫn
Contact 16 fieldmodule crm
Đối tác/khách hàng — bảng contacts.
Field
Kiểu
Bắt buộc
Mô tả
id
string
x
Khoá chính
name
string
x
Tên liên hệ
company
string
x
Tên công ty
phone
string
x
Điện thoại
email
string
x
Email
taxCode
string
x
Mã số thuế
address
string
x
Địa chỉ
province
string
x
Tỉnh/thành
status
ContactStatus = lead|prospect|customer|inactive
x
Trạng thái quan hệ
dealStage
DealStage
x
Còn trong schema Contact nhưng deal đã chuyển sang sống trên Project — field lịch sử
dealValue
number
x
KHÔNG còn dùng để hiển thị (từ 20/08) — UI đọc rollup tính từ Project thay thế, xem trang Tổng quan mục "Rollup giá trị đối tác"
source
string
x
Nguồn khách hàng
tags
string[]
x
Tag, gồm nganh:* + gp:*
notes
string
x
Ghi chú tự do
assigneeId
string
x
Người phụ trách — dùng để scope quyền member
createdAt
string
x
Ngày tạo
lastContactDate
string
x
Lần chăm sóc gần nhất
stageHistory
StageHistoryEntry[]
x
Lịch sử đổi giai đoạn
closedReason
string
Lý do đóng (won/lost)
StageHistoryEntry 2 field
Field
Kiểu
Bắt buộc
Mô tả
stage
DealStage
x
Giai đoạn
enteredAt
string
x
Thời điểm vào giai đoạn
Activity 8 fieldmodule crm
Tương tác với đối tác (gọi/zalo/email/gặp/ghi chú) — bảng activities.
Field
Kiểu
Bắt buộc
Mô tả
id
string
x
Khoá
contactId
string
x
Đối tác liên quan
type
ActivityType = call|zalo|email|meeting|note
x
Loại tương tác
date
string
x
Ngày
summary
string
x
Tóm tắt
nextStep
string
Bước tiếp theo
nextStepDate
string
Ngày hẹn tiếp theo
userId
string
x
Người ghi nhận
createdAt
string
x
Ngày tạo
Project 17 fieldmodule projects
Dự án — bảng projects. Từ 27/07 mang luôn "deal" (bán hàng), tách khỏi status (giao hàng).
Tag gp:* (giải pháp — không có bộ tag riêng cấp dự án)
dealStage
DealStage
Giai đoạn bán — nhãn hiển thị khác nhau khi xem từ Đối tác (nhãn "quan hệ") vs xem từ Dự án (nhãn "bán hàng"), cùng 1 bộ giá trị
dealValue
number
Giá trị hợp đồng — hiện trên thẻ phễu; nguồn của rollup ở Contact
contactId
string
Đối tác sở hữu (1 dự án — 1 đối tác, quy tắc nghiệp vụ không cho N-N)
CalendarEvent 10 fieldmodule calendar
Field
Kiểu
Bắt buộc
Mô tả
id
string
x
Khoá
title
string
x
Tiêu đề
type
meeting|task|reminder|deadline
x
Loại sự kiện
date
string
x
Ngày
time
string
Giờ
duration
number
Thời lượng (phút)
description
string
Mô tả
assigneeIds
string[]
x
Người tham gia — rỗng = sự kiện chung, ai cũng thấy (quy ước scope quyền)
color
string
x
Màu hiển thị
link
string
Liên kết đính kèm
recurringId
string
Sinh ra từ EventRecurrence nào
EventRecurrence 12 field
Sự kiện lặp — engine sinh CalendarEvent tự động theo freq.
Field
Kiểu
Bắt buộc
Mô tả
id
string
x
Khoá
title
string
x
Tiêu đề mẫu
type
CalendarEvent['type']
x
Loại
time
string
Giờ
duration
number
Thời lượng
description
string
Mô tả
assigneeIds
string[]
x
Người tham gia
color
string
x
Màu
link
string
Liên kết
freq
weekly|monthly
x
Tần suất lặp
startDate
string (YYYY-MM-DD)
x
Ngày bắt đầu lặp
active
boolean
x
Đang bật
lastGen
string (YYYY-MM-DD)
x
Đã sinh sự kiện tới ngày nào ('' = chưa sinh lần nào)
createdAt
string
x
Ngày tạo
Invoice 14 fieldmodule invoices
Field
Kiểu
Bắt buộc
Mô tả
id
string
x
Khoá
number
string
x
Số hoá đơn (vd CTC202600001)
type
gtgt|ban_hang
x
Loại hoá đơn
status
InvoiceStatus = draft|sent|paid|overdue|cancelled
x
Trạng thái
contactId
string
x
Khách hàng
issueDate
string
x
Ngày phát hành
dueDate
string
x
Hạn thanh toán — dùng tính daysOverdue
items
InvoiceItem[]
x
Dòng hàng hoá/dịch vụ
subtotal
number
x
Tổng trước thuế
taxRate
number
x
Thuế suất
taxAmount
number
x
Tiền thuế
total
number
x
Tổng thanh toán
notes
string
x
Ghi chú
bankAccount
string
x
STK nhận tiền
bankName
string
x
Ngân hàng
createdAt
string
x
Ngày tạo
links
DocLink[]
Đính kèm (ảnh OCR quét...)
Type Invoice CHƯA khai tags?: string[] — trang Quản lý tag vẫn đọc/ghi field này qua ép kiểu an toàn (ghi trong CONTEXT.md gốc, khuyến nghị bổ sung vào type khi tiện).
InvoiceItem 6 field
Field
Kiểu
Bắt buộc
Mô tả
id
string
x
Khoá
description
string
x
Mô tả hàng hoá
unit
string
x
Đơn vị tính
quantity
number
x
Số lượng
unitPrice
number
x
Đơn giá
amount
number
x
Thành tiền
Transaction 10 fieldmodule finance
Field
Kiểu
Bắt buộc
Mô tả
id
string
x
Khoá
type
TransactionType = income|expense
x
Loại giao dịch
category
string
x
Danh mục
amount
number
x
Số tiền
description
string
x
Diễn giải
date
string
x
Ngày
reference
string
x
Số tham chiếu
contactId
string
Đối tác liên quan
invoiceId
string
Hoá đơn liên quan
projectId
string
Dự án liên quan
tags
string[]
x
Tag tc:*
links
DocLink[]
Đính kèm
recurringId
string
Sinh ra từ Recurrence nào — kế thừa tags/contactId/projectId của recurrence gốc
Recurrence 10 field
Giao dịch định kỳ — engine tự sinh Transaction hàng tháng.
Field
Kiểu
Bắt buộc
Mô tả
id
string
x
Khoá
type
TransactionType
x
Loại
category
string
x
Danh mục
amount
number
x
Số tiền
description
string
x
Diễn giải
dayOfMonth
number (1-31)
x
Ngày lặp trong tháng, tự kẹp theo số ngày thực của tháng
reference
string
x
Số tham chiếu
links
DocLink[]
Đính kèm
active
boolean
x
Đang bật
lastRun
string (YYYY-MM)
x
Đã sinh giao dịch tới tháng nào
createdAt
string
x
Ngày tạo
Notification 6 field
Field
Kiểu
Bắt buộc
Mô tả
id
string
x
Khoá
type
task|invoice|reminder|system
x
Loại
title
string
x
Tiêu đề
message
string
x
Nội dung
read
boolean
x
Đã đọc
createdAt
string
x
Ngày tạo
link
string
Đường dẫn liên quan
AppSettings 6 fieldmodule settings
Field
Kiểu
Bắt buộc
Mô tả
companyName
string
x
Tên công ty
taxCode
string
x
Mã số thuế
address
string
x
Địa chỉ
bankAccount
string
x
STK
bankName
string
x
Ngân hàng
defaultTaxRate
number
x
Thuế suất mặc định
invoicePrefix
string
x
Tiền tố số hoá đơn
Vendor 11 fieldmodule budget-comms
Nhà cung cấp (NCC) gắn 1 chiến dịch — module Ngân sách truyền thông.
Field
Kiểu
Bắt buộc
Mô tả
id
string
x
Khoá
projectId
string
x
Chiến dịch (project)
name
string
x
Tên NCC
category
string
x
Loại hình
taxCode
string
MST
contractNo
string
Số hợp đồng
contractLink
string
Link hợp đồng
contractValue
number
x
Giá trị hợp đồng (chưa VAT)
vatRate
number
x
Thuế suất VAT
acceptedValue
number
x
Nghiệm thu thực tế — có thể vượt contractValue (overage)
acceptedClientApproved
boolean
x
CĐT đã duyệt phần vượt?
status
VendorStatus = signed|in_progress|accepted|paid
x
Trạng thái
createdAt
string
x
Ngày tạo
VendorPayment 8 field
Field
Kiểu
Bắt buộc
Mô tả
id
string
x
Khoá
vendorId
string
x
NCC
amount
number
x
Số tiền
paidDate
string
Ngày chi
proof
string
Chứng từ
approved
boolean
x
Đã duyệt
approvedBy
string
Người duyệt
createdBy
string
Người tạo
selfApproved
boolean
Tự duyệt (không qua người khác)
createdAt
string
x
Ngày tạo
CampaignBudget 5 field
Cấu hình "Lớp A" cho từng chiến dịch — 2 công thức tính ngân sách.
Field
Kiểu
Bắt buộc
Mô tả
projectId
string
x
Chiến dịch
method
BudgetMethod = percent|reserve
x
Công thức
percent
number
Dùng khi method=percent
reserveFixed
number
Dùng khi method=reserve
reserveMargin
number
Biên dự phòng
KeyMessage 9 field
Field
Kiểu
Bắt buộc
Mô tả
id
string
x
Khoá
projectId
string
x
Chiến dịch
phase
string
x
Giai đoạn (vd "GĐ1-Booming")
month
string (YYYY-MM)
Tháng
product
string
Sản phẩm
message
string
x
Thông điệp chính
tone
string
Giọng điệu
sourceChunk
string
Grounding chống bịa — bắt buộc khi AI trích xuất trực tiếp
approved
number (1|0|-1)
x
1 đã duyệt · 0 chờ · -1 loại
createdAt
string
x
Ngày tạo
BcChange 9 field
Nhật ký thay đổi kiểu NotebookLM — trích dẫn + minh chứng before→after.
Cơ chế review kiểu Figma — pin nhận xét neo theo trường dữ liệu hoặc toạ độ % trên trang.
Field
Kiểu
Bắt buộc
Mô tả
id
string
x
Khoá Comment
pin.cid
string
"<entity_type>:<entity_id>:<field_path>" — ưu tiên neo theo entity
pin.pageKey
string
Pathname trang — dùng khi neo toạ độ tự do
pin.xPct / pin.yPct
number
Toạ độ % trong <main>
pin.label
string
Nhãn gọn hiển thị
pin.snapshot
string
Giá trị field lúc pin — chống lệch khi số liệu đổi sau đó
thread[].id/author/body/ts
CommentMessage[]
x
[0]=comment gốc, còn lại là reply; author = userId
resolved / resolvedBy / resolvedAt
boolean/string/string
resolved bắt buộc
Trạng thái xử lý
createdAt
string
x
Ngày tạo
Đây là Comment trong blob (đồng bộ qua store.comments, thuộc CLIENT_SCHEMA v14) — KHÁC với bảng SQL comments ở Nhóm B bên dưới (tiền thân cũ, còn tồn tại trong tenant.js nhưng không có route server.js nào ghi vào — suy luận đây là schema dự phòng/legacy chưa dọn, không phải nguồn dữ liệu review hiện dùng).
Nhóm B — Bảng server riêng (ngoài blob, per-owner, KHÔNG qua /api/state)
20 bảng có tên cố định, khai tường minh trong tenant.js: revisions · settings · worklog · comments · share_tokens · auth_users · user_data · workflows · workflow_runs · workflow_state · skills · dev_requests · library_items · library_boards · bots · bot_chat_links · bot_link_codes · bot_api_keys · quotes · contracts. Cộng 9 bảng thực thể sinh trong vòng lặp là 29 bảng mỗi tenant — đếm thật trong sqlite_master.
Nguồn: CREATE TABLE trong sync-server/tenant.js. Không phải TypeScript interface (không có type FE tương ứng — FE tự định nghĩa type cục bộ khi gọi API), liệt kê theo đúng tên cột SQL.
auth_users 9 cột
Cột
Kiểu SQL
Mô tả
id
TEXT PK
Khoá — quy ước u1..u7 cho nhân sự thật (không phải random)
username
TEXT UNIQUE NOT NULL
Đăng nhập
pass_hash
TEXT NOT NULL
Mật khẩu đã băm
display_name
TEXT DEFAULT ''
Tên hiển thị
role
TEXT DEFAULT 'member'
admin|member
modules
TEXT DEFAULT '[]'
Mảng JSON module được cấp
active
INTEGER DEFAULT 1
Còn hoạt động
created_at
TEXT NOT NULL
Ngày tạo
title
TEXT DEFAULT ''
Chức danh tự do — cột thêm sau qua ALTER TABLE, tách khỏi role
data_scope
TEXT NOT NULL DEFAULT ''
Trục phạm vi dữ liệu (thêm 02/09): self · team · all. Độc lập với role — quản trị vẫn có thể bị hạn chế phạm vi. Rỗng = suy theo role.
session_ver
INTEGER NOT NULL DEFAULT 0
Số phiên bản phiên (thêm 02/09): chữ ký cookie phủ cả cột này, nên tăng nó là mọi phiên cũ của người đó mất hiệu lực ngay. Dùng cho POST /api/me/sessions/revoke-all.
Quy ước id: u0..u7 cho nhân sự thật thời đầu, tài khoản thêm sau dùng id sinh ngẫu nhiên dạng au_<hex> — cả hai dạng cùng tồn tại trong tenant ctc.
bot_api_keys 11 cộtkhoá cho trợ lý ngoài
Bảng then chốt của kiến trúc "trợ lý gọi vào ERP". Mỗi khoá là một danh tính có phạm vi cố định.
Cột
Kiểu SQL
Mô tả
id
TEXT PK
Khoá
bot_id
TEXT
Bot Telegram gắn kèm, nếu có
created_by
TEXT
Người cấp khoá
scope_user_id
TEXT NOT NULL
Gắn chết một tài khoản. ERP tra khoá ra người này rồi áp nguyên quyền của họ — bên ngoài không tự khai danh tính được.
label
TEXT DEFAULT ''
Ghi chú do người cấp đặt (vd workspace:linh)
key_hash
TEXT NOT NULL
Chỉ lưu SHA-256. Bản rõ hiện đúng một lần lúc cấp, không lấy lại được.
key_prefix
TEXT
Vài ký tự đầu để nhận diện trên giao diện (vd erpbot_a498f…)
skill_ids
TEXT NOT NULL DEFAULT '[]'
Kỹ năng phát hành cho khoá này — agent cầm khoá mới biết các kỹ năng đó
last_used_at
TEXT
Lần gọi cuối. Cột chẩn đoán quan trọng nhất: bot báo "đã làm" mà cột này không đổi thì lời gọi chưa hề tới ERP.
revoked_at
TEXT
Thu hồi — có hiệu lực tức thì, khoá đã thu hồi không hiện trên giao diện
created_at
TEXT
Ngày cấp
workflows 8 cộtmodule workflows
Cột
Kiểu SQL
Mô tả
id
TEXT PK
Khoá
owner_id
TEXT NOT NULL
Chủ sở hữu — GET chỉ trả của mình + được share, kể cả admin
name
TEXT DEFAULT ''
Tên quy trình
active
INTEGER DEFAULT 0
Đang bật. Vá 03/09:upsertWorkflow chỉ đổi cột này khi người gọi thực sự gửi trường active — trước đó mọi lời gọi thiếu trường đều bị hiểu là lệnh tắt, làm quy trình đang chạy dừng lặng lẽ.
def
TEXT DEFAULT '{}'
JSON định nghĩa — dạng graph (nodes, edges) (builder canvas @xyflow/react)
shared_with
TEXT DEFAULT '[]'
Mảng userId được chia sẻ (viewer)
versions
TEXT DEFAULT '[]'
Lịch sử phiên bản, cap 10 — server tự đẩy version khi def đổi
created_at / updated_at
TEXT
Mốc thời gian
workflow_runs 6 cột
Cột
Kiểu SQL
Mô tả
id
TEXT PK
Khoá
wf_id
TEXT NOT NULL
Workflow chạy
user_id
TEXT NOT NULL
Người/máy chạy
ts
TEXT NOT NULL
Thời điểm chạy
result
TEXT NOT NULL
Kết quả (thành công/lỗi)
note
TEXT DEFAULT ''
Ghi chú — cap 500 dòng/workflow
skills 9 cộtmodule skills
Cột
Kiểu SQL
Mô tả
id
TEXT PK
Khoá
owner_id
TEXT NOT NULL
Chủ sở hữu
name
TEXT DEFAULT ''
Tên kỹ năng
category
TEXT DEFAULT ''
Danh mục
def
TEXT DEFAULT '{}'
JSON (summary, steps, checklist, tags, resources)
shared_with
TEXT DEFAULT '[]'
Chia sẻ mức Xem — đọc và nhân bản, không sửa được
shared_edit
TEXT NOT NULL DEFAULT '[]'
Cột mới 03/09 — chia sẻ mức Sửa: sửa được nội dung nhưng KHÔNG đổi được chia sẻ, KHÔNG phát hành, KHÔNG xoá. Tách riêng khỏi shared_with để không phải đoán ý bản ghi cũ: mọi chia sẻ trước 03/09 đều là chỉ-xem.
Bảng quan trọng nhất trong toàn hệ thống, và trước bản này tài liệu chưa mô tả nó. Toàn bộ dữ liệu nghiệp vụ của một tenant nằm ở đây dưới dạng một chuỗi JSON duy nhất; mọi bảng thực thể (tasks, contacts, projects…) chỉ là bản dựng lại từ nó.
Cột
Kiểu SQL
Mô tả
rev
INTEGER PK AUTOINCREMENT
Số phiên bản tăng dần. Client giữ rev đang có; server so để biết cần đẩy gì qua SSE.
state
TEXT
Toàn bộ state client dạng JSON, cấu trúc lồng {version: 14, state: {…}}. CLIENT_SCHEMA = 14 và KHÔNG được bump — bump là mọi client cũ mất dữ liệu.
updated_at
TEXT
Thời điểm ghi
actor_id
TEXT NOT NULL DEFAULT ''
Thêm 02/09. Ai ghi bản này. Trước đó không truy được thay đổi về người nào.
source
TEXT NOT NULL DEFAULT 'system'
Thêm 02/09. Ghi qua đường nào: web · api-key · agent · system — phân biệt được thay đổi đến từ trợ lý hay từ người thật.
Ghi vào bảng này KHÔNG bao giờ là ghi đè thẳng: mergeScopedState nhận state client gửi lên, giữ nguyên phần ngoài phạm vi của người đó, chạy sanitizeRefs ép các tham chiếu ngoài phạm vi về rỗng, rồi mới tạo rev mới.
Trọng số bảng thực thể (materialized view — ENTITY_TABLES)
9 bảng sinh lại từ blob để phục vụ truy vấn nhanh (KHÔNG phải nguồn sự thật): users, tasks, contacts, activities, projects, events, invoices, transactions, notifications — mỗi bảng cấu trúc id TEXT PRIMARY KEY, data TEXT NOT NULL + vài cột index phụ (vd tasks có thêm status, assignee, due). Bảng users là "vestigial" — blob không còn top-level key users (đã thay bằng auth_users + /api/team).