Files
alla-allaos-fullstack/plans/task-l.1.md
phaichayon 771ebfc308 task-l.1
2026-06-22 10:59:31 +07:00

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.businessRole remains 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.businessRole only 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:

  1. Leads / Enquiries
  2. Quotations
  3. Dashboard
  4. Approval
  5. Customers / Contacts
  6. 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:

  1. Files Added
  2. Files Modified
  3. Schema Added
  4. Migration / Backfill
  5. Resolver Changes
  6. UI Added
  7. API Added
  8. Permissions Added
  9. Audit Added
  10. Verification Result
  11. 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