seubert-claims/tasks/mvp-ondeck.md
lorentz b036a93da2 Initial commit: OnDeck project
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 13:20:52 +00:00

16 KiB

OnDeck MVP — Policy Renewal Workflow System

Minimum Viable Product scope for the OnDeck policy renewal management platform. This document defines the feature set required for initial production deployment.


Table of Contents

  1. User Authentication
  2. Logging
  3. AMS360 Data Sync
  4. Task Management & Renewal Workflow
  5. Renewal Grouping & Policy Exclusion

1. User Authentication

1.1 Authentication Providers

Provider Purpose Status
Local credentials Development and testing (3 built-in accounts: admin, manager, ae) Existing
Microsoft Entra ID (Azure AD) Production SSO Existing

1.2 Account Linking

When a user signs in via Microsoft Entra ID and the email matches an existing local account, the accounts are linked automatically by populating the entraOid field on the existing user record.

  • First lookup: find user by email where entraOid is null (pre-created local account)
  • If found: update the record with the Entra Object ID to link the accounts
  • Fallback: standard upsert by entraOid for net-new users

Reference: src/lib/auth.ts:110-144

1.3 Role-Based Access Control

Four roles with granular permissions:

Role Key Permissions
Admin Full access — users, clients, tasks, designations, templates, sync, reports, audit
Manager Read users; read/write clients, tasks, templates; assign tasks; view reports
Account Executive Read/write clients and tasks; read designations
Claims Read clients; read/write tasks

Permission categories: User Management, Client Management, Task Management, Designation Management, Template Management, Sync Operations, Reports, Audit.

Role resolution combines two sources:

  • Entra Group mappingsEntraGroupRoleMapping table maps Azure AD groups to application roles
  • Direct assignmentsUserRole join table for explicit per-user role grants

Reference: src/lib/auth/roles.ts, src/lib/auth/permissions.ts

1.4 Session Strategy

  • JWT-based sessions (strategy: 'jwt')
  • Session callback enriches the token with: id, entraOid, roles[], merged permissions object (union of all assigned role permissions)

2. Logging

2.1 Authentication Logging

Event Details
Sign-in success Provider, user ID, timestamp
Sign-in failure Provider, email attempted, failure reason
Account linking User ID, linked Entra OID

2.2 Action Logging (Audit)

All user-initiated mutations on entities are recorded in the AuditLog model:

Field Description
userId Actor who performed the action
action Operation type (create, update, delete)
entityType Target entity (Client, Policy, Task, etc.)
entityId Target record ID
oldValues JSON snapshot of previous state
newValues JSON snapshot of new state
ipAddress Request origin
userAgent Client identifier
createdAt Timestamp

Reference: AuditLog model in prisma/schema.prisma

2.3 Error Logging

  • API route errors with request context
  • Sync failures with stream identification and row-level detail
  • Unhandled exceptions with stack traces

2.4 Info Logging

  • Sync progress: rows fetched, processed, inserted, updated per stream
  • Scheduled job execution: start, completion, next scheduled run
  • System events: application startup, configuration changes

2.5 Alerts & Notifications

Alerts are delivered through the existing Notification model:

Alert Type Trigger
Overdue tasks Task dueDate has passed with status not in COMPLETED/NA/CANCELLED
Sync failures Any sync stream fails after retry exhaustion
System health Scheduler down, database connectivity issues

User notification preferences are stored in NotificationPreference with per-type toggles for: in-app, email, and Teams delivery channels.

Reference: Notification and NotificationPreference models in prisma/schema.prisma


3. AMS360 Data Sync

3.1 Overview

Sync data from the AFW SQL Server database (AMS360 source of truth) into the local PostgreSQL database. The sync engine connects to AFW via a managed connection pool (max 10 connections, 60s request timeout).

Reference: src/lib/sync/afw-connection.ts

3.2 Department Scope

Item Current State MVP Target
Departments synced 5 departments (Dept1, Dept8, Dept9, Dept10, Dept12) All 13 departments

MVP change: Remove the hardcoded ShortName IN (...) filter in the policy sync query so all AFW departments are included.

Reference: src/lib/sync/afw-queries.ts:221 — current 5-department WHERE clause

3.3 Sync Streams

Three parallel streams execute concurrently via Promise.all():

Stream Source (AFW) Target (Local) Key Mapping
Employees → Users AFW_Employee (Status = 'A') User EmpCode, Name, Email, DefaultGLDeptCode → department
Customers → Clients AFW_Customer (Active = 'Y') Client CustId → amsCustomerId, contact fields, producer code
Policies → Policies AFW_BasicPolInfo + 20 joined lookup tables Policy Complex CTE with company, department, personnel lookups; up to 2 additional reps and 2 additional execs per policy

Reference: src/lib/sync/sync-engine.ts, src/lib/sync/afw-queries.ts, src/lib/sync/mappers.ts

3.4 Sync Modes

Mode Behavior
Incremental (default) Syncs records changed since last successful sync, using ChangedDate filtering where supported
Full Re-syncs all records regardless of last sync timestamp

Signature: runSync(triggeredBy?: string, isIncremental: boolean = true)

3.5 Scheduling & Triggers

Mechanism Details
Scheduled Configurable cron expression stored in SyncConfig table (default: 0 2 * * * — daily at 2 AM)
Manual POST /api/sync with optional date range selection; requires sync.trigger permission

Configuration keys in SyncConfig:

  • sync_enabled — master toggle
  • sync_schedule_cron — cron expression
  • policy_start_date / policy_end_date — date range for policy sync window

Reference: src/lib/sync/scheduler.ts

3.6 Retry Logic

Each sync stream independently retries on failure:

  • Max retries: 3
  • Backoff: Exponential — 2s, 4s, 8s

3.7 Sync Logging

Every sync execution is recorded in the SyncLog model:

Field Description
syncType Which stream or overall sync
startedAt / completedAt Timing
status Success or failure
rowsProcessed Total rows fetched from AFW
rowsInserted New records created
rowsUpdated Existing records updated
errorMessage Failure details (if any)
triggeredBy User ID or "scheduler"

3.8 API Endpoints

Method Path Purpose Permission
POST /api/sync Trigger manual sync sync.trigger
GET /api/sync List recent sync logs sync.trigger
GET /api/sync/status Scheduler status sync.trigger

4. Task Management & Renewal Workflow

4.1 Task Templates

Templates define repeatable task patterns for renewal workflows:

Field Description
name Template display name
description Detailed instructions
department PERSONAL_LINES, COMMERCIAL_LINES, CLAIMS, BENEFITS, OTHER
timing PRE_RENEWAL or POST_RENEWAL
daysOffset Signed integer — number of days before (negative) or after (positive) policy expiration
defaultPriority LOW, MEDIUM, HIGH, or URGENT
designationId Optional — restrict template to clients with a specific designation
displayOrder Presentation ordering
isActive Soft-disable toggle

Designation-based rules: When a template has a designationId, it only generates tasks for clients whose primary or secondary designation matches.

Reference: TaskTemplate model in prisma/schema.prisma

4.2 Task Auto-Generation

Tasks are generated from templates based on policy expiration dates:

  1. Identify policies approaching expiration within the configured window
  2. Match applicable templates by department and (optionally) client designation
  3. Calculate due date: policy.expirationDate + template.daysOffset
  4. Create task records linked to the client, policy, and source template

4.3 Task Statuses

Status Description
NOT_STARTED Default state on creation
IN_PROGRESS Work underway
COMPLETED Finished — captures completedAt timestamp and completedBy user
BLOCKED Cannot proceed — requires intervention
NA Not applicable — requires naReason documentation
CANCELLED Voided — requires cancelledReason documentation

4.4 Priority Levels

Priority Usage
LOW Routine, no urgency
MEDIUM Standard renewal tasks
HIGH Approaching deadline or important client
URGENT Immediate attention required

4.5 Timing & Day Offsets

Tasks are classified as either:

  • PRE_RENEWAL — due before the policy expiration date (negative daysOffset)
  • POST_RENEWAL — due after the policy expiration date (positive daysOffset)

The daysOffset value is configurable per template, allowing precise scheduling of each step in the renewal workflow.

4.6 Task Assignment

  • Tasks support multi-user assignment via the TaskAssignment join table
  • Workload visibility: GET /api/dashboard/workload provides manager-facing metrics on task distribution across team members
  • Dashboard displays per-user statistics: overdue, due today, upcoming

4.7 N/A Marking

When a task is marked as NA, the naReason field is required. This ensures accountability and auditability for skipped tasks. Similarly, CANCELLED status requires cancelledReason.

4.8 API Endpoints

Method Path Purpose Permission
GET /api/tasks List tasks with filtering (status, clientId, assignedToMe, pagination) tasks.read
POST /api/tasks Create task tasks.write

Query parameters: status, clientId, assignedToMe, page, limit (default 50).


5. Renewal Grouping & Policy Exclusion

Status: NEW — not yet implemented. This section defines net-new functionality for the MVP.

5.1 Problem Statement

A single client may have multiple policies with the same expiration date. Currently, there is no mechanism to control how renewal tasks are generated across those policies — whether they share a single task set or need separate ones. Additionally, some policies (being non-renewed, rewritten, or cancelled) should be excluded from task generation entirely.

5.2 Default Behavior: Client-Level Task Sets

When no explicit grouping exists, the system generates one set of renewal tasks per client based on the client's policies and their expiration dates. This preserves backward compatibility with the existing workflow.

5.3 Renewal Groups

A new RenewalGroup entity enables manual control over how policies are grouped for task generation:

RenewalGroup model (new):

Field Type Description
id UUID Primary key
name String User-provided label (e.g., "Main BOP + Auto bundle")
clientId FK → Client Owning client
createdBy FK → User Who created the group
createdAt DateTime Timestamp
updatedAt DateTime Timestamp

RenewalGroupPolicy join table (new):

Field Type Description
renewalGroupId FK → RenewalGroup Group membership
policyId FK → Policy Included policy

5.4 Manual Grouping

Users can create explicit renewal groups by selecting which policies should share a single task set:

  • Select a client
  • Choose one or more policies from that client
  • Name the group
  • Task generation uses the group rather than the full client policy set

Use case: A client has 5 policies but 3 of them are bundled and should be renewed together. Create one group with those 3 policies — they get one shared task set. The remaining 2 policies can be in separate groups or fall through to default behavior.

5.5 Splitting

When a client's policies need separate renewal workflows, users can split them into distinct renewal groups:

  • Each group gets its own independent set of renewal tasks
  • Groups can contain one or many policies
  • Ungrouped policies fall back to default client-level behavior

5.6 Policy Exclusion

Individual policies can be marked as excluded from task generation:

Policy model addition (new field):

Field Type Description
isExcludedFromRenewal Boolean (default: false) Excludes policy from task generation
exclusionReason String (nullable) Required when excluded — documents why

Use cases for exclusion:

  • Policy is being non-renewed
  • Policy is being rewritten under a new policy number
  • Policy is being moved to a different carrier outside the normal renewal flow
  • Client has requested cancellation

Excluded policies are skipped during task auto-generation but remain visible in the client's policy list with a visual indicator.

5.7 Task Generation Logic (Updated)

With renewal grouping and exclusion in place, task auto-generation follows this precedence:

  1. Exclude any policy where isExcludedFromRenewal = true
  2. Grouped policies: For each RenewalGroup, generate one task set using the group's policies
  3. Ungrouped policies: For remaining policies not in any group, generate one task set per client (default behavior)

Appendix

A. Technology Stack

Layer Technology
Frontend React, TypeScript, Next.js 16 (App Router), Tailwind CSS, shadcn/ui
Backend Next.js API Routes, NextAuth.js 4.x (JWT)
Database PostgreSQL via Prisma ORM 7.x
Source DB SQL Server via mssql (AFW/AMS360)
Scheduling node-cron
Deployment Docker

B. Data Model Index

Model Purpose Status
User Application users synced from AFW employees Existing
Client Customers synced from AFW Existing
Policy Insurance policies synced from AFW Existing
Task Renewal workflow tasks Existing
TaskTemplate Reusable task definitions Existing
TaskAssignment Multi-user task assignment Existing
Designation Client shape/classification Existing
AuditLog Action audit trail Existing
Notification User-facing alerts Existing
NotificationPreference Per-user delivery prefs Existing
SyncLog Sync execution history Existing
SyncConfig Sync configuration key-value store Existing
EntraGroupRoleMapping Azure AD group → role mapping Existing
UserRole Direct user → role assignment Existing
RenewalGroup Policy grouping for shared task sets New
RenewalGroupPolicy Group ↔ Policy join table New