622 lines
22 KiB
Markdown
622 lines
22 KiB
Markdown
|
|
# 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*
|