Client PortalMemberships
CPD Configuration
Configure CPD categories, requirements, and point rules for your organization
Set up Continuing Professional Development (CPD) tracking for your members with customizable categories, point requirements, and approval workflows.
Spreadsheet intent cross-check
The supplied spreadsheet indicates that:
- Courses and Events both support selecting CPD Category (
cpdCategories). - Membership plans may specify required CPD points (spreadsheet:
MembershipPlan.requiredCpdPoints).
Data Model Cross‑Reference (Entities)
- CPD categories:
CPD Category - Member CPD records / awarded points:
CPD Record - Plan-level CPD requirement:
Membership Plan
Capabilities
| Action | ROLE_CLIENT_ADMIN | ROLE_CLIENT_USER |
|---|---|---|
| View CPD settings | ✅ | ✅ |
| Create categories | ✅ | ❌ |
| Edit categories | ✅ | ❌ |
| Set requirements | ✅ | ❌ |
| Approve member CPD | ✅ | ✅ |
Features
CPD Categories
Define categories for different types of professional development:
- Create Category
Add a new CPD category with name and description.
Examples:
- Technical Skills
- Professional Ethics
- Leadership Development
- Industry Knowledge
- Set Requirements
Configure required points per calendar year for each category.
- Points must be greater than 0
- Points must be divisible by 0.5
- Set if category is required for certification
- Configure Display
Set display order and whether category is active.
Acceptance Criteria
Frontend
- CPD categories list with CRUD operations
- Member CPD summary view
- Drag-and-drop category reordering
- Requirements configuration per category
- Bulk approve/reject functionality
Backend / API
- Backend behavior supports this feature as documented.
Permissions
- Access is restricted per the Capabilities matrix on this page (or equivalent role rules).
Business Rules
- Category names must be unique per organization
- At least one category must be marked as required for certification
- Cannot delete category with existing member records
- Rejected submissions can be resubmitted by member
Error Handling
- Error states return clear messages and appropriate HTTP status codes.
Point Assignment Rules
Configure how points are automatically assigned:
Events
- Set default CPD points per event
- Override points for specific events
- Points awarded on attendance confirmation
Courses
- Set points per course completion
- Require minimum score for point award
- Points awarded on course completion
Manual Entry
- Allow member self-reporting
- Require evidence upload
- Set approval workflow
Acceptance Criteria
Frontend
- Points allocation rules interface
- Validation for point values (0.5 increments)
Backend / API
- Backend behavior supports this feature as documented.
Permissions
- Access is restricted per the Capabilities matrix on this page (or equivalent role rules).
Business Rules
- Required points must be > 0 and divisible by 0.5
- Points are assigned to the calendar year of the activity date
Error Handling
- Error states return clear messages and appropriate HTTP status codes.
Approval Workflow
For manually submitted CPD:
- Member submits CPD claim with evidence
- Claim appears in approval queue
- Admin reviews submission
- Approve or reject with reason
- Member notified of decision
- Points added to member's record (if approved)
Acceptance Criteria
Frontend
- Approval queue with filtering
Backend / API
- Backend behavior supports this feature as documented.
Permissions
- Access is restricted per the Capabilities matrix on this page (or equivalent role rules).
Business Rules
- All business rules for this feature are enforced.
Error Handling
- Error states return clear messages and appropriate HTTP status codes.
Export CPD Records
Client admins often need to export CPD data for annual audits/compliance reporting.
Export capabilities:
- Export member CPD summary (per member totals by year and category)
- Export raw CPD records (event/course/manual) with approvals and evidence links
- Filter by date range, category, member, and status
Acceptance Criteria
Frontend
- CPD export action exists from the CPD admin area.
- Export can be filtered (year/date range, category, member, status) before generating.
- Export formats include CSV (minimum) and optionally Excel.
Backend / API
- Exports are generated server-side and returned as a downloadable file.
Permissions
- Only
ROLE_CLIENT_ADMIN(and permittedROLE_CLIENT_USERroles if enabled) can export CPD data.
Business Rules
- Export respects tenant isolation and only includes the client’s members.
Error Handling
- Large exports either stream or generate asynchronously with progress/error feedback.
Implementation Contracts
Backend (API)
GET /api/cpd/categories # List all categories
POST /api/cpd/categories # Create category
PUT /api/cpd/categories/{id} # Update category
DELETE /api/cpd/categories/{id} # Delete category
PUT /api/cpd/categories/reorder # Reorder categories
GET /api/cpd/settings # Get CPD settings
PUT /api/cpd/settings # Update settings
GET /api/cpd/approvals # Pending approvals
POST /api/cpd/approvals/{id}/approve # Approve submission
POST /api/cpd/approvals/{id}/reject # Reject submission
POST /api/cpd/approvals/bulk # Bulk approve/reject
GET /api/cpd/members/{id}/summary # Member CPD summary
GET /api/cpd/members/{id}/records # Member CPD records
POST /api/cpd/export/summary # Export CPD summaries (CSV/Excel)
POST /api/cpd/export/records # Export CPD records (CSV/Excel)
Data Model
interface CpdCategory {
id: string;
name: string;
description: string;
requiredPointsPerYear: number;
requiredForCertification: boolean;
displayOrder: number;
active: boolean;
createdAt: string;
updatedAt: string;
}
interface CpdApproval {
id: string;
memberId: string;
memberName: string;
categoryId: string;
categoryName: string;
points: number;
activityDate: string;
description: string;
evidenceUrl?: string;
status: 'pending' | 'approved' | 'rejected';
reviewedBy?: string;
reviewedAt?: string;
rejectionReason?: string;
submittedAt: string;
}
Validation Rules
// Category validation
{
name: z.string().min(2).max(100),
requiredPointsPerYear: z.number()
.positive()
.multipleOf(0.5),
requiredForCertification: z.boolean()
}
// At least one category must be required
const hasRequiredCategory = categories.some(c => c.requiredForCertification);
Error Handling
| Error | HTTP Status | Message |
|---|---|---|
| Duplicate name | 409 | "A category with this name already exists" |
| Invalid points | 400 | "Points must be a positive number divisible by 0.5" |
| Has records | 400 | "Cannot delete category with existing records" |
| No required category | 400 | "At least one category must be required for certification" |