seubert-claims/dev/prd-input-policy-renewal-system.md

622 lines
22 KiB
Markdown
Raw Permalink Normal View History

# OnDeck - PRD Input Document
## Project Overview
**Project Name:** OnDeck - Policy Renewal Workflow Management System
**Client Type:** Insurance Brokerage
**Purpose:** Streamline policy renewal workflows by centralizing client and policy data from AMS360, enabling custom client classifications, personnel assignments, and department-specific task management tied to policy renewal dates.
---
## 1. Problem Statement
Insurance brokerages managing large policy portfolios face challenges coordinating renewal activities across departments. Currently:
- Client and policy data lives in AMS360 with no custom workflow layer
- No systematic way to assign internal designations or classifications to clients
- Renewal tasks are tracked manually or in disconnected systems
- No visibility into workload distribution or deadline compliance across teams
- Personnel assignments to clients aren't centrally managed
**Goal:** Create OnDeck - a unified web application that syncs AMS360 data, adds custom metadata layers, and provides renewal-based task management with dashboards for workload visibility.
---
## 2. User Personas & Roles
### 2.1 Role Definitions
| Role | Description | Key Permissions |
|------|-------------|-----------------|
| **Admin** | System administrators, IT staff | Full access: user management, sync configuration, all data, system settings |
| **Manager** | Department leads, supervisors | View all department data, assign tasks, manage personnel assignments, view all dashboards |
| **Account Executive** | Handles client relationships, renewals | View assigned clients, manage own tasks, update client info, view personal dashboard |
| **Claims** | Claims department staff | View assigned clients, manage claims-related tasks, limited client edit |
### 2.2 Authorization Model
**Dual-layer approach:**
1. **Entra ID Groups → Base Role Mapping**
- Entra Group membership determines initial role assignment
- Example: `SG-OnDeck-Admins` → Admin role
- MFA enforced via Entra Conditional Access policies
2. **In-App Granular Permissions**
- Fine-grained permissions within roles
- Client-level access assignments (primary/additional personnel)
- Department-based data filtering
- Custom permission overrides when needed
---
## 3. Functional Requirements
### 3.1 Authentication & Authorization
#### 3.1.1 Entra ID Integration
- **SSO via Entra ID** using MSAL.js or NextAuth.js Azure AD provider
- **Token claims must include:** `groups`, `preferred_username`, `name`, `oid`
- **Group-to-role mapping** configurable by Admin
- **Leverage existing MFA** - no additional MFA implementation needed
- **Session management:** Configurable timeout, refresh token handling
#### 3.1.2 In-App Permission System
- Permission sets per role (CRUD matrix)
- Ability to assign individuals to specific clients (overrides department-level access)
- Audit log of permission changes
- Admin UI for managing Entra Group → Role mappings
### 3.2 AMS360 Data Sync Engine
#### 3.2.1 Sync Configuration
- **Frequency:** Daily automated sync (configurable time window)
- **Method:** Direct SQL connection to AMS360 database
- **Direction:** One-way (AMS360 → Local Postgres)
- **Entities to sync:**
- Clients (customer master data)
- Policies (policy details, renewal dates, coverage info)
#### 3.2.2 Sync Behavior
- **Initial load:** Full sync of all active clients and policies
- **Incremental sync:** Based on `ModifiedDate` or equivalent timestamp fields
- **Conflict handling:** AMS360 is source of truth for synced fields
- **Sync logging:** Record sync runs, row counts, errors
- **Manual trigger:** Admin can initiate on-demand sync
#### 3.2.3 Data Mapping (AMS360 → Local)
```
AMS360 Client → local.clients
- CustomerID → ams_customer_id (unique key for sync)
- CustomerName → name
- Address fields → address_*
- Phone/Email → contact_*
- Producer → producer_code
- (other relevant fields TBD based on AMS360 schema)
AMS360 Policy → local.policies
- PolicyID → ams_policy_id (unique key for sync)
- CustomerID → client_id (FK to local.clients)
- PolicyNumber → policy_number
- EffectiveDate → effective_date
- ExpirationDate → expiration_date (THIS IS THE RENEWAL DATE)
- PolicyType → policy_type
- Carrier → carrier_name
- Premium → premium_amount
- Status → status
```
### 3.3 Client Management (Custom Data Layer)
#### 3.3.1 Custom Client Fields (Local Only)
These fields are NOT synced from AMS360 - they are managed entirely in the app:
| Field | Type | Description |
|-------|------|-------------|
| `shape` | FK to shapes table | Primary designation (user-defined) |
| `shape2` | FK to shapes table | Secondary designation (user-defined) |
| `primary_personnel_id` | FK to users | Primary account owner (Entra user) |
| `notes` | Text | Internal notes |
| `custom_fields` | JSONB | Extensible custom field storage |
#### 3.3.2 Shape/Designation Management
- Admin-configurable list of Shape values
- Each shape has: `id`, `name`, `description`, `color` (for UI), `active`
- Shapes can be deactivated but not deleted (preserve historical data)
- Clients can have one Shape and one Shape2 (both optional)
#### 3.3.3 Personnel Assignment
- **Primary Personnel:** One Entra user designated as primary owner
- **Additional Personnel:** Zero or more Entra users with access
- Personnel dropdown populated from Entra ID users (synced or on-demand lookup)
- Assignment changes logged for audit
### 3.4 Task/Action Management
#### 3.4.1 Task Definition
Tasks represent actions to be taken relative to policy renewal dates.
**Task Properties:**
| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `title` | String | Task name |
| `description` | Text | Detailed instructions |
| `department` | Enum | Personal Lines, Commercial Lines, Claims, etc. |
| `timing` | Enum | `PRE_RENEWAL` or `POST_RENEWAL` |
| `days_offset` | Integer | Days before (negative) or after (positive) renewal |
| `due_date` | Date | Calculated: policy.expiration_date + days_offset |
| `status` | Enum | `NOT_STARTED`, `IN_PROGRESS`, `COMPLETED`, `BLOCKED`, `CANCELLED` |
| `priority` | Enum | `LOW`, `MEDIUM`, `HIGH`, `URGENT` |
| `client_id` | FK | Associated client |
| `policy_id` | FK | Associated policy (optional - can be client-level) |
| `assigned_users` | Many-to-many | One or more Entra users |
| `created_by` | FK | User who created |
| `created_at` | Timestamp | |
| `updated_at` | Timestamp | |
| `completed_at` | Timestamp | When status changed to COMPLETED |
| `completed_by` | FK | User who completed |
#### 3.4.2 Task Templates
- **Department-based templates:** Predefined task sets per department
- Templates define: title, description, timing, days_offset, default priority
- When a policy approaches renewal, tasks can be auto-generated from templates
- Admin can manage templates per department
**Example Template:**
```
Department: Personal Lines
Template Name: "Standard Auto Renewal"
Tasks:
1. Review coverage limits (PRE_RENEWAL, -45 days)
2. Contact client for updates (PRE_RENEWAL, -30 days)
3. Submit renewal to carrier (PRE_RENEWAL, -21 days)
4. Send renewal docs to client (POST_RENEWAL, +3 days)
```
#### 3.4.3 Task Assignment
- Tasks can be assigned to one or more users
- Assignment can be manual or auto-assigned to client's primary personnel
- Bulk task assignment for efficiency
- Reassignment with audit trail
#### 3.4.4 Task Lifecycle
```
NOT_STARTED → IN_PROGRESS → COMPLETED
BLOCKED → IN_PROGRESS → COMPLETED
Any status → CANCELLED (with reason)
```
### 3.5 Dashboards & Reporting
#### 3.5.1 Personal Dashboard (Account Executive, Claims)
- **My Tasks:** Filtered to assigned tasks
- Overdue (red highlight)
- Due today
- Due this week
- Upcoming (next 30 days)
- **My Clients:** Quick access to assigned clients
- **Quick Stats:**
- Tasks completed this week/month
- Overdue task count
- Upcoming renewals count
#### 3.5.2 Manager Dashboard
- **Team Overview:**
- Workload distribution (tasks per team member)
- Completion rates by user
- Overdue tasks by user
- **Department Metrics:**
- Tasks by status (pie/bar chart)
- Renewal timeline (upcoming 90 days)
- SLA compliance (% tasks completed on time)
- **Drill-down capability:** Click to see individual user details
#### 3.5.3 Admin Dashboard
- All Manager dashboard features, plus:
- **System Health:**
- Last sync time, status, row counts
- Error logs
- User activity summary
- **Cross-department views**
#### 3.5.4 KPIs to Track
| KPI | Calculation | Visualization |
|-----|-------------|---------------|
| Tasks Completed | Count by period | Line chart (trend) |
| Tasks Overdue | Count where due_date < today AND status not COMPLETED | Number + list |
| Upcoming Renewals | Policies expiring in next 30/60/90 days | Count + calendar view |
| Workload per Person | Active tasks assigned per user | Bar chart |
| On-Time Completion Rate | (Completed on/before due_date) / Total completed | Percentage |
| Avg Days to Complete | Mean(completed_at - created_at) | Number |
---
## 4. Data Architecture
### 4.1 Database Schema (PostgreSQL)
```sql
-- Entra user cache (synced from Entra or populated on first login)
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
entra_oid VARCHAR(36) UNIQUE NOT NULL, -- Entra Object ID
email VARCHAR(255) UNIQUE NOT NULL,
display_name VARCHAR(255),
department VARCHAR(100),
is_active BOOLEAN DEFAULT true,
last_login_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Role definitions
CREATE TABLE roles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(50) UNIQUE NOT NULL, -- Admin, Manager, Account Executive, Claims
description TEXT,
permissions JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMP DEFAULT NOW()
);
-- Entra Group to Role mapping
CREATE TABLE entra_group_role_mappings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
entra_group_id VARCHAR(36) NOT NULL, -- Entra Group Object ID
entra_group_name VARCHAR(255),
role_id UUID REFERENCES roles(id),
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(entra_group_id)
);
-- User role assignments (can override group-based roles)
CREATE TABLE user_roles (
user_id UUID REFERENCES users(id),
role_id UUID REFERENCES roles(id),
assigned_by UUID REFERENCES users(id),
assigned_at TIMESTAMP DEFAULT NOW(),
PRIMARY KEY (user_id, role_id)
);
-- Shape/Designation definitions
CREATE TABLE shapes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100) NOT NULL,
description TEXT,
color VARCHAR(7), -- Hex color for UI
is_active BOOLEAN DEFAULT true,
display_order INTEGER,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Clients (synced from AMS360 + custom fields)
CREATE TABLE clients (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- AMS360 synced fields
ams_customer_id VARCHAR(50) UNIQUE NOT NULL,
name VARCHAR(255) NOT NULL,
address_line1 VARCHAR(255),
address_line2 VARCHAR(255),
city VARCHAR(100),
state VARCHAR(50),
zip_code VARCHAR(20),
phone VARCHAR(50),
email VARCHAR(255),
producer_code VARCHAR(50),
ams_created_at TIMESTAMP,
ams_modified_at TIMESTAMP,
-- Custom fields (local only)
shape_id UUID REFERENCES shapes(id),
shape2_id UUID REFERENCES shapes(id),
primary_personnel_id UUID REFERENCES users(id),
notes TEXT,
custom_fields JSONB DEFAULT '{}',
-- Metadata
last_synced_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Additional personnel assignments for clients
CREATE TABLE client_personnel (
client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
assigned_at TIMESTAMP DEFAULT NOW(),
assigned_by UUID REFERENCES users(id),
PRIMARY KEY (client_id, user_id)
);
-- Policies (synced from AMS360)
CREATE TABLE policies (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- AMS360 synced fields
ams_policy_id VARCHAR(50) UNIQUE NOT NULL,
client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
policy_number VARCHAR(100),
policy_type VARCHAR(100),
effective_date DATE,
expiration_date DATE NOT NULL, -- This is the RENEWAL DATE
carrier_name VARCHAR(255),
premium_amount DECIMAL(12, 2),
status VARCHAR(50),
ams_created_at TIMESTAMP,
ams_modified_at TIMESTAMP,
-- Metadata
last_synced_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Index for renewal date queries
CREATE INDEX idx_policies_expiration ON policies(expiration_date);
CREATE INDEX idx_policies_client ON policies(client_id);
-- Department enum
CREATE TYPE department_type AS ENUM (
'PERSONAL_LINES',
'COMMERCIAL_LINES',
'CLAIMS',
'BENEFITS',
'OTHER'
);
-- Task timing enum
CREATE TYPE task_timing AS ENUM ('PRE_RENEWAL', 'POST_RENEWAL');
-- Task status enum
CREATE TYPE task_status AS ENUM (
'NOT_STARTED',
'IN_PROGRESS',
'COMPLETED',
'BLOCKED',
'CANCELLED'
);
-- Task priority enum
CREATE TYPE task_priority AS ENUM ('LOW', 'MEDIUM', 'HIGH', 'URGENT');
-- Task templates (department-based)
CREATE TABLE task_templates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
description TEXT,
department department_type NOT NULL,
timing task_timing NOT NULL,
days_offset INTEGER NOT NULL, -- Negative = before renewal, Positive = after
default_priority task_priority DEFAULT 'MEDIUM',
is_active BOOLEAN DEFAULT true,
display_order INTEGER,
created_by UUID REFERENCES users(id),
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Tasks (actual task instances)
CREATE TABLE tasks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title VARCHAR(255) NOT NULL,
description TEXT,
department department_type NOT NULL,
timing task_timing NOT NULL,
days_offset INTEGER NOT NULL,
due_date DATE NOT NULL,
status task_status DEFAULT 'NOT_STARTED',
priority task_priority DEFAULT 'MEDIUM',
client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
policy_id UUID REFERENCES policies(id) ON DELETE SET NULL,
template_id UUID REFERENCES task_templates(id), -- If generated from template
created_by UUID REFERENCES users(id),
completed_at TIMESTAMP,
completed_by UUID REFERENCES users(id),
cancelled_reason TEXT,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Task assignments (many-to-many)
CREATE TABLE task_assignments (
task_id UUID REFERENCES tasks(id) ON DELETE CASCADE,
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
assigned_at TIMESTAMP DEFAULT NOW(),
assigned_by UUID REFERENCES users(id),
PRIMARY KEY (task_id, user_id)
);
-- Indexes for task queries
CREATE INDEX idx_tasks_due_date ON tasks(due_date);
CREATE INDEX idx_tasks_status ON tasks(status);
CREATE INDEX idx_tasks_client ON tasks(client_id);
CREATE INDEX idx_tasks_department ON tasks(department);
-- Sync log
CREATE TABLE sync_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
sync_type VARCHAR(50) NOT NULL, -- 'clients', 'policies', 'full'
started_at TIMESTAMP NOT NULL,
completed_at TIMESTAMP,
status VARCHAR(50) NOT NULL, -- 'running', 'completed', 'failed'
rows_processed INTEGER DEFAULT 0,
rows_inserted INTEGER DEFAULT 0,
rows_updated INTEGER DEFAULT 0,
error_message TEXT,
triggered_by UUID REFERENCES users(id) -- NULL if scheduled
);
-- Audit log
CREATE TABLE audit_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id),
action VARCHAR(100) NOT NULL,
entity_type VARCHAR(50) NOT NULL,
entity_id UUID,
old_values JSONB,
new_values JSONB,
ip_address INET,
user_agent TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_audit_logs_user ON audit_logs(user_id);
CREATE INDEX idx_audit_logs_entity ON audit_logs(entity_type, entity_id);
CREATE INDEX idx_audit_logs_created ON audit_logs(created_at);
```
### 4.2 AMS360 Connection Requirements
- **Connection type:** SQL Server (typically)
- **Credentials:** Read-only service account recommended
- **Network:** May require VPN or firewall rules
- **Tables to query:** (Exact names depend on AMS360 version)
- Customer table
- Policy table
- (Reference AMS360 data dictionary for exact schema)
---
## 5. Technical Architecture
### 5.1 Recommended Stack
| Layer | Technology | Rationale |
|-------|------------|-----------|
| **Frontend** | React + TypeScript | Familiarity, strong ecosystem |
| **UI Framework** | Tailwind CSS + shadcn/ui | Modern, accessible components |
| **State Management** | TanStack Query | Server state caching, mutations |
| **Backend** | Node.js + Express or Next.js API Routes | JavaScript consistency |
| **ORM** | Prisma or Drizzle | Type-safe database access |
| **Database** | PostgreSQL | Robust, JSONB support |
| **Auth** | MSAL.js + NextAuth.js (Azure AD provider) | Native Entra integration |
| **Sync Engine** | Node.js service + node-mssql | AMS360 SQL connection |
| **Scheduler** | node-cron or external (systemd timer) | Daily sync trigger |
| **Hosting** | Docker on Linux server | Your standard deployment |
### 5.2 Authentication Flow
```
1. User navigates to app
2. App redirects to Entra ID login (MSAL.js)
3. User authenticates + MFA (per Entra CA policies)
4. Entra returns ID token + access token
5. Token includes group claims
6. App backend:
a. Validates token
b. Looks up entra_oid in users table (create if first login)
c. Resolves roles from group claims + user_roles table
d. Creates session
7. Frontend receives user context with permissions
```
### 5.3 Sync Engine Architecture
```
┌─────────────────┐ ┌──────────────────┐
│ AMS360 DB │ ──SQL── │ Sync Service │
│ (SQL Server) │ │ (Node.js) │
└─────────────────┘ └────────┬─────────┘
┌────────▼─────────┐
│ PostgreSQL │
│ (Local DB) │
└──────────────────┘
Sync Process:
1. Cron triggers sync at configured time
2. Sync service connects to AMS360 (read-only)
3. Queries records modified since last sync
4. Upserts into local Postgres (match on ams_*_id)
5. Logs sync results
6. Closes connections
```
---
## 6. Non-Functional Requirements
### 6.1 Security
- All traffic over HTTPS
- Entra ID MFA enforced via Conditional Access
- Database credentials in environment variables / secrets manager
- Read-only AMS360 connection
- RBAC enforced at API layer
- Audit logging for sensitive operations
- Session timeout configurable (default: 8 hours)
### 6.2 Performance
- Dashboard queries < 2 seconds
- Sync should complete within 1 hour for typical data volumes
- Pagination on all list endpoints (default 50, max 100)
- Database indexes on frequently queried fields
### 6.3 Availability
- Daily sync window during off-hours (e.g., 2 AM)
- Sync failures should not affect app availability
- Sync retry logic (3 attempts with backoff)
### 6.4 Data Integrity
- Foreign key constraints enforced
- Soft deletes where appropriate
- Sync never deletes local records (mark inactive if removed from AMS360)
---
## 7. MVP Scope vs. Future Phases
### Phase 1 - MVP
- [ ] Entra ID authentication with group-based roles
- [ ] AMS360 daily sync (Clients + Policies)
- [ ] Client list with custom fields (Shape, Shape2, Primary Personnel)
- [ ] Basic task management (CRUD, assignment, status)
- [ ] Personal dashboard (my tasks, overdue, upcoming)
- [ ] Admin: Shape management, sync trigger
### Phase 2
- [ ] Task templates per department
- [ ] Auto-generation of tasks from templates based on renewal dates
- [ ] Manager dashboard with team metrics
- [ ] Bulk operations (assign, update status)
- [ ] Email notifications for overdue tasks
### Phase 3
- [ ] Advanced reporting / export
- [ ] Calendar view for renewals
- [ ] Additional personnel assignments
- [ ] Custom fields beyond Shape/Shape2
- [ ] API for external integrations
---
## 8. Open Questions / Decisions Needed
1. **AMS360 access:** Who provides credentials? Any data restrictions?
2. **Departments:** Are the listed departments (Personal Lines, Commercial, Claims, Benefits) complete?
3. **Task auto-generation:** Should tasks auto-generate when policy enters renewal window, or manual trigger?
4. **Notifications:** Email? In-app only? Teams integration?
5. **Mobile:** Responsive web sufficient, or native app needed?
6. **Hosting:** On-prem server or cloud (Azure)?
---
## 9. Glossary
| Term | Definition |
|------|------------|
| **AMS360** | Agency Management System by Vertafore - source of client/policy data |
| **Shape/Shape2** | Custom client designations defined by the brokerage |
| **Renewal Date** | Policy expiration date - triggers workflow tasks |
| **Pre-Renewal Task** | Action to be completed before policy expires |
| **Post-Renewal Task** | Action to be completed after policy renews |
| **Primary Personnel** | Main user responsible for a client |
| **Entra ID** | Microsoft's identity platform (formerly Azure AD) |
---
*Document Version: 1.0*
*Created: January 2025*
*Last Updated: January 2025*