8.2 KiB
Task L.1: CRM Multi-Role User Assignment & Scope Management
Objective
Extend Task L authorization foundation so one user can hold multiple CRM role profiles within the same organization.
Business decision:
1 User = Many CRM Roles
Examples:
Sales Manager + Sales
CRM Admin + Department Manager
Sales Support + Marketing
This task adds role assignment persistence, assignment UI, and updates effective CRM access resolution.
Background
Task L introduced:
crmRoleProfiles
memberships.branchScopeIds
memberships.productTypeScopeIds
resolved CRM access helper
CRM Settings > Roles
Current limitation:
membership.businessRole = single role
This is not enough for real business operation.
Task L.1 moves CRM role assignment into a dedicated model while keeping membership as organization access.
Target Model
Membership remains organization-level access:
memberships
- userId
- organizationId
- role
- permissions
CRM role assignments become CRM-specific authorization rows:
crm_user_role_assignments
- id
- organizationId
- userId
- roleProfileId
- branchScopeMode
- branchScopeIds
- productTypeScopeMode
- productTypeScopeIds
- isPrimary
- isActive
- assignedBy
- assignedAt
- createdAt
- updatedAt
- deletedAt
Scope L1.1 Schema
Add table:
crm_user_role_assignments
Fields:
id
organizationId
userId
roleProfileId
branchScopeMode
branchScopeIds
productTypeScopeMode
productTypeScopeIds
isPrimary
isActive
assignedBy
assignedAt
createdAt
updatedAt
deletedAt
Recommended scope modes:
all
selected
none
inherit
Unique constraint:
organizationId + userId + roleProfileId
Rules:
- one user can have many role profiles
- same role profile cannot be duplicated for the same user/org
- only one primary CRM role per user/org
- soft delete supported
- old
membership.businessRoleremains temporarily for compatibility
Scope L1.2 Migration / Backfill
Backfill existing users.
For each membership:
membership.businessRole
create one CRM role assignment matching the role profile.
Rules:
- if businessRole has a matching
crmRoleProfiles.code, create assignment - if no matching role profile exists, skip and log warning
- set
isPrimary = true - branch/product scope from membership fields if available
- idempotent migration
- no duplicate assignments
Scope L1.3 Effective CRM Access Resolver
Update resolved access helper.
Old:
membership.businessRole
+
membership.permissions
+
roleProfile.permissions
New:
membership
+
all active crm_user_role_assignments
+
all assigned crmRoleProfiles
+
direct membership.permissions
Permission behavior:
effectivePermissions = union(all roleProfile.permissions, membership.permissions)
Ownership behavior:
Use most permissive access among active role profiles:
own
team
branch
organization
all
Branch scope behavior:
if any role has all -> all
else union selected branchScopeIds
else none
Product type scope behavior:
if any role has all -> all
else union selected productTypeScopeIds
else none
Approval authority:
effectiveApprovalAuthority = union(all roleProfile.approvalAuthority)
Primary role:
used for display only
not permission limit
Scope L1.4 User Assignment UI
Add page or tab:
CRM Settings
└─ User Role Assignments
or add tab inside existing user detail:
Users
└─ CRM Roles
Recommended UI:
User
Assigned CRM Roles
Branch Scope
Product Type Scope
Primary Role
Status
Actions
Actions:
Assign Role
Edit Scope
Set Primary
Deactivate Assignment
Reactivate Assignment
Remove Assignment
Scope L1.5 Assignment Form
Fields:
User
Role Profile
Branch Scope Mode
Branches
Product Type Scope Mode
Product Types
Primary
Active
Behavior:
- role profile dropdown from active
crmRoleProfiles - branch options from
crm_branch - product type options from
crm_product_type - if scope mode = all, disable selected list
- if scope mode = selected, require at least one selected option
- if primary = true, unset other primary role assignments for same user/org
Scope L1.6 API Routes
Add:
GET /api/crm/settings/user-role-assignments
POST /api/crm/settings/user-role-assignments
PATCH /api/crm/settings/user-role-assignments/[id]
DELETE /api/crm/settings/user-role-assignments/[id]
Optional:
GET /api/crm/settings/users/[userId]/role-assignments
All APIs must:
requireOrganizationAccess
requirePermission("crm.role.assignment.manage")
filter by organizationId
audit mutations
return user-safe errors
Scope L1.7 Permissions
Add permissions:
crm.role.assignment.read
crm.role.assignment.create
crm.role.assignment.update
crm.role.assignment.delete
crm.role.assignment.manage
Grant default to:
system_admin
crm_admin
Optional read to:
sales_manager
department_manager
top_manager
Scope L1.8 Audit
Audit entity type:
crm_user_role_assignment
Actions:
create
update
delete
activate
deactivate
set_primary
scope_update
Audit payload should include:
userId
roleProfileId
branchScopeMode
branchScopeIds
productTypeScopeMode
productTypeScopeIds
isPrimary
isActive
Scope L1.9 Compatibility Layer
Keep membership.businessRole temporarily.
Rules:
- do not remove column yet
- do not rely on it when role assignments exist
- fallback to
membership.businessRoleonly when user has no active CRM role assignment - document deprecation
Add technical debt:
membership.businessRole is deprecated for CRM authorization after Task L.1.
Scope L1.10 Server-Side Enforcement Migration
Update all CRM modules to use resolved multi-role access:
Leads
Enquiries
Customers
Contacts
Quotations
Approvals
Dashboard
Settings
Reports if present
Priority:
- Leads / Enquiries
- Quotations
- Dashboard
- Approval
- Customers / Contacts
- Settings
Rules:
- no UI-only enforcement
- all list/detail/action routes must check resolved CRM access
- pricing visibility must remain permission-based
Scope L1.11 Verification Matrix
Create verification cases:
Marketing + Sales Support
Expected:
can view leads
can create support quotation if permission exists
cannot approve
cannot view revenue unless granted
Sales + Sales Manager
Expected:
can see own sales records
can see team records if manager role scope allows
can create quotation
can approve only if role profile has authority
CRM Admin + Department Manager
Expected:
can manage CRM settings
can approve department-level workflow
can view configured analytics
Sales with Crane + Bangkok Scope
Expected:
can see Crane Bangkok records
cannot see Solar Rayong records
Scope L1.12 Documentation / ADR
Create ADR:
docs/adr/0014-crm-multi-role-user-assignment.md
Document:
1 user = many CRM roles
membership is organization access
crm_user_role_assignments is CRM authorization
permission union rule
scope union rule
primary role display-only rule
businessRole deprecation
Update:
docs/implementation/technical-debt.md
Explicit Non-Scope
Do NOT implement:
Keycloak group sync
External identity role sync
Team hierarchy module
Advanced ABAC rules
Approval delegation
Temporary role expiration
Role request workflow
Output
After completion, summarize:
- Files Added
- Files Modified
- Schema Added
- Migration / Backfill
- Resolver Changes
- UI Added
- API Added
- Permissions Added
- Audit Added
- Verification Result
- Remaining Risks
Definition of Done
Task L.1 is complete when:
- user can have multiple CRM role assignments
- each assignment can have branch/product scope
- one primary role can be selected
- effective permissions are unioned from all active roles
- effective branch/product scope is resolved correctly
- membership.businessRole is no longer primary CRM authorization source
- role assignment UI works
- role assignment API works
- all CRM critical routes use multi-role resolver
- audit logs are created for assignment changes
- ADR 0014 exists