Trường Số PRD/10 Settings
Trường Số · Product Requirements · 09/2026

PRD 10 · Settings and administration

Tenant profile, branches, users and invitations, roles with a unified permission catalog, catalogs (tags, sources, channels, levels, rooms, fee plans, expense categories, job titles), integrations (Zalo, SePay, e-invoice), notification policies, and the audit log viewer.

Key facts, origin and limits
Status
Draft v1
Owner module
Cài đặt
Backend
EXISTING roles, permissions catalog, sale profiles, view configs, school config · NEW branch profile, invitations, audit viewer, integrations
Phase
P1
Depends on
00 Overview, 12 Non-functional

Drafted by an AI assistant in a single session on 10/09/2026, from the original request quoted verbatim on the hub page; the Proposed design section was added on 16/09/2026. A proposal for team discussion, not a decision of the product team.

Sources

  • Public competitor research (PSE ONE, pse4.one), the center-crm-frontend codebase and the staging /v3 OpenAPI specification (182 paths), as of 10/09/2026.
  • The existing / extend / new tags on this page come from a comparison with the staging /v3 specification at that date.

Limits

  • Verification pending in-app survey, because automated login to the reference product was not authorised in the drafting session:
    • Competitor's settings tree, default roles ("quản lý, kế toán, giáo viên, tư vấn") and permission granularity ("phân quyền").
    • Whether branches are separate logins or one login with a switcher.
    • Which catalogs are configurable (sources, statuses, fee types, expense categories).
    • Their email auto-send configuration fields.

Proposed design

Settings is its own area with a 220 px sub-navigation on the left; every page is a plain list or form in the Cloud theme, and the most important thing to see is the permission matrix with a per-cell scope (None / Own / Branch / All) together with the read-only audit log and its before/after diff.

1 · Settings hub

/settings · desktop

Settings hub
Onboarding checklist at the top (5 of 7 done, the two open items link to their settings page, SET-01) and 12 cards in three groups, Organisation · Catalogs and services · Operations: Tenant, Branches, Users, Roles and scope, Data catalogs, Courses and fee plans, Admissions pipeline, Service plan, Finance policy, Notifications, Integrations, Audit log. Each card carries one line of live figures so the owner sees what needs attention.

2 · Users and invitations

/settings/users · desktop

Users and invitations
220 px settings sub-navigation on the left (section 3), status tabs and a table with every column from section 6: Name, Email/phone, Role (chip), Branch (chip, possibly several), Last login, Status, Actions. Three states are shown: Active, Pending invitation with its 7-day expiry (SET-02) plus Resend / Revoke, an expired invitation, and one Disabled user with Reactivate. The note restates the rule that the last owner cannot be disabled.

3 · Invite user drawer

/settings/users (mời) · drawer · desktop

Invite user drawer
Drawer over the list with the SET-02 fields: name, email or phone, role, job title (Staff link, PRD 06), the accessible branches as checkboxes (creating user_branch_scope on acceptance) and a message. The callout states the 7-day link validity and that the invitee signs in with an existing FLYER account or registers; Cancel sits far left, Send invitation is the primary button.

4 · Roles list

/settings/roles · desktop

Roles list
The seven default roles of section 4 (owner, branch_manager, admissions, academic_admin, accountant, teacher, viewer, with their School role) plus one custom role "Receptionist" just cloned from Academic admin (SET-03, confirmation toast). Columns Name, Code, Permission count, Users, Default; the ⋯ menu shows that "Restore defaults" applies only to default roles and "Delete role" is available only when nobody is assigned. The callout restates the constraint that at least one owner role with scope All must remain.

5 · Permission matrix with scope

/settings/roles/{id} · ma trận · desktop

Permission matrix with scope
Rows are permissions coded <module>.<action> grouped by module (Admissions, Academic, Finance, Settings), columns are the eight roles; each cell is a checkbox with its scope Own / Branch / All and a four-level scope picker opened on the cell being edited. The Receptionist column is being edited: finance.payment.create just went from Branch to None (amber cell); the bar below reports one unsaved change written through PUT /roles/{id}/permissions, effective from the next request (SET-03).

6 · Branches list

/settings/branches · desktop

Branches list
Four KPI tiles and a table with every column from section 6: Name (with cash fund and receiving account), Code, Address, Phone, Students, Classes, Users, Status, Actions. Three states: Active, Preparing (a new branch without students) and Paused with an Activate action. The callout explains SET-05: adding a branch creates a FLYER school under the center, the owner is automatically a member, and the branch code is unique and immutable.

7 · Branch profile drawer

/settings/branches/{id} · drawer · desktop

Branch profile drawer
Branch profile per SET-04: name, code (locked after creation), address, phone, map link, opening hours per day group, default receiving account (SePay-linked), cash fund, own logo and status. The footer has Cancel on the left, Pause branch (red) and Save profile; this profile feeds receipts, messages ({chi_nhanh}, {sdt}) and the parent portal.

8 · Data catalogs

/settings/tags (MasterDataPage dùng chung) · desktop

Data catalogs
One shared MasterDataPage for eight catalogs (Status, Source, Channel, Tag, Level, Room, Job title, Expense category) switched by tabs; the table has a ⋮⋮ drag handle for ordering, Colour, Name, Code, In-use count, Active toggle and actions. The create row sits at the top with the five Cloud theme colours; the rule "disable instead of delete when in use" is shown by "Referral" (128 customers) offering only Disable while "Class transfer" (0) can be deleted.

9 · Integrations

/settings/integrations · desktop

Integrations
Four cards per SET-07 with two explicit states: SePay (Connected, two accounts, last sync, link-token hidden after save), Zalo OA (Connected, 6 of 8 templates approved, monthly ZNS quota), E-invoice (Not connected, provider and credential form, Test button) and SMTP email (Connection error with the last error code). The outbound Webhook card below lists URL, signing secret and emitted events. Every connected card has a red Disconnect action.

10 · Audit log

/settings/audit-log · desktop

Audit log
Filters by Actor, Action, Object, Branch and date range (SET-06); the table has every column: Time, Actor, Action (event code), Object, Branch, IP. Sample rows mix user and system events (SePay sync, SMTP failure) with sensitive settings changes; the "read-only" note and the Export CSV button express the rule that log rows cannot be edited.

11 · Before/after diff drawer

/settings/audit-log/{id} · drawer · desktop

Before/after diff drawer
Opened from the role.permissions_changed row: an info block (time, actor, action, object, branch, IP and device) and a Field · Before · After table with old values struck through in red and new values in green. The callout links to the same-session event (role.created); the footer only offers Close, Open role and Copy JSON, with no edit button (SET-06).

1Purpose

Settings is where the owner shapes the product to the center: which branches exist, who can do what, which lists the forms offer, and which external services are connected. The current app has settings pages for users, roles, sale permissions, tags, sources, channels and tenant, three of which do not persist. This PRD replaces the mocks, consolidates settings under one route with a left sub-navigation, and adds what the new modules need.

2What exists today

PageStateAction
/crm/settings/users (sale profiles list, role assign, school access modal calling a non-spec endpoint)partly workingrebuild as Users with invitations and branch scope
/crm/settings/roles (list, drawer with matrix; create is a mock toast; update works)partly workingimplement create via POST /v3/crm/roles; unify scope vocabulary
/crm/settings/sale-permissionsworkingmerge into Users
/crm/settings/tagsworking (API)keep
/crm/settings/sources, /channelslocalStorage mocksswitch to API (PRD 01)
/crm/settings/tenant (name, logo, locale, timezone, business hours)not persistedpersist via school-config and center update
Custom fields (create dialog inside customer form)workingsurface under Settings → Trường tuỳ chỉnh
Packages catalog (CRM sales packages)working in detail tabsurface under Settings → Gói tư vấn (distinct from fee plans)
Pipelines and stages editor (in pipeline selector)workingalso reachable from Settings → Quy trình

3Settings navigation

/settings Trung tâm /settings/tenant name, logo, legal info (SchoolCompany), locale, timezone, business hours, holidays Chi nhánh /settings/branches list of schools; profile: name, code, address, phone, map link, default bank account, cash drawer Người dùng /settings/users staff with access; invite; role; branch scope; deactivate Vai trò /settings/roles role list; permission matrix by module Quy trình tuyển sinh /settings/pipelines pipelines, stages, statuses (existing editor) Nguồn & Kênh /settings/sources sources; /settings/channels Nhãn /settings/tags Trường tuỳ chỉnh /settings/custom-fields Gói tư vấn /settings/packages CRM sales packages Khoá học & Gói phí /settings/courses courses; /settings/fee-plans Trình độ /settings/levels Phòng học /settings/rooms Chức danh /settings/job-titles Danh mục chi /settings/expense-categories Tài chính /settings/finance receipt numbering, discount limits, thresholds, VAT defaults, bank accounts (SePay) Điểm danh /settings/attendance policy (PRD 04) Thông báo /settings/notifications per-type channel policy, quiet hours, ZNS cap; templates at /settings/templates Tích hợp /settings/integrations Zalo OA, SePay, e-invoice provider, webhooks Nhật ký hệ thống /settings/audit-log Dữ liệu /settings/data imports (PRD 11), exports, anonymisation requests

Left sub-navigation 220 px inside the content area; each page is a plain list or form per the Cloud theme. /crm/settings/* redirects to the matching new route.

4Roles and permissions

Vocabulary

Default roles

Role codeVI nameSchool role derivedSummary
ownerChủ trung tâmOWNEReverything, all branches
branch_managerQuản lý chi nhánhADMINall modules within assigned branches; approvals
admissionsTư vấn tuyển sinhnone (or TEACHER if also teaching)leads own, trials, placements, convert
academic_adminGiáo vụADMINstudents, classes, attendance, communication, materials issue
accountantKế toánADMIN (finance)finance, inventory manage, staff contracts and pay
teacherGiáo viênTEACHERown classes, attendance, class notes, self timesheet
viewerChỉ xemnoneread-only dashboards and lists (investors, auditors)

The consolidated matrix is the union of the per-PRD tables (01 to 09). Roles can be cloned and edited; default roles are locked for code and name but their permissions can be adjusted by owner; a "Khôi phục mặc định" resets.

Users and branch scope

5User stories and acceptance criteria

SET-01First owner onboarding
  • Given a new center created via onboarding
  • When the owner lands on the dashboard
  • Then a checklist appears: add branch details, invite staff, create courses and fee plans, add rooms, connect bank account, connect Zalo, import students; each item links to its settings page and ticks when done.
SET-02Invite a user with role and branches
  • Given permission settings.users
  • When I invite name, email or phone, role, branches
  • Then an invitation is sent with a 7-day link; the Users list shows Chờ mời; resend and revoke are available; on acceptance the user appears active with memberships created.
SET-03Create a role
  • Given the roles page
  • When I clone Giáo vụ, rename it "Lễ tân", and untick finance.payment.create
  • Then the role is created through POST /v3/crm/roles and PUT /roles/{id}/permissions, and users assigned to it see the effect on next request.
SET-04Branch profile
  • Given a school under the center
  • When I edit address, phone, map link, opening hours, default receiving account, cash drawer name, logo override
  • Then the profile is saved and used on receipts, messages ({chi_nhanh}, {sdt}) and the parent portal.
SET-05Add a branch
  • Given owner
  • When I add a branch with name and code
  • Then a FLYER school is created under the center, the owner is a member, and the branch appears in the switcher.
SET-06Audit log
  • Given permission settings.audit
  • When I filter by actor, action, entity, date
  • Then I see entries with before/after diff and can export CSV; entries are read-only.
SET-07Integrations
  • Given the integrations page
  • When I connect SePay (existing link-token flow), Zalo OA (PRD 07), or an e-invoice provider (credentials form)
  • Then each card shows status, last sync, errors, and a disconnect action; secrets are masked after save.

6Screens (field lists)

PageFields / columnsValidation
Trung tâmTên trung tâm; Mã; Logo (upload); Pháp nhân (legal_name, type, tax_id or citizen_id, address, issued_date, issued_by from SchoolCompany); Ngôn ngữ mặc định; Múi giờ; Giờ làm việc per weekday; Ngày nghỉ (list)tax id 10 or 13 digits; citizen id 12 digits
Chi nhánh listTên, Mã, Địa chỉ, SĐT, Học viên, Lớp, Người dùng, Trạng thái; + Thêm chi nhánhcode unique
Người dùng listHọ tên, Email/SĐT, Vai trò (chips), Chi nhánh (chips), Đăng nhập gần nhất, Trạng thái; actions Sửa, Vô hiệu, Gửi lại lời mờilast owner cannot be removed
Vai tròlist: Tên, Mã, Số quyền, Người dùng, Mặc định; matrix: rows = permissions grouped by module, columns = scope radio (Không / Của mình / Chi nhánh / Tất cả)at least one owner role with all
Nguồn, Kênh, Nhãn, Trình độ, Phòng, Chức danh, Danh mục chigeneric MasterDataPage: name, code (where applicable), colour, position (drag), active; inline create; deactivate instead of delete when in usename unique per tenant
Tài chínhReceipt prefix and scope; Discount limits per role; Approval thresholds (discount, refund, expense, inventory adjust); VAT defaults per line type; Credit sessions on (create / full payment); Refund policy (admin fee percent, rounding); Bank accounts (existing list with SePay status)percent 0–100
Thông báoTable: message type × channels checkboxes, quiet hours apply; Quiet hours; ZNS monthly cap; Digest settings
Tích hợpCards: SePay (accounts linked), Zalo OA (name, templates approved x/y), E-invoice (provider select, credentials, test), Webhooks (outgoing URL and secret for events)
Nhật kýThời gian, Người thực hiện, Hành động, Đối tượng, Chi nhánh, IP; expand diff

7Data model

EntityFieldsNotes
center (existing Center)+ logo_file_id; default_locale; timezone; business_hours jsonb; settings jsonbextend
branch_profileschool_id PK; center_id; address; phone; map_url; opening_hours jsonb; default_bank_account_id; cash_account_id; logo_file_id; is_activenew
invitationid; center_id; email; phone; role_id; school_ids; token; expires_at; accepted_at; created_bynew
user_branch_scopesale_profile_id; school_idnew (replaces implicit accessible_schools)
crm_role (existing)+ is_locked; cloned_from; scope per permission stored as {code, scope}extend (permissions with scope)
audit_logPRD 12 section 6new
integrationid; center_id; type enum(sepay, zalo, einvoice, webhook); config jsonb (encrypted secrets); status; last_sync_at; last_errornew
onboarding_checklistcenter_id; items jsonb {key: done_at}new

8API contract

MethodPathRequest / responseStatus
GET/v3/crm/permissions-catalogextended with new module codesextend
GET/POST/v3/crm/roles; GET /roles/{id}; PUT /roles/{id}/permissionspermissions body becomes [{code, scope}]; POST /roles/{id}/clone; POST /roles/{id}/resetextend
GET/v3/crm/me/permissionsscope values own|branch|all; accessible_schools = branch scopeextend
GET/POST/PUT/v3/crm/sale-profiles[...]; POST /{id}/rolesunchangedexisting
PUT/v3/crm/sale-profiles/{id}/branches{school_ids[]} replaces non-spec users/{id}/school-accessnew
GET/POST/DELETE/v3/crm/invitations; POST /{id}/resend; POST /v3/crm/invitations/accept{email|phone, role_id, school_ids} / {token}new
POST/v3/crm/sale-profiles/{id}/deactivate | /reactivatenew
GET/PATCH/v3/crm/centers/{id}tenant profile fieldsextend (GET list exists)
GET/POST/v3/school/branches; GET/PATCH /branches/{school_id}creates FLYER school under centernew
GET/PUT/v3/teacher/school-configsettings blocks: attendance, finance, notificationsextend
GET/PUT/v3/teacher/school-finance/companylegal profileexisting
GET/POST/PATCH/DELETE/v3/crm/sources, /channels, /tags, /custom-fields, /packages, /pipelines, /stages, /statuses (+reorder)unchangedexisting
GET/POST/PATCH/v3/school/levels, /rooms, /job-titles, /expense-categories, /fee-plans, /holidayssee module PRDsnew
GET/v3/school/audit-logsactor_id, action, entity_type, entity_id, school_id, from, to, page; format=csvnew (generalises customers/{id}/audit-logs)
GET/POST/DELETE/v3/school/integrations[/{type}]; POST /{type}/testnew
GET/PATCH/v3/school/onboarding-checklistnew
POST/v3/school/exports/fulltenant export job (PRD 12)new

9Permissions

Codeownerbranch_manageraccountantacademic_admin
settings.tenantyesnolegal infono
settings.branchesyesown branch profilenono
settings.usersyesbranch (invite non-owner roles)nono
settings.rolesyesnonono
settings.catalogs (sources, channels, tags, levels, rooms, job titles, courses)yesroomsexpense categories, fee planslevels, rooms, courses
settings.financeyesnoyesno
settings.notifications, settings.templatesyesnonono
settings.integrationsyesnosepay, einvoiceno
settings.audityesbranchfinance actionsno
settings.data (imports, exports, anonymise)yesimportsimports (finance)imports (students, classes)

10Analytics events and edge cases

EventProperties
onboarding_checklist_item_donekey, days_since_signup
user_invited / invitation_acceptedrole, branches
role_created / role_permissions_changedcloned_from, changes
branch_createdcount_after
integration_connected / integration_failedtype
audit_log_viewed / exportedfilters

Trường Số (truongso.net) · FLYER internal